mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-23 14:23:54 +00:00
Compare commits
154 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
204a578aaa | ||
|
|
de28c06af5 | ||
|
|
20a5457d5f | ||
|
|
fa575a0103 | ||
|
|
841ce47ecd | ||
|
|
df495d9c04 | ||
|
|
5f755d4ec9 | ||
|
|
a195d25546 | ||
|
|
177e89a399 | ||
|
|
04fcad21d4 | ||
|
|
13dea58e35 | ||
|
|
38dc6fe73e | ||
|
|
33c9a1dade | ||
|
|
b13677363f | ||
|
|
50e5c6a25c | ||
|
|
3812ed2488 | ||
|
|
1cc8a3980c | ||
|
|
b08c6b9441 | ||
|
|
8de3ee27f4 | ||
|
|
82714d163d | ||
|
|
4fa3fb6486 | ||
|
|
62de2e4d50 | ||
|
|
146dcb5e41 | ||
|
|
4889e458d4 | ||
|
|
8f4d9b37d2 | ||
|
|
0e83ff303c | ||
|
|
3d2bd9a898 | ||
|
|
96f6a38869 | ||
|
|
4ef766c05d | ||
|
|
2c721e5177 | ||
|
|
f58617f733 | ||
|
|
1166c5b526 | ||
|
|
a5cacbe383 | ||
|
|
d9302ff982 | ||
|
|
3751b98e7a | ||
|
|
a66938eee8 | ||
|
|
529d150737 | ||
|
|
395e2d55f7 | ||
|
|
4cb677418f | ||
|
|
ad819b354a | ||
|
|
48404bde98 | ||
|
|
a36e6fc14b | ||
|
|
f3eed5f283 | ||
|
|
53fc9e5b71 | ||
|
|
1479109324 | ||
|
|
f106752b48 | ||
|
|
467d3ab59c | ||
|
|
6076516c24 | ||
|
|
af91fdf21e | ||
|
|
4bb8492f43 | ||
|
|
4428a6e738 | ||
|
|
1f3548eee4 | ||
|
|
64d77656cf | ||
|
|
21f191b889 | ||
|
|
60402414f1 | ||
|
|
6fb9d02e8d | ||
|
|
eaac01b58c | ||
|
|
af724ad58a | ||
|
|
590918af32 | ||
|
|
0b59db8fb0 | ||
|
|
04deadd684 | ||
|
|
dd08aa4d45 | ||
|
|
8b3bda82f7 | ||
|
|
6aafb90acd | ||
|
|
a0e12b84c7 | ||
|
|
e56eb2fa67 | ||
|
|
28e75551ed | ||
|
|
045a4aae2d | ||
|
|
53a9e9284d | ||
|
|
189175c4db | ||
|
|
aa9c96d0b5 | ||
|
|
ceaf6db82c | ||
|
|
e4570e22ae | ||
|
|
c9a28309b2 | ||
|
|
6d50eed4f9 | ||
|
|
e922666d5f | ||
|
|
43223f0759 | ||
|
|
2641630b97 | ||
|
|
f4d5a13934 | ||
|
|
61544a0be6 | ||
|
|
e422d0ba86 | ||
|
|
d0d8d77337 | ||
|
|
3f468e4752 | ||
|
|
3b6ced2a58 | ||
|
|
f29bb26880 | ||
|
|
6ede8601b4 | ||
|
|
8a15d19348 | ||
|
|
e0b4e5f76f | ||
|
|
6c04af02cb | ||
|
|
351049b74f | ||
|
|
30127f8a3e | ||
|
|
6863ac02a1 | ||
|
|
24c30e65fb | ||
|
|
c4dd02761a | ||
|
|
72b7a89e9a | ||
|
|
453976e28f | ||
|
|
914fa61819 | ||
|
|
e1a2edb604 | ||
|
|
d245f3f939 | ||
|
|
433296d718 | ||
|
|
28c3c683a0 | ||
|
|
50ba36eb1e | ||
|
|
c69c809264 | ||
|
|
fac05a3bd1 | ||
|
|
60e34d5b03 | ||
|
|
8b480df72d | ||
|
|
5deef1c098 | ||
|
|
11d4fcd56f | ||
|
|
d78648b73c | ||
|
|
525c228bd7 | ||
|
|
ba6a53c6f5 | ||
|
|
0d1dd62b8a | ||
|
|
5ac7265474 | ||
|
|
08701568a1 | ||
|
|
faedce906a | ||
|
|
f5908741d3 | ||
|
|
5414cc1f86 | ||
|
|
371a989f9b | ||
|
|
8b3af3be93 | ||
|
|
d6e519f1c4 | ||
|
|
90fd0a5055 | ||
|
|
3af41f6515 | ||
|
|
ee39054537 | ||
|
|
b5218d371a | ||
|
|
7f5298a802 | ||
|
|
2ffd56a993 | ||
|
|
e0d7eed9d3 | ||
|
|
b0ec26bca0 | ||
|
|
2116c9ad0e | ||
|
|
d02d6675ab | ||
|
|
1a43c0e540 | ||
|
|
e6b46c6c1b | ||
|
|
387afaa2c2 | ||
|
|
f9fea8b53a | ||
|
|
21c58c0aa2 | ||
|
|
d5a8b54503 | ||
|
|
c390928368 | ||
|
|
c4897008dc | ||
|
|
fe09a85e53 | ||
|
|
1b9ee5c882 | ||
|
|
cc5e3dc6a2 | ||
|
|
8a9470b9b3 | ||
|
|
e0a21b2bd4 | ||
|
|
dfeff25a31 | ||
|
|
9510db0ace | ||
|
|
1576d1ff2b | ||
|
|
764e1bbb79 | ||
|
|
83e718c4cd | ||
|
|
16fd51a213 | ||
|
|
1744b4752c | ||
|
|
7ad7848856 | ||
|
|
6df4e192f9 | ||
|
|
bc3169a1b5 | ||
|
|
b026cf20f4 |
25
.github/workflows/docker.yml
vendored
Normal file
25
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Publish to Registry
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Publish to Github Docker Package Registry
|
||||
uses: elgohr/Publish-Docker-Github-Action@2.21
|
||||
with:
|
||||
name: oam-dev/kubevela/vela-core
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
registry: docker.pkg.github.com
|
||||
tags: "latest"
|
||||
- name: Publish to Docker Hub Registry
|
||||
uses: elgohr/Publish-Docker-Github-Action@2.21
|
||||
with:
|
||||
name: oamdev/vela-core
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
tags: "latest"
|
||||
9
.github/workflows/e2e.yml
vendored
9
.github/workflows/e2e.yml
vendored
@@ -34,9 +34,18 @@ jobs:
|
||||
with:
|
||||
version: "v0.7.0"
|
||||
|
||||
- name: Load Image to kind cluster
|
||||
run: make kind-load
|
||||
|
||||
- name: install Kubebuilder
|
||||
uses: RyanSiu1995/kubebuilder-action@v1
|
||||
|
||||
- name: Run Make
|
||||
run: make
|
||||
|
||||
- name: Run Make Manager
|
||||
run: make manager
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
make e2e-setup
|
||||
|
||||
11
.github/workflows/go.yml
vendored
11
.github/workflows/go.yml
vendored
@@ -30,5 +30,16 @@ jobs:
|
||||
with:
|
||||
version: "v0.7.0"
|
||||
|
||||
- name: install Kubebuilder
|
||||
uses: RyanSiu1995/kubebuilder-action@v1
|
||||
|
||||
- name: Run Make test
|
||||
run: make test
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ./coverage.txt
|
||||
flags: unittests
|
||||
name: codecov-umbrella
|
||||
|
||||
65
.github/workflows/release.yml
vendored
65
.github/workflows/release.yml
vendored
@@ -32,6 +32,8 @@ jobs:
|
||||
run: make generate-source
|
||||
- name: Run cross-build
|
||||
run: make cross-build
|
||||
- name: Run compress binary
|
||||
run: make compress
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
@@ -41,24 +43,67 @@ jobs:
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}
|
||||
- name: Upload Linux
|
||||
- name: Upload Linux tar.gz
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./bin/vela-linux-amd64
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-linux-amd64
|
||||
asset_path: ./_bin/vela-linux-amd64.tar.gz
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-linux-amd64.tar.gz
|
||||
asset_content_type: binary/octet-stream
|
||||
- name: Upload MacOS
|
||||
- name: Upload Linux zip
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./bin/vela-darwin-amd64
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-darwin-amd64
|
||||
asset_path: ./_bin/vela-linux-amd64.zip
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-linux-amd64.zip
|
||||
asset_content_type: binary/octet-stream
|
||||
- name: Upload Windows
|
||||
- name: Upload MacOS tar.gz
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./bin/vela-windows-amd64.exe
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-windows-amd64.exe
|
||||
asset_content_type: binary/octet-stream
|
||||
asset_path: ./_bin/vela-darwin-amd64.tar.gz
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-darwin-amd64.tar.gz
|
||||
asset_content_type: binary/octet-stream
|
||||
- name: Upload MacOS zip
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./_bin/vela-darwin-amd64.zip
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-darwin-amd64.zip
|
||||
asset_content_type: binary/octet-stream
|
||||
- name: Upload Windows tar.gz
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./_bin/vela-windows-amd64.tar.gz
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-windows-amd64.tar.gz
|
||||
asset_content_type: binary/octet-stream
|
||||
- name: Upload Windows zip
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./_bin/vela-windows-amd64.zip
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-windows-amd64.zip
|
||||
asset_content_type: binary/octet-stream
|
||||
- name: Upload Checksums
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./_bin/sha256sums.txt
|
||||
asset_name: sha256sums.txt
|
||||
asset_content_type: text/plain
|
||||
- name: Publish to Github Docker Package Registry
|
||||
uses: elgohr/Publish-Docker-Github-Action@2.21
|
||||
with:
|
||||
name: oam-dev/kubevela/vela-core
|
||||
username: $GITHUB_ACTOR
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
registry: docker.pkg.github.com
|
||||
tags: "${{ steps.get_version.outputs.VERSION }}"
|
||||
- name: Publish to Docker Hub Registry
|
||||
uses: elgohr/Publish-Docker-Github-Action@2.21
|
||||
with:
|
||||
name: oamdev/vela-core
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
tags: "${{ steps.get_version.outputs.VERSION }}"
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -5,6 +5,7 @@
|
||||
*.so
|
||||
*.dylib
|
||||
bin
|
||||
_bin
|
||||
e2e/vela
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
@@ -12,6 +13,7 @@ e2e/vela
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
coverage.txt
|
||||
|
||||
# Kubernetes Generated files - skip generated files, except for vendored files
|
||||
|
||||
@@ -35,9 +37,13 @@ config/crd/bases
|
||||
tmp/
|
||||
|
||||
cmd/vela/fake/source.go
|
||||
cmd/vela/fake/chart_source.go
|
||||
charts/vela-core/crds/_.yaml
|
||||
|
||||
.vela/
|
||||
|
||||
# Dashboard
|
||||
dashboard/node_modules/
|
||||
node_modules/
|
||||
.eslintcache
|
||||
dashboard/dist/
|
||||
dashboard/package-lock.json
|
||||
|
||||
3
CODE_OF_CONDUCT.md
Normal file
3
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Code of Conduct
|
||||
|
||||
KubeVela follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
172
CONTRIBUTING.md
172
CONTRIBUTING.md
@@ -5,110 +5,130 @@ contributing to `kubevela` or build a PoC (Proof of Concept).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Golang version 1.12+
|
||||
2. Kubernetes version v1.15+ with `~/.kube/config` configured.
|
||||
3. OAM Kubernetes Runtime installed.
|
||||
4. Kustomize version 3.8+
|
||||
5. ginkgo 1.14.0+ (just for [E2E test](https://github.com/oam-dev/kubevela/blob/master/DEVELOPMENT.md#e2e-test))
|
||||
6. golangci-lint 1.31.0+, it will install automatically if you run `make`, you can [install it manually](https://golangci-lint.run/usage/install/#local-installation) if the installation is too slow.
|
||||
1. Golang version 1.13+
|
||||
2. Kubernetes version v1.16+ with `~/.kube/config` configured.
|
||||
3. ginkgo 1.14.0+ (just for [E2E test](https://github.com/oam-dev/kubevela/blob/master/DEVELOPMENT.md#e2e-test))
|
||||
4. golangci-lint 1.31.0+, it will install automatically if you run `make`, you can [install it manually](https://golangci-lint.run/usage/install/#local-installation) if the installation is too slow.
|
||||
|
||||
## Build
|
||||
|
||||
* Clone this project
|
||||
|
||||
```shell script
|
||||
git clone git@github.com:oam-dev/kubevela.git
|
||||
```
|
||||
|
||||
* Install Template CRD into your cluster
|
||||
* Build Vela CLI
|
||||
|
||||
```shell script
|
||||
make install
|
||||
make
|
||||
```
|
||||
|
||||
* Install template object
|
||||
* Configure vela to PATH
|
||||
|
||||
after build, make will create `vela` binary to `bin/`, Set this path to PATH.
|
||||
|
||||
```shell script
|
||||
kubectl apply -f config/samples/
|
||||
export PATH=$PATH:/your/path/to/project/kubevela/bin
|
||||
```
|
||||
|
||||
## Develop & Debug
|
||||
If you change Template CRD, remember to rerun `make install`.
|
||||
Then you can use `vela` command directly.
|
||||
|
||||
Use the following command to develop and debug.
|
||||
* Build Vela Core
|
||||
|
||||
```shell script
|
||||
$ cd cmd/vela
|
||||
$ go run main.go COMMAND [FLAG]
|
||||
make manager
|
||||
```
|
||||
|
||||
* Run Vela Core
|
||||
|
||||
Firstly make sure your cluster has CRDs.
|
||||
|
||||
```shell script
|
||||
make core-install
|
||||
```
|
||||
|
||||
Run locally:
|
||||
|
||||
```shell script
|
||||
make core-run
|
||||
```
|
||||
|
||||
This command will run controller locally, it will use your local KubeConfig which means you need to have a k8s cluster
|
||||
locally. If you don't have a one, we suggest that you could setup up a cluster with [kind](https://kind.sigs.k8s.io/).
|
||||
|
||||
## Use
|
||||
|
||||
* Create environment
|
||||
|
||||
```shell script
|
||||
vela env init myenv --namespace myenv --email my@email.com --domain kubevela.io
|
||||
```
|
||||
|
||||
* Create Component
|
||||
|
||||
For example, use the following command to create and run an application.
|
||||
|
||||
```shell script
|
||||
$ go run main.go run containerized app2057 nginx:1.9.4
|
||||
Creating AppConfig app2057
|
||||
$ vela comp deploy mycomp -t webservice --image crccheck/hello-world --port 8000
|
||||
Creating AppConfig appcomp
|
||||
SUCCEED
|
||||
|
||||
$ kubectl get oam
|
||||
NAME WORKLOAD-KIND
|
||||
component.core.oam.dev/app2057 ContainerizedWorkload
|
||||
|
||||
NAME AGE
|
||||
containerizedworkload.core.oam.dev/poc 53m
|
||||
|
||||
NAME AGE
|
||||
applicationconfiguration.core.oam.dev/app2057 69s
|
||||
|
||||
NAME DEFINITION-NAME
|
||||
traitdefinition.core.oam.dev/simplerollouttraits.extend.oam.dev simplerollouttraits.extend.oam.dev
|
||||
|
||||
NAME DEFINITION-NAME
|
||||
workloaddefinition.core.oam.dev/containerizedworkloads.core.oam.dev containerizedworkloads.core.oam.dev
|
||||
workloaddefinition.core.oam.dev/deployments.apps deployments.apps
|
||||
workloaddefinition.core.oam.dev/statefulsets.apps statefulsets.apps
|
||||
```
|
||||
|
||||
## E2E test
|
||||
```
|
||||
$ make e2e-test
|
||||
Running Suite: Trait Suite
|
||||
==========================
|
||||
Random Seed: 1596559178
|
||||
Will run 5 of 5 specs
|
||||
* Add Trait
|
||||
|
||||
Trait env init
|
||||
should print env initiation successful message
|
||||
kubevela/e2e/commonContext.go:14
|
||||
Create env succeed, current env is default
|
||||
•
|
||||
------------------------------
|
||||
Trait env switch
|
||||
should show env switch message
|
||||
kubevela/e2e/commonContext.go:40
|
||||
Switch env succeed, current env is default
|
||||
•
|
||||
------------------------------
|
||||
Trait run
|
||||
should print successful creation information
|
||||
kubevela/e2e/commonContext.go:76
|
||||
Creating AppConfig app-trait-basic
|
||||
SUCCEED
|
||||
•
|
||||
------------------------------
|
||||
Trait kubevela attach trait
|
||||
should print successful attached information
|
||||
kubevela/e2e/trait/trait_test.go:24
|
||||
Applying trait for app
|
||||
```shell script
|
||||
$ vela route mycomp
|
||||
Adding route for app abc
|
||||
Succeeded!
|
||||
•
|
||||
------------------------------
|
||||
Trait delete
|
||||
should print successful deletion information
|
||||
kubevela/e2e/commonContext.go:85
|
||||
Deleting AppConfig "app-trait-basic"
|
||||
DELETE SUCCEED
|
||||
•
|
||||
Ran 5 of 5 Specs in 9.717 seconds
|
||||
SUCCESS! -- 5 Passed | 0 Failed | 0 Pending | 0 Skipped
|
||||
PASS
|
||||
```
|
||||
|
||||
* Check Status
|
||||
|
||||
```
|
||||
$ vela comp status abc
|
||||
Showing status of Component abc deployed in Environment t2
|
||||
Component Status:
|
||||
Name: abc PodSpecWorkload(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind PodSpecWorkload workload is unknown for HealthScope
|
||||
Traits
|
||||
└─Trait/route
|
||||
|
||||
Last Deployment:
|
||||
Created at: 2020-09-18 18:47:09 +0800 CST
|
||||
Updated at: 2020-09-18T18:47:16+08:00
|
||||
```
|
||||
|
||||
* Delete App
|
||||
|
||||
```shell script
|
||||
$ vela app ls
|
||||
abc
|
||||
|
||||
$ vela app delete abc
|
||||
Deleting Application "abc"
|
||||
delete apps succeed abc from t2
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
### Unit test
|
||||
|
||||
```shell script
|
||||
make test
|
||||
```
|
||||
|
||||
### E2E test
|
||||
|
||||
** Before e2e test start, make sure you have vela-core running.**
|
||||
|
||||
```shell script
|
||||
make core-run
|
||||
```
|
||||
|
||||
Start to test.
|
||||
|
||||
```
|
||||
make e2e-test
|
||||
```
|
||||
|
||||
## Make a pull request
|
||||
|
||||
192
DESIGN.md
Normal file
192
DESIGN.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# KubeVela Design
|
||||
|
||||
This document is the detailed design and architecture of the KubeVela being built in this repository.
|
||||
|
||||
> All the diagram in this documentation could be found in [this slides](https://docs.google.com/presentation/d/1Y3gnKrd7fUZGgee7Ia9vBsRIYhcZLQwMUCDkk1RJvQc/edit?usp=sharing).
|
||||
|
||||
## Overview
|
||||
|
||||
KubeVela is a simple, complete, but highly extensible cloud native application platform based on Kubernetes and Open Application Model (OAM). KubeVela intends to bring application-centric experience to its end users and democratize building cloud native application platforms for platform engineers.
|
||||
|
||||
## User Stories
|
||||
|
||||
As a end user (e.g. application developers, operators etc) of the platform, I only want to focus on coding my business logic and ship them to various environments at ease. Let's say:
|
||||
- Here's my source code.
|
||||
- Here's my application configuration (described in end user PoV).
|
||||
- Deploy it in test environment.
|
||||
- Deploy it in production environment.
|
||||
- Monitoring, debugging, rollout/rollback the application.
|
||||
- Dockerfile is fine, but please keep it simple.
|
||||
|
||||
As a platform engineer, I want to build a easy-to-use platform for my end users. In detail, the platform should be:
|
||||
- Heroku-like (_in terms of both user experience and functionality_).
|
||||
- and I prefer to build my own version with OSS tools, particularly, with Kubernetes (for obvious reason).
|
||||
- Easy to build.
|
||||
- I am too busy to reinvent any wheel, I want to reuse every capability in Kubernetes community as possible, with minimal effort. Writing some simple CRD and controllers is fine, but please, just the simple ones like copy-paste.
|
||||
- Powerful and highly extensible.
|
||||
- I don't want to lock my users with restricted abstractions and capabilities like traditional PaaS or FaaS/Serverless. I love Kubernetes and what it has enabled. So in terms of capability, I hope my platform is fully open and has unlimited possibilities like Kubernetes itself, rather than another opinionated close system like traditional PaaS.
|
||||
|
||||
|
||||
## Core Principles
|
||||
|
||||
In nutshell, the principles for KubeVela project are:
|
||||
|
||||
- For end users, it out-of-the-box ships a fully featured PaaS-like experience, nothing special.
|
||||
- For platform builders, it works like a special Kubernetes "distro" or extensible PaaS core that could be used to build something more complex. It allows platform builders to ship any existing capabilities in cloud native ecosystem to end users with minimal effort, or develop a new capability at ease in a standardized and Kubernetes-native approach.
|
||||
|
||||
## Design Details
|
||||
|
||||
### 1. Application Centric
|
||||
|
||||
The design of KubeVela intends to make users think in terms of application, not containers or infrastructure.
|
||||
|
||||
Lacking application context impacts the user experience and significantly raised the bar to adopt cloud native stack. We believe "application" is the natural mindset of developers and it's the core concept an application platform should expose.
|
||||
|
||||

|
||||
|
||||
KubeVela intends to let developers push code, define application in developer facing primitives, and make daily operations as configurations of the application.
|
||||
|
||||
Thus, KubeVela choose to:
|
||||
1. Introduce "application" as first class citizen and main API.
|
||||
2. Build the whole system around "application", i.e. model capabilities of Kubernetes as application configuration, with clarity and manageability.
|
||||
|
||||
#### Solution
|
||||
|
||||
Instead of creating a in-house "application CRD", KubeVela adopts [Open Application Model (OAM)](https://github.com/oam-dev/spec) as its application definition, since OAM:
|
||||
1. Defines micro-services application by default.
|
||||
2. Can model operations as part of the application (i.e. `Trait`).
|
||||
2. Highly extensible: every workload and trait in OAM is a independent definition, no abstraction or capability lock-in.
|
||||
|
||||
### 2. Capability Oriented Architecture
|
||||
|
||||
To enable platform builders use KubeVela as the extensible "PaaS core", KubeVela intends to make its every capability a standalone "plug-in".
|
||||
|
||||

|
||||
|
||||
For example, there are several "built-in" workload types in KubeVela such as `Web Service` or `Task`. It is by design that they are all independent CRD controllers that abstract Kubernetes built-in workloads and create Service automatically if needed. KubeVela itself is **NOT** aware of the specification or implementation of these workload types.
|
||||
|
||||
This means platform builders are free to bring their own workload types by simply install a CRD controller, or even just reference a k8s built-in resource like StatefulSet as new workload type.
|
||||
|
||||
Similarly, all the "built-in" operations such as `scaling` or `rollout` (i.e. "traits" in KubeVela) are also independent CRD controllers which are **NOT** bound with specific workload types. Platform builders are free to bring their own traits implementations by simply providing a CRD controller, reference a k8s built-in resource like `HPA` or `NetworkPolicy` as trait is also possible.
|
||||
|
||||
This loosely coupled design of KubeVela adopts the idea of Capability Oriented Architecture (COA), i.e. instead of creating a close system like traditional PaaS, KubeVela intends to become an application-centric framework to connect end users with underlying infrastructure capabilities.
|
||||
|
||||
#### Solution
|
||||
|
||||
We decide to build KubeVela core with [OAM Kubernetes runtime](https://github.com/crossplane/oam-kubernetes-runtime) which already implemented the building block features such as standalone workload type and trait controllers. Whether a given trait could work with certain workload types is also clarified as `appliesTo` field of trait definition. For platform builders, OAM Kubernetes Runtime also provided a [lightweight library](https://github.com/crossplane/oam-kubernetes-runtime/blob/2be3900a3817aed570de9ec353e6ab0b50e100f0/pkg/controller/v1alpha2/core/traits/manualscalertrait/manualscalertrait_controller.go#L42) to implement trait controller so it doesn't need to care about workload kinds.
|
||||
|
||||
##### Capability Register and Discovery
|
||||
|
||||
KubeVela leverages [OAM definition objects](https://github.com/oam-dev/spec/blob/master/4.workload_definitions.md) to register and discover workloads and traits:
|
||||
|
||||
|
||||
```console
|
||||
$ kubectl apply -f workload-definition.yaml # register a new workload type
|
||||
$ kubectl apply -f trait-definition.yaml # register a new trait
|
||||
```
|
||||
|
||||
Note that OAM definition objects only care about API resource, not including the controllers. Thus KubeVela intends to include a **CRD registry** so whenever a new API resource is installed as workload or trait, KubeVela could install its controller automatically from the registry. That of course means we envision the CRD registry could register a CRD and Helm chart (which contains the manifest of the controller). In practice, we are currently evaluating RedHat's Operator Lifecycle Manager (OLM) but no the final conclusion yet.
|
||||
|
||||
##### Cloud Services Integration
|
||||
|
||||
For capabilities like cloud services, KubeVela intends to leverage Kubernetes as the universal control plane so [Crossplane](https://github.com/crossplane/crossplane) core will be used to register cloud services as workload types.
|
||||
|
||||
|
||||
### 3. Extensible User Interface
|
||||
|
||||
KubeVela is built with Kubernetes and OAM also adopts Kubernetes API resource model. So in nutshell, **ALL** functionalities of KubeVela core are able to be consumed by simple `kubectl`, for example:
|
||||
|
||||
```yaml
|
||||
$ kubectl apply -f frontend-component.yaml # create frontend component
|
||||
$ kubectl apply -f backend-component.yaml # create backend component
|
||||
$ kubectl apply -f application-config.yaml # assign operational traits to components
|
||||
```
|
||||
|
||||
However, Kubernetes API should not be the end game. Declarative YAML objects are great to build platforms like KubeVela with but when directly exposed to end users, it creates heavy mental burden and high learning curve. For example, even with highest level abstraction, the default [containerized workload schema](https://github.com/oam-dev/spec/blob/master/core/workloads/schema/containerized_workload.md) of OAM still has dozens fields to learn with many rough edges to be aware.
|
||||
|
||||
Thus KubeVela intends to introduce a lightweight user facing layer on top of its Kubernetes API objects with following goals in mind:
|
||||
|
||||
- Shorten the learning curve of new developers. Most capabilities in KubeVela are developed by big
|
||||
companies that run very complex workloads. However, for the bigger developer community, the new user facing layer will provide a much simpler path to on-board KubeVela.
|
||||
- Developers can describe their applications and behavior of their components without making assumptions on availability of specific Kubernetes API. For instance, a developer will be able to model auto-scaling needs without referring to the CRD of auto-scaling trait.
|
||||
- Provides a single source of truth of the application description. The user facing layer allows developers to work with a single artifact to capture the application definition. This artifact is the definitive truth of how the application is supposed to look like. It simplifies administrative tasks such as change management. It also serves as an anchor for application truth to avoid configuration drifts during operation.
|
||||
- Highly extensible. For example, when a new workload type or trait is installed, the end users could access this new capability directly from user interface layer, no re-compile or re-deploy of KubeVela is required.
|
||||
|
||||
#### Solution
|
||||
|
||||
We concluded above requirements of the user interface layer as introducing a modeling language on top of the existing model objects. With this context, we decide to adopt [CUElang](https://github.com/cuelang/cue) as the modeling language of KubeVela. In detail, every workload or trait definition will inline a cue template as the contract between user and Kubernetes API object.
|
||||
|
||||
However, we don't assume users need to learn or write CUElang directly. Instead, the reason we choose CUElang is it's just perfect to help us create developer facing tools based on it. Thus, KubeVela introduced three most common tools based to CUElang for developers to use:
|
||||
|
||||
1. A command line tool.
|
||||
2. A GUI dashboard.
|
||||
3. A Docker Compose style `appfile`.
|
||||
|
||||
An example for `vela cli`:
|
||||
|
||||
```console
|
||||
$ vela comp deploy frontend -t webservice --image oamdev/testapp:v1 --port 80 --app helloworld
|
||||
```
|
||||
|
||||
The `-t webservice --image oamdev/testapp:v1 --port 80` arguments are not hard coded, they are schema defined by in-line CUE template of `WebService` workload definition.
|
||||
|
||||
The `appfile` is essentially a YAML version of command line tool so we can support more complex and serious scenarios by simply running `$ vela up hello-world.yaml`:
|
||||
|
||||
```yaml
|
||||
version: "1.0-alpha.1"
|
||||
|
||||
name: helloworld
|
||||
|
||||
services:
|
||||
express-server:
|
||||
type: webservice # workload type
|
||||
build:
|
||||
docker:
|
||||
file: Dockerfile
|
||||
context: .
|
||||
|
||||
image: oamdev/testapp:v1
|
||||
cmd: ["node", "server.js"]
|
||||
ports:
|
||||
- 8080:80
|
||||
env:
|
||||
- FOO=bar
|
||||
|
||||
scale: # scaling trait
|
||||
replica: 2
|
||||
auto:
|
||||
range: "1-10"
|
||||
cpu: 80
|
||||
qps: 1000
|
||||
|
||||
canary: # canary trait
|
||||
step: 5
|
||||
headers:
|
||||
- "foo:bar.*"
|
||||
|
||||
redis:
|
||||
image: oamdev/redis
|
||||
|
||||
secrets:
|
||||
my-secret: /local-path/my-secret # load local file into k8s secret
|
||||
```
|
||||
|
||||
The schema of above `appfile` is not hard coded, they are defined by in-line CUE templates of `WebService` workload definition, `Scaling` trait definition and `Canary` trait definition.
|
||||
|
||||
We will skip the example of dashboard, but similarly, the schema of GUI forms are defined by in-lined CUE template of definition objects.
|
||||
|
||||
## Architecture
|
||||
|
||||

|
||||
|
||||
From highest level, KubeVela is composed by only two components:
|
||||
|
||||
### 1. User interface layer
|
||||
Including: `cli`, `dashboard`, `appfile`, they are all client side tools to provide developer facing abstractions by leveraging CUElang based parametering and templating.
|
||||
### 2. KubeVela core
|
||||
Including:
|
||||
- [OAM Kubernetes runtime](https://github.com/crossplane/oam-kubernetes-runtime) to provide application level building blocks such as `Component` and `Application` etc.
|
||||
- [Built-in workload and trait controllers](https://github.com/oam-dev/kubevela/tree/master/pkg/controller/v1alpha1) to implement core capabilities such as `webservice`, `route` and `rollout` etc.
|
||||
- Capability Management: manage features of KubeVela following Capability Oriented Architecture.
|
||||
- Every feature of KubeVela is a "addon", and it is registered by Kubernetes API resource (including CRD) leveraging OAM definition objects.
|
||||
- CRD Registry: register controllers of Kubernetes add-ons and discover them by CRD. This will enable automatically install controllers/operators when CRD is missing in the cluster.
|
||||
56
Makefile
56
Makefile
@@ -4,10 +4,11 @@ VELA_VERSION ?= 0.1.0
|
||||
GIT_COMMIT ?= git-$(shell git rev-parse --short HEAD)
|
||||
VELA_VERSION_VAR := github.com/oam-dev/kubevela/version.VelaVersion
|
||||
VELA_GITVERSION_VAR := github.com/oam-dev/kubevela/version.GitRevision
|
||||
LDFLAGS ?= "-X $(VELA_VERSION_VAR)=$(VELA_VERSION) -X $(VELA_GITVERSION_VAR)=$(GIT_COMMIT) -X main.chartTGZSource=$$(cat -) -s -w"
|
||||
LDFLAGS ?= "-X $(VELA_VERSION_VAR)=$(VELA_VERSION) -X $(VELA_GITVERSION_VAR)=$(GIT_COMMIT)"
|
||||
|
||||
GOX = go run github.com/mitchellh/gox
|
||||
TARGETS := darwin/amd64 linux/amd64 windows/amd64
|
||||
DIST_DIRS := find * -type d -exec
|
||||
|
||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||
ifeq (,$(shell go env GOBIN))
|
||||
@@ -20,11 +21,13 @@ all: build
|
||||
|
||||
# Run tests
|
||||
test: fmt vet lint
|
||||
go test ./pkg/... -coverprofile cover.out
|
||||
./hack/unit_test.sh
|
||||
|
||||
# Build manager binary
|
||||
build: fmt vet lint
|
||||
go run hack/chart/generate.go | go build -o bin/vela -ldflags ${LDFLAGS} cmd/vela/main.go
|
||||
go run hack/chart/generate.go
|
||||
go build -o bin/vela -ldflags ${LDFLAGS} cmd/vela/main.go
|
||||
git checkout cmd/vela/fake/chart_source.go
|
||||
|
||||
npm-build:
|
||||
cd dashboard && npm run build && cd ./..
|
||||
@@ -32,11 +35,26 @@ npm-build:
|
||||
npm-install:
|
||||
cd dashboard && npm install && cd ./..
|
||||
|
||||
generate-doc:
|
||||
rm -r documentation/cli/*
|
||||
go run hack/docgen/gen.go
|
||||
|
||||
generate-source:
|
||||
go run hack/frontend/source.go
|
||||
|
||||
cross-build:
|
||||
go run hack/chart/generate.go | GO111MODULE=on CGO_ENABLED=0 $(GOX) -ldflags $(LDFLAGS) -parallel=3 -output="bin/vela-{{.OS}}-{{.Arch}}" -osarch='$(TARGETS)' ./cmd/vela/
|
||||
go run hack/chart/generate.go
|
||||
GO111MODULE=on CGO_ENABLED=0 $(GOX) -ldflags $(LDFLAGS) -parallel=3 -output="_bin/{{.OS}}-{{.Arch}}/vela" -osarch='$(TARGETS)' ./cmd/vela/
|
||||
|
||||
compress:
|
||||
( \
|
||||
cd _bin && \
|
||||
$(DIST_DIRS) cp ../LICENSE {} \; && \
|
||||
$(DIST_DIRS) cp ../README.md {} \; && \
|
||||
$(DIST_DIRS) tar -zcf vela-{}.tar.gz {} \; && \
|
||||
$(DIST_DIRS) zip -r vela-{}.zip {} \; && \
|
||||
sha256sum vela-* > sha256sums.txt \
|
||||
)
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config
|
||||
run: fmt vet
|
||||
@@ -51,7 +69,7 @@ vet:
|
||||
go vet ./...
|
||||
|
||||
lint: golangci
|
||||
$(GOLANGCILINT) run -E golint,goimports ./...
|
||||
$(GOLANGCILINT) run --timeout 10m -E golint,goimports ./...
|
||||
|
||||
# Build the docker image
|
||||
docker-build: test
|
||||
@@ -62,8 +80,10 @@ docker-push:
|
||||
docker push ${IMG}
|
||||
|
||||
e2e-setup:
|
||||
bin/vela install --image-pull-policy IfNotPresent --image-repo vela-core-test --image-tag $(GIT_COMMIT)
|
||||
ginkgo version
|
||||
ginkgo -v -r e2e/setup
|
||||
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=vela-core,app.kubernetes.io/instance=kubevela -n vela-system --timeout=600s
|
||||
bin/vela dashboard &
|
||||
|
||||
e2e-test:
|
||||
@@ -77,6 +97,10 @@ e2e-api-test:
|
||||
e2e-cleanup:
|
||||
# Clean up
|
||||
|
||||
# load docker image to the kind cluster
|
||||
kind-load:
|
||||
docker build -t vela-core-test:$(GIT_COMMIT) .
|
||||
kind load docker-image vela-core-test:$(GIT_COMMIT) || { echo >&2 "kind not installed or error loading image: $(IMAGE)"; exit 1; }
|
||||
|
||||
# Image URL to use all building/pushing image targets
|
||||
IMG ?= vela-core:latest
|
||||
@@ -88,29 +112,29 @@ core-test: generate fmt vet manifests
|
||||
go test ./pkg/... -coverprofile cover.out
|
||||
|
||||
# Build manager binary
|
||||
manager: generate fmt vet
|
||||
manager: generate fmt vet lint manifests
|
||||
go build -o bin/manager ./cmd/core/main.go
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config
|
||||
core-run: generate fmt vet manifests
|
||||
go run ./cmd/core/main.go
|
||||
|
||||
# Install CRDs into a cluster
|
||||
# Install CRDs and Definitions of Vela Core into a cluster, this is for develop convenient.
|
||||
core-install: manifests
|
||||
kustomize build config/crd | kubectl apply -f -
|
||||
kubectl apply -f charts/vela-core/crds/
|
||||
kubectl apply -f charts/vela-core/templates/definitions/
|
||||
kubectl apply -f charts/third_party/prometheus
|
||||
|
||||
# Uninstall CRDs from a cluster
|
||||
# Uninstall CRDs and Definitions of Vela Core from a cluster, this is for develop convenient.
|
||||
core-uninstall: manifests
|
||||
kustomize build config/crd | kubectl delete -f -
|
||||
|
||||
# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
|
||||
core-deploy: manifests
|
||||
cd config/manager && kustomize edit set image controller=${IMG}
|
||||
kustomize build config/default | kubectl apply -f -
|
||||
kubectl delete -f charts/vela-core/crds/
|
||||
kubectl delete -f charts/vela-core/templates/definitions/
|
||||
kubectl delete -f charts/third_party/prometheus
|
||||
|
||||
# Generate manifests e.g. CRD, RBAC etc.
|
||||
manifests: controller-gen
|
||||
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=charts/vela/crds
|
||||
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=charts/vela-core/crds
|
||||
rm charts/vela-core/crds/_.yaml
|
||||
|
||||
# Generate code
|
||||
generate: controller-gen
|
||||
|
||||
4
PROJECT
4
PROJECT
@@ -1,3 +1,7 @@
|
||||
domain: oam.dev
|
||||
repo: github.com/oam-dev/kubevela
|
||||
resources:
|
||||
- group: standard
|
||||
kind: Route
|
||||
version: v1alpha1
|
||||
version: "2"
|
||||
|
||||
338
README.md
338
README.md
@@ -1,6 +1,27 @@
|
||||
# KubeVela
|
||||
|
||||
The Open Application Platform based on Kubernetes and OAM.
|
||||
The Open Application Platform based on Kubernetes and Open Application Model (OAM).
|
||||
|
||||
## Project Status
|
||||
|
||||
:rotating_light: **Warning: this project is still a work in progress with lots of rough edges, please don't look inside unless you know what you are doing.**
|
||||
|
||||
KubeVela project is initialized and maintained by the cloud native community since day 0 with [bootstrapping contributors from 8+ different organizations](https://github.com/oam-dev/kubevela/graphs/contributors). We intend for KubeVela to have a open governance since the very beginning and donate the project to neutral foundation as soon as it's released.
|
||||
|
||||
## Purpose and Goal
|
||||
|
||||
- For developers and operators
|
||||
- KubeVela, as an out-of-box Cloud Native Application Management Platform, provides numerous workloads and operation tooling for application defining, deployment, scaling, traffic, rollout, routing, monitoring, logging, alerting, CI/CD and so on.
|
||||
- For platform builders
|
||||
- KubeVela, as a highly extensible PaaS/Serverless Core, provides pluggable capabilities, an elegant way to integrate any workloads and operational capabilities (i.e. traits).
|
||||
|
||||
## Design and Architecture
|
||||
|
||||
Read more about [KubeVela's high level design and architecture](DESIGN.md).
|
||||
|
||||
## Demo Instructions
|
||||
|
||||
See the demo instructions below get a sense of what we've accomplished and are working on.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -11,87 +32,267 @@ The Open Application Platform based on Kubernetes and OAM.
|
||||
|
||||
### Get the Vela CLI
|
||||
|
||||
Download the `vela` binary from the [Releases page](https://github.com/oam-dev/kubevela/releases). Change file mod and add it to `$PATH` to get started.
|
||||
Download the `vela` binary from the [releases page](https://github.com/oam-dev/kubevela/releases). Unpack the `vela` binary and add it to `$PATH` to get started.
|
||||
|
||||
For exmaple:
|
||||
```shell
|
||||
chmod a+x vela-v0.0.2-darwin-amd64
|
||||
sudo mv ./vela-v0.0.2-darwin-amd64 /usr/local/bin/vela
|
||||
sudo mv ./vela /usr/local/bin/vela
|
||||
```
|
||||
|
||||
### Install Vela Core
|
||||
|
||||
```shell script
|
||||
```console
|
||||
$ vela install
|
||||
- Installing Vela Core:
|
||||
- Installing builtin capabilities:
|
||||
Successful applied 4 kinds of Workloads and Traits: deployments.apps,containerizedworkloads.core.oam.dev,manualscalertraits.core.oam.dev,simplerollouttraits.extend.oam.dev.
|
||||
syncing workload definitions from cluster...
|
||||
[WARN]handle template task: #Template.metadata.name: reference "task" not found
|
||||
get 5 workload definitions from cluster, syncing...5 workload definitions successfully synced
|
||||
syncing trait definitions from cluster...
|
||||
[WARN]handle template metricstraits.standard.oam.dev: #Template.metadata.name: reference "metricstraits" not found
|
||||
get 2 trait definitions from cluster, syncing...2 trait definitions successfully synced
|
||||
- Finished.
|
||||
```
|
||||
This command will install vela core controller into your K8s cluster, along with built-in workloads and traits.
|
||||
|
||||
## Using KubeVela
|
||||
|
||||
After `vela install` you will see available workloads and traits.
|
||||
|
||||
```console
|
||||
$ vela workloads
|
||||
NAME DEFINITION
|
||||
backend podspecworkloads.standard.oam.dev
|
||||
task jobs.batch.k8s.io
|
||||
webservice podspecworkloads.standard.oam.dev
|
||||
```
|
||||
|
||||
## Demos
|
||||
|
||||
#### Create ENV
|
||||
|
||||
```
|
||||
$ vela env init test --namespace test
|
||||
Create env succeed, current env is test
|
||||
|
||||
$ vela env ls
|
||||
NAME CURRENT NAMESPACE
|
||||
default default
|
||||
test * test
|
||||
|
||||
$ vela env switch default
|
||||
Switch env succeed, current env is default
|
||||
|
||||
$ vela env delete test
|
||||
test deleted
|
||||
|
||||
$ vela env delete default
|
||||
Error: you can't delete current using default
|
||||
```console
|
||||
$ vela traits
|
||||
NAME DEFINITION APPLIES TO
|
||||
route routes.standard.oam.dev webservice,backend
|
||||
scale manualscalertraits.core.oam.dev webservice,backend
|
||||
```
|
||||
|
||||
#### workload run
|
||||
```shell script
|
||||
$ vela comp run -t deployment app123 -p 80 --image nginx:1.9.4
|
||||
Creating AppConfig app123
|
||||
### Create environment
|
||||
|
||||
Before working with your application, you should prepare an deploy environment for it (e.g. test, staging, prod etc).
|
||||
|
||||
```console
|
||||
$ vela env init demo --namespace demo --email my@email.com --domain kubevela.io
|
||||
ENVIROMENT demo CREATED, Namespace: demo, Email: my@email.com.
|
||||
```
|
||||
|
||||
Vela will create a Kubernetes namespace called `demo` , with namespace level issuer for certificate generation using the email you provided.
|
||||
|
||||
You could check the environment metadata in your local:
|
||||
|
||||
```console
|
||||
$ cat ~/.vela/envs/demo/config.json
|
||||
{"name":"demo","namespace":"demo","email":"my@email.com","domain":"kubevela.io","issuer":"oam-env-demo"}
|
||||
```
|
||||
|
||||
|
||||
### Create simple component
|
||||
|
||||
Then let's create application, we will use the `demo` environment.
|
||||
|
||||
```console
|
||||
$ vela comp deploy mycomp -t webservice --image crccheck/hello-world --port 8000 --app myapp
|
||||
Creating AppConfig appcomp
|
||||
SUCCEED
|
||||
|
||||
$ vela comp status app123
|
||||
```
|
||||
|
||||
#### app
|
||||
### Create micro-services application
|
||||
|
||||
Vela supports micro-services application by default thanks to Open Application Model.
|
||||
|
||||
```console
|
||||
$ vela comp deploy db -t backend --image crccheck/hello-world --app myapp
|
||||
Creating App myapp
|
||||
SUCCEED
|
||||
```
|
||||
$ vela app ls
|
||||
app123
|
||||
poc08032042
|
||||
poc1039
|
||||
|
||||
```console
|
||||
$ vela comp ls
|
||||
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
|
||||
ccc ccc deployment Deployed 2020-08-27 10:56:41 +0800 CST
|
||||
com1 com1 Deployed 2020-08-26 16:45:50 +0800 CST
|
||||
com2 com1 Deployed 2020-08-26 16:45:50 +0800 CST
|
||||
myapp myapp route,scale Deployed 2020-08-19 15:11:17 +0800 CST
|
||||
|
||||
$ vela app delete app123
|
||||
Deleting AppConfig "app123"
|
||||
DELETE SUCCEED
|
||||
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
|
||||
db myapp backend Deployed 2020-09-18 22:42:04 +0800 CST
|
||||
mycomp myapp webservice Deployed 2020-09-18 22:42:04 +0800 CST
|
||||
```
|
||||
|
||||
#### Auto-Completion
|
||||
#### Under the hood
|
||||
|
||||
In Kubernetes, vela creates an OAM application configuration named `myapp` to manage all related components.
|
||||
|
||||
```console
|
||||
$ kubectl get appconfig -n demo
|
||||
NAME AGE
|
||||
myapp 24s
|
||||
```
|
||||
|
||||
```console
|
||||
$ kubectl get components -n demo
|
||||
NAME AGE
|
||||
mycomp 24s
|
||||
db 10s
|
||||
```
|
||||
|
||||
Vela Core is responsible for managing the underlying Kubernetes resources linked with the components and application configuration above.
|
||||
|
||||
```console
|
||||
$ kubectl get deployment -n demo
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
mycomp 1/1 1 1 38s
|
||||
db 1/1 1 1 20s
|
||||
```
|
||||
|
||||
```console
|
||||
$ kubectl get svc -n demo
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
mycomp ClusterIP 172.21.4.228 <none> 8080/TCP 49s
|
||||
```
|
||||
|
||||
### Manage operational configurations of the application
|
||||
|
||||
Vela leverages OAM trait system to manage operational configurations such as `scale`, `route`, `canary`, `autocale`etc in application centric approach.
|
||||
|
||||
Let's take `route` as example.
|
||||
|
||||
### `route`
|
||||
|
||||
If you want to use `route`, please make sure you have [nginx-ingress controller[https://kubernetes.github.io/ingress-nginx/deploy/] in your cluster.
|
||||
|
||||
```console
|
||||
$ vela route mycomp --app myapp
|
||||
Adding route for app mycomp
|
||||
Succeeded!
|
||||
```
|
||||
|
||||
For now you have to check the public address manually (this will be fixed soon so `vela route` will return visiting URL as result):
|
||||
|
||||
```console
|
||||
$ kubectl get ingress -n demo
|
||||
NAME HOSTS ADDRESS PORTS AGE
|
||||
mycomp-trait-5b576c4fc mycomp.kubevela.io 123.57.10.233 80, 443 73s
|
||||
```
|
||||
|
||||
And after you configure the `kubevela.io` domain pointing to the public address above.
|
||||
|
||||
Your application will be reached by `https://mycomp.kubevela.io` with `mTLS` automatically enabled.
|
||||
|
||||
### Under the hood
|
||||
|
||||
Vela will manage the underlying Kubernetes resource which implements the `route` trait.
|
||||
|
||||
```console
|
||||
$ kubectl get routes.standard.oam.dev -n demo
|
||||
NAME AGE
|
||||
mycomp-trait-5b576c4fc 18s
|
||||
```
|
||||
|
||||
`routes.standard.oam.dev` is a CRD controller which will manage ingress, domain, certificate etc for your application.
|
||||
|
||||
### Check status
|
||||
|
||||
|
||||
Check the application:
|
||||
|
||||
```console
|
||||
$ vela app show myapp
|
||||
About:
|
||||
|
||||
Name: myapp
|
||||
Created at: 2020-09-18 22:42:04.191171 +0800 CST
|
||||
Updated at: 2020-09-18 22:51:11.128997 +0800 CST
|
||||
|
||||
|
||||
Environment:
|
||||
|
||||
Namespace: demo
|
||||
|
||||
Components:
|
||||
|
||||
Name Type Traits
|
||||
db backend
|
||||
mycomp webservice route
|
||||
```
|
||||
|
||||
Check specific component:
|
||||
|
||||
```console
|
||||
$ vela comp show mycomp
|
||||
About:
|
||||
|
||||
Name: mycomp
|
||||
WorkloadType: webservice
|
||||
Application: myapp
|
||||
|
||||
Environment:
|
||||
|
||||
Namespace: demo
|
||||
|
||||
Arguments:
|
||||
|
||||
image: crccheck/hello-world
|
||||
name: mycomp
|
||||
port: 8000
|
||||
|
||||
|
||||
Traits:
|
||||
|
||||
route:
|
||||
domain: mycomp.kubevela.io
|
||||
issuer: oam-env-demo
|
||||
name: route
|
||||
```
|
||||
|
||||
```
|
||||
$ vela comp status mycomp
|
||||
Showing status of Component mycomp deployed in Environment demo
|
||||
Component Status:
|
||||
Name: mycomp PodSpecWorkload(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind PodSpecWorkload workload is unknown for HealthScope
|
||||
Traits
|
||||
└─Trait/route
|
||||
|
||||
Last Deployment:
|
||||
Created at: 2020-09-18 22:42:04 +0800 CST
|
||||
Updated at: 2020-09-18T22:51:11+08:00
|
||||
```
|
||||
|
||||
### Delete application or component
|
||||
|
||||
```console
|
||||
$ vela app ls
|
||||
myapp
|
||||
```
|
||||
|
||||
```console
|
||||
$ vela comp ls
|
||||
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
|
||||
db myapp backend Deployed 2020-09-18 22:42:04 +0800 CST
|
||||
mycomp myapp webservice route Deployed 2020-09-18 22:42:04 +0800 CST
|
||||
```
|
||||
|
||||
```console
|
||||
$ vela comp delete db
|
||||
Deleting Component 'db' from Application 'db'
|
||||
```
|
||||
|
||||
```console
|
||||
$ vela comp ls
|
||||
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
|
||||
mycomp myapp webservice route Deployed 2020-09-18 22:42:04 +0800 CST
|
||||
```
|
||||
|
||||
```console
|
||||
$ vela app delete myapp
|
||||
Deleting Application "myapp"
|
||||
delete apps succeed myapp from demo
|
||||
```
|
||||
|
||||
## Dashboard
|
||||
|
||||
Vela has a simple client side dashboard for you to interact with (note it's still under development). The functionality is equivalent to the vela cli.
|
||||
|
||||
```console
|
||||
$ vela dashboard
|
||||
```
|
||||
|
||||
#### Auto-completion
|
||||
|
||||
##### bash
|
||||
|
||||
```shell script
|
||||
```console
|
||||
To load completions in your current shell session:
|
||||
$ source <(vela completion bash)
|
||||
|
||||
@@ -104,7 +305,7 @@ MacOS:
|
||||
|
||||
##### zsh
|
||||
|
||||
```shell script
|
||||
```console
|
||||
To load completions in your current shell session:
|
||||
$ source <(vela completion zsh)
|
||||
|
||||
@@ -114,21 +315,24 @@ $ vela completion zsh > "${fpath[1]}/_vela"
|
||||
|
||||
### Clean your environment
|
||||
|
||||
```shell script
|
||||
$ helm uninstall vela-core -n oam-system
|
||||
release "vela-core" uninstalled
|
||||
```console
|
||||
$ helm uninstall kubevela -n vela-system
|
||||
release "kubevela" uninstalled
|
||||
```
|
||||
|
||||
```shell script
|
||||
$ kubectl delete crd workloaddefinitions.core.oam.dev traitdefinitions.core.oam.dev
|
||||
```console
|
||||
$ kubectl delete crd workloaddefinitions.core.oam.dev traitdefinitions.core.oam.dev scopedefinitions.core.oam.dev
|
||||
customresourcedefinition.apiextensions.k8s.io "workloaddefinitions.core.oam.dev" deleted
|
||||
customresourcedefinition.apiextensions.k8s.io "traitdefinitions.core.oam.dev" deleted
|
||||
```
|
||||
|
||||
```shell script
|
||||
```console
|
||||
$ rm -r ~/.vela
|
||||
```
|
||||
|
||||
## CONTRIBUTING
|
||||
## Contributing
|
||||
Check out [CONTRIBUTING.md](./CONTRIBUTING.md) to see how to develop with KubeVela.
|
||||
|
||||
## Code of Conduct
|
||||
This project has adopted the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for further details.
|
||||
|
||||
|
||||
59
api/core.oam.dev/v1alpha2/appdeploy_types.go
Normal file
59
api/core.oam.dev/v1alpha2/appdeploy_types.go
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
runtimev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// ApplicationDeploymentSpec defines the desired state of ApplicationDeployment
|
||||
type ApplicationDeploymentSpec struct {
|
||||
//TODO add spec here
|
||||
}
|
||||
|
||||
// ApplicationDeploymentStatus defines the observed state of ApplicationDeployment
|
||||
type ApplicationDeploymentStatus struct {
|
||||
//TODO add status field here
|
||||
runtimev1alpha1.ConditionedStatus `json:",inline"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:categories={oam}
|
||||
// +kubebuilder:subresource:status
|
||||
// ApplicationDeployment is the Schema for the ApplicationDeployment API
|
||||
type ApplicationDeployment struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ApplicationDeploymentSpec `json:"spec,omitempty"`
|
||||
Status ApplicationDeploymentStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// ApplicationDeploymentList contains a list of ApplicationDeployment
|
||||
type ApplicationDeploymentList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []ApplicationDeployment `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&ApplicationDeployment{}, &ApplicationDeploymentList{})
|
||||
}
|
||||
35
api/core.oam.dev/v1alpha2/groupversion_info.go
Normal file
35
api/core.oam.dev/v1alpha2/groupversion_info.go
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package v1alpha2 contains API Schema definitions for the core.oam.dev v1alpha2 API group
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=core.oam.dev
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
SchemeGroupVersion = schema.GroupVersion{Group: "core.oam.dev", Version: "v1alpha2"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
115
api/core.oam.dev/v1alpha2/zz_generated.deepcopy.go
Normal file
115
api/core.oam.dev/v1alpha2/zz_generated.deepcopy.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ApplicationDeployment) DeepCopyInto(out *ApplicationDeployment) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDeployment.
|
||||
func (in *ApplicationDeployment) DeepCopy() *ApplicationDeployment {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ApplicationDeployment)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ApplicationDeployment) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ApplicationDeploymentList) DeepCopyInto(out *ApplicationDeploymentList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ApplicationDeployment, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDeploymentList.
|
||||
func (in *ApplicationDeploymentList) DeepCopy() *ApplicationDeploymentList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ApplicationDeploymentList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ApplicationDeploymentList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ApplicationDeploymentSpec) DeepCopyInto(out *ApplicationDeploymentSpec) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDeploymentSpec.
|
||||
func (in *ApplicationDeploymentSpec) DeepCopy() *ApplicationDeploymentSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ApplicationDeploymentSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ApplicationDeploymentStatus) DeepCopyInto(out *ApplicationDeploymentStatus) {
|
||||
*out = *in
|
||||
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDeploymentStatus.
|
||||
func (in *ApplicationDeploymentStatus) DeepCopy() *ApplicationDeploymentStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ApplicationDeploymentStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -40,11 +40,13 @@ type Capability struct {
|
||||
Name string `json:"name"`
|
||||
Type CapType `json:"type"`
|
||||
CueTemplate string `json:"template,omitempty"`
|
||||
CueTemplateURI string `json:"templateURI,omitempty"`
|
||||
Parameters []Parameter `json:"parameters,omitempty"`
|
||||
DefinitionPath string `json:"definition"`
|
||||
CrdName string `json:"crdName,omitempty"`
|
||||
Center string `json:"center,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
//trait only
|
||||
AppliesTo []string `json:"appliesTo,omitempty"`
|
||||
@@ -56,10 +58,11 @@ type Capability struct {
|
||||
}
|
||||
|
||||
type Chart struct {
|
||||
Repo string `json:"repo"`
|
||||
URL string `json:"url"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Repo string `json:"repo"`
|
||||
URL string `json:"url"`
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type Installation struct {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package types
|
||||
|
||||
const (
|
||||
DefaultOAMNS = "oam-system"
|
||||
DefaultOAMReleaseName = "vela-core"
|
||||
DefaultOAMNS = "vela-system"
|
||||
DefaultOAMReleaseName = "kubevela"
|
||||
DefaultOAMRuntimeChartName = "vela-core"
|
||||
DefaultOAMVersion = ">0.0.0-0"
|
||||
|
||||
@@ -11,13 +11,11 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
AnnAPIVersion = "definition.oam.dev/apiVersion"
|
||||
AnnKind = "definition.oam.dev/kind"
|
||||
AnnAPIVersion = "definition.oam.dev/apiVersion"
|
||||
AnnKind = "definition.oam.dev/kind"
|
||||
AnnDescription = "definition.oam.dev/description"
|
||||
|
||||
// Indicate which workloadDefinition generate from
|
||||
AnnWorkloadDef = "workload.oam.dev/name"
|
||||
// Indicate which traitDefinition generate from
|
||||
AnnTraitDef = "trait.oam.dev/name"
|
||||
LabelPodSpecable = "workload.oam.dev/podspecable"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -27,8 +25,13 @@ const (
|
||||
|
||||
type EnvMeta struct {
|
||||
Name string `json:"name"`
|
||||
Current string `json:"current,omitempty"`
|
||||
Namespace string `json:"namespace"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Domain string `json:"domain,omitempty"`
|
||||
|
||||
// Below are not arguments, should be auto-generated
|
||||
Issuer string `json:"issuer"`
|
||||
Current string `json:"current,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -23,20 +23,20 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ContainerizedSpec defines the desired state of Containerized
|
||||
type ContainerizedSpec struct {
|
||||
// PodSpecWorkloadSpec defines the desired state of PodSpecWorkload
|
||||
type PodSpecWorkloadSpec struct {
|
||||
// Replicas is the desired number of replicas of the given podSpec.
|
||||
// These are replicas in the sense that they are instantiations of the same podSpec.
|
||||
// If unspecified, defaults to 1.
|
||||
Replicas *int32 `json:"replicas"`
|
||||
|
||||
// PodSpec describes the pods that will be created,
|
||||
// we omit the meta part as it will be exactly the same as the containerized
|
||||
// we omit the meta part as it will be exactly the same as the PodSpecWorkload
|
||||
PodSpec v1.PodSpec `json:"podSpec"`
|
||||
}
|
||||
|
||||
// ContainerizedStatus defines the observed state of Containerized
|
||||
type ContainerizedStatus struct {
|
||||
// PodSpecWorkloadStatus defines the observed state of PodSpecWorkload
|
||||
type PodSpecWorkloadStatus struct {
|
||||
cpv1alpha1.ConditionedStatus `json:",inline"`
|
||||
|
||||
// Resources managed by this workload.
|
||||
@@ -45,36 +45,36 @@ type ContainerizedStatus struct {
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// Containerized is the Schema for the containerizeds API
|
||||
// PodSpecWorkload is the Schema for the PodSpec API
|
||||
// +kubebuilder:resource:categories={oam}
|
||||
// +kubebuilder:subresource:status
|
||||
type Containerized struct {
|
||||
type PodSpecWorkload struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ContainerizedSpec `json:"spec,omitempty"`
|
||||
Status ContainerizedStatus `json:"status,omitempty"`
|
||||
Spec PodSpecWorkloadSpec `json:"spec,omitempty"`
|
||||
Status PodSpecWorkloadStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ContainerizedList contains a list of Containerized
|
||||
type ContainerizedList struct {
|
||||
// PodSpecWorkloadList contains a list of PodSpecWorkload
|
||||
type PodSpecWorkloadList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Containerized `json:"items"`
|
||||
Items []PodSpecWorkload `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Containerized{}, &ContainerizedList{})
|
||||
SchemeBuilder.Register(&PodSpecWorkload{}, &PodSpecWorkloadList{})
|
||||
}
|
||||
|
||||
var _ oam.Workload = &Containerized{}
|
||||
var _ oam.Workload = &PodSpecWorkload{}
|
||||
|
||||
func (in *Containerized) SetConditions(c ...cpv1alpha1.Condition) {
|
||||
func (in *PodSpecWorkload) SetConditions(c ...cpv1alpha1.Condition) {
|
||||
in.Status.SetConditions(c...)
|
||||
}
|
||||
|
||||
func (in *Containerized) GetCondition(c cpv1alpha1.ConditionType) cpv1alpha1.Condition {
|
||||
func (in *PodSpecWorkload) GetCondition(c cpv1alpha1.ConditionType) cpv1alpha1.Condition {
|
||||
return in.Status.GetCondition(c)
|
||||
}
|
||||
153
api/v1alpha1/route_types.go
Normal file
153
api/v1alpha1/route_types.go
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
runtimev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||||
"github.com/crossplane/oam-kubernetes-runtime/pkg/oam"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// RouteSpec defines the desired state of Route
|
||||
type RouteSpec struct {
|
||||
// WorkloadReference to the workload whose metrics needs to be exposed
|
||||
WorkloadReference runtimev1alpha1.TypedReference `json:"workloadRef,omitempty"`
|
||||
|
||||
// Host is the host of the route
|
||||
Host string `json:"host"`
|
||||
|
||||
// TLS indicate route trait will create SSL secret using cert-manager with specified issuer
|
||||
// If this is nil, route trait will use a selfsigned issuer
|
||||
TLS *TLS `json:"tls,omitempty"`
|
||||
|
||||
// Rules contain multiple rules of route
|
||||
Rules []Rule `json:"rules,omitempty"`
|
||||
|
||||
// Provider indicate which ingress controller implementation the route trait will use, by default it's nginx-ingress
|
||||
Provider string `json:"provider,omitempty"`
|
||||
}
|
||||
|
||||
// Rule defines to route rule
|
||||
type Rule struct {
|
||||
// Name will become the suffix of underlying ingress created by this rule, if not, will use index as suffix.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// Path is location Path, default for "/"
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
// RewriteTarget will rewrite request from Path to RewriteTarget path.
|
||||
RewriteTarget string `json:"rewriteTarget,omitempty"`
|
||||
|
||||
// CustomHeaders pass a custom list of headers to the backend service.
|
||||
CustomHeaders map[string]string `json:"customHeaders,omitempty"`
|
||||
|
||||
// DefaultBackend will become the ingress default backend if the backend is not available
|
||||
DefaultBackend *runtimev1alpha1.TypedReference `json:"defaultBackend,omitempty"`
|
||||
|
||||
// Backend indicate how to connect backend service
|
||||
// If it's nil, will auto discovery
|
||||
Backend *Backend `json:"backend,omitempty"`
|
||||
}
|
||||
|
||||
type TLS struct {
|
||||
IssuerName string `json:"issuerName,omitempty"`
|
||||
|
||||
// Type indicate the issuer is ClusterIssuer or Issuer(namespace issuer), by default, it's Issuer
|
||||
// +kubebuilder:default:=Issuer
|
||||
Type IssuerType `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type IssuerType string
|
||||
|
||||
const (
|
||||
ClusterIssuer IssuerType = "ClusterIssuer"
|
||||
NamespaceIssuer IssuerType = "Issuer"
|
||||
)
|
||||
|
||||
// Route will automatically discover podSpec and label for BackendService.
|
||||
// If BackendService is already set, discovery won't work.
|
||||
// If BackendService is not set, the discovery mechanism will work.
|
||||
type Backend struct {
|
||||
// ReadTimeout used for setting read timeout duration for backend service, the unit is second.
|
||||
ReadTimeout int `json:"readTimeout,omitempty"`
|
||||
// SendTimeout used for setting send timeout duration for backend service, the unit is second.
|
||||
SendTimeout int `json:"sendTimeout,omitempty"`
|
||||
// BackendService specifies the backend K8s service and port, it's optional
|
||||
BackendService *BackendServiceRef `json:"backendService,omitempty"`
|
||||
}
|
||||
|
||||
// BackendServiceRef specifies the backend K8s service and port, if specified, the two fields are all required
|
||||
type BackendServiceRef struct {
|
||||
// Port allow you direct specify backend service port.
|
||||
Port intstr.IntOrString `json:"port"`
|
||||
// ServiceName allow you direct specify K8s service for backend service.
|
||||
ServiceName string `json:"serviceName"`
|
||||
}
|
||||
|
||||
// RouteStatus defines the observed state of Route
|
||||
type RouteStatus struct {
|
||||
Ingresses []runtimev1alpha1.TypedReference `json:"ingresses,omitempty"`
|
||||
Service *runtimev1alpha1.TypedReference `json:"service,omitempty"`
|
||||
runtimev1alpha1.ConditionedStatus `json:",inline"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:categories={oam}
|
||||
// +kubebuilder:subresource:status
|
||||
// Route is the Schema for the routes API
|
||||
type Route struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec RouteSpec `json:"spec,omitempty"`
|
||||
Status RouteStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// RouteList contains a list of Route
|
||||
type RouteList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Route `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Route{}, &RouteList{})
|
||||
}
|
||||
|
||||
var _ oam.Trait = &Route{}
|
||||
|
||||
func (r *Route) SetConditions(c ...runtimev1alpha1.Condition) {
|
||||
r.Status.SetConditions(c...)
|
||||
}
|
||||
|
||||
func (r *Route) GetCondition(c runtimev1alpha1.ConditionType) runtimev1alpha1.Condition {
|
||||
return r.Status.GetCondition(c)
|
||||
}
|
||||
|
||||
// GetWorkloadReference of this ManualScalerTrait.
|
||||
func (r *Route) GetWorkloadReference() runtimev1alpha1.TypedReference {
|
||||
return r.Spec.WorkloadReference
|
||||
}
|
||||
|
||||
// SetWorkloadReference of this ManualScalerTrait.
|
||||
func (r *Route) SetWorkloadReference(rt runtimev1alpha1.TypedReference) {
|
||||
r.Spec.WorkloadReference = rt
|
||||
}
|
||||
@@ -26,102 +26,37 @@ import (
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Containerized) DeepCopyInto(out *Containerized) {
|
||||
func (in *Backend) DeepCopyInto(out *Backend) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Containerized.
|
||||
func (in *Containerized) DeepCopy() *Containerized {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Containerized)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Containerized) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ContainerizedList) DeepCopyInto(out *ContainerizedList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Containerized, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerizedList.
|
||||
func (in *ContainerizedList) DeepCopy() *ContainerizedList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ContainerizedList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ContainerizedList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ContainerizedSpec) DeepCopyInto(out *ContainerizedSpec) {
|
||||
*out = *in
|
||||
if in.Replicas != nil {
|
||||
in, out := &in.Replicas, &out.Replicas
|
||||
*out = new(int32)
|
||||
if in.BackendService != nil {
|
||||
in, out := &in.BackendService, &out.BackendService
|
||||
*out = new(BackendServiceRef)
|
||||
**out = **in
|
||||
}
|
||||
in.PodSpec.DeepCopyInto(&out.PodSpec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerizedSpec.
|
||||
func (in *ContainerizedSpec) DeepCopy() *ContainerizedSpec {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backend.
|
||||
func (in *Backend) DeepCopy() *Backend {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ContainerizedSpec)
|
||||
out := new(Backend)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ContainerizedStatus) DeepCopyInto(out *ContainerizedStatus) {
|
||||
func (in *BackendServiceRef) DeepCopyInto(out *BackendServiceRef) {
|
||||
*out = *in
|
||||
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
|
||||
if in.Resources != nil {
|
||||
in, out := &in.Resources, &out.Resources
|
||||
*out = make([]corev1alpha1.TypedReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
out.Port = in.Port
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerizedStatus.
|
||||
func (in *ContainerizedStatus) DeepCopy() *ContainerizedStatus {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendServiceRef.
|
||||
func (in *BackendServiceRef) DeepCopy() *BackendServiceRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ContainerizedStatus)
|
||||
out := new(BackendServiceRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -223,6 +158,252 @@ func (in *MetricsTraitStatus) DeepCopy() *MetricsTraitStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSpecWorkload) DeepCopyInto(out *PodSpecWorkload) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpecWorkload.
|
||||
func (in *PodSpecWorkload) DeepCopy() *PodSpecWorkload {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSpecWorkload)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *PodSpecWorkload) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSpecWorkloadList) DeepCopyInto(out *PodSpecWorkloadList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]PodSpecWorkload, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpecWorkloadList.
|
||||
func (in *PodSpecWorkloadList) DeepCopy() *PodSpecWorkloadList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSpecWorkloadList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *PodSpecWorkloadList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSpecWorkloadSpec) DeepCopyInto(out *PodSpecWorkloadSpec) {
|
||||
*out = *in
|
||||
if in.Replicas != nil {
|
||||
in, out := &in.Replicas, &out.Replicas
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
in.PodSpec.DeepCopyInto(&out.PodSpec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpecWorkloadSpec.
|
||||
func (in *PodSpecWorkloadSpec) DeepCopy() *PodSpecWorkloadSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSpecWorkloadSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSpecWorkloadStatus) DeepCopyInto(out *PodSpecWorkloadStatus) {
|
||||
*out = *in
|
||||
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
|
||||
if in.Resources != nil {
|
||||
in, out := &in.Resources, &out.Resources
|
||||
*out = make([]corev1alpha1.TypedReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpecWorkloadStatus.
|
||||
func (in *PodSpecWorkloadStatus) DeepCopy() *PodSpecWorkloadStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSpecWorkloadStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Route) DeepCopyInto(out *Route) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route.
|
||||
func (in *Route) DeepCopy() *Route {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Route)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Route) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RouteList) DeepCopyInto(out *RouteList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Route, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteList.
|
||||
func (in *RouteList) DeepCopy() *RouteList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RouteList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *RouteList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RouteSpec) DeepCopyInto(out *RouteSpec) {
|
||||
*out = *in
|
||||
out.WorkloadReference = in.WorkloadReference
|
||||
if in.TLS != nil {
|
||||
in, out := &in.TLS, &out.TLS
|
||||
*out = new(TLS)
|
||||
**out = **in
|
||||
}
|
||||
if in.Rules != nil {
|
||||
in, out := &in.Rules, &out.Rules
|
||||
*out = make([]Rule, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteSpec.
|
||||
func (in *RouteSpec) DeepCopy() *RouteSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RouteSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RouteStatus) DeepCopyInto(out *RouteStatus) {
|
||||
*out = *in
|
||||
if in.Ingresses != nil {
|
||||
in, out := &in.Ingresses, &out.Ingresses
|
||||
*out = make([]corev1alpha1.TypedReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Service != nil {
|
||||
in, out := &in.Service, &out.Service
|
||||
*out = new(corev1alpha1.TypedReference)
|
||||
**out = **in
|
||||
}
|
||||
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteStatus.
|
||||
func (in *RouteStatus) DeepCopy() *RouteStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RouteStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Rule) DeepCopyInto(out *Rule) {
|
||||
*out = *in
|
||||
if in.CustomHeaders != nil {
|
||||
in, out := &in.CustomHeaders, &out.CustomHeaders
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.DefaultBackend != nil {
|
||||
in, out := &in.DefaultBackend, &out.DefaultBackend
|
||||
*out = new(corev1alpha1.TypedReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.Backend != nil {
|
||||
in, out := &in.Backend, &out.Backend
|
||||
*out = new(Backend)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
|
||||
func (in *Rule) DeepCopy() *Rule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Rule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScapeServiceEndPoint) DeepCopyInto(out *ScapeServiceEndPoint) {
|
||||
*out = *in
|
||||
@@ -250,3 +431,18 @@ func (in *ScapeServiceEndPoint) DeepCopy() *ScapeServiceEndPoint {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TLS) DeepCopyInto(out *TLS) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLS.
|
||||
func (in *TLS) DeepCopy() *TLS {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TLS)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Download and unpack operator Lifecycle Manager (coreos)
|
||||
curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/0.15.1/install.sh | bash -s 0.15.1
|
||||
24
charts/third_party/grafana/grafana-operator.yaml
vendored
24
charts/third_party/grafana/grafana-operator.yaml
vendored
@@ -1,24 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: my-grafana-operator
|
||||
---
|
||||
apiVersion: operators.coreos.com/v1
|
||||
kind: OperatorGroup
|
||||
metadata:
|
||||
name: operatorgroup
|
||||
namespace: my-grafana-operator
|
||||
spec:
|
||||
targetNamespaces:
|
||||
- my-grafana-operator
|
||||
---
|
||||
apiVersion: operators.coreos.com/v1alpha1
|
||||
kind: Subscription
|
||||
metadata:
|
||||
name: my-grafana-operator
|
||||
namespace: my-grafana-operator
|
||||
spec:
|
||||
channel: alpha
|
||||
name: grafana-operator
|
||||
source: operatorhubio-catalog
|
||||
sourceNamespace: olm
|
||||
@@ -1,6 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
labels:
|
||||
mornitoring: oam
|
||||
name: monitoring
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,265 +0,0 @@
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.4
|
||||
creationTimestamp: null
|
||||
name: podmonitors.monitoring.coreos.com
|
||||
spec:
|
||||
group: monitoring.coreos.com
|
||||
names:
|
||||
kind: PodMonitor
|
||||
listKind: PodMonitorList
|
||||
plural: podmonitors
|
||||
singular: podmonitor
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: PodMonitor defines monitoring for a set of pods.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: Specification of desired Pod selection for target discovery
|
||||
by Prometheus.
|
||||
properties:
|
||||
jobLabel:
|
||||
description: The label to use to retrieve the job name from.
|
||||
type: string
|
||||
namespaceSelector:
|
||||
description: Selector to select which namespaces the Endpoints objects
|
||||
are discovered from.
|
||||
properties:
|
||||
any:
|
||||
description: Boolean describing whether all namespaces are selected
|
||||
in contrast to a list restricting them.
|
||||
type: boolean
|
||||
matchNames:
|
||||
description: List of namespace names.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
podMetricsEndpoints:
|
||||
description: A list of endpoints allowed as part of this PodMonitor.
|
||||
items:
|
||||
description: PodMetricsEndpoint defines a scrapeable endpoint of
|
||||
a Kubernetes Pod serving Prometheus metrics.
|
||||
properties:
|
||||
honorLabels:
|
||||
description: HonorLabels chooses the metric's labels on collisions
|
||||
with target labels.
|
||||
type: boolean
|
||||
honorTimestamps:
|
||||
description: HonorTimestamps controls whether Prometheus respects
|
||||
the timestamps present in scraped data.
|
||||
type: boolean
|
||||
interval:
|
||||
description: Interval at which metrics should be scraped
|
||||
type: string
|
||||
metricRelabelings:
|
||||
description: MetricRelabelConfigs to apply to samples before
|
||||
ingestion.
|
||||
items:
|
||||
description: 'RelabelConfig allows dynamic rewriting of the
|
||||
label set, being applied to samples before ingestion. It
|
||||
defines `<metric_relabel_configs>`-section of Prometheus
|
||||
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
|
||||
properties:
|
||||
action:
|
||||
description: Action to perform based on regex matching.
|
||||
Default is 'replace'
|
||||
type: string
|
||||
modulus:
|
||||
description: Modulus to take of the hash of the source
|
||||
label values.
|
||||
format: int64
|
||||
type: integer
|
||||
regex:
|
||||
description: Regular expression against which the extracted
|
||||
value is matched. Default is '(.*)'
|
||||
type: string
|
||||
replacement:
|
||||
description: Replacement value against which a regex replace
|
||||
is performed if the regular expression matches. Regex
|
||||
capture groups are available. Default is '$1'
|
||||
type: string
|
||||
separator:
|
||||
description: Separator placed between concatenated source
|
||||
label values. default is ';'.
|
||||
type: string
|
||||
sourceLabels:
|
||||
description: The source labels select values from existing
|
||||
labels. Their content is concatenated using the configured
|
||||
separator and matched against the configured regular
|
||||
expression for the replace, keep, and drop actions.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
targetLabel:
|
||||
description: Label to which the resulting value is written
|
||||
in a replace action. It is mandatory for replace actions.
|
||||
Regex capture groups are available.
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
params:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
description: Optional HTTP URL parameters
|
||||
type: object
|
||||
path:
|
||||
description: HTTP path to scrape for metrics.
|
||||
type: string
|
||||
port:
|
||||
description: Name of the pod port this endpoint refers to. Mutually
|
||||
exclusive with targetPort.
|
||||
type: string
|
||||
proxyUrl:
|
||||
description: ProxyURL eg http://proxyserver:2195 Directs scrapes
|
||||
to proxy through this endpoint.
|
||||
type: string
|
||||
relabelings:
|
||||
description: 'RelabelConfigs to apply to samples before ingestion.
|
||||
More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
|
||||
items:
|
||||
description: 'RelabelConfig allows dynamic rewriting of the
|
||||
label set, being applied to samples before ingestion. It
|
||||
defines `<metric_relabel_configs>`-section of Prometheus
|
||||
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
|
||||
properties:
|
||||
action:
|
||||
description: Action to perform based on regex matching.
|
||||
Default is 'replace'
|
||||
type: string
|
||||
modulus:
|
||||
description: Modulus to take of the hash of the source
|
||||
label values.
|
||||
format: int64
|
||||
type: integer
|
||||
regex:
|
||||
description: Regular expression against which the extracted
|
||||
value is matched. Default is '(.*)'
|
||||
type: string
|
||||
replacement:
|
||||
description: Replacement value against which a regex replace
|
||||
is performed if the regular expression matches. Regex
|
||||
capture groups are available. Default is '$1'
|
||||
type: string
|
||||
separator:
|
||||
description: Separator placed between concatenated source
|
||||
label values. default is ';'.
|
||||
type: string
|
||||
sourceLabels:
|
||||
description: The source labels select values from existing
|
||||
labels. Their content is concatenated using the configured
|
||||
separator and matched against the configured regular
|
||||
expression for the replace, keep, and drop actions.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
targetLabel:
|
||||
description: Label to which the resulting value is written
|
||||
in a replace action. It is mandatory for replace actions.
|
||||
Regex capture groups are available.
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
scheme:
|
||||
description: HTTP scheme to use for scraping.
|
||||
type: string
|
||||
scrapeTimeout:
|
||||
description: Timeout after which the scrape is ended
|
||||
type: string
|
||||
targetPort:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
description: 'Deprecated: Use ''port'' instead.'
|
||||
x-kubernetes-int-or-string: true
|
||||
type: object
|
||||
type: array
|
||||
podTargetLabels:
|
||||
description: PodTargetLabels transfers labels on the Kubernetes Pod
|
||||
onto the target.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
sampleLimit:
|
||||
description: SampleLimit defines per-scrape limit on number of scraped
|
||||
samples that will be accepted.
|
||||
format: int64
|
||||
type: integer
|
||||
selector:
|
||||
description: Selector to select Pod objects.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the key
|
||||
and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship to
|
||||
a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- podMetricsEndpoints
|
||||
- selector
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
@@ -1,212 +0,0 @@
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.4
|
||||
creationTimestamp: null
|
||||
name: probes.monitoring.coreos.com
|
||||
spec:
|
||||
group: monitoring.coreos.com
|
||||
names:
|
||||
kind: Probe
|
||||
listKind: ProbeList
|
||||
plural: probes
|
||||
singular: probe
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Probe defines monitoring for a set of static targets or ingresses.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: Specification of desired Ingress selection for target discovery
|
||||
by Prometheus.
|
||||
properties:
|
||||
interval:
|
||||
description: Interval at which targets are probed using the configured
|
||||
prober. If not specified Prometheus' global scrape interval is used.
|
||||
type: string
|
||||
jobName:
|
||||
description: The job name assigned to scraped metrics by default.
|
||||
type: string
|
||||
module:
|
||||
description: 'The module to use for probing specifying how to probe
|
||||
the target. Example module configuring in the blackbox exporter:
|
||||
https://github.com/prometheus/blackbox_exporter/blob/master/example.yml'
|
||||
type: string
|
||||
prober:
|
||||
description: Specification for the prober to use for probing targets.
|
||||
The prober.URL parameter is required. Targets cannot be probed if
|
||||
left empty.
|
||||
properties:
|
||||
path:
|
||||
description: Path to collect metrics from. Defaults to `/probe`.
|
||||
type: string
|
||||
scheme:
|
||||
description: HTTP scheme to use for scraping. Defaults to `http`.
|
||||
type: string
|
||||
url:
|
||||
description: Mandatory URL of the prober.
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
scrapeTimeout:
|
||||
description: Timeout for scraping metrics from the Prometheus exporter.
|
||||
type: string
|
||||
targets:
|
||||
description: Targets defines a set of static and/or dynamically discovered
|
||||
targets to be probed using the prober.
|
||||
properties:
|
||||
ingress:
|
||||
description: Ingress defines the set of dynamically discovered
|
||||
ingress objects which hosts are considered for probing.
|
||||
properties:
|
||||
namespaceSelector:
|
||||
description: Select Ingress objects by namespace.
|
||||
properties:
|
||||
any:
|
||||
description: Boolean describing whether all namespaces
|
||||
are selected in contrast to a list restricting them.
|
||||
type: boolean
|
||||
matchNames:
|
||||
description: List of namespace names.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
relabelingConfigs:
|
||||
description: 'RelabelConfigs to apply to samples before ingestion.
|
||||
More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
|
||||
items:
|
||||
description: 'RelabelConfig allows dynamic rewriting of
|
||||
the label set, being applied to samples before ingestion.
|
||||
It defines `<metric_relabel_configs>`-section of Prometheus
|
||||
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
|
||||
properties:
|
||||
action:
|
||||
description: Action to perform based on regex matching.
|
||||
Default is 'replace'
|
||||
type: string
|
||||
modulus:
|
||||
description: Modulus to take of the hash of the source
|
||||
label values.
|
||||
format: int64
|
||||
type: integer
|
||||
regex:
|
||||
description: Regular expression against which the extracted
|
||||
value is matched. Default is '(.*)'
|
||||
type: string
|
||||
replacement:
|
||||
description: Replacement value against which a regex
|
||||
replace is performed if the regular expression matches.
|
||||
Regex capture groups are available. Default is '$1'
|
||||
type: string
|
||||
separator:
|
||||
description: Separator placed between concatenated source
|
||||
label values. default is ';'.
|
||||
type: string
|
||||
sourceLabels:
|
||||
description: The source labels select values from existing
|
||||
labels. Their content is concatenated using the configured
|
||||
separator and matched against the configured regular
|
||||
expression for the replace, keep, and drop actions.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
targetLabel:
|
||||
description: Label to which the resulting value is written
|
||||
in a replace action. It is mandatory for replace actions.
|
||||
Regex capture groups are available.
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
selector:
|
||||
description: Select Ingress objects by labels.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector
|
||||
that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In, NotIn,
|
||||
Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values.
|
||||
If the operator is In or NotIn, the values array
|
||||
must be non-empty. If the operator is Exists or
|
||||
DoesNotExist, the values array must be empty.
|
||||
This array is replaced during a strategic merge
|
||||
patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field is
|
||||
"key", the operator is "In", and the values array contains
|
||||
only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
staticConfig:
|
||||
description: 'StaticConfig defines static targets which are considers
|
||||
for probing. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#static_config.'
|
||||
properties:
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Labels assigned to all metrics scraped from the
|
||||
targets.
|
||||
type: object
|
||||
static:
|
||||
description: Targets is a list of URLs to probe using the
|
||||
configured prober.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,94 +0,0 @@
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.4
|
||||
creationTimestamp: null
|
||||
name: prometheusrules.monitoring.coreos.com
|
||||
spec:
|
||||
group: monitoring.coreos.com
|
||||
names:
|
||||
kind: PrometheusRule
|
||||
listKind: PrometheusRuleList
|
||||
plural: prometheusrules
|
||||
singular: prometheusrule
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: PrometheusRule defines alerting rules for a Prometheus instance
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: Specification of desired alerting rule definitions for Prometheus.
|
||||
properties:
|
||||
groups:
|
||||
description: Content of Prometheus rule file
|
||||
items:
|
||||
description: 'RuleGroup is a list of sequentially evaluated recording
|
||||
and alerting rules. Note: PartialResponseStrategy is only used
|
||||
by ThanosRuler and will be ignored by Prometheus instances. Valid
|
||||
values for this field are ''warn'' or ''abort''. More info: https://github.com/thanos-io/thanos/blob/master/docs/components/rule.md#partial-response'
|
||||
properties:
|
||||
interval:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
partial_response_strategy:
|
||||
type: string
|
||||
rules:
|
||||
items:
|
||||
description: Rule describes an alerting or recording rule.
|
||||
properties:
|
||||
alert:
|
||||
type: string
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
expr:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
x-kubernetes-int-or-string: true
|
||||
for:
|
||||
type: string
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
record:
|
||||
type: string
|
||||
required:
|
||||
- expr
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- name
|
||||
- rules
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,82 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/name: prometheus-operator
|
||||
app.kubernetes.io/version: v0.41.1
|
||||
name: prometheus-operator
|
||||
rules:
|
||||
- apiGroups:
|
||||
- monitoring.coreos.com
|
||||
resources:
|
||||
- alertmanagers
|
||||
- alertmanagers/finalizers
|
||||
- prometheuses
|
||||
- prometheuses/finalizers
|
||||
- thanosrulers
|
||||
- thanosrulers/finalizers
|
||||
- servicemonitors
|
||||
- podmonitors
|
||||
- probes
|
||||
- prometheusrules
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- statefulsets
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
- secrets
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- list
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- services
|
||||
- services/finalizers
|
||||
- endpoints
|
||||
verbs:
|
||||
- get
|
||||
- create
|
||||
- update
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes
|
||||
verbs:
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- namespaces
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- authentication.k8s.io
|
||||
resources:
|
||||
- tokenreviews
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- authorization.k8s.io
|
||||
resources:
|
||||
- subjectaccessreviews
|
||||
verbs:
|
||||
- create
|
||||
@@ -1,34 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/name: prometheus-operator
|
||||
app.kubernetes.io/version: v0.41.1
|
||||
name: prometheus-operator
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: prometheus-operator
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: prometheus-operator
|
||||
namespace: monitoring
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/name: prometheus-operator
|
||||
app.kubernetes.io/version: v0.41.1
|
||||
name: prometheus-operator-admin
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: "cluster-admin"
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: prometheus-operator
|
||||
namespace: monitoring
|
||||
@@ -1,60 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/name: prometheus-operator
|
||||
app.kubernetes.io/version: v0.41.1
|
||||
name: prometheus-operator
|
||||
namespace: monitoring
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/name: prometheus-operator
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/name: prometheus-operator
|
||||
app.kubernetes.io/version: v0.41.1
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --kubelet-service=kube-system/kubelet
|
||||
- --logtostderr=true
|
||||
- --config-reloader-image=jimmidyson/configmap-reload:v0.4.0
|
||||
- --prometheus-config-reloader=quay.io/coreos/prometheus-config-reloader:v0.41.1
|
||||
image: quay.io/coreos/prometheus-operator:v0.41.1
|
||||
name: prometheus-operator
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
resources:
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 200Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
- args:
|
||||
- --logtostderr
|
||||
- --secure-listen-address=:8443
|
||||
- --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
|
||||
- --upstream=http://127.0.0.1:8080/
|
||||
image: quay.io/coreos/kube-rbac-proxy:v0.4.1
|
||||
name: kube-rbac-proxy
|
||||
ports:
|
||||
- containerPort: 8443
|
||||
name: https
|
||||
securityContext:
|
||||
runAsUser: 65534
|
||||
nodeSelector:
|
||||
beta.kubernetes.io/os: linux
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65534
|
||||
serviceAccountName: prometheus-operator
|
||||
@@ -1,18 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/name: prometheus-operator
|
||||
app.kubernetes.io/version: v0.41.1
|
||||
name: prometheus-operator
|
||||
namespace: monitoring
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- name: https
|
||||
port: 8443
|
||||
targetPort: https
|
||||
selector:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/name: prometheus-operator
|
||||
@@ -1,9 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/name: prometheus-operator
|
||||
app.kubernetes.io/version: v0.41.1
|
||||
name: prometheus-operator
|
||||
namespace: monitoring
|
||||
@@ -1,6 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: vela-core
|
||||
description: A Helm chart for Kubernetes
|
||||
description: A Helm chart for Kube Vela core
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -101,16 +101,29 @@ spec:
|
||||
to match a value.
|
||||
properties:
|
||||
fieldPath:
|
||||
description: FieldPath specifies got value from
|
||||
workload/trait object
|
||||
type: string
|
||||
op:
|
||||
description: ConditionOperator specifies the operator
|
||||
to match a value.
|
||||
type: string
|
||||
value:
|
||||
description: Value specifies an expected value This
|
||||
is mutually exclusive with ValueFrom
|
||||
type: string
|
||||
valueFrom:
|
||||
description: ValueFrom specifies expected value
|
||||
from AppConfig This is mutually exclusive with
|
||||
Value
|
||||
properties:
|
||||
fieldPath:
|
||||
type: string
|
||||
required:
|
||||
- fieldPath
|
||||
type: object
|
||||
required:
|
||||
- op
|
||||
- value
|
||||
type: object
|
||||
type: array
|
||||
fieldPath:
|
||||
@@ -233,16 +246,29 @@ spec:
|
||||
requirement to match a value.
|
||||
properties:
|
||||
fieldPath:
|
||||
description: FieldPath specifies got value
|
||||
from workload/trait object
|
||||
type: string
|
||||
op:
|
||||
description: ConditionOperator specifies the
|
||||
operator to match a value.
|
||||
type: string
|
||||
value:
|
||||
description: Value specifies an expected value
|
||||
This is mutually exclusive with ValueFrom
|
||||
type: string
|
||||
valueFrom:
|
||||
description: ValueFrom specifies expected
|
||||
value from AppConfig This is mutually exclusive
|
||||
with Value
|
||||
properties:
|
||||
fieldPath:
|
||||
type: string
|
||||
required:
|
||||
- fieldPath
|
||||
type: object
|
||||
required:
|
||||
- op
|
||||
- value
|
||||
type: object
|
||||
type: array
|
||||
fieldPath:
|
||||
@@ -338,6 +364,8 @@ spec:
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
reason:
|
||||
type: string
|
||||
to:
|
||||
description: DependencyToObject represents the object that
|
||||
dependency data goes to.
|
||||
@@ -365,10 +393,47 @@ spec:
|
||||
type: object
|
||||
required:
|
||||
- from
|
||||
- reason
|
||||
- to
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
historyWorkloads:
|
||||
description: HistoryWorkloads will record history but still working
|
||||
revision workloads.
|
||||
items:
|
||||
description: HistoryWorkload contain the old component revision
|
||||
that are still running
|
||||
properties:
|
||||
revision:
|
||||
description: component revision of this workload
|
||||
type: string
|
||||
workloadRef:
|
||||
description: Reference to running workload.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: APIVersion of the referenced object.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referenced object.
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
uid:
|
||||
description: UID of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
observedGeneration:
|
||||
description: The generation observed by the appConfig controller.
|
||||
format: int64
|
||||
type: integer
|
||||
status:
|
||||
description: Status is a place holder for a customized controller
|
||||
to fill if it needs a single place to summarize the status of the
|
||||
@@ -482,8 +547,6 @@ spec:
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- dependency
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.5
|
||||
creationTimestamp: null
|
||||
name: applicationdeployments.core.oam.dev
|
||||
spec:
|
||||
group: core.oam.dev
|
||||
names:
|
||||
categories:
|
||||
- oam
|
||||
kind: ApplicationDeployment
|
||||
listKind: ApplicationDeploymentList
|
||||
plural: applicationdeployments
|
||||
singular: applicationdeployment
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: ApplicationDeployment is the Schema for the ApplicationDeployment
|
||||
API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: ApplicationDeploymentSpec defines the desired state of ApplicationDeployment
|
||||
type: object
|
||||
status:
|
||||
description: ApplicationDeploymentStatus defines the observed state of
|
||||
ApplicationDeployment
|
||||
properties:
|
||||
conditions:
|
||||
description: Conditions of the resource.
|
||||
items:
|
||||
description: A Condition that may apply to a resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: LastTransitionTime is the last time this condition
|
||||
transitioned from one status to another.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: A Message containing details about this condition's
|
||||
last transition from one status to another, if any.
|
||||
type: string
|
||||
reason:
|
||||
description: A Reason for this condition's last transition from
|
||||
one status to another.
|
||||
type: string
|
||||
status:
|
||||
description: Status of this condition; is it currently True,
|
||||
False, or Unknown?
|
||||
type: string
|
||||
type:
|
||||
description: Type of this condition. At most one of each condition
|
||||
type may apply to a resource at any point in time.
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
@@ -72,6 +72,7 @@ spec:
|
||||
will specify parameter values using this name.
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or not a value for this
|
||||
parameter must be supplied when authoring an ApplicationConfiguration.
|
||||
type: boolean
|
||||
@@ -82,7 +83,9 @@ spec:
|
||||
type: array
|
||||
workload:
|
||||
description: A Workload that will be created for each ApplicationConfiguration
|
||||
that includes this Component. Workloads must be defined by a WorkloadDefinition.
|
||||
that includes this Component. Workload is an instance of a workloadDefinition.
|
||||
We either use the GVK info or a special "type" field in the workload
|
||||
to associate the content of the workload with its workloadDefinition
|
||||
type: object
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
@@ -137,6 +140,10 @@ spec:
|
||||
- name
|
||||
- revision
|
||||
type: object
|
||||
observedGeneration:
|
||||
description: The generation observed by the component controller.
|
||||
format: int64
|
||||
type: integer
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
|
||||
@@ -119,10 +119,76 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
health:
|
||||
type: string
|
||||
healthConditions:
|
||||
description: WorkloadHealthConditions represents health condition
|
||||
of workloads in the scope
|
||||
items:
|
||||
description: WorkloadHealthCondition represents informative health
|
||||
condition.
|
||||
properties:
|
||||
componentName:
|
||||
description: ComponentName represents the component name if
|
||||
target is a workload
|
||||
type: string
|
||||
diagnosis:
|
||||
type: string
|
||||
healthStatus:
|
||||
description: HealthStatus represents health status strings.
|
||||
type: string
|
||||
targetWorkload:
|
||||
description: A TypedReference refers to an object by Name, Kind,
|
||||
and APIVersion. It is commonly used to reference cluster-scoped
|
||||
objects or objects where the namespace is already known.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: APIVersion of the referenced object.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referenced object.
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
uid:
|
||||
description: UID of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
workloadStatus:
|
||||
description: WorkloadStatus represents status of workloads whose
|
||||
HealthStatus is UNKNOWN.
|
||||
type: string
|
||||
required:
|
||||
- healthStatus
|
||||
type: object
|
||||
type: array
|
||||
scopeHealthCondition:
|
||||
description: ScopeHealthCondition represents health condition summary
|
||||
of the scope
|
||||
properties:
|
||||
healthStatus:
|
||||
description: HealthStatus represents health status strings.
|
||||
type: string
|
||||
healthyWorkloads:
|
||||
format: int64
|
||||
type: integer
|
||||
total:
|
||||
format: int64
|
||||
type: integer
|
||||
unhealthyWorkloads:
|
||||
format: int64
|
||||
type: integer
|
||||
unknownWorkloads:
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- healthStatus
|
||||
type: object
|
||||
required:
|
||||
- health
|
||||
- scopeHealthCondition
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
|
||||
@@ -85,6 +85,16 @@ spec:
|
||||
builders
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
podSpecPath:
|
||||
description: PodSpecPath indicates where/if this workload has K8s
|
||||
podSpec field if one workload has podSpec, trait can do lot's of
|
||||
assumption such as port, env, volume fields.
|
||||
type: string
|
||||
revisionLabel:
|
||||
description: RevisionLabel indicates which label for underlying resources(e.g.
|
||||
pods) of this workload can be used by trait to create resource selectors(e.g.
|
||||
label selector for pods).
|
||||
type: string
|
||||
required:
|
||||
- definitionRef
|
||||
type: object
|
||||
|
||||
@@ -6,22 +6,22 @@ metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.5
|
||||
creationTimestamp: null
|
||||
name: containerizeds.standard.oam.dev
|
||||
name: podspecworkloads.standard.oam.dev
|
||||
spec:
|
||||
group: standard.oam.dev
|
||||
names:
|
||||
categories:
|
||||
- oam
|
||||
kind: Containerized
|
||||
listKind: ContainerizedList
|
||||
plural: containerizeds
|
||||
singular: containerized
|
||||
kind: PodSpecWorkload
|
||||
listKind: PodSpecWorkloadList
|
||||
plural: podspecworkloads
|
||||
singular: podspecworkload
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Containerized is the Schema for the containerizeds API
|
||||
description: PodSpecWorkload is the Schema for the PodSpec API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
@@ -36,11 +36,11 @@ spec:
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: ContainerizedSpec defines the desired state of Containerized
|
||||
description: PodSpecWorkloadSpec defines the desired state of PodSpecWorkload
|
||||
properties:
|
||||
podSpec:
|
||||
description: PodSpec describes the pods that will be created, we omit
|
||||
the meta part as it will be exactly the same as the containerized
|
||||
the meta part as it will be exactly the same as the PodSpecWorkload
|
||||
properties:
|
||||
activeDeadlineSeconds:
|
||||
description: Optional duration in seconds the pod may be active
|
||||
@@ -5694,7 +5694,7 @@ spec:
|
||||
- replicas
|
||||
type: object
|
||||
status:
|
||||
description: ContainerizedStatus defines the observed state of Containerized
|
||||
description: PodSpecWorkloadStatus defines the observed state of PodSpecWorkload
|
||||
properties:
|
||||
conditions:
|
||||
description: Conditions of the resource.
|
||||
255
charts/vela-core/crds/standard.oam.dev_routes.yaml
Normal file
255
charts/vela-core/crds/standard.oam.dev_routes.yaml
Normal file
@@ -0,0 +1,255 @@
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.5
|
||||
creationTimestamp: null
|
||||
name: routes.standard.oam.dev
|
||||
spec:
|
||||
group: standard.oam.dev
|
||||
names:
|
||||
categories:
|
||||
- oam
|
||||
kind: Route
|
||||
listKind: RouteList
|
||||
plural: routes
|
||||
singular: route
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Route is the Schema for the routes API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: RouteSpec defines the desired state of Route
|
||||
properties:
|
||||
host:
|
||||
description: Host is the host of the route
|
||||
type: string
|
||||
provider:
|
||||
description: Provider indicate which ingress controller implementation
|
||||
the route trait will use, by default it's nginx-ingress
|
||||
type: string
|
||||
rules:
|
||||
description: Rules contain multiple rules of route
|
||||
items:
|
||||
description: Rule defines to route rule
|
||||
properties:
|
||||
backend:
|
||||
description: Backend indicate how to connect backend service
|
||||
If it's nil, will auto discovery
|
||||
properties:
|
||||
backendService:
|
||||
description: BackendService specifies the backend K8s service
|
||||
and port, it's optional
|
||||
properties:
|
||||
port:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
description: Port allow you direct specify backend service
|
||||
port.
|
||||
x-kubernetes-int-or-string: true
|
||||
serviceName:
|
||||
description: ServiceName allow you direct specify K8s
|
||||
service for backend service.
|
||||
type: string
|
||||
required:
|
||||
- port
|
||||
- serviceName
|
||||
type: object
|
||||
readTimeout:
|
||||
description: ReadTimeout used for setting read timeout duration
|
||||
for backend service, the unit is second.
|
||||
type: integer
|
||||
sendTimeout:
|
||||
description: SendTimeout used for setting send timeout duration
|
||||
for backend service, the unit is second.
|
||||
type: integer
|
||||
type: object
|
||||
customHeaders:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: CustomHeaders pass a custom list of headers to
|
||||
the backend service.
|
||||
type: object
|
||||
defaultBackend:
|
||||
description: DefaultBackend will become the ingress default
|
||||
backend if the backend is not available
|
||||
properties:
|
||||
apiVersion:
|
||||
description: APIVersion of the referenced object.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referenced object.
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
uid:
|
||||
description: UID of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
name:
|
||||
description: Name will become the suffix of underlying ingress
|
||||
created by this rule, if not, will use index as suffix.
|
||||
type: string
|
||||
path:
|
||||
description: Path is location Path, default for "/"
|
||||
type: string
|
||||
rewriteTarget:
|
||||
description: RewriteTarget will rewrite request from Path to
|
||||
RewriteTarget path.
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
tls:
|
||||
description: TLS indicate route trait will create SSL secret using
|
||||
cert-manager with specified issuer If this is nil, route trait will
|
||||
use a selfsigned issuer
|
||||
properties:
|
||||
issuerName:
|
||||
type: string
|
||||
type:
|
||||
default: Issuer
|
||||
description: Type indicate the issuer is ClusterIssuer or Issuer(namespace
|
||||
issuer), by default, it's Issuer
|
||||
type: string
|
||||
type: object
|
||||
workloadRef:
|
||||
description: WorkloadReference to the workload whose metrics needs
|
||||
to be exposed
|
||||
properties:
|
||||
apiVersion:
|
||||
description: APIVersion of the referenced object.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referenced object.
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
uid:
|
||||
description: UID of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
status:
|
||||
description: RouteStatus defines the observed state of Route
|
||||
properties:
|
||||
conditions:
|
||||
description: Conditions of the resource.
|
||||
items:
|
||||
description: A Condition that may apply to a resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: LastTransitionTime is the last time this condition
|
||||
transitioned from one status to another.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: A Message containing details about this condition's
|
||||
last transition from one status to another, if any.
|
||||
type: string
|
||||
reason:
|
||||
description: A Reason for this condition's last transition from
|
||||
one status to another.
|
||||
type: string
|
||||
status:
|
||||
description: Status of this condition; is it currently True,
|
||||
False, or Unknown?
|
||||
type: string
|
||||
type:
|
||||
description: Type of this condition. At most one of each condition
|
||||
type may apply to a resource at any point in time.
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
ingresses:
|
||||
items:
|
||||
description: A TypedReference refers to an object by Name, Kind,
|
||||
and APIVersion. It is commonly used to reference cluster-scoped
|
||||
objects or objects where the namespace is already known.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: APIVersion of the referenced object.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referenced object.
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
uid:
|
||||
description: UID of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
service:
|
||||
description: A TypedReference refers to an object by Name, Kind, and
|
||||
APIVersion. It is commonly used to reference cluster-scoped objects
|
||||
or objects where the namespace is already known.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: APIVersion of the referenced object.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referenced object.
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
uid:
|
||||
description: UID of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
@@ -1,42 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: backend-service
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
|
||||
definition.oam.dev/kind: "ContainerizedWorkload"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: containerizeds.standard.oam.dev
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ContainerizedWorkload"
|
||||
metadata: name: containerized.name
|
||||
spec: {
|
||||
containers: [{
|
||||
image: containerized.image
|
||||
name: containerized.name
|
||||
ports: [{
|
||||
containerPort: containerized.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
containerized: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,43 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: containerizeds.standard.oam.dev
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
|
||||
definition.oam.dev/kind: "ContainerizedWorkload"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: containerizeds.standard.oam.dev
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ContainerizedWorkload"
|
||||
metadata:
|
||||
name: containerized.name
|
||||
spec: {
|
||||
containers: [{
|
||||
image: containerized.image
|
||||
name: containerized.name
|
||||
ports: [{
|
||||
containerPort: containerized.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
containerized: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
39
charts/vela-core/templates/definitions/backend.yaml
Normal file
39
charts/vela-core/templates/definitions/backend.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: backend
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
|
||||
definition.oam.dev/kind: "PodSpecWorkload"
|
||||
definition.oam.dev/description: "Backend worker without ports exposed"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: podspecworkload.standard.oam.dev
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
extension:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "PodSpecWorkload"
|
||||
metadata:
|
||||
name: parameter.name
|
||||
spec: {
|
||||
replicas: 1
|
||||
podSpec: {
|
||||
containers: [{
|
||||
image: parameter.image
|
||||
name: parameter.name
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
#backend: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
}
|
||||
parameter: #backend
|
||||
|
||||
@@ -2,6 +2,10 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: ScopeDefinition
|
||||
metadata:
|
||||
name: healthscopes.core.oam.dev
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
|
||||
definition.oam.dev/kind: HealthScope
|
||||
namespace: default
|
||||
spec:
|
||||
workloadRefsPath: spec.workloadRefs
|
||||
allowComponentOverlap: true
|
||||
@@ -1,25 +1,31 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
|
||||
definition.oam.dev/kind: "ManualScalerTrait"
|
||||
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
|
||||
definition.oam.dev/kind: ManualScalerTrait
|
||||
definition.oam.dev/description: "Scale replica for workload"
|
||||
name: scaler
|
||||
namespace: default
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- core.oam.dev/v1alpha2.ContainerizedWorkload
|
||||
- apps/v1.Deployment
|
||||
- webservice
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
template: |-
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: scale.replica
|
||||
replicaCount: parameter.replica
|
||||
}
|
||||
}
|
||||
scale: {
|
||||
#scale: {
|
||||
//+short=r
|
||||
replica: *2 | int
|
||||
}
|
||||
parameter: #scale
|
||||
53
charts/vela-core/templates/definitions/metricstraits.yaml
Normal file
53
charts/vela-core/templates/definitions/metricstraits.yaml
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
labels:
|
||||
mornitoring: oam
|
||||
name: monitoring
|
||||
|
||||
---
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: metric
|
||||
namespace: default
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
|
||||
definition.oam.dev/kind: MetricsTrait
|
||||
definition.oam.dev/description: "Add metric monitoring for workload"
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
- backend
|
||||
- task
|
||||
- containerizedworkloads.core.oam.dev
|
||||
- clonesetworkloads.apps.kruise.io
|
||||
- deployments.apps
|
||||
- statefulsets.apps
|
||||
definitionRef:
|
||||
name: metricstraits.standard.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
extension:
|
||||
template: |-
|
||||
#metrics: {
|
||||
// +usage=format of the metrics, default as prometheus
|
||||
// +short=f
|
||||
format: *"prometheus" | string
|
||||
path: *"/metrics" | string
|
||||
scheme: *"http" | string
|
||||
enabled: *true | bool
|
||||
port: *8080 | >=1024 & uint16
|
||||
// +usage= the label selector for the pods, default is the workload labels
|
||||
selector?: [string]: string
|
||||
}
|
||||
spec: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "MetricsTrait"
|
||||
spec: {
|
||||
scrapeService: parameter
|
||||
}
|
||||
}
|
||||
parameter: #metrics
|
||||
output: {}
|
||||
|
||||
48
charts/vela-core/templates/definitions/podspecworkload.yaml
Normal file
48
charts/vela-core/templates/definitions/podspecworkload.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: podspecworkloads.standard.oam.dev
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
|
||||
definition.oam.dev/kind: "PodSpecWorkload"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: podspecworkloads.standard.oam.dev
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
extension:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "PodSpecWorkload"
|
||||
metadata:
|
||||
name: parameter.name
|
||||
spec: {
|
||||
replicas: 1
|
||||
podSpec: {
|
||||
containers: [{
|
||||
image: parameter.image
|
||||
name: parameter.name
|
||||
ports: [{
|
||||
containerPort: parameter.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
#webservice: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
parameter: #webservice
|
||||
|
||||
35
charts/vela-core/templates/definitions/routetrait.yaml
Normal file
35
charts/vela-core/templates/definitions/routetrait.yaml
Normal file
@@ -0,0 +1,35 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: route
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
|
||||
definition.oam.dev/kind: Route
|
||||
definition.oam.dev/description: "Add a route for workload"
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- core.oam.dev/v1alpha2.ContainerizedWorkload
|
||||
- standard.oam.dev/v1alpha1.PodSpecWorkload
|
||||
- deployments.apps
|
||||
- webservice
|
||||
workloadRefPath: spec.workloadRef
|
||||
definitionRef:
|
||||
name: routes.standard.oam.dev
|
||||
extension:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Route"
|
||||
spec: {
|
||||
host: parameter.domain
|
||||
tls: {
|
||||
issuerName: parameter.issuer
|
||||
}
|
||||
}
|
||||
}
|
||||
#route: {
|
||||
domain: *"" | string
|
||||
issuer: *"" | string
|
||||
}
|
||||
parameter: #route
|
||||
|
||||
49
charts/vela-core/templates/definitions/tasks.yaml
Normal file
49
charts/vela-core/templates/definitions/tasks.yaml
Normal file
@@ -0,0 +1,49 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: task
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "v1"
|
||||
definition.oam.dev/kind: "Job"
|
||||
definition.oam.dev/description: "One-time off task or job"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: jobs
|
||||
extension:
|
||||
defaultTraits:
|
||||
- monitor
|
||||
- logging
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "v1"
|
||||
kind: "Job"
|
||||
metadata: name: parameter.name
|
||||
spec: {
|
||||
parallelism: parameter.count
|
||||
completions: parameter.count
|
||||
template:
|
||||
spec:
|
||||
containers: [{
|
||||
image: parameter.image
|
||||
name: parameter.name
|
||||
ports: [{
|
||||
containerPort: parameter.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
#task: {
|
||||
// +usage=specify number of tasks to run in parallel
|
||||
// +short=c
|
||||
count: *1 | int
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
parameter: #task
|
||||
48
charts/vela-core/templates/definitions/webservices.yaml
Normal file
48
charts/vela-core/templates/definitions/webservices.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: webservice
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
|
||||
definition.oam.dev/kind: "PodSpecWorkload"
|
||||
definition.oam.dev/description: "Long running service with ports exposed"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: podspecworkloads.standard.oam.dev
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
extension:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "PodSpecWorkload"
|
||||
metadata:
|
||||
name: parameter.name
|
||||
spec: {
|
||||
replicas: 1
|
||||
podSpec: {
|
||||
containers: [{
|
||||
image: parameter.image
|
||||
name: parameter.name
|
||||
ports: [{
|
||||
containerPort: parameter.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
#webservice: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
parameter: #webservice
|
||||
@@ -18,7 +18,7 @@ metadata:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: manager-rolebinding
|
||||
name: {{ include "kubevela.fullname" . }}:manager-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
@@ -33,7 +33,7 @@ subjects:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: leader-election-role
|
||||
name: {{ include "kubevela.fullname" . }}:leader-election-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
@@ -66,11 +66,11 @@ rules:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: leader-election-rolebinding
|
||||
name: {{ include "kubevela.fullname" . }}:leader-election-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: leader-election-role
|
||||
name: {{ include "kubevela.fullname" . }}:leader-election-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
@@ -80,6 +80,7 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "kubevela.labels" . | nindent 4 }}
|
||||
spec:
|
||||
@@ -110,6 +111,7 @@ spec:
|
||||
- "--use-webhook=true"
|
||||
- "--webhook-port={{ .Values.webhookService.port }}"
|
||||
- "--webhook-cert-dir={{ .Values.certificate.mountPath }}"
|
||||
- "--health-addr=:{{ .Values.healthCheck.port }}"
|
||||
{{ end }}
|
||||
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
|
||||
imagePullPolicy: {{ quote .Values.image.pullPolicy }}
|
||||
@@ -120,6 +122,21 @@ spec:
|
||||
- containerPort: {{ .Values.webhookService.port }}
|
||||
name: webhook-server
|
||||
protocol: TCP
|
||||
- containerPort: {{ .Values.healthCheck.port }}
|
||||
name: healthz
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: healthz
|
||||
initialDelaySeconds: 90
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: healthz
|
||||
initialDelaySeconds: 90
|
||||
periodSeconds: 5
|
||||
volumeMounts:
|
||||
- mountPath: {{ .Values.certificate.mountPath }}
|
||||
name: tls-cert-vol
|
||||
@@ -141,4 +158,4 @@ spec:
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: metricstraits.standard.oam.dev
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- containerizedworkloads.core.oam.dev
|
||||
- clonesetworkloads.apps.kruise.io
|
||||
- deployments.apps
|
||||
- statefulsets.apps
|
||||
definitionRef:
|
||||
name: metricstraits.standard.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "MetricsTrait"
|
||||
metadata:
|
||||
name: metricstraits.name
|
||||
spec: {
|
||||
containers: [{
|
||||
image: containerized.image
|
||||
name: containerized.name
|
||||
ports: [{
|
||||
containerPort: containerized.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
containerized: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: task
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "v1"
|
||||
definition.oam.dev/kind: "Job"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: jobs
|
||||
extension:
|
||||
defaultTraits:
|
||||
- monitor
|
||||
- logging
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "v1"
|
||||
kind: "Job"
|
||||
metadata: name: task
|
||||
spec: {
|
||||
parallelism: taskSpec.count
|
||||
completions: taskSpec.count
|
||||
template:
|
||||
spec:
|
||||
containers: [{
|
||||
image: taskSpec.image
|
||||
name: taskSpec.name
|
||||
ports: [{
|
||||
containerPort: taskSpec.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
taskSpec: {
|
||||
// +usage=specify number of tasks to run in parallel
|
||||
// +short=c
|
||||
count: *1 | int
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
22
charts/vela-core/templates/velaConfig.yaml
Normal file
22
charts/vela-core/templates/velaConfig.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: vela-config
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
certificates.cert-manager.io: |
|
||||
{
|
||||
"repo": "jetstack",
|
||||
"urL": "https://charts.jetstack.io",
|
||||
"name": "cert-manager",
|
||||
"namespace": "cert-manager",
|
||||
"version": "1.0.3"
|
||||
}
|
||||
servicemonitors.monitoring.coreos.com: |
|
||||
{
|
||||
"repo": "prometheus-community",
|
||||
"urL": "https://prometheus-community.github.io/helm-charts",
|
||||
"name": "kube-prometheus-stack",
|
||||
"namespace": "monitoring",
|
||||
"version": "9.4.4"
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: mutating-webhook-configuration
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Values.certificate.certificateName }}
|
||||
webhooks:
|
||||
@@ -31,7 +31,7 @@ webhooks:
|
||||
service:
|
||||
name: {{ template "kubevela.name" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /mutate-standard-oam-dev-v1alpha1-containerized
|
||||
path: /mutate-standard-oam-dev-v1alpha1-podspecworkload
|
||||
failurePolicy: Fail
|
||||
name: mcontainerized.kb.io
|
||||
rules:
|
||||
@@ -43,14 +43,14 @@ webhooks:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- Containerized
|
||||
- podspecworkloads
|
||||
|
||||
---
|
||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: validating-webhook-configuration
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Values.certificate.certificateName }}
|
||||
webhooks:
|
||||
@@ -78,7 +78,7 @@ webhooks:
|
||||
service:
|
||||
name: {{ template "kubevela.name" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validate-standard-oam-dev-v1alpha1-containerized
|
||||
path: /validate-standard-oam-dev-v1alpha1-podspecworkload
|
||||
failurePolicy: Fail
|
||||
name: vcontainerized.kb.io
|
||||
rules:
|
||||
@@ -89,15 +89,15 @@ webhooks:
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- Containerized
|
||||
- podspecworkloads
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "kubevela.name" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "kubevela.labels" . | nindent 4 }}
|
||||
spec:
|
||||
@@ -113,7 +113,7 @@ spec:
|
||||
---
|
||||
# The following manifests contain a self-signed issuer CR and a certificate CR.
|
||||
# More document can be found at https://docs.cert-manager.io
|
||||
apiVersion: cert-manager.io/v1alpha2
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: {{ .Values.certificate.issuerName | quote }}
|
||||
@@ -121,7 +121,7 @@ spec:
|
||||
selfSigned: {}
|
||||
|
||||
---
|
||||
apiVersion: cert-manager.io/v1alpha2
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: {{ .Values.certificate.certificateName }}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: web-service
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
|
||||
definition.oam.dev/kind: "ContainerizedWorkload"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: containerizeds.standard.oam.dev
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ContainerizedWorkload"
|
||||
metadata:
|
||||
name: containerized.name
|
||||
spec: {
|
||||
containers: [{
|
||||
image: containerized.image
|
||||
name: containerized.name
|
||||
ports: [{
|
||||
containerPort: containerized.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
containerized: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
@@ -6,8 +6,8 @@ replicaCount: 1
|
||||
useWebhook: true
|
||||
image:
|
||||
repository: oamdev/vela-core
|
||||
tag: 0.1
|
||||
pullPolicy: IfNotPresent
|
||||
tag: latest
|
||||
pullPolicy: Always
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
@@ -63,6 +63,9 @@ webhookService:
|
||||
type: ClusterIP
|
||||
port: 9443
|
||||
|
||||
healthCheck:
|
||||
port: 9440
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
108
cmd/core/main.go
108
cmd/core/main.go
@@ -1,10 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
@@ -16,26 +20,45 @@ import (
|
||||
injectorcontroller "github.com/oam-dev/trait-injector/controllers"
|
||||
"github.com/oam-dev/trait-injector/pkg/injector"
|
||||
"github.com/oam-dev/trait-injector/pkg/plugin"
|
||||
certmanager "github.com/wonderflow/cert-manager-api/pkg/apis/certmanager/v1"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
velacoreoamdev "github.com/oam-dev/kubevela/api/core.oam.dev/v1alpha2"
|
||||
velacore "github.com/oam-dev/kubevela/api/v1alpha1"
|
||||
velacontroller "github.com/oam-dev/kubevela/pkg/controller"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/dependency"
|
||||
velawebhook "github.com/oam-dev/kubevela/pkg/webhook"
|
||||
)
|
||||
|
||||
var scheme = runtime.NewScheme()
|
||||
const (
|
||||
kubevelaName = "kubevela"
|
||||
)
|
||||
|
||||
var (
|
||||
setupLog = ctrl.Log.WithName(kubevelaName)
|
||||
scheme = runtime.NewScheme()
|
||||
waitSecretTimeout = 90 * time.Second
|
||||
waitSecretInterval = 2 * time.Second
|
||||
)
|
||||
|
||||
func init() {
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
_ = crdv1.AddToScheme(scheme)
|
||||
_ = oamcore.AddToScheme(scheme)
|
||||
_ = monitoring.AddToScheme(scheme)
|
||||
_ = velacore.AddToScheme(scheme)
|
||||
_ = velacoreoamdev.AddToScheme(scheme)
|
||||
_ = injectorv1alpha1.AddToScheme(scheme)
|
||||
_ = certmanager.AddToScheme(scheme)
|
||||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
@@ -47,6 +70,7 @@ func main() {
|
||||
var webhookPort int
|
||||
var useWebhook, useTraitInjector bool
|
||||
var controllerArgs oamcontroller.Args
|
||||
var healthAddr string
|
||||
|
||||
flag.BoolVar(&useWebhook, "use-webhook", false, "Enable Admission Webhook")
|
||||
flag.BoolVar(&useTraitInjector, "use-trait-injector", false, "Enable TraitInjector")
|
||||
@@ -60,6 +84,7 @@ func main() {
|
||||
flag.BoolVar(&logCompress, "log-compress", true, "Enable compression on the rotated logs.")
|
||||
flag.IntVar(&controllerArgs.RevisionLimit, "revision-limit", 50,
|
||||
"RevisionLimit is the maximum number of revisions that will be maintained. The default value is 50.")
|
||||
flag.StringVar(&healthAddr, "health-addr", ":9440", "The address the health endpoint binds to.")
|
||||
flag.Parse()
|
||||
|
||||
// setup logging
|
||||
@@ -79,24 +104,44 @@ func main() {
|
||||
o.DestWritter = w
|
||||
}))
|
||||
|
||||
setupLog := ctrl.Log.WithName("vela-runtime")
|
||||
// install dependency charts first
|
||||
k8sClient, err := client.New(ctrl.GetConfigOrDie(), client.Options{Scheme: scheme})
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to create a kubernetes client")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = dependency.Install(k8sClient); err != nil {
|
||||
setupLog.Error(err, "unable to install the dependency")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
Scheme: scheme,
|
||||
MetricsBindAddress: metricsAddr,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: "vela-runtime",
|
||||
Port: webhookPort,
|
||||
CertDir: certDir,
|
||||
Scheme: scheme,
|
||||
MetricsBindAddress: metricsAddr,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: kubevelaName,
|
||||
Port: webhookPort,
|
||||
CertDir: certDir,
|
||||
HealthProbeBindAddress: healthAddr,
|
||||
})
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to create a controller manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := registerHealthChecks(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to register ready/health checks")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if useWebhook {
|
||||
setupLog.Info("vela webhook enabled, will serving at :" + strconv.Itoa(webhookPort))
|
||||
oamwebhook.Add(mgr)
|
||||
velawebhook.Register(mgr)
|
||||
if err := waitWebhookSecretVolume(certDir, waitSecretTimeout, waitSecretInterval); err != nil {
|
||||
setupLog.Error(err, "unable to get webhook secret")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err = oamv1alpha2.Setup(mgr, controllerArgs, logging.NewLogrLogger(setupLog)); err != nil {
|
||||
@@ -133,3 +178,50 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// registerHealthChecks is used to create readiness&liveness probes
|
||||
func registerHealthChecks(mgr ctrl.Manager) error {
|
||||
setupLog.Info("creating readiness/health check")
|
||||
if err := mgr.AddReadyzCheck("ping", healthz.Ping); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mgr.AddHealthzCheck("ping", healthz.Ping); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// waitWebhookSecretVolume waits for webhook secret ready to avoid mgr running crash
|
||||
func waitWebhookSecretVolume(certDir string, timeout, interval time.Duration) error {
|
||||
start := time.Now()
|
||||
for {
|
||||
time.Sleep(interval)
|
||||
if time.Since(start) > timeout {
|
||||
return fmt.Errorf("getting webhook secret timeout after %s", timeout.String())
|
||||
}
|
||||
setupLog.Info(fmt.Sprintf("waiting webhook secret, time consumed: %d/%d seconds ...",
|
||||
int64(time.Since(start).Seconds()), int64(timeout.Seconds())))
|
||||
if _, err := os.Stat(certDir); !os.IsNotExist(err) {
|
||||
f, _ := os.Open(certDir)
|
||||
defer f.Close()
|
||||
// check if dir is empty
|
||||
if _, err := f.Readdir(1); err == io.EOF {
|
||||
continue
|
||||
}
|
||||
// check if secret files are empty
|
||||
err := filepath.Walk(certDir, func(path string, info os.FileInfo, err error) error {
|
||||
// even Cert dir is created, cert files are still empty for a while
|
||||
if info.Size() == 0 {
|
||||
return errors.New("secret is not ready")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err == nil {
|
||||
setupLog.Info(fmt.Sprintf("webhook secret is ready (time consumed: %d seconds)",
|
||||
int64(time.Since(start).Seconds())))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
66
cmd/core/main_test.go
Normal file
66
cmd/core/main_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var (
|
||||
testdir = "testdir"
|
||||
testTimeout = 2 * time.Second
|
||||
testInterval = 1 * time.Second
|
||||
)
|
||||
|
||||
func TestGinkgo(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "test main")
|
||||
}
|
||||
|
||||
var _ = Describe("test waitSecretVolume", func() {
|
||||
BeforeEach(func() {
|
||||
err := os.MkdirAll(testdir, 0755)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
AfterEach(func() {
|
||||
os.RemoveAll(testdir)
|
||||
})
|
||||
|
||||
When("dir not exist or empty", func() {
|
||||
It("return timeout error", func() {
|
||||
err := waitWebhookSecretVolume(testdir, testTimeout, testInterval)
|
||||
Expect(err).To(HaveOccurred())
|
||||
By("remove dir")
|
||||
os.RemoveAll(testdir)
|
||||
err = waitWebhookSecretVolume(testdir, testTimeout, testInterval)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
When("dir contains empty file", func() {
|
||||
It("return timeout error", func() {
|
||||
By("add empty file")
|
||||
_, err := os.Create(testdir + "/emptyFile")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = waitWebhookSecretVolume(testdir, testTimeout, testInterval)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
When("files in dir are not empty", func() {
|
||||
It("return nil", func() {
|
||||
By("add non-empty file")
|
||||
_, err := os.Create(testdir + "/file")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = ioutil.WriteFile(testdir+"/file", []byte("test"), os.ModeAppend)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = waitWebhookSecretVolume(testdir, testTimeout, testInterval)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
5
cmd/vela/fake/chart_source.go
Normal file
5
cmd/vela/fake/chart_source.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package fake
|
||||
|
||||
// ChartSource is a base64-encoded, gzipped tarball of the default Helm chart(charts/vela-core).
|
||||
// Its value is initialized at build time. This whole file will be rewrite at that time, please don't change this file.
|
||||
var ChartSource string
|
||||
157
cmd/vela/main.go
157
cmd/vela/main.go
@@ -1,174 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/oam-dev/kubevela/api/types"
|
||||
"github.com/oam-dev/kubevela/cmd/vela/fake"
|
||||
"github.com/oam-dev/kubevela/pkg/commands"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/system"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
|
||||
"github.com/crossplane/oam-kubernetes-runtime/apis/core"
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/spf13/cobra"
|
||||
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/klog"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = k8sruntime.NewScheme()
|
||||
)
|
||||
|
||||
// chartTGZSource is a base64-encoded, gzipped tarball of the default Helm chart.
|
||||
// Its value is initialized at build time.
|
||||
var chartTGZSource string
|
||||
|
||||
func init() {
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
|
||||
_ = core.AddToScheme(scheme)
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
command := newCommand()
|
||||
command := commands.NewCommand()
|
||||
|
||||
if err := command.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func newCommand() *cobra.Command {
|
||||
ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
|
||||
|
||||
cmds := &cobra.Command{
|
||||
Use: "vela",
|
||||
DisableFlagParsing: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
allCommands := cmd.Commands()
|
||||
cmd.Printf("✈️ A Micro App Platform for Kubernetes.\n\nUsage:\n vela [flags]\n vela [command]\n\nAvailable Commands:\n\n")
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeStart)
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeApp)
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeTraits)
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeOthers)
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeSystem)
|
||||
cmd.Println("Flags:")
|
||||
cmd.Println(" -h, --help help for vela")
|
||||
cmd.Println()
|
||||
cmd.Println(`Use "vela [command] --help" for more information about a command.`)
|
||||
},
|
||||
SilenceUsage: true,
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
}
|
||||
cmds.PersistentFlags().StringP("env", "e", "", "specify env name for application")
|
||||
restConf, err := config.GetConfig()
|
||||
if err != nil {
|
||||
fmt.Println("get kubeconfig err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
commandArgs := types.Args{
|
||||
Config: restConf,
|
||||
Schema: scheme,
|
||||
}
|
||||
|
||||
if err := system.InitDirs(); err != nil {
|
||||
fmt.Println("InitDir err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cmds.AddCommand(
|
||||
// Getting Start
|
||||
commands.NewInstallCommand(commandArgs, chartTGZSource, ioStream),
|
||||
commands.NewEnvCommand(commandArgs, ioStream),
|
||||
|
||||
// Getting Start
|
||||
NewVersionCommand(),
|
||||
|
||||
// Apps
|
||||
commands.NewAppsCommand(commandArgs, ioStream),
|
||||
|
||||
// Workloads
|
||||
commands.AddCompCommands(commandArgs, ioStream),
|
||||
|
||||
// Capability Systems
|
||||
commands.CapabilityCommandGroup(commandArgs, ioStream),
|
||||
|
||||
// System
|
||||
commands.SystemCommandGroup(commandArgs, ioStream),
|
||||
commands.NewCompletionCommand(),
|
||||
|
||||
commands.NewTraitsCommand(ioStream),
|
||||
commands.NewWorkloadsCommand(ioStream),
|
||||
|
||||
commands.NewDashboardCommand(commandArgs, ioStream, fake.FrontendSource),
|
||||
|
||||
commands.NewLogsCommand(commandArgs, ioStream),
|
||||
)
|
||||
|
||||
// Traits
|
||||
if err = commands.AddTraitCommands(cmds, commandArgs, ioStream); err != nil {
|
||||
fmt.Println("Add trait commands from traitDefinition err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// this is for mute klog
|
||||
fset := flag.NewFlagSet("logs", flag.ContinueOnError)
|
||||
klog.InitFlags(fset)
|
||||
_ = fset.Set("v", "-1")
|
||||
return cmds
|
||||
}
|
||||
|
||||
func PrintHelpByTag(cmd *cobra.Command, all []*cobra.Command, tag string) {
|
||||
cmd.Printf(" %s:\n\n", tag)
|
||||
table := uitable.New()
|
||||
for _, c := range all {
|
||||
if val, ok := c.Annotations[types.TagCommandType]; ok && val == tag {
|
||||
table.AddRow(" "+c.Use, c.Long)
|
||||
for _, subcmd := range c.Commands() {
|
||||
table.AddRow(" "+subcmd.Use, " "+subcmd.Long)
|
||||
}
|
||||
}
|
||||
}
|
||||
cmd.Println(table.String())
|
||||
if tag == types.TypeTraits {
|
||||
if len(table.Rows) > 0 {
|
||||
cmd.Println()
|
||||
}
|
||||
cmd.Println(" Want more? < install more capabilities by `vela cap` >")
|
||||
}
|
||||
cmd.Println()
|
||||
}
|
||||
|
||||
func NewVersionCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Prints out build version information",
|
||||
Long: "Prints out build version information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf(`Version: %v
|
||||
GitRevision: %v
|
||||
GolangVersion: %v
|
||||
`,
|
||||
version.VelaVersion,
|
||||
version.GitRevision,
|
||||
runtime.Version())
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
types.TagCommandType: types.TypeStart,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: ingresses.networking.k8s.io
|
||||
annotations:
|
||||
"definition.oam.dev/apiVersion": "networking.k8s.io/v1beta1"
|
||||
"definition.oam.dev/kind": "Ingress"
|
||||
spec:
|
||||
revisionEnabled: true
|
||||
appliesToWorkloads:
|
||||
- core.oam.dev/v1alpha2.ContainerizedWorkload
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: ingresses.networking.k8s.io
|
||||
extension:
|
||||
install:
|
||||
helm:
|
||||
repo: stable
|
||||
name: nginx-ingress
|
||||
url: https://kubernetes-charts.storage.googleapis.com/
|
||||
version: 1.41.2
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "networking.k8s.io/v1beta1"
|
||||
kind: "Ingress"
|
||||
spec: {
|
||||
rules: [{
|
||||
host: route.domain
|
||||
http: paths: [{
|
||||
backend: {
|
||||
serviceName: route.service
|
||||
servicePort: route.port
|
||||
}}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
route: {
|
||||
domain: string
|
||||
port: *80 | int
|
||||
service: string
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: simplerollouttraits.extend.oam.dev
|
||||
annotations:
|
||||
"definition.oam.dev/apiVersion": "extend.oam.dev/v1alpha2"
|
||||
"definition.oam.dev/kind": "SimpleRolloutTrait"
|
||||
spec:
|
||||
revisionEnabled: true
|
||||
appliesToWorkloads:
|
||||
- core.oam.dev/v1alpha2.ContainerizedWorkload
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: simplerollouttraits.extend.oam.dev
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "extend.oam.dev/v1alpha2"
|
||||
kind: "SimpleRolloutTrait"
|
||||
spec: {
|
||||
replica: rollout.replica
|
||||
maxUnavailable: rollout.maxUnavailable
|
||||
batch: rollout.batch
|
||||
}
|
||||
}
|
||||
rollout: {
|
||||
replica: *3 | int
|
||||
maxUnavailable: *1 | int
|
||||
batch: *2 | int
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: containerizedworkloads.core.oam.dev
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
|
||||
definition.oam.dev/kind: "ContainerizedWorkload"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: containerizedworkloads.core.oam.dev
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ContainerizedWorkload"
|
||||
metadata: name: containerized.name
|
||||
spec: {
|
||||
containers: [{
|
||||
image: containerized.image
|
||||
name: containerized.name
|
||||
ports: [{
|
||||
containerPort: containerized.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
containerized: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: deployments.apps
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "apps/v1"
|
||||
definition.oam.dev/kind: "Deployment"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: deployments.apps
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
metadata: name: deployment.name
|
||||
spec: {
|
||||
selector:
|
||||
matchLabels:
|
||||
app: deployment.name
|
||||
template: {
|
||||
metadata:
|
||||
labels:
|
||||
app: deployment.name
|
||||
spec: containers: [{
|
||||
image: deployment.image
|
||||
name: deployment.name
|
||||
env: deployment.env
|
||||
ports: [{
|
||||
containerPort: deployment.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deployment: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *8080 | int
|
||||
env: [...{
|
||||
name: string
|
||||
value: string
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ webhooks:
|
||||
service:
|
||||
name: webhook-service
|
||||
namespace: system
|
||||
path: /mutate-standard-oam-dev-v1alpha1-containerized
|
||||
path: /mutate-standard-oam-dev-v1alpha1-podspecworkload
|
||||
failurePolicy: Fail
|
||||
name: mcontainerized.kb.io
|
||||
name: mpodspecworkload.kb.io
|
||||
rules:
|
||||
- apiGroups:
|
||||
- standard.oam.dev
|
||||
@@ -41,7 +41,7 @@ webhooks:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- Containerized
|
||||
- PodSpecWorkload
|
||||
|
||||
---
|
||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
||||
@@ -74,9 +74,9 @@ webhooks:
|
||||
service:
|
||||
name: webhook-service
|
||||
namespace: system
|
||||
path: /validate-standard-oam-dev-v1alpha1-containerized
|
||||
path: /validate-standard-oam-dev-v1alpha1-podspecworkload
|
||||
failurePolicy: Fail
|
||||
name: vcontainerized.kb.io
|
||||
name: vpodspecworkload.kb.io
|
||||
rules:
|
||||
- apiGroups:
|
||||
- standard.oam.dev
|
||||
@@ -87,4 +87,4 @@ webhooks:
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- Containerized
|
||||
- PodSpecWorkload
|
||||
|
||||
@@ -12,9 +12,7 @@ export default defineConfig({
|
||||
hmr: true,
|
||||
},
|
||||
locale: {
|
||||
// default zh-CN
|
||||
default: 'en-US',
|
||||
// default true, when it is true, will use `navigator.language` overwrite default
|
||||
antd: false,
|
||||
baseNavigator: false,
|
||||
},
|
||||
@@ -36,22 +34,14 @@ export default defineConfig({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
// redirect: `/${envname}/ApplicationList`,
|
||||
redirect: `/ApplicationList`,
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList',
|
||||
icon: 'table',
|
||||
// path: `/${envname}/ApplicationList`,
|
||||
path: `/ApplicationList`,
|
||||
component: './ApplicationList',
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList.ApplicationListDetail',
|
||||
hideInMenu: true,
|
||||
path: '/ApplicationList/ApplicationListDetail',
|
||||
component: './ApplicationList/ApplicationListDetail',
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList.WorkloadDetail',
|
||||
icon: 'smile',
|
||||
@@ -67,35 +57,22 @@ export default defineConfig({
|
||||
hideInMenu: true,
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList.CreateApplication',
|
||||
name: 'ApplicationList.Components',
|
||||
hideInMenu: true,
|
||||
path: '/ApplicationList/CreateApplication',
|
||||
component: './ApplicationList/CreateApplication',
|
||||
path: '/ApplicationList/:appName/Components',
|
||||
component: './ApplicationList/Components',
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList.Components.createComponent',
|
||||
hideInMenu: true,
|
||||
path: '/ApplicationList/:appName/createComponent',
|
||||
component: './ApplicationList/CreateComponent',
|
||||
},
|
||||
{
|
||||
name: 'Workload',
|
||||
icon: 'table',
|
||||
path: '/Workload',
|
||||
routes: [
|
||||
// {
|
||||
// name: 'Deployment',
|
||||
// icon: 'table',
|
||||
// path: '/Workload/Deployment',
|
||||
// component: './Workload/Deployment',
|
||||
// },
|
||||
// {
|
||||
// name: 'Containerized',
|
||||
// icon: 'smile',
|
||||
// path: '/Workload/Containerized',
|
||||
// component: './Workload/Containerized',
|
||||
// },
|
||||
// {
|
||||
// name: 'Detail',
|
||||
// icon: 'smile',
|
||||
// path: '/Workload/Detail',
|
||||
// component: './Workload/Detail',
|
||||
// hideInMenu: true,
|
||||
// },
|
||||
{
|
||||
name: 'WorkloadItem',
|
||||
icon: 'smile',
|
||||
@@ -109,25 +86,6 @@ export default defineConfig({
|
||||
name: 'Traits',
|
||||
icon: 'table',
|
||||
routes: [
|
||||
// {
|
||||
// name: 'Scale',
|
||||
// icon: 'table',
|
||||
// path: '/Traits/Scale',
|
||||
// component: './Traits/Scale',
|
||||
// },
|
||||
// {
|
||||
// name: 'Rollout',
|
||||
// icon: 'smile',
|
||||
// path: '/Traits/Rollout',
|
||||
// component: './Traits/Rollout',
|
||||
// },
|
||||
// {
|
||||
// name: 'Detail',
|
||||
// icon: 'smile',
|
||||
// path: '/Traits/Detail',
|
||||
// component: './Traits/Detail',
|
||||
// hideInMenu: true,
|
||||
// },
|
||||
{
|
||||
name: 'TraitItem',
|
||||
icon: 'smile',
|
||||
@@ -136,12 +94,6 @@ export default defineConfig({
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// name: 'Release',
|
||||
// icon: 'table',
|
||||
// path: '/Release',
|
||||
// component: './Release',
|
||||
// },
|
||||
{
|
||||
name: 'Capability',
|
||||
icon: 'table',
|
||||
@@ -183,8 +135,13 @@ export default defineConfig({
|
||||
],
|
||||
// Theme for antd: https://ant.design/docs/react/customize-theme-cn
|
||||
theme: {
|
||||
// ...darkTheme,
|
||||
// 主题配置
|
||||
'primary-color': defaultSettings.primaryColor,
|
||||
'link-color': defaultSettings.linkColor,
|
||||
'link-hover-color': defaultSettings.linkHoverColor,
|
||||
'disabled-bg': defaultSettings.disabledBg,
|
||||
'disabled-color': defaultSettings.disabledColor,
|
||||
'btn-disable-color': defaultSettings.btnDisableColor,
|
||||
},
|
||||
// @ts-ignore
|
||||
title: false,
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
const proSettings = {
|
||||
navTheme: 'dark',
|
||||
// 拂晓蓝
|
||||
primaryColor: '#1890ff',
|
||||
// 主题颜色配置
|
||||
primaryColor: '#1B58F4', // 全局主色
|
||||
linkColor: '#1B58F4', // 链接色
|
||||
linkHoverColor: '#1B58F4',
|
||||
disabledBg: '#EBEBEB', // 失效背景色,
|
||||
disabledColor: '#BEBEBE', // 失效文本色,
|
||||
btnDisableColor: '#A4A4A4', // 禁用btn文字颜色
|
||||
layout: 'side',
|
||||
contentWidth: 'Fluid',
|
||||
fixedHeader: false,
|
||||
fixSiderbar: true,
|
||||
colorWeak: false,
|
||||
menu: {
|
||||
locale: true,
|
||||
locale: false,
|
||||
},
|
||||
title: 'Micro App Engine',
|
||||
pwa: false,
|
||||
|
||||
@@ -8,11 +8,8 @@
|
||||
export default {
|
||||
dev: {
|
||||
'/api': {
|
||||
target: 'http://123.56.222.218:8081/',
|
||||
target: 'http://127.0.0.1:38081/',
|
||||
changeOrigin: true,
|
||||
// pathRewrite: {
|
||||
// "^/api": "",
|
||||
// },
|
||||
},
|
||||
},
|
||||
test: {
|
||||
|
||||
@@ -35,34 +35,44 @@ export default class CreateTraitItem extends React.PureComponent {
|
||||
return this.formRefStep2.current.getFieldsValue();
|
||||
};
|
||||
|
||||
resetFields = () => {
|
||||
return this.formRefStep2.current.resetFields();
|
||||
};
|
||||
|
||||
validateFields = () => {
|
||||
return this.formRefStep2.current.validateFields();
|
||||
};
|
||||
|
||||
setDefaultValue = (traitType) => {
|
||||
this.formRefStep2.current.setFieldsValue({ name: traitType });
|
||||
this.traitSelectChange(traitType);
|
||||
};
|
||||
|
||||
traitSelectChange = async (value, isType = 1) => {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/getTraitByName',
|
||||
payload: {
|
||||
traitName: value,
|
||||
},
|
||||
});
|
||||
this.setState({
|
||||
parameters: res.parameters,
|
||||
});
|
||||
if (isType === 2) {
|
||||
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
|
||||
} else if (isType) {
|
||||
// 进行默认值填写
|
||||
const parameters = _.get(res, 'parameters', []);
|
||||
if (parameters.length) {
|
||||
const initialObj = {};
|
||||
parameters.forEach((item) => {
|
||||
if (item.default) {
|
||||
initialObj[item.name] = item.default;
|
||||
}
|
||||
});
|
||||
this.formRefStep2.current.setFieldsValue(initialObj);
|
||||
if (value) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/getTraitByName',
|
||||
payload: {
|
||||
traitName: value,
|
||||
},
|
||||
});
|
||||
this.setState({
|
||||
parameters: res.parameters,
|
||||
});
|
||||
if (isType === 2) {
|
||||
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
|
||||
} else if (isType) {
|
||||
// 进行默认值填写
|
||||
const parameters = _.get(res, 'parameters', []);
|
||||
if (parameters.length) {
|
||||
const initialObj = {};
|
||||
parameters.forEach((item) => {
|
||||
if (item.default) {
|
||||
initialObj[item.name] = item.default;
|
||||
}
|
||||
});
|
||||
this.formRefStep2.current.setFieldsValue(initialObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -77,8 +87,12 @@ export default class CreateTraitItem extends React.PureComponent {
|
||||
name="control-ref"
|
||||
className="traitItem"
|
||||
>
|
||||
<Form.Item name="name" label="Trait">
|
||||
<Select placeholder="Select a Trait" allowClear onChange={this.traitSelectChange}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="Trait"
|
||||
rules={[{ required: true, message: 'Please Select a Trait!' }]}
|
||||
>
|
||||
<Select placeholder="Select a Trait" onChange={this.traitSelectChange}>
|
||||
{availableTraitList.map((item) => {
|
||||
return (
|
||||
<Option value={item.name} key={item.name}>
|
||||
@@ -92,8 +106,34 @@ export default class CreateTraitItem extends React.PureComponent {
|
||||
<div className="relativeBox">
|
||||
{this.state.parameters ? (
|
||||
this.state.parameters.map((item) => {
|
||||
return (
|
||||
<Form.Item name={item.name} label={item.name} key={item.name}>
|
||||
return item.type === 4 ? (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
required: item.required || false,
|
||||
message: `Please input ${item.name} !`,
|
||||
},
|
||||
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
required: item.required || false,
|
||||
message: `Please input ${item.name} !`,
|
||||
},
|
||||
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
@@ -23,9 +23,8 @@ export default class WorkSpaceDropDown extends React.Component {
|
||||
return env.current === '*';
|
||||
});
|
||||
this.setState({
|
||||
envs: envs,
|
||||
workSpaceName: envName,
|
||||
namespace: namespace,
|
||||
namespace,
|
||||
});
|
||||
this.props.dispatch({
|
||||
type: 'globalData/currentEnv',
|
||||
|
||||
@@ -1,23 +1,36 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Button, Row, Col, Modal, Select, message } from 'antd';
|
||||
import { Button, Row, Col, Modal, Select, message, Breadcrumb, Form, Input } from 'antd';
|
||||
import './index.less';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
import _ from 'lodash';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 8,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 16,
|
||||
},
|
||||
};
|
||||
|
||||
@connect(({ loading, applist, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
currentEnv: globalData.currentEnv,
|
||||
returnObj: applist.returnObj,
|
||||
}))
|
||||
class Trait extends React.Component {
|
||||
formRefStep2 = React.createRef();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visible: false,
|
||||
selectValue: null,
|
||||
compList: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,6 +38,19 @@ class Trait extends React.Component {
|
||||
this.getInitialData();
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
if (nextProps.currentEnv === this.props.currentEnv) {
|
||||
return true;
|
||||
}
|
||||
this.props.dispatch({
|
||||
type: 'applist/getList',
|
||||
payload: {
|
||||
url: `/api/envs/${nextProps.currentEnv}/apps/`,
|
||||
},
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
getInitialData = async () => {
|
||||
if (this.props.currentEnv) {
|
||||
await this.props.dispatch({
|
||||
@@ -37,28 +63,64 @@ class Trait extends React.Component {
|
||||
};
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
visible: true,
|
||||
},
|
||||
() => {
|
||||
if (this.formRefStep2.current) {
|
||||
this.formRefStep2.current.resetFields();
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
handleOk = () => {
|
||||
const { selectValue } = this.state;
|
||||
if (selectValue) {
|
||||
this.setState({
|
||||
visible: false,
|
||||
handleOk = async () => {
|
||||
await this.formRefStep2.current.validateFields();
|
||||
const { title } = this.props.propsObj;
|
||||
if (title) {
|
||||
const submitObj = {
|
||||
name: title,
|
||||
flags: [],
|
||||
};
|
||||
const submitData = this.formRefStep2.current.getFieldValue();
|
||||
Object.keys(submitData).forEach((currentKey) => {
|
||||
if (
|
||||
currentKey !== 'name' &&
|
||||
currentKey !== 'appName' &&
|
||||
currentKey !== 'compName' &&
|
||||
submitData[currentKey]
|
||||
) {
|
||||
submitObj.flags.push({
|
||||
name: currentKey,
|
||||
value: submitData[currentKey].toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
const { history } = this.props.propsObj;
|
||||
history.push({
|
||||
pathname: '/ApplicationList/ApplicationListDetail',
|
||||
state: {
|
||||
appName: selectValue,
|
||||
envName: this.props.currentEnv,
|
||||
traitType: this.props.propsObj.title,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.warn('please select a application');
|
||||
const { currentEnv: envName } = this.props;
|
||||
const { appName, compName } = submitData;
|
||||
if (envName && appName && compName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/attachOneTraits',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
compName,
|
||||
params: submitObj,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
message.success(res);
|
||||
const { history } = this.props.propsObj;
|
||||
history.push({
|
||||
pathname: `/ApplicationList/${appName}/Components`,
|
||||
state: { appName, envName },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,107 +130,231 @@ class Trait extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
onChange = (value) => {
|
||||
onChange = async (value) => {
|
||||
this.setState({
|
||||
selectValue: value,
|
||||
compList: [],
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
payload: {
|
||||
envName: this.props.currentEnv,
|
||||
appName: value,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
const compData = _.get(res, 'components', []);
|
||||
const compList = [];
|
||||
compData.forEach((item) => {
|
||||
compList.push({
|
||||
compName: item.name,
|
||||
});
|
||||
});
|
||||
this.setState({
|
||||
compList,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSearch = () => {};
|
||||
|
||||
render() {
|
||||
const { btnValue, title, settings, btnIsShow, crdInfo, appliesTo } = this.props.propsObj;
|
||||
const appList = _.get(this.props, 'returnObj', []);
|
||||
const { btnValue, title, settings = [], btnIsShow, crdInfo, appliesTo } = this.props.propsObj;
|
||||
const initialObj = {};
|
||||
if (settings.length) {
|
||||
settings.forEach((item) => {
|
||||
if (item.default) {
|
||||
initialObj[item.name] = item.default;
|
||||
}
|
||||
});
|
||||
}
|
||||
let appList = _.get(this.props, 'returnObj', []);
|
||||
if (!appList) {
|
||||
appList = [];
|
||||
}
|
||||
const { compList = [] } = this.state;
|
||||
return (
|
||||
<PageContainer>
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<div className="deployment">
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{title}</p>
|
||||
{crdInfo ? (
|
||||
<p>
|
||||
{crdInfo.apiVersion}
|
||||
<span>,kind=</span>
|
||||
{crdInfo.kind}
|
||||
</p>
|
||||
) : (
|
||||
<p />
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">Applies To</p>
|
||||
<p>{Array.isArray(appliesTo) ? appliesTo.join(', ') : appliesTo}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title">Configurable Properties:</p>
|
||||
{settings.map((item, index) => {
|
||||
return (
|
||||
<Row key={index.toString()}>
|
||||
<Col span="8">
|
||||
<p>{item.name}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{item.default || item.usage}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
className="create-button"
|
||||
onClick={this.showModal}
|
||||
style={{ display: btnIsShow ? 'block' : 'none' }}
|
||||
>
|
||||
{btnValue}
|
||||
</Button>
|
||||
<Modal
|
||||
title="Select a Application"
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={[
|
||||
<Button key="back" onClick={this.handleCancel}>
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button key="submit" type="primary" onClick={this.handleOk}>
|
||||
Next
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
allowClear
|
||||
value={this.state.selectValue}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select a Application"
|
||||
optionFilterProp="children"
|
||||
onChange={this.onChange}
|
||||
onSearch={this.onSearch}
|
||||
filterOption={(input, option) =>
|
||||
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Traits</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>{title}</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<PageContainer>
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<div className="deployment">
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{title}</p>
|
||||
{crdInfo ? (
|
||||
<p>
|
||||
{crdInfo.apiVersion}
|
||||
<span>,kind=</span>
|
||||
{crdInfo.kind}
|
||||
</p>
|
||||
) : (
|
||||
<p />
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">Applies To:</p>
|
||||
<p>{Array.isArray(appliesTo) ? appliesTo.join(', ') : appliesTo}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title">Configurable Properties:</p>
|
||||
{settings.map((item, index) => {
|
||||
return (
|
||||
<Row key={index.toString()}>
|
||||
<Col span="8">
|
||||
<p>{item.name}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{item.default || item.usage}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
className="create-button"
|
||||
onClick={this.showModal}
|
||||
style={{ display: btnIsShow ? 'block' : 'none' }}
|
||||
>
|
||||
{appList.length ? (
|
||||
appList.map((item) => {
|
||||
return (
|
||||
<Option key={item.name} value={item.name}>
|
||||
{item.name}
|
||||
</Option>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment />
|
||||
)}
|
||||
</Select>
|
||||
</Modal>
|
||||
</Col>
|
||||
</Row>
|
||||
</PageContainer>
|
||||
{btnValue}
|
||||
</Button>
|
||||
<Modal
|
||||
title="Attach Trait"
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={[
|
||||
<Button key="back" onClick={this.handleCancel}>
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button key="submit" type="primary" onClick={this.handleOk}>
|
||||
Submit
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form
|
||||
labelAlign="left"
|
||||
{...layout}
|
||||
ref={this.formRefStep2}
|
||||
name="control-ref"
|
||||
className="traitItem"
|
||||
initialValues={initialObj}
|
||||
>
|
||||
<Form.Item
|
||||
label="App"
|
||||
name="appName"
|
||||
rules={[{ required: true, message: 'Please Select a Application!' }]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
value={this.state.selectValue}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select a Application"
|
||||
optionFilterProp="children"
|
||||
onChange={this.onChange}
|
||||
onSearch={this.onSearch}
|
||||
filterOption={(input, option) =>
|
||||
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
{appList.length ? (
|
||||
appList.map((item, index) => {
|
||||
return (
|
||||
<Option key={index.toString()} value={item.name}>
|
||||
{item.name}
|
||||
</Option>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment />
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Component"
|
||||
name="compName"
|
||||
rules={[{ required: true, message: 'Please Select a Component!' }]}
|
||||
>
|
||||
<Select
|
||||
allowClear
|
||||
// value={this.state.selectValue}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select a Component"
|
||||
>
|
||||
{compList.length ? (
|
||||
compList.map((item) => {
|
||||
return (
|
||||
<Option key={item.compName} value={item.compName}>
|
||||
{item.compName}
|
||||
</Option>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment />
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<div className="relativeBox">
|
||||
<Form.Item label="Properties" />
|
||||
{settings ? (
|
||||
settings.map((item) => {
|
||||
return item.type === 4 ? (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
required: item.required || false,
|
||||
message: `Please input ${item.name} !`,
|
||||
},
|
||||
{
|
||||
pattern: /^[0-9]*$/,
|
||||
message: `${item.name} only use digits(0-9).`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
required: item.required || false,
|
||||
message: `Please input ${item.name} !`,
|
||||
},
|
||||
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Col>
|
||||
</Row>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,226 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Button, Row, Col } from 'antd';
|
||||
import { Button, Row, Col, Modal, Select, Breadcrumb, Form } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
import _ from 'lodash';
|
||||
import './index.less';
|
||||
|
||||
export default class Workload extends React.PureComponent {
|
||||
const { Option } = Select;
|
||||
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 8,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 16,
|
||||
},
|
||||
};
|
||||
|
||||
@connect(({ loading, applist, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
currentEnv: globalData.currentEnv,
|
||||
returnObj: applist.returnObj,
|
||||
}))
|
||||
export default class Workload extends React.Component {
|
||||
formRefStep2 = React.createRef();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visible: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitialData();
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
if (nextProps.currentEnv === this.props.currentEnv) {
|
||||
return true;
|
||||
}
|
||||
this.props.dispatch({
|
||||
type: 'applist/getList',
|
||||
payload: {
|
||||
url: `/api/envs/${nextProps.currentEnv}/apps/`,
|
||||
},
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
getInitialData = async () => {
|
||||
if (this.props.currentEnv) {
|
||||
await this.props.dispatch({
|
||||
type: 'applist/getList',
|
||||
payload: {
|
||||
url: `/api/envs/${this.props.currentEnv}/apps/`,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
showModal = () => {
|
||||
this.setState(
|
||||
{
|
||||
visible: true,
|
||||
},
|
||||
() => {
|
||||
if (this.formRefStep2.current) {
|
||||
this.formRefStep2.current.resetFields();
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
const submitData = await this.formRefStep2.current.validateFields();
|
||||
const { history } = this.props.propsObj;
|
||||
history.push({
|
||||
pathname: `/ApplicationList/${submitData.appName}/createComponent`,
|
||||
state: {
|
||||
...submitData,
|
||||
isCreate: false,
|
||||
envName: this.props.currentEnv,
|
||||
WorkloadType: this.props.propsObj.title,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
onChange = () => {};
|
||||
|
||||
onSearch = () => {};
|
||||
|
||||
render() {
|
||||
const {
|
||||
btnValue,
|
||||
pathname,
|
||||
title,
|
||||
crdInfo,
|
||||
state,
|
||||
settings,
|
||||
hrefAddress,
|
||||
btnIsShow,
|
||||
} = this.props.propsObj;
|
||||
const { btnValue, title, crdInfo, settings, btnIsShow } = this.props.propsObj;
|
||||
let appList = _.get(this.props, 'returnObj', []);
|
||||
if (!appList) {
|
||||
appList = [];
|
||||
}
|
||||
return (
|
||||
<PageContainer>
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<div className="deployment">
|
||||
<a href={hrefAddress}>?</a>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{title}</p>
|
||||
{crdInfo ? (
|
||||
<p>
|
||||
{crdInfo.apiVersion}
|
||||
<span>,kind=</span>
|
||||
{crdInfo.kind}
|
||||
</p>
|
||||
) : (
|
||||
<p />
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title">Configurable Settings:</p>
|
||||
{settings.map((item, index) => {
|
||||
if (item.name === 'name') {
|
||||
return <Fragment key={index.toString()} />;
|
||||
}
|
||||
return (
|
||||
<Row key={index.toString()}>
|
||||
<Col span="8">
|
||||
<p>{item.name}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
{
|
||||
// eslint-disable-next-line consistent-return
|
||||
}
|
||||
<p>{item.default || item.usage}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
|
||||
<Button type="primary" className="create-button">
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Workloads</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>{title}</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<PageContainer>
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<div className="deployment">
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{title}</p>
|
||||
{crdInfo ? (
|
||||
<p>
|
||||
{crdInfo.apiVersion}
|
||||
<span>,kind=</span>
|
||||
{crdInfo.kind}
|
||||
</p>
|
||||
) : (
|
||||
<p />
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title">Configurable Settings:</p>
|
||||
{settings.map((item, index) => {
|
||||
if (item.name === 'name') {
|
||||
return <Fragment key={index.toString()} />;
|
||||
}
|
||||
return (
|
||||
<Row key={index.toString()}>
|
||||
<Col span="8">
|
||||
<p>{item.name}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
{
|
||||
// eslint-disable-next-line consistent-return
|
||||
}
|
||||
<p>{item.default || item.usage}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/* <Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
|
||||
<Button type="primary" className="create-button">
|
||||
{btnValue}
|
||||
</Button>
|
||||
</Link> */}
|
||||
<Button
|
||||
type="primary"
|
||||
className="create-button"
|
||||
onClick={() => this.showModal()}
|
||||
style={{ display: btnIsShow ? 'block' : 'none' }}
|
||||
>
|
||||
{btnValue}
|
||||
</Button>
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</PageContainer>
|
||||
</Col>
|
||||
</Row>
|
||||
<Modal
|
||||
title="Add Component"
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={[
|
||||
<Button key="back" onClick={this.handleCancel}>
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button key="submit" type="primary" onClick={this.handleOk}>
|
||||
Next
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form
|
||||
labelAlign="left"
|
||||
{...layout}
|
||||
ref={this.formRefStep2}
|
||||
name="control-ref"
|
||||
className="traitItem"
|
||||
>
|
||||
<Form.Item
|
||||
label="App"
|
||||
name="appName"
|
||||
rules={[{ required: true, message: 'Please Select a Application!' }]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select a Application"
|
||||
optionFilterProp="children"
|
||||
onChange={this.onChange}
|
||||
onSearch={this.onSearch}
|
||||
filterOption={(input, option) =>
|
||||
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
{appList.length ? (
|
||||
appList.map((item, index) => {
|
||||
return (
|
||||
<Option key={index.toString()} value={item.name}>
|
||||
{item.name}
|
||||
</Option>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment />
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +29,6 @@ beforeEach(async () => {
|
||||
describe('Ant Design Pro E2E test', () => {
|
||||
const testPage = (path) => async () => {
|
||||
await page.goto(`${BASE_URL}${path}`);
|
||||
await page.waitForSelector('footer', {
|
||||
timeout: 2000,
|
||||
});
|
||||
const haveFooter = await page.evaluate(
|
||||
() => document.getElementsByTagName('footer').length > 0,
|
||||
);
|
||||
expect(haveFooter).toBeTruthy();
|
||||
};
|
||||
|
||||
const routers = formatter(RouterConfig);
|
||||
@@ -46,12 +39,5 @@ describe('Ant Design Pro E2E test', () => {
|
||||
it('topmenu should have footer', async () => {
|
||||
const params = '?navTheme=light&layout=topmenu';
|
||||
await page.goto(`${BASE_URL}${params}`);
|
||||
await page.waitForSelector('footer', {
|
||||
timeout: 2000,
|
||||
});
|
||||
const haveFooter = await page.evaluate(
|
||||
() => document.getElementsByTagName('footer').length > 0,
|
||||
);
|
||||
expect(haveFooter).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,3 +69,35 @@ ol {
|
||||
.ant-page-header-heading {
|
||||
display: none !important;
|
||||
}
|
||||
.ant-pro-page-container-warp {
|
||||
display: none;
|
||||
}
|
||||
.ant-pro-basicLayout-content {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.ant-pro-basicLayout-content .ant-pro-page-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.breadCrumb {
|
||||
padding: 12px 24px;
|
||||
background: #fff;
|
||||
}
|
||||
.ant-breadcrumb a:hover {
|
||||
color: #1b58f4 !important;
|
||||
}
|
||||
// 对齐
|
||||
.ant-form-item-label > label::before {
|
||||
display: inline-block;
|
||||
width: 7.09px;
|
||||
height: 14px;
|
||||
margin-right: 4px;
|
||||
color: rgb(255, 77, 79);
|
||||
font-size: 14px;
|
||||
font-family: SimSun, sans-serif;
|
||||
line-height: 1;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading {
|
||||
height: calc(100% - 46px) !important;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* https://github.com/ant-design/ant-design-pro-layout
|
||||
*/
|
||||
import ProLayout from '@ant-design/pro-layout';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Link, useIntl, connect, history } from 'umi';
|
||||
import RightContent from '@/components/GlobalHeader/RightContent';
|
||||
import {
|
||||
@@ -16,15 +16,6 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import _ from 'lodash';
|
||||
|
||||
// const menuDataRender = (menuList) => {
|
||||
// return menuList.map((item) => {
|
||||
// const localItem = {
|
||||
// ...item,
|
||||
// children: item.children ? menuDataRender(item.children) : undefined,
|
||||
// };
|
||||
// return localItem;
|
||||
// });
|
||||
// };
|
||||
const AddIcon = (menuData) => {
|
||||
return menuData.map((item) => {
|
||||
const name = _.get(item, 'name', '');
|
||||
@@ -52,18 +43,43 @@ const AddIcon = (menuData) => {
|
||||
|
||||
const BasicLayout = (props) => {
|
||||
const { settings, dispatch, menus } = props;
|
||||
const [currentSelectKeys, setCurrentSelectedKeys] = useState('');
|
||||
const timerRef = useRef();
|
||||
const getCurrentSelectKeys = () => {
|
||||
const pathnameCur = props.history.location.pathname;
|
||||
if (pathnameCur) {
|
||||
if (pathnameCur.includes('Application')) {
|
||||
setCurrentSelectedKeys(['applist']);
|
||||
} else if (pathnameCur.includes('Capability')) {
|
||||
setCurrentSelectedKeys(['Capability']);
|
||||
} else if (pathnameCur.includes('System/Env')) {
|
||||
setCurrentSelectedKeys(['Env']);
|
||||
} else if (pathnameCur.includes('Workload')) {
|
||||
const arr = pathnameCur.split('/');
|
||||
const key = arr[arr.length - 1];
|
||||
setCurrentSelectedKeys([key]);
|
||||
} else if (pathnameCur.includes('Traits')) {
|
||||
const arr = pathnameCur.split('/');
|
||||
const key = arr[arr.length - 1];
|
||||
setCurrentSelectedKeys([key]);
|
||||
}
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (dispatch) {
|
||||
// dispatch({
|
||||
// type: 'user/fetchCurrent',
|
||||
// });
|
||||
// dispatch({
|
||||
// type: 'settings/getSetting',
|
||||
// });
|
||||
dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
timerRef.current = props.history.listen((route) => {
|
||||
getCurrentSelectKeys(route.pathname);
|
||||
});
|
||||
return () => {
|
||||
if (timerRef.current) {
|
||||
timerRef.current = null;
|
||||
}
|
||||
};
|
||||
// setCurrentSelectedKeys('applist')
|
||||
}, []);
|
||||
|
||||
const { formatMessage } = useIntl();
|
||||
@@ -75,9 +91,19 @@ const BasicLayout = (props) => {
|
||||
if (menuItemProps.isUrl || !menuItemProps.path) {
|
||||
return defaultDom;
|
||||
}
|
||||
|
||||
return <Link to={menuItemProps.path}>{defaultDom}</Link>;
|
||||
// return <Link to={menuItemProps.path}>{defaultDom}</Link>;
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
setCurrentSelectedKeys([menuItemProps.key]);
|
||||
history.push(menuItemProps.path);
|
||||
}}
|
||||
>
|
||||
{defaultDom}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
selectedKeys={currentSelectKeys}
|
||||
breadcrumbRender={(routers = []) => [
|
||||
{
|
||||
path: '/',
|
||||
|
||||
@@ -2,17 +2,15 @@ import { getapplist, createApp, getAppDetail, deleteApp } from '@/services/appli
|
||||
|
||||
const TestModel = {
|
||||
namespace: 'applist',
|
||||
state: {
|
||||
// initailState: '8880'
|
||||
},
|
||||
state: {},
|
||||
effects: {
|
||||
*getList({ payload }, { call, put }) {
|
||||
const res = yield call(getapplist, payload);
|
||||
// getlist是引入services层那个js文件的getlist方法,payload是后台要求传递的参数,res就是后台返过来的数据
|
||||
yield put({
|
||||
type: 'addList', // 这就是reducer的addNum方法,put用来触发reducer中的方法,payload是传过去的参数。同时也能触发同等级effects中的方法
|
||||
type: 'addList',
|
||||
payload: {
|
||||
returnObj: res, // 把后台返回的数据赋值给num,假如哪个reducer中的方法是由这里effects去触发的,哪个num名必须是这里的名字num,如果reducer中的方法不是这触发,那名字可以随意取
|
||||
returnObj: res,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -10,9 +10,7 @@ import {
|
||||
|
||||
const TestModel = {
|
||||
namespace: 'capability',
|
||||
state: {
|
||||
// initailState: '8880'
|
||||
},
|
||||
state: {},
|
||||
effects: {
|
||||
*getCapabilityCenterlist({ payload }, { call }) {
|
||||
const res = yield call(getCapabilityCenterlist, payload);
|
||||
|
||||
22
dashboard/src/models/components.js
Normal file
22
dashboard/src/models/components.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { getComponentList, getComponentDetail, deleteComponent } from '@/services/components.js';
|
||||
|
||||
const TestModel = {
|
||||
namespace: 'components',
|
||||
state: {},
|
||||
effects: {
|
||||
*getComponentList({ payload }, { call }) {
|
||||
const res = yield call(getComponentList, payload);
|
||||
return res;
|
||||
},
|
||||
*getComponentDetail({ payload }, { call }) {
|
||||
const res = yield call(getComponentDetail, payload);
|
||||
return res;
|
||||
},
|
||||
*deleteComponent({ payload }, { call }) {
|
||||
const res = yield call(deleteComponent, payload);
|
||||
return res;
|
||||
},
|
||||
},
|
||||
reducers: {},
|
||||
};
|
||||
export default TestModel;
|
||||
@@ -6,7 +6,7 @@ const globalModel = {
|
||||
effects: {
|
||||
*currentEnv({ payload }, { put }) {
|
||||
yield put({
|
||||
type: 'setCurrentEnv', // 这就是reducer的addNum方法,put用来触发reducer中的方法,payload是传过去的参数。同时也能触发同等级effects中的方法
|
||||
type: 'setCurrentEnv',
|
||||
payload,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,36 +1,31 @@
|
||||
import { capabilityList } from '@/services/capability.js';
|
||||
import { getTraits } from '@/services/trait.js';
|
||||
import { getWorkload } from '@/services/workload.js';
|
||||
|
||||
function getMenuList(response) {
|
||||
function getMenuList(workload, trait) {
|
||||
let workloadList = [];
|
||||
let traitList = [];
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
response = response.filter((item) => {
|
||||
return item.status === 'installed';
|
||||
});
|
||||
response.forEach((item) => {
|
||||
if (item.type === 'workload') {
|
||||
workloadList.push(item.name);
|
||||
} else if (item.type === 'trait') {
|
||||
traitList.push(item.name);
|
||||
}
|
||||
});
|
||||
// 在此之前要对workloadList和traitList进行一次去重操作
|
||||
workloadList = workloadList.map((item) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
item = item.charAt(0).toUpperCase() + item.slice(1);
|
||||
return {
|
||||
name: item,
|
||||
path: `/Workload/${item}`,
|
||||
};
|
||||
});
|
||||
traitList = traitList.map((item) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
item = item.charAt(0).toUpperCase() + item.slice(1);
|
||||
return {
|
||||
name: item,
|
||||
path: `/Traits/${item}`,
|
||||
};
|
||||
});
|
||||
if (workload) {
|
||||
workloadList = workload.map((item) => {
|
||||
let name1 = item.name;
|
||||
name1 = name1.charAt(0).toUpperCase() + name1.slice(1);
|
||||
return {
|
||||
name: name1,
|
||||
path: `/Workload/${name1}`,
|
||||
key: name1,
|
||||
};
|
||||
});
|
||||
}
|
||||
if (trait) {
|
||||
traitList = trait.map((item) => {
|
||||
let name1 = item.name;
|
||||
name1 = name1.charAt(0).toUpperCase() + name1.slice(1);
|
||||
return {
|
||||
name: name1,
|
||||
path: `/Traits/${name1}`,
|
||||
key: name1,
|
||||
};
|
||||
});
|
||||
}
|
||||
// 只是动态生成侧边栏(name,path,icon),路由还是config.js里面配置的路由
|
||||
const menuList = [
|
||||
{
|
||||
@@ -41,6 +36,7 @@ function getMenuList(response) {
|
||||
name: 'ApplicationList',
|
||||
icon: 'Table',
|
||||
path: `/ApplicationList`,
|
||||
key: 'applist',
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList.ApplicationListDetail',
|
||||
@@ -76,13 +72,10 @@ function getMenuList(response) {
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// name: 'Release',
|
||||
// path: '/Release',
|
||||
// },
|
||||
{
|
||||
name: 'Capability',
|
||||
path: '/Capability',
|
||||
key: 'Capability',
|
||||
},
|
||||
{
|
||||
path: '/System',
|
||||
@@ -91,6 +84,7 @@ function getMenuList(response) {
|
||||
{
|
||||
name: 'Env',
|
||||
path: '/System/Env',
|
||||
key: 'Env',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -110,8 +104,9 @@ const TestModel = {
|
||||
},
|
||||
effects: {
|
||||
*getMenuData({ payload }, { call, put }) {
|
||||
let response = yield call(capabilityList, payload);
|
||||
response = getMenuList(response);
|
||||
const workloadList = yield call(getWorkload, payload);
|
||||
const traitList = yield call(getTraits, payload);
|
||||
const response = getMenuList(workloadList, traitList);
|
||||
yield put({
|
||||
type: 'saveMenuData',
|
||||
payload: response,
|
||||
|
||||
@@ -2,9 +2,7 @@ import { getTraitByName, getTraits, attachOneTraits, deleteOneTrait } from '@/se
|
||||
|
||||
const TestModel = {
|
||||
namespace: 'trait',
|
||||
state: {
|
||||
// initailState: '8880'
|
||||
},
|
||||
state: {},
|
||||
effects: {
|
||||
*getTraitByName({ payload }, { call }) {
|
||||
const res = yield call(getTraitByName, payload);
|
||||
|
||||
@@ -2,9 +2,7 @@ import { createWorkload, getWorkload, getWorkloadByName } from '@/services/workl
|
||||
|
||||
const TestModel = {
|
||||
namespace: 'workload',
|
||||
state: {
|
||||
// initailState: '8880'
|
||||
},
|
||||
state: {},
|
||||
effects: {
|
||||
*createWorkload({ payload }, { call }) {
|
||||
const res = yield call(createWorkload, payload);
|
||||
|
||||
@@ -43,8 +43,6 @@ const Topology = () => {
|
||||
],
|
||||
};
|
||||
|
||||
// const width = document.getElementById('container').scrollWidth;
|
||||
// const height = document.getElementById('container').scrollHeight || 500;
|
||||
const width = 1000;
|
||||
const height = 400;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Button, Row, Col, Tabs, Popconfirm, message, Tooltip, Modal, Spin } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
import CreateTraitItem from '../../../components/AttachOneTrait/index.jsx';
|
||||
import Topology from './Topology.jsx';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
@@ -17,112 +15,160 @@ class TableList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
appDetailData: {},
|
||||
compDetailData: {},
|
||||
visible: false,
|
||||
traitList: [],
|
||||
availableTraitList: [],
|
||||
envName: '',
|
||||
appName: '',
|
||||
compName: '',
|
||||
compList: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitialData(1);
|
||||
this.getInitialData();
|
||||
}
|
||||
|
||||
getInitialData = async (times) => {
|
||||
const appName = _.get(this.props, 'location.state.appName', '');
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
const traitType = _.get(this.props, 'location.state.traitType', '');
|
||||
if (appName && envName) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
if (this.props.compName !== nextProps.compName) {
|
||||
this.getInitialData(nextProps.compName);
|
||||
}
|
||||
}
|
||||
|
||||
getInitialData = async (nextCompName) => {
|
||||
const appName = _.get(this.props, 'appName', '');
|
||||
const envName = _.get(this.props, 'envName', '');
|
||||
const compList = _.get(this.props, 'compList', []);
|
||||
let compName = _.get(this.props, 'compName', '');
|
||||
compName = nextCompName || compName;
|
||||
this.setState({
|
||||
compList,
|
||||
});
|
||||
if (appName && envName && compName) {
|
||||
this.setState({
|
||||
envName,
|
||||
appName,
|
||||
compName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
type: 'components/getComponentDetail',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
compName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
this.setState({
|
||||
appDetailData: res,
|
||||
compDetailData: res,
|
||||
});
|
||||
}
|
||||
const traits = await this.props.dispatch({
|
||||
type: 'trait/getTraits',
|
||||
});
|
||||
this.setState({
|
||||
traitList: traits,
|
||||
});
|
||||
const workloadType = _.get(res, 'Workload.workload.kind', '');
|
||||
if (workloadType && workloadType === 'ContainerizedWorkload') {
|
||||
this.getAcceptTrait('containerized');
|
||||
} else if (workloadType && workloadType === 'Deployment') {
|
||||
this.getAcceptTrait('deployment');
|
||||
}
|
||||
// 如果traitType存在,是从特定trait跳转来新增单个trait的
|
||||
if (traitType && times === 1) {
|
||||
// this.createTrait(traitType)
|
||||
await this.setState({
|
||||
visible: true,
|
||||
const traits = await this.props.dispatch({
|
||||
type: 'trait/getTraits',
|
||||
});
|
||||
this.child.setDefaultValue(traitType);
|
||||
if (traits) {
|
||||
const checkedTrait = [];
|
||||
if (res.traits) {
|
||||
res.traits.forEach((item) => {
|
||||
const traitNameObj = _.get(item, 'trait.metadata.annotations', '');
|
||||
const traitName =
|
||||
traitNameObj['vela.oam.dev/traitDef'] || traitNameObj['trait.oam.dev/name'];
|
||||
checkedTrait.push(traitName);
|
||||
});
|
||||
}
|
||||
const newTraits = traits.filter((item) => {
|
||||
if (checkedTrait.includes(item.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
this.setState({
|
||||
traitList: newTraits,
|
||||
});
|
||||
}
|
||||
const workloadType = _.get(res, 'workload.kind', '');
|
||||
if (workloadType) {
|
||||
this.getAcceptTrait(workloadType.toLowerCase());
|
||||
}
|
||||
// if (workloadType && workloadType === '') {
|
||||
// this.getAcceptTrait('containerized');
|
||||
// } else if (workloadType && workloadType === 'Deployment') {
|
||||
// this.getAcceptTrait('deployment');
|
||||
// }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getAcceptTrait = (workloadType) => {
|
||||
const res = this.state.traitList.filter((item) => {
|
||||
if (item.appliesTo.indexOf(workloadType) !== -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// getAcceptTrait = (workloadType) => {
|
||||
// const res = this.state.traitList.filter((item) => {
|
||||
// if (item.appliesTo) {
|
||||
// if(item.appliesTo==='*'){
|
||||
// return true;
|
||||
// }
|
||||
// if (item.appliesTo.indexOf(workloadType) !== -1) {
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
// return false;
|
||||
// });
|
||||
// this.setState(() => ({
|
||||
// availableTraitList: res,
|
||||
// }));
|
||||
// };
|
||||
|
||||
getAcceptTrait = () => {
|
||||
const res = this.state.traitList;
|
||||
this.setState(() => ({
|
||||
availableTraitList: res,
|
||||
}));
|
||||
};
|
||||
|
||||
deleteApp = async (e) => {
|
||||
deleteComp = async (e) => {
|
||||
e.stopPropagation();
|
||||
const { currentEnv: envName } = this.props;
|
||||
const { appDetailData } = this.state;
|
||||
const appName = _.get(appDetailData, 'Workload.workload.metadata.name', '');
|
||||
if (appName && envName) {
|
||||
const { envName, appName, compName, compList } = this.state;
|
||||
if (appName && envName && compName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/deleteApp',
|
||||
type: 'components/deleteComponent',
|
||||
payload: {
|
||||
appName,
|
||||
envName,
|
||||
compName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
this.props.history.push({ pathname: '/ApplicationList' });
|
||||
if (compList.length === 1) {
|
||||
// 只有一个组件时会同时删除应用
|
||||
this.props.history.push({
|
||||
pathname: `/ApplicationList`,
|
||||
});
|
||||
} else {
|
||||
// 删除当前component成功后,刷新当前页面
|
||||
this.props.getInitCompList(compList.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
deleteTrait = async (e, item) => {
|
||||
e.stopPropagation();
|
||||
const { appName, envName } = this.state;
|
||||
const { appName, envName, compName } = this.state;
|
||||
const traitNameObj = _.get(item, 'trait.metadata.annotations', '');
|
||||
const traitName = traitNameObj['vela.oam.dev/traitDef'] || traitNameObj['trait.oam.dev/name'];
|
||||
if (traitName && appName && envName) {
|
||||
if (traitName && appName && envName && compName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/deleteOneTrait',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
traitName,
|
||||
compName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
this.getInitialData(2);
|
||||
this.getInitialData(compName);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -132,12 +178,20 @@ class TableList extends React.Component {
|
||||
};
|
||||
|
||||
createTrait = async () => {
|
||||
await this.setState({
|
||||
visible: true,
|
||||
});
|
||||
await this.setState(
|
||||
{
|
||||
visible: true,
|
||||
},
|
||||
() => {
|
||||
if (this.child) {
|
||||
this.child.resetFields();
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
await this.child.validateFields();
|
||||
const submitData = this.child.getSelectValue();
|
||||
if (submitData.name) {
|
||||
const submitObj = {
|
||||
@@ -152,13 +206,14 @@ class TableList extends React.Component {
|
||||
});
|
||||
}
|
||||
});
|
||||
const { envName, appName } = this.state;
|
||||
if (envName && appName) {
|
||||
const { envName, appName, compName } = this.state;
|
||||
if (envName && appName && compName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/attachOneTraits',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
compName,
|
||||
params: submitObj,
|
||||
},
|
||||
});
|
||||
@@ -167,7 +222,7 @@ class TableList extends React.Component {
|
||||
visible: false,
|
||||
});
|
||||
message.success(res);
|
||||
this.getInitialData(2);
|
||||
this.getInitialData(compName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -187,43 +242,32 @@ class TableList extends React.Component {
|
||||
|
||||
gotoWorkloadDetail = (e) => {
|
||||
e.stopPropagation();
|
||||
const appName = _.get(this.props, 'location.state.appName', '');
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
if (appName && envName) {
|
||||
this.props.history.push({
|
||||
pathname: '/ApplicationList/WorkloadDetail',
|
||||
state: { appName, envName },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
gotoTraitDetail = (e, traitItem) => {
|
||||
gotoTraitDetail = (e) => {
|
||||
e.stopPropagation();
|
||||
const appName = _.get(this.props, 'location.state.appName', '');
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
if (appName && envName) {
|
||||
this.props.history.push({
|
||||
pathname: '/ApplicationList/TraitDetail',
|
||||
state: { traitItem, appName, envName },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const status = _.get(this.state.appDetailData, 'Status', '');
|
||||
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
|
||||
const Traits = _.get(this.state.appDetailData, 'Traits', []);
|
||||
const { compDetailData } = this.state;
|
||||
const status = _.get(compDetailData, 'status', '');
|
||||
const Workload = _.get(compDetailData, 'workload', {});
|
||||
const Traits = _.get(compDetailData, 'traits', []);
|
||||
let containers = {};
|
||||
// if (Workload.kind === 'ContainerizedWorkload') {
|
||||
// containers = _.get(Workload, 'spec.containers[0]', {});
|
||||
// } else if (Workload.kind === 'Deployment') {
|
||||
// containers = _.get(Workload, 'spec.template.spec.containers[0]', {});
|
||||
// }
|
||||
containers = _.get(Workload, 'spec.containers[0]', {});
|
||||
if (_.get(Workload, 'kind', '') === 'Job') {
|
||||
containers = _.get(Workload, 'spec.template.spec.containers[0]', {});
|
||||
} else {
|
||||
containers = _.get(Workload, 'spec.podSpec.containers[0]', {});
|
||||
}
|
||||
let { loadingAll } = this.props;
|
||||
loadingAll = loadingAll || false;
|
||||
const colorObj = {
|
||||
Deployed: '#4CAF51',
|
||||
Staging: '#F44337',
|
||||
UNKNOWN: '#1890ff',
|
||||
};
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ margin: '8px' }}>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="card-container app-detial">
|
||||
<h2>{_.get(Workload, 'metadata.name')}</h2>
|
||||
@@ -234,15 +278,17 @@ class TableList extends React.Component {
|
||||
<TabPane tab="Summary" key="1">
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<div className="summaryBox1" onClick={(e) => this.gotoWorkloadDetail(e)}>
|
||||
{/* <div className="summaryBox1"> */}
|
||||
<div
|
||||
className="summaryBox1"
|
||||
onClick={(e) => this.gotoWorkloadDetail(e)}
|
||||
style={{ background: colorObj[status] || '#1890ff' }}
|
||||
>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{Workload.kind}</p>
|
||||
<p>{Workload.apiVersion}</p>
|
||||
</Col>
|
||||
<Col span="2">
|
||||
{/* <a href="JavaScript:;">?</a> */}
|
||||
<p className="title hasCursor" onClick={this.hrefClick}>
|
||||
?
|
||||
</p>
|
||||
@@ -253,6 +299,21 @@ class TableList extends React.Component {
|
||||
</p>
|
||||
<p className="title">Settings:</p>
|
||||
<Row>
|
||||
{_.get(Workload, 'kind', '') === 'Job' ? (
|
||||
<Fragment>
|
||||
<Col span="8">
|
||||
<p>count</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>
|
||||
{_.get(Workload, 'spec.completions', '') ||
|
||||
_.get(Workload, 'spec.parallelism', '')}
|
||||
</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment />
|
||||
)}
|
||||
{Object.keys(containers).map((currentKey) => {
|
||||
if (currentKey === 'ports') {
|
||||
return (
|
||||
@@ -268,6 +329,18 @@ class TableList extends React.Component {
|
||||
// eslint-disable-next-line no-else-return
|
||||
} else if (currentKey === 'name') {
|
||||
return <Fragment key={currentKey} />;
|
||||
// eslint-disable-next-line no-else-return
|
||||
} else if (currentKey === 'env') {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>env</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{_.get(containers[currentKey], '[0].value', '')}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
@@ -282,23 +355,18 @@ class TableList extends React.Component {
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
<div className="summaryBox2">
|
||||
<p className="title">Status:</p>
|
||||
<p>{status}</p>
|
||||
{/* <Row>
|
||||
<Col span="8">
|
||||
<p>Available Replicas</p>
|
||||
<p>Ready Replicas</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>1</p>
|
||||
<p>1</p>
|
||||
</Col>
|
||||
</Row> */}
|
||||
</div>
|
||||
<Popconfirm
|
||||
title="Are you sure delete this app?"
|
||||
onConfirm={(e) => this.deleteApp(e)}
|
||||
title={
|
||||
this.state.compList.length === 1 ? (
|
||||
<div>
|
||||
<p>There is only one component in the current application,</p>
|
||||
<p>Do you want to delete the entire application?</p>
|
||||
</div>
|
||||
) : (
|
||||
'Are you sure delete this component?'
|
||||
)
|
||||
}
|
||||
onConfirm={(e) => this.deleteComp(e)}
|
||||
onCancel={this.cancel}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
@@ -312,11 +380,7 @@ class TableList extends React.Component {
|
||||
Traits.map((item, index) => {
|
||||
const traitItem = _.get(item, 'trait', {});
|
||||
const annotations = _.get(traitItem, 'metadata.annotations', {});
|
||||
let traitType = 1;
|
||||
const spec = _.get(traitItem, 'spec', {});
|
||||
if (traitItem.kind === 'Ingress') {
|
||||
traitType = 2;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="summaryBox"
|
||||
@@ -355,54 +419,22 @@ class TableList extends React.Component {
|
||||
</Row>
|
||||
<p className="title">Properties:</p>
|
||||
<Row>
|
||||
{traitType === 2 ? (
|
||||
<Fragment>
|
||||
<Col span="8">
|
||||
<p>domain</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{_.get(spec, 'rules[0].host', '')}</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p>service</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>
|
||||
{_.get(
|
||||
spec,
|
||||
'rules[0].http.paths[0].backend.serviceName',
|
||||
'',
|
||||
)}
|
||||
</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p>port</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>
|
||||
{_.get(
|
||||
spec,
|
||||
'rules[0].http.paths[0].backend.servicePort',
|
||||
'',
|
||||
)}
|
||||
</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
) : (
|
||||
Object.keys(spec).map((currentKey) => {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>{currentKey}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{spec[currentKey]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})
|
||||
)}
|
||||
{/* {Object.keys(spec).map((currentKey) => {
|
||||
{Object.keys(spec).map((currentKey) => {
|
||||
if (spec[currentKey].constructor === Object) {
|
||||
const backend = _.get(spec, `${currentKey}`, {});
|
||||
return Object.keys(backend).map((currentKey1) => {
|
||||
return (
|
||||
<Fragment key={currentKey1}>
|
||||
<Col span="8">
|
||||
<p>{currentKey1}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{backend[currentKey1]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
});
|
||||
}
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
@@ -413,7 +445,7 @@ class TableList extends React.Component {
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})} */}
|
||||
})}
|
||||
</Row>
|
||||
<div style={{ clear: 'both', height: '32px' }}>
|
||||
<Popconfirm
|
||||
@@ -456,13 +488,12 @@ class TableList extends React.Component {
|
||||
</Row>
|
||||
</TabPane>
|
||||
<TabPane tab="Topology" key="2">
|
||||
{/* <p>Topology</p> */}
|
||||
<Topology />
|
||||
<p>Topology</p>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<Modal
|
||||
title="attach a trait"
|
||||
title="Attach a Trait"
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
@@ -484,7 +515,7 @@ class TableList extends React.Component {
|
||||
/>
|
||||
</Modal>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
.app-detial {
|
||||
.ant-tabs {
|
||||
height: 800px;
|
||||
min-height: 800px;
|
||||
min-height: 500px;
|
||||
padding: 10px 20px;
|
||||
overflow: auto;
|
||||
background-color: #fff;
|
||||
.ant-tabs-content-holder {
|
||||
background: #fff;
|
||||
@@ -19,7 +17,6 @@
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 36px;
|
||||
// color: #fff;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
190
dashboard/src/pages/ApplicationList/Components/index.jsx
Normal file
190
dashboard/src/pages/ApplicationList/Components/index.jsx
Normal file
@@ -0,0 +1,190 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'umi';
|
||||
import { Breadcrumb, Button, Menu, Spin, Popconfirm, message } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
import './index.less';
|
||||
import ComponentDetail from '../ComponentDetail/index.jsx';
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
currentEnv: globalData.currentEnv,
|
||||
}))
|
||||
class TableList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
envName: '',
|
||||
componentName: '',
|
||||
defaultSelectedKeys: '',
|
||||
appName: '',
|
||||
compList: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitData();
|
||||
}
|
||||
|
||||
// shouldComponentUpdate(nextProps) {
|
||||
// if (nextProps.currentEnv === this.props.currentEnv) {
|
||||
// return true;
|
||||
// }
|
||||
// this.getInitData(nextProps.currentEnv);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
getInitData = async (changeEnvName) => {
|
||||
let appName = '';
|
||||
let description = '';
|
||||
let envName = this.props.currentEnv;
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
description = _.get(this.props, 'location.state.description', '');
|
||||
envName = _.get(this.props, 'location.state.envName', this.props.currentEnv);
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('description', description);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
description = sessionStorage.getItem('description');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
appName,
|
||||
envName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
payload: {
|
||||
envName: changeEnvName || envName,
|
||||
appName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
const compData = _.get(res, 'components', []);
|
||||
const compList = [];
|
||||
compData.forEach((item) => {
|
||||
compList.push({
|
||||
compName: item.name,
|
||||
});
|
||||
});
|
||||
this.setState({
|
||||
compList,
|
||||
});
|
||||
if (compList.length) {
|
||||
this.changeComponent({
|
||||
key: compList[0].compName,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
changeComponent = ({ key }) => {
|
||||
this.setState({
|
||||
componentName: key,
|
||||
defaultSelectedKeys: key,
|
||||
});
|
||||
};
|
||||
|
||||
deleteApp = async (e) => {
|
||||
e.stopPropagation();
|
||||
const { envName } = this.state;
|
||||
const { appName } = this.state;
|
||||
if (appName && envName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/deleteApp',
|
||||
payload: {
|
||||
appName,
|
||||
envName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
this.props.history.push({ pathname: '/ApplicationList' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cancel = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { envName, componentName, defaultSelectedKeys, appName, compList } = this.state;
|
||||
const { loadingAll } = this.props;
|
||||
return (
|
||||
<div style={{ height: '100%' }}>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Applications</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>{appName}</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Components</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>{componentName}</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="appComponent">
|
||||
<div className="left">
|
||||
<Menu
|
||||
mode="inline"
|
||||
onClick={this.changeComponent}
|
||||
defaultSelectedKeys={[defaultSelectedKeys]}
|
||||
selectedKeys={defaultSelectedKeys}
|
||||
>
|
||||
<Menu.ItemGroup key="g1" title="Components">
|
||||
{compList.map((item) => {
|
||||
return <Menu.Item key={item.compName}>{item.compName}</Menu.Item>;
|
||||
})}
|
||||
</Menu.ItemGroup>
|
||||
</Menu>
|
||||
<div className="addComp">
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/ApplicationList/${appName}/createComponent`,
|
||||
state: { appName, envName, isCreate: false },
|
||||
}}
|
||||
>
|
||||
add a new comp
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
{defaultSelectedKeys ? (
|
||||
<div className="right">
|
||||
<div className="btn">
|
||||
<Popconfirm
|
||||
title="Are you sure delete this app?"
|
||||
placement="bottom"
|
||||
onConfirm={(e) => this.deleteApp(e)}
|
||||
onCancel={this.cancel}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Button type="primary">Delete App</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
<ComponentDetail
|
||||
appName={appName}
|
||||
compName={componentName}
|
||||
envName={envName}
|
||||
getInitCompList={this.getInitData}
|
||||
compList={compList}
|
||||
history={this.props.history}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ width: '100%', height: '100%', background: '#FFFFFF' }} />
|
||||
)}
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TableList;
|
||||
48
dashboard/src/pages/ApplicationList/Components/index.less
Normal file
48
dashboard/src/pages/ApplicationList/Components/index.less
Normal file
@@ -0,0 +1,48 @@
|
||||
.ant-spin-nested-loading {
|
||||
height: 100%;
|
||||
}
|
||||
.ant-spin-container {
|
||||
height: 100%;
|
||||
}
|
||||
.appComponent {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
.left {
|
||||
width: 200px;
|
||||
background: #fff;
|
||||
.ant-menu-item-group-title {
|
||||
color: #000;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
border-top: 1px solid #efefef;
|
||||
border-bottom: 1px solid #efefef;
|
||||
}
|
||||
.ant-menu-item-group-list .ant-menu-item,
|
||||
.ant-menu-item-group-list .ant-menu-submenu-title {
|
||||
padding-left: 16px !important;
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
}
|
||||
.addComp {
|
||||
padding: 8px 16px;
|
||||
color: blue;
|
||||
font-size: 16px;
|
||||
text-decoration: underline;
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #efefef;
|
||||
border-right: 1px solid #efefef;
|
||||
border-bottom: 1px solid #efefef;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
.btn {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Button, Row, Col, Form, Input, Select, Steps, message } from 'antd';
|
||||
import { Button, Row, Col, Form, Input, Select, Steps, message, Breadcrumb } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
import _ from 'lodash';
|
||||
@@ -46,24 +46,40 @@ class TableList extends React.Component {
|
||||
workloadSettings: [],
|
||||
step1SubmitObj: {},
|
||||
step1InitialValues: {
|
||||
workload_type: '',
|
||||
workloadType: '',
|
||||
},
|
||||
step1Settings: [],
|
||||
appName: '',
|
||||
envName: '',
|
||||
isCreate: '',
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
const activeStep = _.get(this.props, 'location.state.activeStep', 0);
|
||||
this.setState(() => ({
|
||||
current: activeStep,
|
||||
}));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitalData();
|
||||
}
|
||||
|
||||
getInitalData = async () => {
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
let isCreate = '';
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
isCreate = _.get(this.props, 'location.state.isCreate', false);
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
sessionStorage.setItem('isCreate', isCreate);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
isCreate = sessionStorage.getItem('isCreate');
|
||||
}
|
||||
this.setState({
|
||||
appName,
|
||||
envName,
|
||||
isCreate,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'workload/getWorkload',
|
||||
});
|
||||
@@ -73,24 +89,6 @@ class TableList extends React.Component {
|
||||
this.setState({
|
||||
traitList: traits,
|
||||
});
|
||||
// 如果直接跳转到第二步,需要设置值
|
||||
const traitType = _.get(this.props, 'location.state.TraitType', '');
|
||||
if (traitType) {
|
||||
// let availableTraitList = traits.filter((item)=>{
|
||||
// return item.name === traitType
|
||||
// })
|
||||
this.setState({
|
||||
availableTraitList: traits,
|
||||
traitNum: [
|
||||
{
|
||||
refname: null,
|
||||
initialData: { name: traitType },
|
||||
uniq: new Date().valueOf(),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(res) && res.length) {
|
||||
this.setState(
|
||||
() => ({
|
||||
@@ -98,11 +96,17 @@ class TableList extends React.Component {
|
||||
}),
|
||||
() => {
|
||||
if (this.state.current === 0) {
|
||||
const WorkloadType = _.get(this.props, 'location.state.WorkloadType', '');
|
||||
let WorkloadType = '';
|
||||
if (this.props.location.state) {
|
||||
WorkloadType = _.get(this.props, 'location.state.WorkloadType', '');
|
||||
sessionStorage.setItem('WorkloadType', WorkloadType);
|
||||
} else {
|
||||
WorkloadType = sessionStorage.getItem('WorkloadType');
|
||||
}
|
||||
this.formRefStep1.current.setFieldsValue({
|
||||
workload_type: WorkloadType || this.state.workloadList[0].name,
|
||||
workloadType: WorkloadType || this.state.workloadList[0].name,
|
||||
});
|
||||
this.workloadTypeChange(this.state.workloadList[0].name);
|
||||
this.workloadTypeChange(WorkloadType || this.state.workloadList[0].name);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -117,7 +121,12 @@ class TableList extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
onFinishStep2 = () => {
|
||||
onFinishStep2 = async () => {
|
||||
const asyncValidateArray = [];
|
||||
this.state.traitNum.forEach((item) => {
|
||||
asyncValidateArray.push(item.refname.validateFields());
|
||||
});
|
||||
await Promise.all(asyncValidateArray);
|
||||
const newTraitNum = this.state.traitNum.map((item) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
item.initialData = item.refname.getSelectValue();
|
||||
@@ -144,8 +153,9 @@ class TableList extends React.Component {
|
||||
};
|
||||
|
||||
changeShowMore = () => {
|
||||
const isMore = this.state.isShowMore;
|
||||
this.setState({
|
||||
isShowMore: true,
|
||||
isShowMore: !isMore,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -163,23 +173,31 @@ class TableList extends React.Component {
|
||||
};
|
||||
|
||||
createApp = async () => {
|
||||
const { step1SubmitObj, traitNum } = this.state;
|
||||
const { traitNum, isCreate } = this.state;
|
||||
const { step1SubmitObj } = this.state;
|
||||
if (isCreate === true || isCreate === 'true') {
|
||||
step1SubmitObj.envName = this.props.currentEnv;
|
||||
} else {
|
||||
step1SubmitObj.envName = this.state.envName;
|
||||
}
|
||||
step1SubmitObj.appName = this.state.appName;
|
||||
const submitObj = _.cloneDeep(step1SubmitObj);
|
||||
const { workload_name: workloadName } = step1SubmitObj;
|
||||
const { workloadName, appName } = step1SubmitObj;
|
||||
submitObj.flags.push({
|
||||
name: 'name',
|
||||
value: workloadName.toString(),
|
||||
});
|
||||
// 处理数据为提交的格式
|
||||
if (traitNum.length) {
|
||||
const { env_name: envName } = step1SubmitObj;
|
||||
const { envName } = step1SubmitObj;
|
||||
const step2SubmitObj = [];
|
||||
traitNum.forEach(({ initialData }) => {
|
||||
if (initialData.name) {
|
||||
const initialObj = {
|
||||
name: initialData.name,
|
||||
env_name: envName,
|
||||
workload_name: workloadName,
|
||||
envName,
|
||||
workloadName,
|
||||
appName,
|
||||
flags: [],
|
||||
};
|
||||
Object.keys(initialData).forEach((key) => {
|
||||
@@ -204,7 +222,8 @@ class TableList extends React.Component {
|
||||
if (res) {
|
||||
message.success(res);
|
||||
this.props.history.push({
|
||||
pathname: '/ApplicationList',
|
||||
pathname: `/ApplicationList/${appName}/Components`,
|
||||
state: { appName, envName: step1SubmitObj.envName },
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -213,13 +232,13 @@ class TableList extends React.Component {
|
||||
await this.formRefStep1.current.validateFields();
|
||||
const currentData = this.formRefStep1.current.getFieldsValue();
|
||||
const submitObj = {
|
||||
env_name: this.props.currentEnv,
|
||||
workload_type: currentData.workload_type,
|
||||
workload_name: currentData.workload_name,
|
||||
envName: this.props.currentEnv,
|
||||
workloadType: currentData.workloadType,
|
||||
workloadName: currentData.workloadName,
|
||||
flags: [],
|
||||
};
|
||||
Object.keys(currentData).forEach((key) => {
|
||||
if (key !== 'workload_name' && key !== 'workload_type' && currentData[key]) {
|
||||
if (key !== 'workloadName' && key !== 'workloadType' && currentData[key]) {
|
||||
submitObj.flags.push({
|
||||
name: key,
|
||||
value: currentData[key].toString(),
|
||||
@@ -232,15 +251,15 @@ class TableList extends React.Component {
|
||||
step1Settings: submitObj.flags,
|
||||
step1SubmitObj: submitObj,
|
||||
});
|
||||
this.getAcceptTrait(currentData.workload_type);
|
||||
this.getAcceptTrait(currentData.workloadType);
|
||||
};
|
||||
|
||||
workloadTypeChange = (value) => {
|
||||
const content = this.formRefStep1.current.getFieldsValue();
|
||||
this.formRefStep1.current.resetFields();
|
||||
const initialObj = {
|
||||
workload_type: content.workload_type,
|
||||
workload_name: content.workload_name,
|
||||
workloadType: content.workloadType,
|
||||
workloadName: content.workloadName,
|
||||
};
|
||||
this.formRefStep1.current.setFieldsValue(initialObj);
|
||||
const currentWorkloadSetting = this.state.workloadList.filter((item) => {
|
||||
@@ -274,8 +293,14 @@ class TableList extends React.Component {
|
||||
|
||||
getAcceptTrait = (workloadType) => {
|
||||
const res = this.state.traitList.filter((item) => {
|
||||
if (item.appliesTo.indexOf(workloadType) !== -1) {
|
||||
return true;
|
||||
if (item.appliesTo) {
|
||||
if (item.appliesTo === '*') {
|
||||
return true;
|
||||
}
|
||||
if (item.appliesTo.indexOf(workloadType) !== -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
@@ -289,16 +314,14 @@ class TableList extends React.Component {
|
||||
this.state.traitNum = this.state.traitNum.filter((item) => {
|
||||
return item.uniq !== uniq;
|
||||
});
|
||||
// this.setState(()=>({
|
||||
// traitNum: this.state.traitNum
|
||||
// }));
|
||||
this.setState((prev) => ({
|
||||
traitNum: prev.traitNum,
|
||||
}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { current, step1InitialValues, traitNum, workloadSettings } = this.state;
|
||||
const { appName, envName } = this.state;
|
||||
const { current, step1InitialValues, traitNum, workloadSettings, isCreate } = this.state;
|
||||
let { workloadList } = this.state;
|
||||
workloadList = Array.isArray(workloadList) ? workloadList : [];
|
||||
let currentDetail;
|
||||
@@ -317,9 +340,14 @@ class TableList extends React.Component {
|
||||
>
|
||||
<div style={{ padding: '16px 48px 0px 16px' }}>
|
||||
<Form.Item
|
||||
name="workload_name"
|
||||
name="workloadName"
|
||||
label="Name"
|
||||
rules={[
|
||||
{
|
||||
pattern: /^[a-z0-9-_]+$/,
|
||||
message:
|
||||
'Names can only use digits(0-9),lowercase letters(a-z),and dashes(-),Underline.',
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input name!',
|
||||
@@ -329,7 +357,7 @@ class TableList extends React.Component {
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="workload_type"
|
||||
name="workloadType"
|
||||
label="Workload Type"
|
||||
rules={[
|
||||
{
|
||||
@@ -356,8 +384,15 @@ class TableList extends React.Component {
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="Settings" />
|
||||
</div>
|
||||
<Form.Item
|
||||
label="Settings"
|
||||
style={{
|
||||
background: 'rgba(0, 0, 0, 0.04)',
|
||||
paddingLeft: '16px',
|
||||
marginLeft: '-10px',
|
||||
}}
|
||||
/>
|
||||
<div className="relativeBox">
|
||||
<p className="hasMore">?</p>
|
||||
{Array.isArray(workloadSettings) && workloadSettings.length ? (
|
||||
@@ -365,7 +400,7 @@ class TableList extends React.Component {
|
||||
if (item.name === 'name') {
|
||||
return <Fragment key={item.name} />;
|
||||
}
|
||||
return (
|
||||
return item.type === 4 ? (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
@@ -375,6 +410,22 @@ class TableList extends React.Component {
|
||||
required: item.required,
|
||||
message: `Please input ${item.name}!`,
|
||||
},
|
||||
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
required: item.required,
|
||||
message: `Please input ${item.name}!`,
|
||||
},
|
||||
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
@@ -389,9 +440,24 @@ class TableList extends React.Component {
|
||||
<Button type="primary" className="floatRightGap" onClick={this.createWorkload}>
|
||||
Next
|
||||
</Button>
|
||||
<Link to="/ApplicationList">
|
||||
<Button className="floatRightGap">Cancle</Button>
|
||||
</Link>
|
||||
{isCreate === true || isCreate === 'true' ? (
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/ApplicationList`,
|
||||
}}
|
||||
>
|
||||
<Button className="floatRightGap">Cancle</Button>
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/ApplicationList/${appName}/Components`,
|
||||
state: { appName, envName },
|
||||
}}
|
||||
>
|
||||
<Button className="floatRightGap">Cancle</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
@@ -403,22 +469,22 @@ class TableList extends React.Component {
|
||||
<div className="minBox" style={{ width: '60%' }}>
|
||||
<div style={{ padding: '0px 48px 0px 16px', width: '60%' }}>
|
||||
<p style={{ fontSize: '18px', lineHeight: '32px' }}>
|
||||
Name:<span>{step1InitialValues.workload_name}</span>
|
||||
Name:<span>{step1InitialValues.workloadName}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ border: '1px solid #eee', padding: '16px 48px 16px 16px' }}>
|
||||
<p className="title">{step1InitialValues.workload_type}</p>
|
||||
<p className="title">{step1InitialValues.workloadType}</p>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>apps/v1</span>
|
||||
<span
|
||||
style={{
|
||||
color: '#1890ff',
|
||||
cursor: 'pointer',
|
||||
display: this.state.isShowMore ? 'none' : 'black',
|
||||
// display: this.state.isShowMore ? 'none' : 'black',
|
||||
}}
|
||||
onClick={this.changeShowMore}
|
||||
>
|
||||
more...
|
||||
{this.state.isShowMore ? 'close...' : 'more...'}
|
||||
</span>
|
||||
</div>
|
||||
{this.state.isShowMore ? (
|
||||
@@ -481,14 +547,14 @@ class TableList extends React.Component {
|
||||
<div>
|
||||
<div className="minBox">
|
||||
<p>
|
||||
Name:<span>{step1InitialValues.workload_name}</span>
|
||||
Name:<span>{step1InitialValues.workloadName}</span>
|
||||
</p>
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<div className="summaryBox1">
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{step1InitialValues.workload_type}</p>
|
||||
<p className="title">{step1InitialValues.workloadType}</p>
|
||||
<p>apps/v1</p>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -548,11 +614,6 @@ class TableList extends React.Component {
|
||||
</Row>
|
||||
</div>
|
||||
<div className="buttonBox">
|
||||
{/* <Link to="/ApplicationList">
|
||||
<Button type="primary" className="floatRight">
|
||||
Confirm
|
||||
</Button>
|
||||
</Link> */}
|
||||
<Button
|
||||
type="primary"
|
||||
className="floatRight"
|
||||
@@ -570,16 +631,43 @@ class TableList extends React.Component {
|
||||
);
|
||||
}
|
||||
return (
|
||||
<PageContainer>
|
||||
<div className="create-container create-app">
|
||||
<Steps current={current}>
|
||||
<Step title="Step 1" description="Choose Workload" />
|
||||
<Step title="Step 2" description="Attach Trait" />
|
||||
<Step title="Step 3" description="Review and confirm" />
|
||||
</Steps>
|
||||
{currentDetail}
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Applications</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
{isCreate === true || isCreate === 'true' ? (
|
||||
<span>{appName}</span>
|
||||
) : (
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/ApplicationList/${appName}/Components`,
|
||||
state: { appName, envName },
|
||||
}}
|
||||
>
|
||||
{appName}
|
||||
</Link>
|
||||
)}
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>createComponent</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
</PageContainer>
|
||||
<PageContainer>
|
||||
<div className="create-container create-app">
|
||||
<Steps current={current}>
|
||||
<Step title="Step 1" description="Choose Workload" />
|
||||
<Step title="Step 2" description="Attach Trait" />
|
||||
<Step title="Step 3" description="Review and confirm" />
|
||||
</Steps>
|
||||
{currentDetail}
|
||||
</div>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
}
|
||||
.relativeBox {
|
||||
position: relative;
|
||||
// border: 1px solid #eee;
|
||||
padding: 0 48px 0 16px;
|
||||
.hasMore {
|
||||
position: absolute;
|
||||
@@ -53,7 +52,7 @@
|
||||
}
|
||||
.summaryBox1 {
|
||||
color: #fff;
|
||||
background: #0097a7;
|
||||
background: rgb(24, 144, 255);
|
||||
}
|
||||
.summaryBox2 {
|
||||
background: yellow;
|
||||
@@ -35,29 +35,35 @@ export default class CreateTraitItem extends React.PureComponent {
|
||||
return this.formRefStep2.current.getFieldsValue();
|
||||
};
|
||||
|
||||
validateFields = () => {
|
||||
return this.formRefStep2.current.validateFields();
|
||||
};
|
||||
|
||||
traitSelectChange = async (value, isType = 1) => {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/getTraitByName',
|
||||
payload: {
|
||||
traitName: value,
|
||||
},
|
||||
});
|
||||
this.setState({
|
||||
parameters: res.parameters,
|
||||
});
|
||||
if (isType === 2) {
|
||||
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
|
||||
} else {
|
||||
// 进行默认值填写
|
||||
const parameters = _.get(res, 'parameters', []);
|
||||
if (parameters.length) {
|
||||
const initialObj = {};
|
||||
parameters.forEach((item) => {
|
||||
if (item.default) {
|
||||
initialObj[item.name] = item.default;
|
||||
}
|
||||
});
|
||||
this.formRefStep2.current.setFieldsValue(initialObj);
|
||||
if (value) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/getTraitByName',
|
||||
payload: {
|
||||
traitName: value,
|
||||
},
|
||||
});
|
||||
this.setState({
|
||||
parameters: res.parameters,
|
||||
});
|
||||
if (isType === 2) {
|
||||
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
|
||||
} else {
|
||||
// 进行默认值填写
|
||||
const parameters = _.get(res, 'parameters', []);
|
||||
if (parameters.length) {
|
||||
const initialObj = {};
|
||||
parameters.forEach((item) => {
|
||||
if (item.default) {
|
||||
initialObj[item.name] = item.default;
|
||||
}
|
||||
});
|
||||
this.formRefStep2.current.setFieldsValue(initialObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -78,8 +84,12 @@ export default class CreateTraitItem extends React.PureComponent {
|
||||
>
|
||||
<div style={{ border: '1px solid #eee', margin: '16px 0px 8px' }}>
|
||||
<div style={{ padding: '16px 48px 0px 16px' }}>
|
||||
<Form.Item name="name" label="Trait">
|
||||
<Select placeholder="Select a Trait" allowClear onChange={this.traitSelectChange}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="Trait"
|
||||
rules={[{ required: true, message: 'Please Select a Trait!' }]}
|
||||
>
|
||||
<Select placeholder="Select a Trait" onChange={this.traitSelectChange}>
|
||||
{availableTraitList.map((item) => {
|
||||
return (
|
||||
<Option value={item.name} key={item.name}>
|
||||
@@ -89,14 +99,39 @@ export default class CreateTraitItem extends React.PureComponent {
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="Properties" />
|
||||
<Form.Item label="Properties" style={{ marginLeft: '-10px' }} />
|
||||
</div>
|
||||
<div className="relativeBox">
|
||||
{/* <p className="hasMore">?</p> */}
|
||||
{this.state.parameters ? (
|
||||
this.state.parameters.map((item) => {
|
||||
return (
|
||||
<Form.Item name={item.name} label={item.name} key={item.name}>
|
||||
return item.type === 4 ? (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
required: item.required || false,
|
||||
message: `Please input ${item.name} !`,
|
||||
},
|
||||
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
required: item.required || false,
|
||||
message: `Please input ${item.name} !`,
|
||||
},
|
||||
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
@@ -1,38 +1,86 @@
|
||||
import React from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { SearchOutlined, BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Row, Col, Form, Select, DatePicker, Spin, Empty } from 'antd';
|
||||
import { Button, Form, Spin, Breadcrumb, Modal, Input, Table, Space, message } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import moment from 'moment';
|
||||
import './index.less';
|
||||
import { Link } from 'umi';
|
||||
|
||||
const { Option } = Select;
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 6,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 18,
|
||||
},
|
||||
};
|
||||
|
||||
@connect(({ loading, applist, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
// 当applist这个models有数据请求行为的时候,loading为true,没有请求的时候为false
|
||||
// loadingList: loading.effects['applist/getList'],
|
||||
// 当applist的effects中的getList有异步请求行为时为true,没有请求行为时为false
|
||||
returnObj: applist.returnObj,
|
||||
currentEnv: globalData.currentEnv,
|
||||
}))
|
||||
class TableList extends React.Component {
|
||||
formRef = React.createRef();
|
||||
|
||||
columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (text, record) => {
|
||||
return (
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/ApplicationList/${record.name}/Components`,
|
||||
state: { appName: record.name, envName: this.props.currentEnv },
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Status',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
render: (text) => {
|
||||
return text;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Created Time',
|
||||
dataIndex: 'createdTime',
|
||||
key: 'createdTime',
|
||||
render: (text) => {
|
||||
return this.getFormatDate(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Actions',
|
||||
dataIndex: 'Actions',
|
||||
key: 'Actions',
|
||||
render: (text, record) => {
|
||||
return (
|
||||
<Space>
|
||||
<Button onClick={() => this.goToDetail(record)}>Details</Button>
|
||||
<Button onClick={() => this.deleteApp(record)}>Delete</Button>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
this.state = {
|
||||
visible: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { currentEnv } = this.props;
|
||||
if (currentEnv) {
|
||||
this.props.dispatch({
|
||||
type: 'applist/getList', // applist对应models层的命名空间namespace
|
||||
payload: {
|
||||
url: `/api/envs/${currentEnv}/apps/`,
|
||||
},
|
||||
});
|
||||
}
|
||||
this.getInitData();
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
@@ -40,29 +88,81 @@ class TableList extends React.Component {
|
||||
return true;
|
||||
}
|
||||
this.props.dispatch({
|
||||
type: 'applist/getList', // applist对应models层的命名空间namespace
|
||||
type: 'applist/getList',
|
||||
payload: {
|
||||
url: `/api/envs/${nextProps.currentEnv}/apps/`,
|
||||
},
|
||||
});
|
||||
return true;
|
||||
// return true;
|
||||
}
|
||||
|
||||
onFinish = () => {
|
||||
// const data = moment(values.createTime).format('YYYY-MM-DD')
|
||||
getInitData = () => {
|
||||
const { currentEnv } = this.props;
|
||||
if (currentEnv) {
|
||||
this.props.dispatch({
|
||||
type: 'applist/getList',
|
||||
payload: {
|
||||
url: `/api/envs/${currentEnv}/apps/`,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
goToDetail = (record) => {
|
||||
this.props.history.push({
|
||||
pathname: `/ApplicationList/${record.name}/Components`,
|
||||
state: { appName: record.name, envName: this.props.currentEnv },
|
||||
});
|
||||
};
|
||||
|
||||
deleteApp = async (record) => {
|
||||
const appName = record.name;
|
||||
const envName = this.props.currentEnv;
|
||||
if (appName && envName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/deleteApp',
|
||||
payload: {
|
||||
appName,
|
||||
envName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
this.getInitData();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
const submitData = await this.formRef.current.validateFields();
|
||||
this.props.history.push({
|
||||
pathname: `/ApplicationList/${submitData.appName}/createComponent`,
|
||||
state: { ...submitData, isCreate: true },
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
onFinish = () => {};
|
||||
|
||||
handleChange = () => {};
|
||||
|
||||
handleAdd = () => {};
|
||||
|
||||
onSelect = () => {
|
||||
// console.log("selected", selectedKeys, info);
|
||||
};
|
||||
onSelect = () => {};
|
||||
|
||||
getHeight = (num) => {
|
||||
return `${num * 55}px`;
|
||||
return `${num * 43}px`;
|
||||
};
|
||||
|
||||
getFormatDate = (time) => {
|
||||
@@ -71,107 +171,71 @@ class TableList extends React.Component {
|
||||
|
||||
render() {
|
||||
let { loadingAll, returnObj } = this.props;
|
||||
const { currentEnv } = this.props;
|
||||
loadingAll = loadingAll || false;
|
||||
returnObj = returnObj || [];
|
||||
const colorObj = {
|
||||
Deployed: 'first1',
|
||||
Staging: 'first2',
|
||||
UNKNOWN: 'first3',
|
||||
};
|
||||
loadingAll = loadingAll || false;
|
||||
return (
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="applist">
|
||||
<Form name="horizontal_login" layout="inline" onFinish={this.onFinish}>
|
||||
<Form.Item name="createTime">
|
||||
<DatePicker placeholder="createTime" />
|
||||
</Form.Item>
|
||||
<Form.Item name="status">
|
||||
<Select
|
||||
placeholder="status"
|
||||
style={{ width: 120 }}
|
||||
onChange={this.handleChange}
|
||||
allowClear
|
||||
>
|
||||
<Option value="True">True</Option>
|
||||
<Option value="False">False</Option>
|
||||
<Option value="UNKNOWN">UNKNOWN</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button icon={<SearchOutlined />} htmlType="submit">
|
||||
Search
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Link to="/ApplicationList/CreateApplication">
|
||||
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Applications</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="applist">
|
||||
<Form name="horizontal_login" layout="inline" onFinish={this.onFinish}>
|
||||
<Form.Item>
|
||||
<Button onClick={this.showModal} type="primary" style={{ marginBottom: 16 }}>
|
||||
create
|
||||
</Button>
|
||||
</Link>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
<Table
|
||||
rowKey={(record) => record.name + Math.random(1, 100)}
|
||||
columns={this.columns}
|
||||
dataSource={returnObj}
|
||||
/>
|
||||
</Spin>
|
||||
<Modal
|
||||
title="Create Application"
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={[
|
||||
<Button key="submit" type="primary" onClick={this.handleOk}>
|
||||
Create
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
|
||||
<Form.Item
|
||||
name="appName"
|
||||
label="Name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input application name!',
|
||||
},
|
||||
{
|
||||
pattern: /^[a-z0-9-_]+$/,
|
||||
message:
|
||||
'Name can only use digits(0-9),lowercase letters(a-z),and dashes(-),Underline.',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="Description">
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
<Row gutter={16}>
|
||||
{Array.isArray(returnObj) && returnObj.length ? (
|
||||
returnObj.map((item, index) => {
|
||||
const { traits = [] } = item;
|
||||
return (
|
||||
<Col span={6} onClick={this.gotoDetail} key={index.toString()}>
|
||||
<Link
|
||||
to={{
|
||||
pathname: '/ApplicationList/ApplicationListDetail',
|
||||
state: { appName: item.name, envName: currentEnv },
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
title={item.name}
|
||||
bordered={false}
|
||||
extra={this.getFormatDate(item.created)}
|
||||
>
|
||||
<div className="cardContent">
|
||||
<div className="box2" style={{ height: this.getHeight(traits.length) }} />
|
||||
<div className="box1">
|
||||
{traits.length ? (
|
||||
<div className="box3" style={{ width: '40px' }} />
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<div
|
||||
className={['hasPadding', colorObj[item.status] || 'first3'].join(
|
||||
' ',
|
||||
)}
|
||||
>
|
||||
<ApartmentOutlined style={{ marginRight: '10px' }} />
|
||||
{item.workload}
|
||||
</div>
|
||||
</div>
|
||||
{traits.map((item1, index1) => {
|
||||
return (
|
||||
<div className="box1" key={index1.toString()}>
|
||||
<div className="box3" style={{ width: '80px' }} />
|
||||
<div className="other hasPadding">
|
||||
<BranchesOutlined style={{ marginRight: '10px' }} />
|
||||
{item1}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
</Col>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div style={{ width: '100%', height: '80%' }}>
|
||||
<Empty />
|
||||
</div>
|
||||
)}
|
||||
</Row>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
</Modal>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,38 +15,38 @@
|
||||
}
|
||||
.box2 {
|
||||
position: absolute;
|
||||
top: 21px;
|
||||
top: 15px;
|
||||
width: 0;
|
||||
padding: 0;
|
||||
border-left: 1px solid black;
|
||||
}
|
||||
.box3 {
|
||||
position: absolute;
|
||||
top: 21px;
|
||||
top: 15px;
|
||||
height: 0;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
.hasPadding {
|
||||
padding: 10px;
|
||||
padding: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
div {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.first1 {
|
||||
margin-left: 40px;
|
||||
background: #1890ff;
|
||||
margin-left: 30px;
|
||||
background: #4caf51;
|
||||
}
|
||||
.first2 {
|
||||
margin-left: 40px;
|
||||
background: red;
|
||||
margin-left: 30px;
|
||||
background: #f44337;
|
||||
}
|
||||
.first3 {
|
||||
margin-left: 40px;
|
||||
background: #92c47c;
|
||||
margin-left: 30px;
|
||||
background: #1890ff;
|
||||
}
|
||||
.other {
|
||||
margin-left: 80px;
|
||||
background: #ee9611;
|
||||
margin-left: 50px;
|
||||
background: #ffc105;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Space, Button, Row, Col, message, Spin } from 'antd';
|
||||
// import { Space, Modal, Button, Row, Col, message, Spin } from 'antd';
|
||||
// import { ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import { Space, Button, Row, Col, message, Spin, Breadcrumb, Modal } from 'antd';
|
||||
import { Link } from 'umi';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import './index.less';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
|
||||
// const { confirm } = Modal;
|
||||
const { confirm } = Modal;
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.capability,
|
||||
@@ -19,6 +19,7 @@ class TableList extends React.PureComponent {
|
||||
this.state = {
|
||||
workloadList: [],
|
||||
traitList: [],
|
||||
capabilityCenterName: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,7 +35,16 @@ class TableList extends React.PureComponent {
|
||||
const workloadList = [];
|
||||
const traitList = [];
|
||||
if (Array.isArray(res)) {
|
||||
const capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
let capabilityCenterName = '';
|
||||
if (this.props.location.state) {
|
||||
capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
sessionStorage.setItem('capabilityCenterName', capabilityCenterName);
|
||||
} else {
|
||||
capabilityCenterName = sessionStorage.getItem('capabilityCenterName');
|
||||
}
|
||||
this.setState({
|
||||
capabilityCenterName,
|
||||
});
|
||||
res.forEach((item) => {
|
||||
if (item.center === capabilityCenterName) {
|
||||
if (item.type === 'workload') {
|
||||
@@ -53,13 +63,12 @@ class TableList extends React.PureComponent {
|
||||
};
|
||||
|
||||
gotoOtherPage = () => {
|
||||
// window.location.href = 'https://github.com/oam-dev/catalog/blob/master/workloads/cloneset/README.md';
|
||||
window.open('https://github.com/oam-dev/catalog/blob/master/workloads/cloneset/README.md');
|
||||
// window.open('https://github.com/oam-dev/catalog/blob/master/workloads/cloneset/README.md');
|
||||
};
|
||||
|
||||
installSignle = async (e, name) => {
|
||||
e.stopPropagation();
|
||||
const capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
const { capabilityCenterName } = this.state;
|
||||
const res = await this.props.dispatch({
|
||||
type: 'capability/syncOneCapability',
|
||||
payload: {
|
||||
@@ -69,32 +78,34 @@ class TableList extends React.PureComponent {
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
// this.getInitialData();
|
||||
window.location.reload();
|
||||
this.getInitialData();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
uninstallSignle = async (e, name) => {
|
||||
e.stopPropagation();
|
||||
// const capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
if (name) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'capability/deleteOneCapability',
|
||||
payload: {
|
||||
// capabilityCenterName,
|
||||
capabilityName: name,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
// this.getInitialData();
|
||||
window.location.reload();
|
||||
this.getInitialData();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
syncAllSignle = async () => {
|
||||
const capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
const { capabilityCenterName } = this.state;
|
||||
if (capabilityCenterName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'capability/syncCapability',
|
||||
@@ -104,56 +115,51 @@ class TableList extends React.PureComponent {
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
// this.getInitialData();
|
||||
window.location.reload();
|
||||
this.getInitialData();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
showDeleteConfirm = () => {
|
||||
message.info('正在开发中...');
|
||||
// // eslint-disable-next-line
|
||||
// const _this = this;
|
||||
// const capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
// if (capabilityCenterName) {
|
||||
// confirm({
|
||||
// title: `Are you sure delete ${capabilityCenterName}?`,
|
||||
// icon: <ExclamationCircleOutlined />,
|
||||
// width: 500,
|
||||
// content: (
|
||||
// <div>
|
||||
// <p style={{ margin: '0px' }}>您本次移除 {capabilityCenterName},将会删除的应用列表:</p>
|
||||
// <Space>
|
||||
// <span>abc</span>
|
||||
// <span>abc</span>
|
||||
// <span>abc</span>
|
||||
// <span>abc</span>
|
||||
// </Space>
|
||||
// <p style={{ margin: '0px' }}>
|
||||
// 确认后,移除 {capabilityCenterName},并且删除相应的应用?
|
||||
// </p>
|
||||
// </div>
|
||||
// ),
|
||||
// okText: 'Yes',
|
||||
// okType: 'danger',
|
||||
// cancelText: 'No',
|
||||
// async onOk() {
|
||||
// const res = await _this.props.dispatch({
|
||||
// type: 'capability/deleteCapability',
|
||||
// payload: {
|
||||
// capabilityName: capabilityCenterName,
|
||||
// },
|
||||
// });
|
||||
// if (res) {
|
||||
// message.success(res);
|
||||
// _this.props.history.push({ pathname: '/Capability' });
|
||||
// }
|
||||
// },
|
||||
// onCancel() {
|
||||
// // console.log('Cancel');
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// eslint-disable-next-line
|
||||
const _this = this;
|
||||
const { capabilityCenterName } = this.state;
|
||||
if (capabilityCenterName) {
|
||||
confirm({
|
||||
title: `Are you sure delete ${capabilityCenterName}?`,
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
width: 500,
|
||||
content: (
|
||||
<div>
|
||||
<p style={{ margin: '0px' }}>您本次移除 {capabilityCenterName},将会删除的应用列表:</p>
|
||||
<p style={{ margin: '0px' }}>
|
||||
确认后,移除 {capabilityCenterName},并且删除相应的应用?
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
okText: 'Yes',
|
||||
okType: 'danger',
|
||||
cancelText: 'No',
|
||||
async onOk() {
|
||||
const res = await _this.props.dispatch({
|
||||
type: 'capability/deleteCapability',
|
||||
payload: {
|
||||
capabilityCenterName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
_this.props.history.push({ pathname: '/Capability' });
|
||||
}
|
||||
},
|
||||
onCancel() {
|
||||
// console.log('Cancel');
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -161,82 +167,101 @@ class TableList extends React.PureComponent {
|
||||
let { loadingAll } = this.props;
|
||||
loadingAll = loadingAll || false;
|
||||
return (
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Space>
|
||||
<Button type="primary" onClick={this.syncAllSignle}>
|
||||
Install all
|
||||
</Button>
|
||||
<Button type="default" onClick={this.showDeleteConfirm}>
|
||||
Remove
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Workloads</h3>
|
||||
<Row>
|
||||
{workloadList.length ? (
|
||||
workloadList.map((item) => {
|
||||
return (
|
||||
<Col span="4" key={item.name}>
|
||||
<div className="itemBox" onClick={this.gotoOtherPage}>
|
||||
<img
|
||||
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
|
||||
alt="workload"
|
||||
/>
|
||||
<p>{item.name}</p>
|
||||
{item.status === 'installed' ? (
|
||||
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
|
||||
uninstall
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment>
|
||||
<div>暂无可用的workload</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Traits</h3>
|
||||
<Row>
|
||||
{traitList.length ? (
|
||||
traitList.map((item) => {
|
||||
return (
|
||||
<Col span="4" key={item.name}>
|
||||
<div className="itemBox" onClick={this.gotoOtherPage}>
|
||||
<img
|
||||
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
|
||||
alt="workload"
|
||||
/>
|
||||
<p>{item.name}</p>
|
||||
{item.status === 'installed' ? (
|
||||
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
|
||||
uninstall
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment>
|
||||
<div>暂无可用的trait</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/Capability">Capability</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Detail</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Space>
|
||||
<Button type="primary" onClick={this.syncAllSignle}>
|
||||
Install all
|
||||
</Button>
|
||||
<Button type="default" onClick={this.showDeleteConfirm}>
|
||||
Remove
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Workloads</h3>
|
||||
<Row>
|
||||
{workloadList.length ? (
|
||||
workloadList.map((item) => {
|
||||
return (
|
||||
<Col span="4" key={item.name}>
|
||||
<div className="itemBox" onClick={this.gotoOtherPage}>
|
||||
<div className="title">{item.name.substr(0, 3).toUpperCase()}</div>
|
||||
<p>{item.name}</p>
|
||||
{item.status === 'installed' ? (
|
||||
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
|
||||
uninstall
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
onClick={(e) => this.installSignle(e, item.name)}
|
||||
type="primary"
|
||||
ghost
|
||||
>
|
||||
install
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment>
|
||||
<div>暂无可用的workload</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Traits</h3>
|
||||
<Row>
|
||||
{traitList.length ? (
|
||||
traitList.map((item) => {
|
||||
return (
|
||||
<Col span="4" key={item.name}>
|
||||
<div className="itemBox" onClick={this.gotoOtherPage}>
|
||||
<div className="title">{item.name.substr(0, 3).toUpperCase()}</div>
|
||||
<p>{item.name}</p>
|
||||
{item.status === 'installed' ? (
|
||||
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
|
||||
uninstall
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
onClick={(e) => this.installSignle(e, item.name)}
|
||||
type="primary"
|
||||
ghost
|
||||
>
|
||||
install
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment>
|
||||
<div>暂无可用的trait</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 10px;
|
||||
margin: 10px 16px 10px 0;
|
||||
padding: 10px 0;
|
||||
background-color: #fff;
|
||||
cursor: pointer;
|
||||
@@ -11,6 +11,12 @@
|
||||
width: 60%;
|
||||
margin: 10px auto;
|
||||
}
|
||||
.title {
|
||||
width: 60%;
|
||||
margin: 10px auto;
|
||||
font-size: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
margin-bottom: 10px;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Button, Table, Space, Modal, Form, Input, message, Spin } from 'antd';
|
||||
// import { ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import { Button, Table, Space, Modal, Form, Input, message, Spin, Breadcrumb } from 'antd';
|
||||
import { CopyOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import { Link } from 'umi';
|
||||
import './index.less';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
|
||||
// const { confirm } = Modal;
|
||||
const { Column } = Table;
|
||||
const { confirm } = Modal;
|
||||
|
||||
const layout = {
|
||||
labelCol: {
|
||||
@@ -32,6 +32,7 @@ class TableList extends React.PureComponent {
|
||||
this.state = {
|
||||
visible: false,
|
||||
capabilityList: [],
|
||||
isCreateLoading: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -56,14 +57,19 @@ class TableList extends React.PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
showModal = async () => {
|
||||
await this.setState({
|
||||
visible: true,
|
||||
isCreateLoading: false,
|
||||
});
|
||||
this.formRef.current.resetFields();
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
const submitData = await this.formRef.current.validateFields();
|
||||
this.setState({
|
||||
isCreateLoading: true,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'capability/createCapabilityCenter',
|
||||
payload: {
|
||||
@@ -85,13 +91,6 @@ class TableList extends React.PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
// handleTest = async () => {
|
||||
// await this.formRef.current.validateFields();
|
||||
// this.setState({
|
||||
// visible: false,
|
||||
// });
|
||||
// };
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
@@ -113,58 +112,63 @@ class TableList extends React.PureComponent {
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
// this.getInitialData();
|
||||
}
|
||||
const newList1 = _.cloneDeep(this.state.capabilityList);
|
||||
newList1[index].btnSyncLoading = false;
|
||||
this.setState(() => ({
|
||||
capabilityList: newList1,
|
||||
}));
|
||||
window.location.reload();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
showDeleteConfirm = () => {
|
||||
message.info('正在开发中...');
|
||||
// if (record) {
|
||||
// // eslint-disable-next-line
|
||||
// const _this = this;
|
||||
// confirm({
|
||||
// title: `Are you sure delete ${record}?`,
|
||||
// icon: <ExclamationCircleOutlined />,
|
||||
// width: 500,
|
||||
// content: (
|
||||
// <div>
|
||||
// <p>您本次移除 {record},将会删除的应用列表:</p>
|
||||
// <Space>
|
||||
// <span>abc</span>
|
||||
// <span>abc</span>
|
||||
// <span>abc</span>
|
||||
// <span>abc</span>
|
||||
// </Space>
|
||||
// <p>确认后,移除{record},并且删除相应的应用?</p>
|
||||
// </div>
|
||||
// ),
|
||||
// okText: 'Yes',
|
||||
// okType: 'danger',
|
||||
// cancelText: 'No',
|
||||
// async onOk() {
|
||||
// const res = await _this.props.dispatch({
|
||||
// type: 'capability/deleteCapability',
|
||||
// payload: {
|
||||
// capabilityName: record,
|
||||
// },
|
||||
// });
|
||||
// if (res) {
|
||||
// message.success(res);
|
||||
// _this.getInitialData();
|
||||
// }
|
||||
// },
|
||||
// onCancel() {
|
||||
// // console.log('Cancel');
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
copyURL = (text) => {
|
||||
const oInput = document.createElement('input');
|
||||
oInput.value = text;
|
||||
document.body.appendChild(oInput);
|
||||
oInput.select();
|
||||
document.execCommand('Copy');
|
||||
oInput.className = 'oInput';
|
||||
oInput.style.display = 'none';
|
||||
message.success('copy success');
|
||||
};
|
||||
|
||||
showDeleteConfirm = (record) => {
|
||||
if (record) {
|
||||
// eslint-disable-next-line
|
||||
const _this = this;
|
||||
confirm({
|
||||
title: `Are you sure delete ${record}?`,
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
width: 500,
|
||||
content: (
|
||||
<div>
|
||||
<p>您本次移除 {record},将会删除的应用列表:</p>
|
||||
<p>确认后,移除{record},并且删除相应的应用?</p>
|
||||
</div>
|
||||
),
|
||||
okText: 'Yes',
|
||||
okType: 'danger',
|
||||
cancelText: 'No',
|
||||
async onOk() {
|
||||
const res = await _this.props.dispatch({
|
||||
type: 'capability/deleteCapability',
|
||||
payload: {
|
||||
capabilityCenterName: record,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
_this.getInitialData();
|
||||
}
|
||||
},
|
||||
onCancel() {
|
||||
// console.log('Cancel');
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -172,107 +176,130 @@ class TableList extends React.PureComponent {
|
||||
let { loadingList } = this.props;
|
||||
loadingList = loadingList || false;
|
||||
capabilityList = Array.isArray(capabilityList) ? capabilityList : [];
|
||||
const { isCreateLoading } = this.state;
|
||||
return (
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingList}>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Space>
|
||||
<Button type="primary" onClick={this.showModal}>
|
||||
Create
|
||||
</Button>
|
||||
{/* <Button type="default">Sync All</Button> */}
|
||||
</Space>
|
||||
</div>
|
||||
<Modal
|
||||
title="Create Capability Center"
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={[
|
||||
// <Button key="test" onClick={this.handleTest}>
|
||||
// Test
|
||||
// </Button>,
|
||||
<Button key="submit" type="primary" onClick={this.handleOk}>
|
||||
Create
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
|
||||
<Form.Item
|
||||
name="Name"
|
||||
label="Name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input name!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="Address"
|
||||
label="URL"
|
||||
rules={[
|
||||
// { pattern: '/^((https|http|ftp|rtsp|mms){0,1}(:\/\/){0,1})\.(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/',
|
||||
// message: 'please input correct URL'
|
||||
// },
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input URL!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Table dataSource={capabilityList} pagination={false} rowKey={(record) => record.name}>
|
||||
<Column
|
||||
title="Name"
|
||||
dataIndex="name"
|
||||
key="name"
|
||||
render={(text, record) => {
|
||||
return (
|
||||
<Link to={{ pathname: '/Capability/Detail', state: { name: record.name } }}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="URL"
|
||||
dataIndex="url"
|
||||
key="url"
|
||||
render={(text) => {
|
||||
return (
|
||||
<a href={text} target="_blank" rel="noreferrer">
|
||||
{text}
|
||||
</a>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Operations"
|
||||
dataIndex="name"
|
||||
key="name"
|
||||
render={(text, record, index) => {
|
||||
return (
|
||||
<Space>
|
||||
<Button
|
||||
loading={record.btnSyncLoading}
|
||||
onClick={() => this.syncSignle(text, index)}
|
||||
>
|
||||
sync
|
||||
</Button>
|
||||
<Button onClick={() => this.showDeleteConfirm(text)}>remove</Button>
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Capability</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingList}>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Space>
|
||||
<Button type="primary" onClick={this.showModal}>
|
||||
Create
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<Modal
|
||||
title="Create Capability Center"
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={[
|
||||
<Button
|
||||
loading={isCreateLoading}
|
||||
key="submit"
|
||||
type="primary"
|
||||
onClick={this.handleOk}
|
||||
>
|
||||
Create
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
|
||||
<Form.Item
|
||||
name="Name"
|
||||
label="Name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input name!',
|
||||
},
|
||||
{
|
||||
pattern: new RegExp('^[0-9a-zA-Z_]{1,60}$', 'g'),
|
||||
message:
|
||||
'The maximum length is 60,should be combination of numbers,alphabets,underline!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="Address"
|
||||
label="URL"
|
||||
rules={[
|
||||
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input URL!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Table dataSource={capabilityList} pagination={false} rowKey={(record) => record.name}>
|
||||
<Column
|
||||
title="Name"
|
||||
dataIndex="name"
|
||||
key="name"
|
||||
render={(text, record) => {
|
||||
return (
|
||||
<Link to={{ pathname: '/Capability/Detail', state: { name: record.name } }}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="URL"
|
||||
dataIndex="url"
|
||||
key="url"
|
||||
width="60%"
|
||||
render={(text) => {
|
||||
return (
|
||||
<div className="hoverItem">
|
||||
<a href={text} target="_blank" rel="noreferrer">
|
||||
{text}
|
||||
</a>
|
||||
<div className="copyIcon" onClick={() => this.copyURL(text)}>
|
||||
<CopyOutlined />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Operations"
|
||||
dataIndex="name"
|
||||
key="name"
|
||||
render={(text, record, index) => {
|
||||
return (
|
||||
<Space>
|
||||
<Button
|
||||
loading={record.btnSyncLoading}
|
||||
onClick={() => this.syncSignle(text, index)}
|
||||
type="primary"
|
||||
ghost
|
||||
>
|
||||
sync
|
||||
</Button>
|
||||
<Button onClick={() => this.showDeleteConfirm(text)}>remove</Button>
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
.hoverItem {
|
||||
.copyIcon {
|
||||
display: none;
|
||||
margin-left: 10px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.hoverItem:hover .copyIcon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user