Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
@@ -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"
|
||||
6
.github/workflows/e2e.yml
vendored
@@ -34,9 +34,15 @@ jobs:
|
||||
with:
|
||||
version: "v0.7.0"
|
||||
|
||||
- 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
|
||||
|
||||
3
.github/workflows/go.yml
vendored
@@ -30,5 +30,8 @@ jobs:
|
||||
with:
|
||||
version: "v0.7.0"
|
||||
|
||||
- name: install Kubebuilder
|
||||
uses: RyanSiu1995/kubebuilder-action@v1
|
||||
|
||||
- name: Run Make test
|
||||
run: make test
|
||||
|
||||
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 }}"
|
||||
5
.gitignore
vendored
@@ -5,6 +5,7 @@
|
||||
*.so
|
||||
*.dylib
|
||||
bin
|
||||
_bin
|
||||
e2e/vela
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
@@ -35,6 +36,7 @@ config/crd/bases
|
||||
tmp/
|
||||
|
||||
cmd/vela/fake/source.go
|
||||
charts/vela-core/crds/_.yaml
|
||||
|
||||
# Dashboard
|
||||
dashboard/node_modules/
|
||||
@@ -44,3 +46,6 @@ dashboard/package-lock.json
|
||||
dashboard/src/.umi/
|
||||
package-lock.json
|
||||
dashboard/src/.umi-production/
|
||||
|
||||
# build
|
||||
charts/vela
|
||||
|
||||
145
CONTRIBUTING.md
@@ -5,12 +5,10 @@ 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
|
||||
@@ -19,96 +17,89 @@ contributing to `kubevela` or build a PoC (Proof of Concept).
|
||||
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`.
|
||||
|
||||
Use the following command to develop and debug.
|
||||
* Install Vela Core
|
||||
|
||||
```shell script
|
||||
$ cd cmd/vela
|
||||
$ go run main.go COMMAND [FLAG]
|
||||
vela install
|
||||
```
|
||||
|
||||
## Use
|
||||
|
||||
* Create ENV
|
||||
|
||||
```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 run 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 Containerized(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind Containerized 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
|
||||
```
|
||||
make e2e-test
|
||||
```
|
||||
|
||||
## Make a pull request
|
||||
|
||||
19
Makefile
@@ -8,6 +8,7 @@ LDFLAGS ?= "-X $(VELA_VERSION_VAR)=$(VELA_VERSION) -X $(VELA_GITVERSION_VAR)=$(G
|
||||
|
||||
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))
|
||||
@@ -36,7 +37,17 @@ 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 +62,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
|
||||
@@ -88,7 +99,7 @@ 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
|
||||
@@ -110,7 +121,7 @@ core-deploy: manifests
|
||||
|
||||
# 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
|
||||
|
||||
# Generate code
|
||||
generate: controller-gen
|
||||
|
||||
4
PROJECT
@@ -1,3 +1,7 @@
|
||||
domain: oam.dev
|
||||
repo: github.com/oam-dev/kubevela
|
||||
resources:
|
||||
- group: standard
|
||||
kind: Route
|
||||
version: v1alpha1
|
||||
version: "2"
|
||||
|
||||
291
README.md
@@ -2,6 +2,8 @@
|
||||
|
||||
The Open Application Platform based on Kubernetes and OAM.
|
||||
|
||||
:rotating_light: **Warning: The project is still under heavy development, its UI/UX is also for demo purpose, please don't look inside unless you know what you are doing** Please contact @wonderflow if you are interested in its full story or becoming one of the boostrap contributors/maintainers. :rotating_light:
|
||||
|
||||
## Install
|
||||
|
||||
### Prerequisites
|
||||
@@ -11,80 +13,265 @@ 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
|
||||
$ 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.
|
||||
|
||||
## Demos
|
||||
|
||||
#### Create ENV
|
||||
After `vela install` you will have workloads and traits locally, and available to use by vela cli.
|
||||
|
||||
```
|
||||
$ 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
|
||||
```
|
||||
|
||||
#### workload run
|
||||
```shell script
|
||||
$ vela comp run -t deployment app123 -p 80 --image nginx:1.9.4
|
||||
Creating AppConfig app123
|
||||
$ vela workloads
|
||||
NAME DEFINITION
|
||||
backend containerizeds.standard.oam.dev
|
||||
containerized containerizedworkloads.core.oam.dev
|
||||
task jobs
|
||||
webservice containerizeds.standard.oam.dev
|
||||
```
|
||||
|
||||
```shell script
|
||||
$ vela traits
|
||||
NAME DEFINITION APPLIES TO
|
||||
route routes.standard.oam.dev webservice
|
||||
backend
|
||||
scale manualscalertraits.core.oam.dev webservice
|
||||
backend
|
||||
```
|
||||
|
||||
### Create ENV
|
||||
|
||||
Before working with your application, you should create an env for it.
|
||||
|
||||
```shell script
|
||||
$ vela env init myenv --namespace myenv --email my@email.com --domain kubevela.io
|
||||
ENV myenv CREATED, Namespace: myenv, Email: my@email.com.
|
||||
```
|
||||
|
||||
It will create a namespace called myenv
|
||||
|
||||
```shell script
|
||||
$ kubectl get ns
|
||||
NAME STATUS AGE
|
||||
myenv Active 40s
|
||||
```
|
||||
|
||||
A namespace level issuer for certificate generation with email.
|
||||
```shell script
|
||||
$ kubectl get issuers.cert-manager.io -n myenv
|
||||
NAME READY AGE
|
||||
oam-env-myenv True 40s
|
||||
```
|
||||
|
||||
A env metadata in your local:
|
||||
|
||||
```shell script
|
||||
$ cat ~/.vela/envs/myenv/config.json
|
||||
{"name":"myenv","namespace":"myenv","email":"my@email.com","domain":"kubevela.io","issuer":"oam-env-myenv"}
|
||||
```
|
||||
|
||||
|
||||
### Create Component
|
||||
|
||||
Then let's create application, we will use our env created by default.
|
||||
|
||||
```shell script
|
||||
$ vela comp run mycomp -t webservice --image crccheck/hello-world --port 8000 --app myapp
|
||||
Creating AppConfig appcomp
|
||||
SUCCEED
|
||||
|
||||
$ vela comp status app123
|
||||
```
|
||||
|
||||
#### app
|
||||
It will create component named `mycomp`.
|
||||
|
||||
```shell script
|
||||
$ kubectl get components -n myenv
|
||||
NAME WORKLOAD-KIND AGE
|
||||
mycomp Containerized 10s
|
||||
```
|
||||
$ vela app ls
|
||||
app123
|
||||
poc08032042
|
||||
poc1039
|
||||
|
||||
And an AppConfig named myapp.
|
||||
|
||||
```shell script
|
||||
$ kubectl get appconfig -n myenv
|
||||
NAME AGE
|
||||
myapp 24s
|
||||
```
|
||||
|
||||
Vela Core will work for AppConfig and create K8s deployment and service.
|
||||
|
||||
```shell script
|
||||
$ kubectl get deployment -n myenv
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
mycomp 1/1 1 1 38s
|
||||
```
|
||||
|
||||
```shell script
|
||||
$ kubectl get svc -n myenv
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
mycomp ClusterIP 172.21.4.228 <none> 8080/TCP 49s
|
||||
```
|
||||
|
||||
### Multiple Component
|
||||
|
||||
Creating a new component in the same application is easy, just use the `--app` flag.
|
||||
|
||||
```shell script
|
||||
$ vela comp run db -t backend --image crccheck/hello-world --app myapp
|
||||
Creating App myapp
|
||||
SUCCEED
|
||||
```
|
||||
|
||||
```shell script
|
||||
$ 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
|
||||
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
|
||||
```
|
||||
|
||||
$ vela app delete app123
|
||||
Deleting AppConfig "app123"
|
||||
DELETE SUCCEED
|
||||
Now we can see the application deployed, let's add route trait for visiting.
|
||||
|
||||
### Add Trait
|
||||
|
||||
```shell script
|
||||
$ vela route mycomp --app myapp
|
||||
Adding route for app mycomp
|
||||
Succeeded!
|
||||
```
|
||||
|
||||
It will create route trait for this component.
|
||||
|
||||
```shell script
|
||||
$ kubeclt get routes.standard.oam.dev -n myenv
|
||||
NAME AGE
|
||||
mycomp-trait-5b576c4fc 18s
|
||||
```
|
||||
|
||||
Controller of route trait which is part of vela core will create an ingress for it.
|
||||
|
||||
```shell script
|
||||
$ kubectl get ingress -n myenv
|
||||
NAME HOSTS ADDRESS PORTS AGE
|
||||
mycomp-trait-5b576c4fc mycomp.kubevela.io 123.57.10.233 80, 443 73s
|
||||
```
|
||||
|
||||
Please configure your domain pointing to the public address.
|
||||
|
||||
Then you will be able to visit it by `https://mycomp.kubevela.io`, `mTLS` is automatically enabled.
|
||||
|
||||
|
||||
### Check Status
|
||||
|
||||
|
||||
App level:
|
||||
|
||||
```shell script
|
||||
$ 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: myenv
|
||||
|
||||
Components:
|
||||
|
||||
Name Type Traits
|
||||
db backend
|
||||
mycomp webservice route
|
||||
```
|
||||
|
||||
Component Level:
|
||||
|
||||
```shell script
|
||||
$ vela comp show mycomp
|
||||
About:
|
||||
|
||||
Name: mycomp
|
||||
WorkloadType: webservice
|
||||
Application: myapp
|
||||
|
||||
Environment:
|
||||
|
||||
Namespace: myenv
|
||||
|
||||
Arguments:
|
||||
|
||||
image: crccheck/hello-world
|
||||
name: mycomp
|
||||
port: 8000
|
||||
|
||||
|
||||
Traits:
|
||||
|
||||
route:
|
||||
domain: mycomp.kubevela.io
|
||||
issuer: oam-env-myenv
|
||||
name: route
|
||||
```
|
||||
|
||||
```
|
||||
$ vela comp status mycomp
|
||||
Showing status of Component mycomp deployed in Environment myenv
|
||||
Component Status:
|
||||
Name: mycomp Containerized(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind Containerized 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 App or Component
|
||||
|
||||
```shell script
|
||||
$ vela app ls
|
||||
myapp
|
||||
```
|
||||
|
||||
```shell script
|
||||
$ 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
|
||||
```
|
||||
|
||||
```shell script
|
||||
$ vela comp delete db
|
||||
Deleting Component 'db' from Application 'db'
|
||||
```
|
||||
|
||||
```shell script
|
||||
$ vela comp ls
|
||||
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
|
||||
mycomp myapp webservice route Deployed 2020-09-18 22:42:04 +0800 CST
|
||||
```
|
||||
|
||||
```shell script
|
||||
$ vela app delete myapp
|
||||
Deleting Application "myapp"
|
||||
delete apps succeed myapp from myenv
|
||||
```
|
||||
|
||||
## Dashboard
|
||||
|
||||
We also prepared a dashboard for you, but it's still in heavily development.
|
||||
|
||||
```shell script
|
||||
$ vela dashboard
|
||||
```
|
||||
|
||||
#### Auto-Completion
|
||||
|
||||
@@ -40,6 +40,7 @@ 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"`
|
||||
@@ -56,10 +57,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 {
|
||||
|
||||
@@ -27,8 +27,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 (
|
||||
|
||||
138
api/v1alpha1/route_types.go
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
|
||||
|
||||
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"
|
||||
"k8s.io/api/networking/v1beta1"
|
||||
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"`
|
||||
|
||||
// Path is location Path, default for "/"
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
// 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"`
|
||||
|
||||
// DefaultBackend uses serviceName
|
||||
DefaultBackend *v1beta1.IngressBackend `json:"defaultBackend,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"`
|
||||
|
||||
// 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 NamespaceIssuer
|
||||
Type IssuerType `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type IssuerType string
|
||||
|
||||
const (
|
||||
ClusterIssuer IssuerType = "ClusterIssuer"
|
||||
NamespaceIssuer IssuerType = "Issuer"
|
||||
)
|
||||
|
||||
// Route will automatically discover podTemplate for Port and SelectLabels if they are not set.
|
||||
// If Port and SelectLabels are already set, discovery won't work.
|
||||
// If Port is not set, the first port discovered will be set.
|
||||
// If SelectLabels are not set, all selectorLabels discovered will be set.
|
||||
type Backend struct {
|
||||
// Protocol means backend-protocol, HTTP, HTTPS, GRPC, GRPCS, AJP and FCGI, By default uses HTTP
|
||||
Protocol string `json:"protocol,omitempty"`
|
||||
// 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"`
|
||||
// Port points to backend service port.
|
||||
Port intstr.IntOrString `json:"port,omitempty"`
|
||||
// SelectLabels for backend service.
|
||||
SelectLabels map[string]string `json:"selectLabels,omitempty"`
|
||||
}
|
||||
|
||||
// RouteStatus defines the observed state of Route
|
||||
type RouteStatus struct {
|
||||
Ingress *runtimev1alpha1.TypedReference `json:"ingress,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
|
||||
}
|
||||
@@ -22,9 +22,33 @@ package v1alpha1
|
||||
|
||||
import (
|
||||
corev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||||
"k8s.io/api/networking/v1beta1"
|
||||
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 *Backend) DeepCopyInto(out *Backend) {
|
||||
*out = *in
|
||||
out.Port = in.Port
|
||||
if in.SelectLabels != nil {
|
||||
in, out := &in.SelectLabels, &out.SelectLabels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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(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 *Containerized) DeepCopyInto(out *Containerized) {
|
||||
*out = *in
|
||||
@@ -223,6 +247,129 @@ 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 *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.DefaultBackend != nil {
|
||||
in, out := &in.DefaultBackend, &out.DefaultBackend
|
||||
*out = new(v1beta1.IngressBackend)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
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.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 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.Ingress != nil {
|
||||
in, out := &in.Ingress, &out.Ingress
|
||||
*out = new(corev1alpha1.TypedReference)
|
||||
**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 *ScapeServiceEndPoint) DeepCopyInto(out *ScapeServiceEndPoint) {
|
||||
*out = *in
|
||||
@@ -250,3 +397,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
|
||||
}
|
||||
|
||||
44
charts/third_party/cert-manager/download-cert-manager.sh
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2019 The Knative Authors
|
||||
#
|
||||
# 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.
|
||||
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Download and unpack cert-manager
|
||||
CERT_MANAGER_VERSION=1.0.0
|
||||
ARCHIVE_DOWNLOAD_URL=https://github.com/jetstack/cert-manager/archive/v${CERT_MANAGER_VERSION}.tar.gz
|
||||
YAML_URL=https://github.com/jetstack/cert-manager/releases/download/v${CERT_MANAGER_VERSION}/cert-manager.yaml
|
||||
|
||||
wget $ARCHIVE_DOWNLOAD_URL
|
||||
tar xzf v${CERT_MANAGER_VERSION}.tar.gz
|
||||
|
||||
(
|
||||
# subshell in downloaded directory
|
||||
cd cert-manager-${CERT_MANAGER_VERSION} || exit
|
||||
|
||||
# Copy the CRD yaml file
|
||||
cp deploy/manifests/00-crds.yaml ../cert-manager-crds.yaml
|
||||
)
|
||||
|
||||
# Download the cert-manager yaml file
|
||||
wget $YAML_URL
|
||||
|
||||
# Clean up.
|
||||
rm -rf cert-manager-${CERT_MANAGER_VERSION}
|
||||
rm v${CERT_MANAGER_VERSION}.tar.gz
|
||||
|
||||
# Add enable-certificate-owner-ref option to cert-manager's controller.
|
||||
# The option is to cleans up secret(certificate) by adding ownerref.
|
||||
patch -l cert-manager.yaml owner-ref.patch
|
||||
@@ -11,124 +11,190 @@ spec:
|
||||
group: core.oam.dev
|
||||
names:
|
||||
categories:
|
||||
- crossplane
|
||||
- oam
|
||||
- crossplane
|
||||
- oam
|
||||
kind: HealthScope
|
||||
listKind: HealthScopeList
|
||||
plural: healthscopes
|
||||
singular: healthscope
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .status.health
|
||||
name: HEALTH
|
||||
type: string
|
||||
name: v1alpha2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: A HealthScope determines an aggregate health status based of
|
||||
the health of components.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .status.health
|
||||
name: HEALTH
|
||||
type: string
|
||||
name: v1alpha2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: A HealthScope determines an aggregate health status based of
|
||||
the health of components.
|
||||
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
|
||||
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: A HealthScopeSpec defines the desired state of a HealthScope.
|
||||
properties:
|
||||
probe-interval:
|
||||
description: ProbeInterval is the amount of time in seconds between
|
||||
probing tries.
|
||||
format: int32
|
||||
type: integer
|
||||
probe-timeout:
|
||||
description: ProbeTimeout is the amount of time in seconds to wait
|
||||
when receiving a response before marked failure.
|
||||
format: int32
|
||||
type: integer
|
||||
workloadRefs:
|
||||
description: WorkloadReferences to the workloads that are in this
|
||||
scope.
|
||||
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.
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: A HealthScopeSpec defines the desired state of a HealthScope.
|
||||
properties:
|
||||
probe-interval:
|
||||
description: ProbeInterval is the amount of time in seconds between
|
||||
probing tries.
|
||||
format: int32
|
||||
type: integer
|
||||
probe-timeout:
|
||||
description: ProbeTimeout is the amount of time in seconds to wait
|
||||
when receiving a response before marked failure.
|
||||
format: int32
|
||||
type: integer
|
||||
workloadRefs:
|
||||
description: WorkloadReferences to the workloads that are in this
|
||||
scope.
|
||||
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
|
||||
required:
|
||||
- workloadRefs
|
||||
type: object
|
||||
status:
|
||||
description: A HealthScopeStatus represents the observed state of a HealthScope.
|
||||
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
|
||||
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:
|
||||
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.
|
||||
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:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
- healthStatus
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- workloadRefs
|
||||
type: object
|
||||
status:
|
||||
description: A HealthScopeStatus represents the observed state of a HealthScope.
|
||||
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
|
||||
health:
|
||||
type: string
|
||||
required:
|
||||
- health
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
required:
|
||||
- scopeHealthCondition
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
|
||||
@@ -0,0 +1,465 @@
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.4
|
||||
creationTimestamp: null
|
||||
name: servicemonitors.monitoring.coreos.com
|
||||
spec:
|
||||
group: monitoring.coreos.com
|
||||
names:
|
||||
kind: ServiceMonitor
|
||||
listKind: ServiceMonitorList
|
||||
plural: servicemonitors
|
||||
singular: servicemonitor
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: ServiceMonitor defines monitoring for a set of services.
|
||||
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 Service selection for target discovery
|
||||
by Prometheus.
|
||||
properties:
|
||||
endpoints:
|
||||
description: A list of endpoints allowed as part of this ServiceMonitor.
|
||||
items:
|
||||
description: Endpoint defines a scrapeable endpoint serving Prometheus
|
||||
metrics.
|
||||
properties:
|
||||
basicAuth:
|
||||
description: 'BasicAuth allow an endpoint to authenticate over
|
||||
basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints'
|
||||
properties:
|
||||
password:
|
||||
description: The secret in the service monitor namespace
|
||||
that contains the password for authentication.
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from. Must
|
||||
be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret or its key must
|
||||
be defined
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
username:
|
||||
description: The secret in the service monitor namespace
|
||||
that contains the username for authentication.
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from. Must
|
||||
be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret or its key must
|
||||
be defined
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
type: object
|
||||
bearerTokenFile:
|
||||
description: File to read bearer token for scraping targets.
|
||||
type: string
|
||||
bearerTokenSecret:
|
||||
description: Secret to mount to read bearer token for scraping
|
||||
targets. The secret needs to be in the same namespace as the
|
||||
service monitor and accessible by the Prometheus Operator.
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from. Must
|
||||
be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret or its key must
|
||||
be defined
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
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 service 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 scraping.
|
||||
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: Name or number of the pod port this endpoint refers
|
||||
to. Mutually exclusive with port.
|
||||
x-kubernetes-int-or-string: true
|
||||
tlsConfig:
|
||||
description: TLS configuration to use when scraping the endpoint
|
||||
properties:
|
||||
ca:
|
||||
description: Stuct containing the CA cert to use for the
|
||||
targets.
|
||||
properties:
|
||||
configMap:
|
||||
description: ConfigMap containing data to use for the
|
||||
targets.
|
||||
properties:
|
||||
key:
|
||||
description: The key to select.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the ConfigMap or its
|
||||
key must be defined
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
secret:
|
||||
description: Secret containing data to use for the targets.
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from. Must
|
||||
be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret or its key
|
||||
must be defined
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
type: object
|
||||
caFile:
|
||||
description: Path to the CA cert in the Prometheus container
|
||||
to use for the targets.
|
||||
type: string
|
||||
cert:
|
||||
description: Struct containing the client cert file for
|
||||
the targets.
|
||||
properties:
|
||||
configMap:
|
||||
description: ConfigMap containing data to use for the
|
||||
targets.
|
||||
properties:
|
||||
key:
|
||||
description: The key to select.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the ConfigMap or its
|
||||
key must be defined
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
secret:
|
||||
description: Secret containing data to use for the targets.
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from. Must
|
||||
be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret or its key
|
||||
must be defined
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
type: object
|
||||
certFile:
|
||||
description: Path to the client cert file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
insecureSkipVerify:
|
||||
description: Disable target certificate validation.
|
||||
type: boolean
|
||||
keyFile:
|
||||
description: Path to the client key file in the Prometheus
|
||||
container for the targets.
|
||||
type: string
|
||||
keySecret:
|
||||
description: Secret containing the client key file for the
|
||||
targets.
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from. Must
|
||||
be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret or its key must
|
||||
be defined
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
serverName:
|
||||
description: Used to verify the hostname for the targets.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
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
|
||||
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 Endpoints 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
|
||||
targetLabels:
|
||||
description: TargetLabels transfers labels on the Kubernetes Service
|
||||
onto the target.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- endpoints
|
||||
- selector
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
245
charts/vela-core/crds/standard.oam.dev_routes.yaml
Normal file
@@ -0,0 +1,245 @@
|
||||
|
||||
---
|
||||
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:
|
||||
backend:
|
||||
description: Backend indicate how to connect backend service If it's
|
||||
nil, will auto discovery
|
||||
properties:
|
||||
port:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
description: Port points to backend service port.
|
||||
x-kubernetes-int-or-string: true
|
||||
protocol:
|
||||
description: Protocol means backend-protocol, HTTP, HTTPS, GRPC,
|
||||
GRPCS, AJP and FCGI, By default uses HTTP
|
||||
type: string
|
||||
readTimeout:
|
||||
description: ReadTimeout used for setting read timeout duration
|
||||
for backend service, the unit is second.
|
||||
type: integer
|
||||
selectLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: SelectLabels for backend service.
|
||||
type: object
|
||||
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 uses serviceName
|
||||
properties:
|
||||
resource:
|
||||
description: Resource is an ObjectRef to another Kubernetes resource
|
||||
in the namespace of the Ingress object. If resource is specified,
|
||||
serviceName and servicePort must not be specified.
|
||||
properties:
|
||||
apiGroup:
|
||||
description: APIGroup is the group for the resource being
|
||||
referenced. If APIGroup is not specified, the specified
|
||||
Kind must be in the core API group. For any other third-party
|
||||
types, APIGroup is required.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind is the type of resource being referenced
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of resource being referenced
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
serviceName:
|
||||
description: Specifies the name of the referenced service.
|
||||
type: string
|
||||
servicePort:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
description: Specifies the port of the referenced service.
|
||||
x-kubernetes-int-or-string: true
|
||||
type: object
|
||||
host:
|
||||
description: Host is the host of the route
|
||||
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
|
||||
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:
|
||||
description: Type indicate the issuer is ClusterIssuer or NamespaceIssuer
|
||||
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
|
||||
ingress:
|
||||
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
|
||||
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: []
|
||||
37
charts/vela-core/templates/backend.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: backend
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
|
||||
definition.oam.dev/kind: "Containerized"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: containerizeds.standard.oam.dev
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Containerized"
|
||||
metadata:
|
||||
name: backend.name
|
||||
spec: {
|
||||
replicas: 1
|
||||
podSpec: {
|
||||
containers: [{
|
||||
image: backend.image
|
||||
name: backend.name
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
backend: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -689,7 +689,7 @@ spec:
|
||||
serviceAccountName: cert-manager-cainjector
|
||||
containers:
|
||||
- name: cert-manager
|
||||
image: "quay.io/jetstack/cert-manager-cainjector:v0.12.0"
|
||||
image: "oamdev/cert-manager-cainjector:v0.12.0"
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- --v=2
|
||||
@@ -737,7 +737,7 @@ spec:
|
||||
serviceAccountName: cert-manager
|
||||
containers:
|
||||
- name: cert-manager
|
||||
image: "quay.io/jetstack/cert-manager-controller:v0.12.0"
|
||||
image: "oamdev/cert-manager-controller:v0.12.0"
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- --v=2
|
||||
@@ -793,7 +793,7 @@ spec:
|
||||
serviceAccountName: cert-manager-webhook
|
||||
containers:
|
||||
- name: cert-manager
|
||||
image: "quay.io/jetstack/cert-manager-webhook:v0.12.0"
|
||||
image: "oamdev/cert-manager-webhook:v0.12.0"
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- --v=2
|
||||
|
||||
@@ -3,8 +3,8 @@ kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: containerizeds.standard.oam.dev
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
|
||||
definition.oam.dev/kind: "ContainerizedWorkload"
|
||||
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
|
||||
definition.oam.dev/kind: "Containerized"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: containerizeds.standard.oam.dev
|
||||
@@ -16,23 +16,26 @@ spec:
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ContainerizedWorkload"
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Containerized"
|
||||
metadata:
|
||||
name: containerized.name
|
||||
name: webservice.name
|
||||
spec: {
|
||||
containers: [{
|
||||
image: containerized.image
|
||||
name: containerized.name
|
||||
ports: [{
|
||||
containerPort: containerized.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
replicas: 1
|
||||
podSpec: {
|
||||
containers: [{
|
||||
image: webservice.image
|
||||
name: webservice.name
|
||||
ports: [{
|
||||
containerPort: webservice.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
containerized: {
|
||||
webservice: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
@@ -40,4 +43,4 @@ spec:
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@ metadata:
|
||||
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
|
||||
definition.oam.dev/kind: ManualScalerTrait
|
||||
name: manualscalertraits.core.oam.dev
|
||||
namespace: default
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- core.oam.dev/v1alpha2.ContainerizedWorkload
|
||||
|
||||
@@ -2,6 +2,10 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: metricstraits.standard.oam.dev
|
||||
namespace: default
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
|
||||
definition.oam.dev/kind: MetricsTrait
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- containerizedworkloads.core.oam.dev
|
||||
@@ -10,32 +14,4 @@ spec:
|
||||
- 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
|
||||
}
|
||||
workloadRefPath: spec.workloadRef
|
||||
37
charts/vela-core/templates/routetrait.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: routes.standard.oam.dev
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
|
||||
definition.oam.dev/kind: Route
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- core.oam.dev/v1alpha2.ContainerizedWorkload
|
||||
- standard.oam.dev/v1alpha1.Containerized
|
||||
- deployments.apps
|
||||
workloadRefPath: spec.workloadRef
|
||||
definitionRef:
|
||||
name: routes.standard.oam.dev
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Route"
|
||||
spec: {
|
||||
host: route.domain
|
||||
path: route.path
|
||||
tls: {
|
||||
issuerName: route.issuer
|
||||
}
|
||||
backend: {
|
||||
port: route.port
|
||||
}
|
||||
}
|
||||
}
|
||||
route: {
|
||||
domain: *"" | string
|
||||
path: *"" | string
|
||||
port: *443 | int
|
||||
issuer: *"" | string
|
||||
}
|
||||
@@ -14,34 +14,34 @@ spec:
|
||||
- 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
|
||||
}
|
||||
apiVersion: "v1"
|
||||
kind: "Job"
|
||||
metadata: name: task.name
|
||||
spec: {
|
||||
parallelism: task.count
|
||||
completions: task.count
|
||||
template:
|
||||
spec:
|
||||
containers: [{
|
||||
image: task.image
|
||||
name: task.name
|
||||
ports: [{
|
||||
containerPort: task.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
|
||||
}
|
||||
|
||||
14
charts/vela-core/templates/velaConfig.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: vela-config
|
||||
namespace: default
|
||||
data:
|
||||
certificates.cert-manager.io: |
|
||||
{
|
||||
"repo": "jetstack",
|
||||
"urL": "https://charts.jetstack.io",
|
||||
"name": "cert-manager",
|
||||
"namespace": "cert-manager",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: web-service
|
||||
name: webservice
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
|
||||
definition.oam.dev/kind: "ContainerizedWorkload"
|
||||
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
|
||||
definition.oam.dev/kind: "Containerized"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: containerizeds.standard.oam.dev
|
||||
@@ -16,23 +16,26 @@ spec:
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ContainerizedWorkload"
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Containerized"
|
||||
metadata:
|
||||
name: containerized.name
|
||||
name: webservice.name
|
||||
spec: {
|
||||
containers: [{
|
||||
image: containerized.image
|
||||
name: containerized.name
|
||||
ports: [{
|
||||
containerPort: containerized.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
replicas: 1
|
||||
podSpec: {
|
||||
containers: [{
|
||||
image: webservice.image
|
||||
name: webservice.name
|
||||
ports: [{
|
||||
containerPort: webservice.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
containerized: {
|
||||
webservice: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
@@ -40,4 +43,4 @@ spec:
|
||||
// +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: ""
|
||||
|
||||
@@ -6,8 +6,11 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
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"
|
||||
|
||||
oamcore "github.com/crossplane/oam-kubernetes-runtime/apis/core"
|
||||
oamcontroller "github.com/crossplane/oam-kubernetes-runtime/pkg/controller"
|
||||
oamv1alpha2 "github.com/crossplane/oam-kubernetes-runtime/pkg/controller/v1alpha2"
|
||||
@@ -16,26 +19,31 @@ 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"
|
||||
|
||||
monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
|
||||
"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/log/zap"
|
||||
|
||||
velacore "github.com/oam-dev/kubevela/api/v1alpha1"
|
||||
velacontroller "github.com/oam-dev/kubevela/pkg/controller"
|
||||
velawebhook "github.com/oam-dev/kubevela/pkg/webhook"
|
||||
)
|
||||
|
||||
var scheme = runtime.NewScheme()
|
||||
|
||||
func init() {
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
_ = crdv1.AddToScheme(scheme)
|
||||
_ = oamcore.AddToScheme(scheme)
|
||||
_ = monitoring.AddToScheme(scheme)
|
||||
_ = velacore.AddToScheme(scheme)
|
||||
_ = injectorv1alpha1.AddToScheme(scheme)
|
||||
_ = certmanager.AddToScheme(scheme)
|
||||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
@@ -93,6 +101,17 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if useWebhook {
|
||||
setupLog.Info("vela webhook enabled, will serving at :" + strconv.Itoa(webhookPort))
|
||||
oamwebhook.Add(mgr)
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/crossplane/oam-kubernetes-runtime/apis/core"
|
||||
"github.com/gosuri/uitable"
|
||||
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
|
||||
"github.com/spf13/cobra"
|
||||
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
@@ -34,7 +35,7 @@ var chartTGZSource string
|
||||
|
||||
func init() {
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
|
||||
_ = certmanager.AddToScheme(scheme)
|
||||
_ = core.AddToScheme(scheme)
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
@@ -97,6 +98,7 @@ func newCommand() *cobra.Command {
|
||||
|
||||
// Getting Start
|
||||
NewVersionCommand(),
|
||||
commands.NewInitCommand(commandArgs, ioStream),
|
||||
|
||||
// Apps
|
||||
commands.NewAppsCommand(commandArgs, ioStream),
|
||||
@@ -129,6 +131,7 @@ func newCommand() *cobra.Command {
|
||||
fset := flag.NewFlagSet("logs", flag.ContinueOnError)
|
||||
klog.InitFlags(fset)
|
||||
_ = fset.Set("v", "-1")
|
||||
|
||||
return cmds
|
||||
}
|
||||
|
||||
|
||||
@@ -74,3 +74,23 @@ rules:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- standard.oam.dev
|
||||
resources:
|
||||
- routes
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- standard.oam.dev
|
||||
resources:
|
||||
- routes/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
|
||||
19
config/samples/issuer.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: cert-manager.io/v1alpha2
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: letsencrypt-prod
|
||||
spec:
|
||||
acme:
|
||||
# You must replace this email address with your own.
|
||||
# Let's Encrypt will use this to contact you about expiring
|
||||
# certificates, and issues related to your account.
|
||||
email: wonderflow@icloud.com
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
privateKeySecretRef:
|
||||
# Secret resource that will be used to store the account's private key.
|
||||
name: example-issuer-account-key
|
||||
# Add a single challenge solver, HTTP01 using nginx
|
||||
solvers:
|
||||
- http01:
|
||||
ingress:
|
||||
class: nginx
|
||||
@@ -1,42 +1,37 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: ingresses.networking.k8s.io
|
||||
name: routes.standard.oam.dev
|
||||
annotations:
|
||||
"definition.oam.dev/apiVersion": "networking.k8s.io/v1beta1"
|
||||
"definition.oam.dev/kind": "Ingress"
|
||||
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
|
||||
definition.oam.dev/kind: Route
|
||||
spec:
|
||||
revisionEnabled: true
|
||||
appliesToWorkloads:
|
||||
- core.oam.dev/v1alpha2.ContainerizedWorkload
|
||||
- standard.oam.dev/v1alpha1.Containerized
|
||||
- deployments.apps
|
||||
workloadRefPath: spec.workloadRef
|
||||
definitionRef:
|
||||
name: ingresses.networking.k8s.io
|
||||
name: routes.standard.oam.dev
|
||||
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"
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Route"
|
||||
spec: {
|
||||
rules: [{
|
||||
host: route.domain
|
||||
http: paths: [{
|
||||
backend: {
|
||||
serviceName: route.service
|
||||
servicePort: route.port
|
||||
}}]
|
||||
}]
|
||||
host: route.domain
|
||||
path: route.path
|
||||
tls: {
|
||||
issuerName: route.issuer
|
||||
}
|
||||
backend: {
|
||||
port: route.port
|
||||
}
|
||||
}
|
||||
}
|
||||
route: {
|
||||
domain: string
|
||||
port: *80 | int
|
||||
service: string
|
||||
domain: *"" | string
|
||||
path: *"" | string
|
||||
port: *443 | int
|
||||
issuer: *"" | string
|
||||
}
|
||||
|
||||
|
||||
@@ -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,13 +34,11 @@ export default defineConfig({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
// redirect: `/${envname}/ApplicationList`,
|
||||
redirect: `/ApplicationList`,
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList',
|
||||
icon: 'table',
|
||||
// path: `/${envname}/ApplicationList`,
|
||||
path: `/ApplicationList`,
|
||||
component: './ApplicationList',
|
||||
},
|
||||
@@ -72,30 +68,23 @@ export default defineConfig({
|
||||
path: '/ApplicationList/CreateApplication',
|
||||
component: './ApplicationList/CreateApplication',
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList.Components',
|
||||
hideInMenu: true,
|
||||
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 +98,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 +106,6 @@ export default defineConfig({
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// name: 'Release',
|
||||
// icon: 'table',
|
||||
// path: '/Release',
|
||||
// component: './Release',
|
||||
// },
|
||||
{
|
||||
name: 'Capability',
|
||||
icon: 'table',
|
||||
@@ -183,8 +147,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://30.11.171.29:38081/',
|
||||
changeOrigin: true,
|
||||
// pathRewrite: {
|
||||
// "^/api": "",
|
||||
// },
|
||||
},
|
||||
},
|
||||
test: {
|
||||
|
||||
@@ -35,34 +35,40 @@ export default class CreateTraitItem extends React.PureComponent {
|
||||
return this.formRefStep2.current.getFieldsValue();
|
||||
};
|
||||
|
||||
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 +83,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 +102,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,18 +1,30 @@
|
||||
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 = {
|
||||
@@ -37,28 +49,61 @@ 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' && 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 } = submitData;
|
||||
if (envName && appName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/attachOneTraits',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
params: submitObj,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
message.success(res);
|
||||
const { history } = this.props.propsObj;
|
||||
history.push({
|
||||
pathname: '/ApplicationList/ApplicationListDetail',
|
||||
state: {
|
||||
appName,
|
||||
envName,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -77,98 +122,175 @@ class Trait extends React.Component {
|
||||
onSearch = () => {};
|
||||
|
||||
render() {
|
||||
const { btnValue, title, settings, btnIsShow, crdInfo, appliesTo } = this.props.propsObj;
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
const appList = _.get(this.props, 'returnObj', []);
|
||||
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="Target"
|
||||
name="appName"
|
||||
rules={[{ required: true, message: 'Please Select a Application!' }]}
|
||||
>
|
||||
<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
|
||||
}
|
||||
>
|
||||
{appList.length ? (
|
||||
appList.map((item) => {
|
||||
return (
|
||||
<Option key={item.name} value={item.name}>
|
||||
{item.name}
|
||||
</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,70 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Button, Row, Col } from 'antd';
|
||||
import { Button, Row, Col, Breadcrumb } from 'antd';
|
||||
import { Link } from 'umi';
|
||||
import './index.less';
|
||||
|
||||
export default class Workload extends React.PureComponent {
|
||||
render() {
|
||||
const {
|
||||
btnValue,
|
||||
pathname,
|
||||
title,
|
||||
crdInfo,
|
||||
state,
|
||||
settings,
|
||||
hrefAddress,
|
||||
btnIsShow,
|
||||
} = this.props.propsObj;
|
||||
const { btnValue, pathname, title, crdInfo, state, settings, btnIsShow } = this.props.propsObj;
|
||||
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">
|
||||
{btnValue}
|
||||
</Button>
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</PageContainer>
|
||||
<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>
|
||||
</Col>
|
||||
</Row>
|
||||
</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,19 @@ 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,22 @@
|
||||
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 {
|
||||
Button,
|
||||
Row,
|
||||
Col,
|
||||
Tabs,
|
||||
Popconfirm,
|
||||
message,
|
||||
Tooltip,
|
||||
Modal,
|
||||
Spin,
|
||||
Breadcrumb,
|
||||
} from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
import { Link } from 'umi';
|
||||
import CreateTraitItem from '../../../components/AttachOneTrait/index.jsx';
|
||||
import Topology from './Topology.jsx';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
@@ -27,18 +38,26 @@ class TableList extends React.Component {
|
||||
}
|
||||
|
||||
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', '');
|
||||
getInitialData = async () => {
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
appName,
|
||||
envName,
|
||||
});
|
||||
if (appName && envName) {
|
||||
this.setState({
|
||||
envName,
|
||||
appName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
payload: {
|
||||
@@ -54,23 +73,17 @@ class TableList extends React.Component {
|
||||
const traits = await this.props.dispatch({
|
||||
type: 'trait/getTraits',
|
||||
});
|
||||
this.setState({
|
||||
traitList: traits,
|
||||
});
|
||||
if (traits) {
|
||||
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,
|
||||
});
|
||||
this.child.setDefaultValue(traitType);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,7 +101,7 @@ class TableList extends React.Component {
|
||||
|
||||
deleteApp = async (e) => {
|
||||
e.stopPropagation();
|
||||
const { currentEnv: envName } = this.props;
|
||||
const { envName } = this.state;
|
||||
const { appDetailData } = this.state;
|
||||
const appName = _.get(appDetailData, 'Workload.workload.metadata.name', '');
|
||||
if (appName && envName) {
|
||||
@@ -138,6 +151,7 @@ class TableList extends React.Component {
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
await this.child.validateFields();
|
||||
const submitData = this.child.getSelectValue();
|
||||
if (submitData.name) {
|
||||
const submitObj = {
|
||||
@@ -187,26 +201,10 @@ 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() {
|
||||
@@ -214,277 +212,277 @@ class TableList extends React.Component {
|
||||
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
|
||||
const Traits = _.get(this.state.appDetailData, '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]', {});
|
||||
let { loadingAll } = this.props;
|
||||
loadingAll = loadingAll || false;
|
||||
const colorObj = {
|
||||
Deployed: '#4CAF51',
|
||||
Staging: '#F44337',
|
||||
UNKNOWN: '#1890ff',
|
||||
};
|
||||
return (
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="card-container app-detial">
|
||||
<h2>{_.get(Workload, 'metadata.name')}</h2>
|
||||
<p style={{ marginBottom: '20px' }}>
|
||||
{Workload.apiVersion}, Kind={Workload.kind}
|
||||
</p>
|
||||
<Tabs>
|
||||
<TabPane tab="Summary" key="1">
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<div className="summaryBox1" onClick={(e) => this.gotoWorkloadDetail(e)}>
|
||||
{/* <div className="summaryBox1"> */}
|
||||
<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>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title">
|
||||
Name:<span>{_.get(Workload, 'metadata.name')}</span>
|
||||
</p>
|
||||
<p className="title">Settings:</p>
|
||||
<Row>
|
||||
{Object.keys(containers).map((currentKey) => {
|
||||
if (currentKey === 'ports') {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>port</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{_.get(containers[currentKey], '[0].containerPort', '')}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
// eslint-disable-next-line no-else-return
|
||||
} else if (currentKey === 'name') {
|
||||
return <Fragment key={currentKey} />;
|
||||
}
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>{currentKey}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{containers[currentKey]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</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)}
|
||||
onCancel={this.cancel}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Button danger>Delete</Button>
|
||||
</Popconfirm>
|
||||
</Col>
|
||||
<Col span="1" />
|
||||
<Col span="10">
|
||||
{Traits.length ? (
|
||||
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"
|
||||
onClick={(e) => this.gotoTraitDetail(e, traitItem)}
|
||||
key={index.toString()}
|
||||
>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{traitItem.kind}</p>
|
||||
<p>{traitItem.apiVersion}</p>
|
||||
</Col>
|
||||
<Col span="2">
|
||||
<p
|
||||
className="title hasCursor"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
?
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
{Object.keys(annotations).map((currentKey3) => {
|
||||
return (
|
||||
<Fragment key={currentKey3}>
|
||||
<Col span="8">
|
||||
<p>{currentKey3}:</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p>{annotations[currentKey3]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</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>
|
||||
<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>ApplicationListDetail</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="card-container app-detial">
|
||||
<h2>{_.get(Workload, 'metadata.name')}</h2>
|
||||
<p style={{ marginBottom: '20px' }}>
|
||||
{Workload.apiVersion}, Kind={Workload.kind}
|
||||
</p>
|
||||
<Tabs>
|
||||
<TabPane tab="Summary" key="1">
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<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">
|
||||
<p className="title hasCursor" onClick={this.hrefClick}>
|
||||
?
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title">
|
||||
Name:<span>{_.get(Workload, 'metadata.name')}</span>
|
||||
</p>
|
||||
<p className="title">Settings:</p>
|
||||
<Row>
|
||||
{Object.keys(containers).map((currentKey) => {
|
||||
if (currentKey === 'ports') {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>port</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>
|
||||
{_.get(
|
||||
spec,
|
||||
'rules[0].http.paths[0].backend.servicePort',
|
||||
'',
|
||||
)}
|
||||
</p>
|
||||
<p>{_.get(containers[currentKey], '[0].containerPort', '')}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
) : (
|
||||
Object.keys(spec).map((currentKey) => {
|
||||
);
|
||||
// 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}>
|
||||
<Col span="8">
|
||||
<p>{currentKey}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{containers[currentKey]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
<Popconfirm
|
||||
title="Are you sure delete this app?"
|
||||
onConfirm={(e) => this.deleteApp(e)}
|
||||
onCancel={this.cancel}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Button danger>Delete</Button>
|
||||
</Popconfirm>
|
||||
</Col>
|
||||
<Col span="1" />
|
||||
<Col span="10">
|
||||
{Traits.length ? (
|
||||
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"
|
||||
onClick={(e) => this.gotoTraitDetail(e, traitItem)}
|
||||
key={index.toString()}
|
||||
>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{traitItem.kind}</p>
|
||||
<p>{traitItem.apiVersion}</p>
|
||||
</Col>
|
||||
<Col span="2">
|
||||
<p
|
||||
className="title hasCursor"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
?
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
{Object.keys(annotations).map((currentKey3) => {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Fragment key={currentKey3}>
|
||||
<Col span="8">
|
||||
<p>{currentKey}</p>
|
||||
<p>{currentKey3}:</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{spec[currentKey]}</p>
|
||||
<Col span="8">
|
||||
<p>{annotations[currentKey3]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})
|
||||
)}
|
||||
{/* {Object.keys(spec).map((currentKey) => {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
})}
|
||||
</Row>
|
||||
<p className="title">Properties:</p>
|
||||
<Row>
|
||||
{traitType === 2 ? (
|
||||
<Fragment>
|
||||
<Col span="8">
|
||||
<p>{currentKey}</p>
|
||||
<p>domain</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{spec[currentKey]}</p>
|
||||
<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>
|
||||
);
|
||||
})} */}
|
||||
</Row>
|
||||
<div style={{ clear: 'both', height: '32px' }}>
|
||||
<Popconfirm
|
||||
title="Are you sure delete this trait?"
|
||||
onConfirm={(e) => this.deleteTrait(e, item)}
|
||||
onCancel={this.cancel}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Button
|
||||
danger
|
||||
className="floatRight"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
) : (
|
||||
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>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</Row>
|
||||
<div style={{ clear: 'both', height: '32px' }}>
|
||||
<Popconfirm
|
||||
title="Are you sure delete this trait?"
|
||||
onConfirm={(e) => this.deleteTrait(e, item)}
|
||||
onCancel={this.cancel}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
<Button
|
||||
danger
|
||||
className="floatRight"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment />
|
||||
)}
|
||||
<Tooltip placement="top" title="Attach Trait">
|
||||
<p
|
||||
className="hasCursor"
|
||||
style={{
|
||||
fontSize: '30px',
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
onClick={this.createTrait}
|
||||
>
|
||||
+
|
||||
</p>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
</TabPane>
|
||||
<TabPane tab="Topology" key="2">
|
||||
{/* <p>Topology</p> */}
|
||||
<Topology />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<Modal
|
||||
title="attach a 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}>
|
||||
Confirm
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<CreateTraitItem
|
||||
onRef={(ref) => {
|
||||
this.child = ref;
|
||||
}}
|
||||
availableTraitList={this.state.availableTraitList}
|
||||
initialValues={{}}
|
||||
/>
|
||||
</Modal>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment />
|
||||
)}
|
||||
<Tooltip placement="top" title="Attach Trait">
|
||||
<p
|
||||
className="hasCursor"
|
||||
style={{
|
||||
fontSize: '30px',
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
onClick={this.createTrait}
|
||||
>
|
||||
+
|
||||
</p>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
</TabPane>
|
||||
<TabPane tab="Topology" key="2">
|
||||
<p>Topology</p>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<Modal
|
||||
title="Attach a 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}>
|
||||
Confirm
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<CreateTraitItem
|
||||
onRef={(ref) => {
|
||||
this.child = ref;
|
||||
}}
|
||||
availableTraitList={this.state.availableTraitList}
|
||||
initialValues={{}}
|
||||
/>
|
||||
</Modal>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 36px;
|
||||
// color: #fff;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
|
||||
108
dashboard/src/pages/ApplicationList/ComponentDetail/Topology.jsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
const Topology = () => {
|
||||
let graph = null;
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 150,
|
||||
y: 50,
|
||||
label: 'node1',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 250,
|
||||
y: 200,
|
||||
label: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 100,
|
||||
y: 350,
|
||||
label: 'node3',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
label: 'edge 1',
|
||||
},
|
||||
{
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
label: 'edge 2',
|
||||
},
|
||||
{
|
||||
source: 'node3',
|
||||
target: 'node1',
|
||||
label: 'edge 3',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = 1000;
|
||||
const height = 400;
|
||||
|
||||
useEffect(() => {
|
||||
if (!graph) {
|
||||
graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
type: 'circle',
|
||||
size: [40],
|
||||
color: '#5B8FF9',
|
||||
style: {
|
||||
fill: '#9EC9FF',
|
||||
lineWidth: 3,
|
||||
},
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#1890ff',
|
||||
fontSize: 14,
|
||||
},
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
style: {
|
||||
background: {
|
||||
fill: '#ffffff',
|
||||
stroke: '#9EC9FF',
|
||||
padding: [2, 2, 2, 2],
|
||||
radius: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
},
|
||||
nodeStateStyles: {
|
||||
// style configurations for hover state
|
||||
hover: {
|
||||
fillOpacity: 0.8,
|
||||
},
|
||||
// style configurations for selected state
|
||||
selected: {
|
||||
lineWidth: 5,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
}, []);
|
||||
|
||||
return <div id="container" style={{ overflow: 'auto', width: '100%', height: '400px' }} />;
|
||||
};
|
||||
|
||||
export default Topology;
|
||||
462
dashboard/src/pages/ApplicationList/ComponentDetail/index.jsx
Normal file
@@ -0,0 +1,462 @@
|
||||
import React, { Fragment } from 'react';
|
||||
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';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
currentEnv: globalData.currentEnv,
|
||||
}))
|
||||
class TableList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
appDetailData: {},
|
||||
visible: false,
|
||||
traitList: [],
|
||||
availableTraitList: [],
|
||||
envName: '',
|
||||
appName: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitialData();
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
if (this.props.appName !== nextProps.appName) {
|
||||
this.getInitialData(nextProps.appName);
|
||||
}
|
||||
}
|
||||
|
||||
getInitialData = async (nextAppName) => {
|
||||
let appName = _.get(this.props, 'appName', '');
|
||||
const envName = _.get(this.props, 'envName', '');
|
||||
appName = nextAppName || appName;
|
||||
if (appName && envName) {
|
||||
this.setState({
|
||||
envName,
|
||||
appName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
this.setState({
|
||||
appDetailData: res,
|
||||
});
|
||||
}
|
||||
const traits = await this.props.dispatch({
|
||||
type: 'trait/getTraits',
|
||||
});
|
||||
if (traits) {
|
||||
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');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getAcceptTrait = (workloadType) => {
|
||||
const res = this.state.traitList.filter((item) => {
|
||||
if (item.appliesTo.indexOf(workloadType) !== -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
this.setState(() => ({
|
||||
availableTraitList: res,
|
||||
}));
|
||||
};
|
||||
|
||||
deleteApp = async (e) => {
|
||||
e.stopPropagation();
|
||||
const { envName } = this.state;
|
||||
const { appDetailData } = this.state;
|
||||
const appName = _.get(appDetailData, 'Workload.workload.metadata.name', '');
|
||||
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' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
deleteTrait = async (e, item) => {
|
||||
e.stopPropagation();
|
||||
const { appName, envName } = 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) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/deleteOneTrait',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
traitName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
this.getInitialData(2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cancel = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
createTrait = async () => {
|
||||
await this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
await this.child.validateFields();
|
||||
const submitData = this.child.getSelectValue();
|
||||
if (submitData.name) {
|
||||
const submitObj = {
|
||||
name: submitData.name,
|
||||
flags: [],
|
||||
};
|
||||
Object.keys(submitData).forEach((currentKey) => {
|
||||
if (currentKey !== 'name' && submitData[currentKey]) {
|
||||
submitObj.flags.push({
|
||||
name: currentKey,
|
||||
value: submitData[currentKey].toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
const { envName, appName } = this.state;
|
||||
if (envName && appName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/attachOneTraits',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
params: submitObj,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
message.success(res);
|
||||
this.getInitialData(2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.warning('please select a trait type');
|
||||
}
|
||||
};
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
hrefClick = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
gotoWorkloadDetail = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
gotoTraitDetail = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
render() {
|
||||
const status = _.get(this.state.appDetailData, 'Status', '');
|
||||
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
|
||||
const Traits = _.get(this.state.appDetailData, 'Traits', []);
|
||||
let containers = {};
|
||||
containers = _.get(Workload, 'spec.containers[0]', {});
|
||||
let { loadingAll } = this.props;
|
||||
loadingAll = loadingAll || false;
|
||||
const colorObj = {
|
||||
Deployed: '#4CAF51',
|
||||
Staging: '#F44337',
|
||||
UNKNOWN: '#1890ff',
|
||||
};
|
||||
return (
|
||||
<div style={{ margin: '8px' }}>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="card-container app-detial">
|
||||
<h2>{_.get(Workload, 'metadata.name')}</h2>
|
||||
<p style={{ marginBottom: '20px' }}>
|
||||
{Workload.apiVersion}, Kind={Workload.kind}
|
||||
</p>
|
||||
<Tabs>
|
||||
<TabPane tab="Summary" key="1">
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<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">
|
||||
<p className="title hasCursor" onClick={this.hrefClick}>
|
||||
?
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title">
|
||||
Name:<span>{_.get(Workload, 'metadata.name')}</span>
|
||||
</p>
|
||||
<p className="title">Settings:</p>
|
||||
<Row>
|
||||
{Object.keys(containers).map((currentKey) => {
|
||||
if (currentKey === 'ports') {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>port</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{_.get(containers[currentKey], '[0].containerPort', '')}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
// 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}>
|
||||
<Col span="8">
|
||||
<p>{currentKey}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{containers[currentKey]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
<Popconfirm
|
||||
title="Are you sure delete this app?"
|
||||
onConfirm={(e) => this.deleteApp(e)}
|
||||
onCancel={this.cancel}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Button danger>Delete</Button>
|
||||
</Popconfirm>
|
||||
</Col>
|
||||
<Col span="1" />
|
||||
<Col span="10">
|
||||
{Traits.length ? (
|
||||
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"
|
||||
onClick={(e) => this.gotoTraitDetail(e, traitItem)}
|
||||
key={index.toString()}
|
||||
>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{traitItem.kind}</p>
|
||||
<p>{traitItem.apiVersion}</p>
|
||||
</Col>
|
||||
<Col span="2">
|
||||
<p
|
||||
className="title hasCursor"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
?
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
{Object.keys(annotations).map((currentKey3) => {
|
||||
return (
|
||||
<Fragment key={currentKey3}>
|
||||
<Col span="8">
|
||||
<p>{currentKey3}:</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p>{annotations[currentKey3]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</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>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</Row>
|
||||
<div style={{ clear: 'both', height: '32px' }}>
|
||||
<Popconfirm
|
||||
title="Are you sure delete this trait?"
|
||||
onConfirm={(e) => this.deleteTrait(e, item)}
|
||||
onCancel={this.cancel}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Button
|
||||
danger
|
||||
className="floatRight"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Fragment />
|
||||
)}
|
||||
<Tooltip placement="top" title="Attach Trait">
|
||||
<p
|
||||
className="hasCursor"
|
||||
style={{
|
||||
fontSize: '30px',
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
onClick={this.createTrait}
|
||||
>
|
||||
+
|
||||
</p>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
</TabPane>
|
||||
<TabPane tab="Topology" key="2">
|
||||
<p>Topology</p>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<Modal
|
||||
title="Attach a 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}>
|
||||
Confirm
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<CreateTraitItem
|
||||
onRef={(ref) => {
|
||||
this.child = ref;
|
||||
}}
|
||||
availableTraitList={this.state.availableTraitList}
|
||||
initialValues={{}}
|
||||
/>
|
||||
</Modal>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TableList;
|
||||
@@ -0,0 +1,47 @@
|
||||
.app-detial {
|
||||
.ant-tabs {
|
||||
min-height: 500px;
|
||||
padding: 10px 20px;
|
||||
background-color: #fff;
|
||||
.ant-tabs-content-holder {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
.summaryBox1,
|
||||
.summaryBox2,
|
||||
.summaryBox {
|
||||
clear: both;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 10px 10px;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 36px;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.summaryBox1 {
|
||||
color: #fff;
|
||||
background: #0097a7;
|
||||
cursor: pointer;
|
||||
}
|
||||
.summaryBox2 {
|
||||
color: #fff;
|
||||
background: #92c47c;
|
||||
}
|
||||
.summaryBox {
|
||||
border: 1px solid black;
|
||||
cursor: pointer;
|
||||
}
|
||||
.hasCursor {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.floatRight {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
126
dashboard/src/pages/ApplicationList/Components/index.jsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'umi';
|
||||
import { Breadcrumb, Button, Menu, Spin } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
import './index.less';
|
||||
import ComponentDetail from '../ComponentDetail/index.jsx';
|
||||
|
||||
@connect(({ loading }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
}))
|
||||
class TableList extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
envName: '',
|
||||
componentName: '',
|
||||
defaultSelectedKeys: 'a1',
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
envName,
|
||||
componentName: appName,
|
||||
});
|
||||
if (appName === 'test33') {
|
||||
this.setState({
|
||||
defaultSelectedKeys: 'a1',
|
||||
});
|
||||
} else if (appName === 'test01') {
|
||||
this.setState({
|
||||
defaultSelectedKeys: 'c2',
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
defaultSelectedKeys: 'c3',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
changeComponent = ({ key }) => {
|
||||
if (key === 'a1') {
|
||||
this.setState({
|
||||
componentName: 'test33',
|
||||
});
|
||||
} else if (key === 'c2') {
|
||||
this.setState({
|
||||
componentName: 'test01',
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
componentName: 'testoo',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { envName, componentName, defaultSelectedKeys } = 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]}
|
||||
>
|
||||
<Menu.ItemGroup key="g1" title="Components">
|
||||
<Menu.Item key="a1">a1(containerized)</Menu.Item>
|
||||
<Menu.Item key="c2">c2(deploy)</Menu.Item>
|
||||
<Menu.Item key="c3">c3(webserver)</Menu.Item>
|
||||
</Menu.ItemGroup>
|
||||
</Menu>
|
||||
<div className="addComp">
|
||||
<Link
|
||||
to={{
|
||||
// pathname: '/ApplicationList/CreateApplication',
|
||||
pathname: `/ApplicationList/${componentName}/createComponent`,
|
||||
state: { appName: componentName, envName },
|
||||
}}
|
||||
>
|
||||
add a new comp
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="right">
|
||||
<div style={{ margin: '10px', float: 'right' }}>
|
||||
<Button type="primary">Delete App</Button>
|
||||
</div>
|
||||
<ComponentDetail appName={componentName} envName={envName} />
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TableList;
|
||||
35
dashboard/src/pages/ApplicationList/Components/index.less
Normal file
@@ -0,0 +1,35 @@
|
||||
.appComponent {
|
||||
display: flex;
|
||||
height: calc(100% - 46px);
|
||||
.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-bottom: 1px solid #efefef;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
flex: 1;
|
||||
// clear: both;
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -52,13 +52,6 @@ class TableList extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
const activeStep = _.get(this.props, 'location.state.activeStep', 0);
|
||||
this.setState(() => ({
|
||||
current: activeStep,
|
||||
}));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitalData();
|
||||
}
|
||||
@@ -73,24 +66,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,7 +73,13 @@ 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,
|
||||
});
|
||||
@@ -117,7 +98,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();
|
||||
@@ -163,7 +149,11 @@ class TableList extends React.Component {
|
||||
};
|
||||
|
||||
createApp = async () => {
|
||||
const { step1SubmitObj, traitNum } = this.state;
|
||||
const { traitNum } = this.state;
|
||||
const { step1SubmitObj } = this.state;
|
||||
if (step1SubmitObj.env_name !== this.props.currentEnv) {
|
||||
step1SubmitObj.env_name = this.props.currentEnv;
|
||||
}
|
||||
const submitObj = _.cloneDeep(step1SubmitObj);
|
||||
const { workload_name: workloadName } = step1SubmitObj;
|
||||
submitObj.flags.push({
|
||||
@@ -289,9 +279,6 @@ 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,
|
||||
}));
|
||||
@@ -320,6 +307,11 @@ class TableList extends React.Component {
|
||||
name="workload_name"
|
||||
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!',
|
||||
@@ -356,8 +348,11 @@ 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' }}
|
||||
/>
|
||||
<div className="relativeBox">
|
||||
<p className="hasMore">?</p>
|
||||
{Array.isArray(workloadSettings) && workloadSettings.length ? (
|
||||
@@ -365,7 +360,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 +370,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 />
|
||||
@@ -548,11 +559,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 +576,29 @@ 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>CreateApplication</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;
|
||||
|
||||
642
dashboard/src/pages/ApplicationList/CreateComponent/index.jsx
Normal file
@@ -0,0 +1,642 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Button, Row, Col, Form, Input, Select, Steps, message, Breadcrumb } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
import _ from 'lodash';
|
||||
import CreateTraitItem from '../createTrait/index.jsx';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Step } = Steps;
|
||||
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 8,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 16,
|
||||
},
|
||||
};
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.workload,
|
||||
currentEnv: globalData.currentEnv,
|
||||
}))
|
||||
class TableList extends React.Component {
|
||||
formRefStep1 = React.createRef();
|
||||
|
||||
formRefStep2All = React.createRef();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
isShowMore: false,
|
||||
traitNum: [
|
||||
{
|
||||
refname: null,
|
||||
initialData: {},
|
||||
uniq: new Date().valueOf(),
|
||||
},
|
||||
],
|
||||
traitList: [],
|
||||
availableTraitList: [],
|
||||
workloadList: [],
|
||||
workloadSettings: [],
|
||||
step1SubmitObj: {},
|
||||
step1InitialValues: {
|
||||
workload_type: '',
|
||||
},
|
||||
step1Settings: [],
|
||||
appName: '',
|
||||
envName: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitalData();
|
||||
}
|
||||
|
||||
getInitalData = async () => {
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
appName,
|
||||
envName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'workload/getWorkload',
|
||||
});
|
||||
const traits = await this.props.dispatch({
|
||||
type: 'trait/getTraits',
|
||||
});
|
||||
this.setState({
|
||||
traitList: traits,
|
||||
});
|
||||
if (Array.isArray(res) && res.length) {
|
||||
this.setState(
|
||||
() => ({
|
||||
workloadList: res,
|
||||
}),
|
||||
() => {
|
||||
if (this.state.current === 0) {
|
||||
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,
|
||||
});
|
||||
this.workloadTypeChange(this.state.workloadList[0].name);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
onFinishStep1 = (values) => {
|
||||
this.setState({
|
||||
current: 1,
|
||||
step1InitialValues: values,
|
||||
isShowMore: false,
|
||||
});
|
||||
};
|
||||
|
||||
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();
|
||||
return item;
|
||||
});
|
||||
// 进行trait数据整理,便于第三步展示
|
||||
this.setState(() => ({
|
||||
traitNum: newTraitNum,
|
||||
current: 2,
|
||||
}));
|
||||
};
|
||||
|
||||
gotoStep2 = () => {
|
||||
this.setState({
|
||||
current: 1,
|
||||
isShowMore: false,
|
||||
});
|
||||
};
|
||||
|
||||
gotoStep1 = () => {
|
||||
this.setState({
|
||||
current: 0,
|
||||
});
|
||||
};
|
||||
|
||||
changeShowMore = () => {
|
||||
this.setState({
|
||||
isShowMore: true,
|
||||
});
|
||||
};
|
||||
|
||||
addMore = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState((prev) => ({
|
||||
traitNum: prev.traitNum.concat([
|
||||
{
|
||||
refname: null,
|
||||
initialData: {},
|
||||
uniq: new Date().valueOf(),
|
||||
},
|
||||
]),
|
||||
}));
|
||||
};
|
||||
|
||||
createApp = async () => {
|
||||
const { traitNum } = this.state;
|
||||
const { step1SubmitObj } = this.state;
|
||||
if (step1SubmitObj.env_name !== this.props.currentEnv) {
|
||||
step1SubmitObj.env_name = this.props.currentEnv;
|
||||
}
|
||||
const submitObj = _.cloneDeep(step1SubmitObj);
|
||||
const { workload_name: workloadName } = step1SubmitObj;
|
||||
submitObj.flags.push({
|
||||
name: 'name',
|
||||
value: workloadName.toString(),
|
||||
});
|
||||
// 处理数据为提交的格式
|
||||
if (traitNum.length) {
|
||||
const { env_name: envName } = step1SubmitObj;
|
||||
const step2SubmitObj = [];
|
||||
traitNum.forEach(({ initialData }) => {
|
||||
if (initialData.name) {
|
||||
const initialObj = {
|
||||
name: initialData.name,
|
||||
env_name: envName,
|
||||
workload_name: workloadName,
|
||||
flags: [],
|
||||
};
|
||||
Object.keys(initialData).forEach((key) => {
|
||||
if (key !== 'name' && initialData[key]) {
|
||||
initialObj.flags.push({
|
||||
name: key,
|
||||
value: initialData[key].toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
step2SubmitObj.push(initialObj);
|
||||
}
|
||||
});
|
||||
submitObj.traits = step2SubmitObj;
|
||||
}
|
||||
const res = await this.props.dispatch({
|
||||
type: 'workload/createWorkload',
|
||||
payload: {
|
||||
params: submitObj,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
const { appName, envName } = this.state;
|
||||
this.props.history.push({
|
||||
pathname: `/ApplicationList/${appName}/Components`,
|
||||
state: { appName, envName },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createWorkload = async () => {
|
||||
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,
|
||||
flags: [],
|
||||
};
|
||||
Object.keys(currentData).forEach((key) => {
|
||||
if (key !== 'workload_name' && key !== 'workload_type' && currentData[key]) {
|
||||
submitObj.flags.push({
|
||||
name: key,
|
||||
value: currentData[key].toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
current: 1,
|
||||
step1InitialValues: currentData,
|
||||
step1Settings: submitObj.flags,
|
||||
step1SubmitObj: submitObj,
|
||||
});
|
||||
this.getAcceptTrait(currentData.workload_type);
|
||||
};
|
||||
|
||||
workloadTypeChange = (value) => {
|
||||
const content = this.formRefStep1.current.getFieldsValue();
|
||||
this.formRefStep1.current.resetFields();
|
||||
const initialObj = {
|
||||
workload_type: content.workload_type,
|
||||
workload_name: content.workload_name,
|
||||
};
|
||||
this.formRefStep1.current.setFieldsValue(initialObj);
|
||||
const currentWorkloadSetting = this.state.workloadList.filter((item) => {
|
||||
return item.name === value;
|
||||
});
|
||||
if (currentWorkloadSetting.length) {
|
||||
this.setState(
|
||||
{
|
||||
workloadSettings: currentWorkloadSetting[0].parameters,
|
||||
},
|
||||
() => {
|
||||
this.state.workloadSettings.forEach((item) => {
|
||||
if (item.default) {
|
||||
initialObj[item.name] = item.default;
|
||||
}
|
||||
});
|
||||
this.formRefStep1.current.setFieldsValue(initialObj);
|
||||
},
|
||||
);
|
||||
}
|
||||
this.setState({
|
||||
traitNum: [
|
||||
{
|
||||
refname: null,
|
||||
initialData: {},
|
||||
uniq: new Date().valueOf(),
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
getAcceptTrait = (workloadType) => {
|
||||
const res = this.state.traitList.filter((item) => {
|
||||
if (item.appliesTo.indexOf(workloadType) !== -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
this.setState(() => ({
|
||||
availableTraitList: res,
|
||||
}));
|
||||
};
|
||||
|
||||
deleteTraitItem = (uniq) => {
|
||||
// 删除的时候不要依据数组的index删除,要一个唯一性的值
|
||||
this.state.traitNum = this.state.traitNum.filter((item) => {
|
||||
return item.uniq !== uniq;
|
||||
});
|
||||
this.setState((prev) => ({
|
||||
traitNum: prev.traitNum,
|
||||
}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { appName, envName } = this.state;
|
||||
const { current, step1InitialValues, traitNum, workloadSettings } = this.state;
|
||||
let { workloadList } = this.state;
|
||||
workloadList = Array.isArray(workloadList) ? workloadList : [];
|
||||
let currentDetail;
|
||||
if (current === 0) {
|
||||
currentDetail = (
|
||||
<div>
|
||||
<div className="minBox">
|
||||
<Form
|
||||
initialValues={step1InitialValues}
|
||||
labelAlign="left"
|
||||
{...layout}
|
||||
ref={this.formRefStep1}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep1}
|
||||
style={{ width: '60%' }}
|
||||
>
|
||||
<div style={{ padding: '16px 48px 0px 16px' }}>
|
||||
<Form.Item
|
||||
name="workload_name"
|
||||
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!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="workload_type"
|
||||
label="Workload Type"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select Workload Type!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select a Workload Type"
|
||||
allowClear
|
||||
onChange={this.workloadTypeChange}
|
||||
>
|
||||
{workloadList.length ? (
|
||||
workloadList.map((item) => {
|
||||
return (
|
||||
<Option value={item.name} key={item.name}>
|
||||
{item.name}
|
||||
</Option>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item
|
||||
label="Settings"
|
||||
style={{ background: 'rgba(0, 0, 0, 0.04)', paddingLeft: '16px' }}
|
||||
/>
|
||||
<div className="relativeBox">
|
||||
<p className="hasMore">?</p>
|
||||
{Array.isArray(workloadSettings) && workloadSettings.length ? (
|
||||
workloadSettings.map((item) => {
|
||||
if (item.name === 'name') {
|
||||
return <Fragment key={item.name} />;
|
||||
}
|
||||
return item.type === 4 ? (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
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 />
|
||||
</Form.Item>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
<div className="buttonBox">
|
||||
<Button type="primary" className="floatRightGap" onClick={this.createWorkload}>
|
||||
Next
|
||||
</Button>
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/ApplicationList/${appName}/Components`,
|
||||
state: { appName, envName },
|
||||
}}
|
||||
>
|
||||
<Button className="floatRightGap">Cancle</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (current === 1) {
|
||||
currentDetail = (
|
||||
<div>
|
||||
<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>
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ border: '1px solid #eee', padding: '16px 48px 16px 16px' }}>
|
||||
<p className="title">{step1InitialValues.workload_type}</p>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>apps/v1</span>
|
||||
<span
|
||||
style={{
|
||||
color: '#1890ff',
|
||||
cursor: 'pointer',
|
||||
display: this.state.isShowMore ? 'none' : 'black',
|
||||
}}
|
||||
onClick={this.changeShowMore}
|
||||
>
|
||||
more...
|
||||
</span>
|
||||
</div>
|
||||
{this.state.isShowMore ? (
|
||||
<div>
|
||||
<p className="title" style={{ marginTop: '16px' }}>
|
||||
Settings:
|
||||
</p>
|
||||
<Row>
|
||||
{this.state.step1Settings.map((item) => {
|
||||
return (
|
||||
<Fragment key={item.name}>
|
||||
<Col span="8">
|
||||
<p>{item.name}:</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{item.value}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
<div ref={this.formRefStep2All}>
|
||||
{traitNum.map((item) => {
|
||||
return (
|
||||
<CreateTraitItem
|
||||
onRef={(ref) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
item.refname = ref;
|
||||
}}
|
||||
key={item.uniq.toString()}
|
||||
availableTraitList={this.state.availableTraitList}
|
||||
uniq={item.uniq}
|
||||
initialValues={item.initialData}
|
||||
deleteTraitItem={this.deleteTraitItem}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<button style={{ marginTop: '16px' }} onClick={this.addMore} type="button">
|
||||
Add More...
|
||||
</button>
|
||||
<div className="buttonBox">
|
||||
<Button type="primary" className="floatRight" onClick={this.onFinishStep2}>
|
||||
Next
|
||||
</Button>
|
||||
<Button className="floatRightGap" onClick={this.gotoStep1}>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
currentDetail = (
|
||||
<div>
|
||||
<div className="minBox">
|
||||
<p>
|
||||
Name:<span>{step1InitialValues.workload_name}</span>
|
||||
</p>
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<div className="summaryBox1">
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{step1InitialValues.workload_type}</p>
|
||||
<p>apps/v1</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title hasMargin">Settings:</p>
|
||||
<Row>
|
||||
{this.state.step1Settings.map((item) => {
|
||||
return (
|
||||
<Fragment key={item.name}>
|
||||
<Col span="8">
|
||||
<p>{item.name}:</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{item.value}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="1" />
|
||||
<Col span="10">
|
||||
{traitNum.map(({ initialData }, index) => {
|
||||
if (initialData.name) {
|
||||
return (
|
||||
<div className="summaryBox" key={index.toString()}>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{initialData.name}</p>
|
||||
<p>core.oam.dev/v1alpha2</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title hasMargin">Properties:</p>
|
||||
<Row>
|
||||
{Object.keys(initialData).map((currentKey) => {
|
||||
if (currentKey !== 'name') {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>{currentKey}:</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{initialData[currentKey]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
return <Fragment key={currentKey} />;
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <Fragment key={index.toString()} />;
|
||||
})}
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<div className="buttonBox">
|
||||
<Button
|
||||
type="primary"
|
||||
className="floatRight"
|
||||
onClick={() => {
|
||||
this.createApp();
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
<Button className="floatRightGap" onClick={this.gotoStep2}>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<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>
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/ApplicationList/${appName}/Components`,
|
||||
state: { appName, envName },
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
{appName}
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>createComponent</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TableList;
|
||||
@@ -0,0 +1,66 @@
|
||||
.create-container {
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
.create-app {
|
||||
.minBox {
|
||||
margin: 20px 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #eee;
|
||||
.title {
|
||||
font-size: 16px;
|
||||
line-height: 36px;
|
||||
}
|
||||
}
|
||||
.buttonBox {
|
||||
clear: both;
|
||||
height: 42px;
|
||||
margin: 0 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
.floatRight {
|
||||
float: right;
|
||||
}
|
||||
.floatRightGap {
|
||||
float: right;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.relativeBox {
|
||||
position: relative;
|
||||
padding: 0 48px 0 16px;
|
||||
.hasMore {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 16px;
|
||||
padding: 4px;
|
||||
color: rgb(24, 144, 255);
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.summaryBox1,
|
||||
.summaryBox2,
|
||||
.summaryBox {
|
||||
clear: both;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 10px 10px;
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.summaryBox1 {
|
||||
color: #fff;
|
||||
background: rgb(24, 144, 255);
|
||||
}
|
||||
.summaryBox2 {
|
||||
background: yellow;
|
||||
}
|
||||
.summaryBox {
|
||||
border: 1px solid black;
|
||||
}
|
||||
.hasMargin {
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
@@ -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}>
|
||||
@@ -92,11 +102,36 @@ export default class CreateTraitItem extends React.PureComponent {
|
||||
<Form.Item label="Properties" />
|
||||
</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,33 +1,41 @@
|
||||
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 { BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Row, Col, Form, Spin, Empty, Breadcrumb, Modal, Input } 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();
|
||||
|
||||
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
|
||||
type: 'applist/getList',
|
||||
payload: {
|
||||
url: `/api/envs/${currentEnv}/apps/`,
|
||||
},
|
||||
@@ -40,29 +48,41 @@ 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')
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
await this.formRef.current.validateFields();
|
||||
// const submitData = await this.formRef.current.validateFields();
|
||||
};
|
||||
|
||||
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) => {
|
||||
@@ -80,98 +100,125 @@ class TableList extends React.Component {
|
||||
UNKNOWN: 'first3',
|
||||
};
|
||||
return (
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="applist">
|
||||
<Form name="horizontal_login" layout="inline" onFinish={this.onFinish}>
|
||||
<Form.Item name="createTime">
|
||||
<DatePicker placeholder="createTime" />
|
||||
<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>
|
||||
<Link to="/ApplicationList/CreateApplication">
|
||||
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
|
||||
create
|
||||
</Button>
|
||||
</Link>
|
||||
</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: '30px' }} />
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<div
|
||||
className={['hasPadding', colorObj[item.status] || 'first3'].join(
|
||||
' ',
|
||||
)}
|
||||
>
|
||||
<ApartmentOutlined style={{ marginRight: '4px' }} />
|
||||
{item.workload}
|
||||
</div>
|
||||
</div>
|
||||
{traits.map((item1, index1) => {
|
||||
return (
|
||||
<div className="box1" key={index1.toString()}>
|
||||
<div className="box3" style={{ width: '50px' }} />
|
||||
<div className="other hasPadding">
|
||||
<BranchesOutlined style={{ marginRight: '4px' }} />
|
||||
{item1}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
</Col>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div style={{ width: '100%', height: '80%' }}>
|
||||
<Empty />
|
||||
</div>
|
||||
)}
|
||||
</Row>
|
||||
</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="name"
|
||||
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="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 }}>
|
||||
create
|
||||
</Button>
|
||||
</Link>
|
||||
<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;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Button, Table, Space, Modal, Form, Input, Tooltip } from 'antd';
|
||||
import { Button, Table, Space, Modal, Form, Input, Tooltip, Breadcrumb } from 'antd';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import './index.less';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
const { confirm } = Modal;
|
||||
@@ -147,64 +148,76 @@ const TableList = (props) => {
|
||||
},
|
||||
];
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Space>
|
||||
<Button type="primary" onClick={showModal}>
|
||||
Create
|
||||
</Button>
|
||||
</Space>
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>System</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Env</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<Modal
|
||||
getContainer={false}
|
||||
title={env && env.envName ? 'Update Env' : 'Create Env'}
|
||||
visible={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
footer={[
|
||||
<Button key="submit" type="primary" onClick={handleOk}>
|
||||
{env && env.envName ? 'Update' : 'Create'}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form {...layout} form={form} name="control-ref" labelAlign="left">
|
||||
<Form.Item
|
||||
name="envName"
|
||||
label="Env"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input Evn!',
|
||||
},
|
||||
{
|
||||
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
|
||||
message: 'The maximum length is 63,should be combination of numbers,alphabets,underline!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled={!!(env && env.envName)} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="namespace"
|
||||
label="Namespace"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please specify a Namespace!',
|
||||
},
|
||||
{
|
||||
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
|
||||
message:
|
||||
'The maximum length is 63,should be combination of numbers,alphabets,underline!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Table rowKey={(record) => record.envName} columns={columns} dataSource={tableEnvs} />
|
||||
</PageContainer>
|
||||
<PageContainer>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Space>
|
||||
<Button type="primary" onClick={showModal}>
|
||||
Create
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<Modal
|
||||
getContainer={false}
|
||||
title={env && env.envName ? 'Update Env' : 'Create Env'}
|
||||
visible={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
footer={[
|
||||
<Button key="submit" type="primary" onClick={handleOk}>
|
||||
{env && env.envName ? 'Update' : 'Create'}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form {...layout} form={form} name="control-ref" labelAlign="left">
|
||||
<Form.Item
|
||||
name="envName"
|
||||
label="Env"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input Evn!',
|
||||
},
|
||||
{
|
||||
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
|
||||
message:
|
||||
'The maximum length is 63,should be combination of numbers,alphabets,underline!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled={!!(env && env.envName)} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="namespace"
|
||||
label="Namespace"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please specify a Namespace!',
|
||||
},
|
||||
{
|
||||
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
|
||||
message:
|
||||
'The maximum length is 63,should be combination of numbers,alphabets,underline!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Table rowKey={(record) => record.envName} columns={columns} dataSource={tableEnvs} />
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default connect((env) => {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table } from 'antd';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table, Breadcrumb, Space } from 'antd';
|
||||
import { CheckCircleOutlined } from '@ant-design/icons';
|
||||
import _ from 'lodash';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const layout = {
|
||||
@@ -27,7 +29,12 @@ const columns = [
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (text) => <a>{text}</a>,
|
||||
render: (text) => (
|
||||
<div>
|
||||
<CheckCircleOutlined style={{ fontSize: '20px', color: '#4CAF51' }} />
|
||||
<a style={{ marginLeft: '6px' }}>{text}</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Ready',
|
||||
@@ -54,6 +61,17 @@ const columns = [
|
||||
dataIndex: 'Age',
|
||||
key: 'Age',
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: () => (
|
||||
<Space>
|
||||
<Button>logs</Button>
|
||||
<Button>exec</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const data = [
|
||||
@@ -120,13 +138,6 @@ const data1 = [
|
||||
LastTransition: '2d',
|
||||
},
|
||||
];
|
||||
// const demoText = `H4sIAAAAAAAA/6xUwW7bOhD8lYc9U4lsWfazgB4C5Na0DZK0lyKHNbmyWVMkQ66UGob/vSBdt3GCxEXRmwjujmZnZrmFtbYKGrgkb9ymI8sgAL3+QiFqZ6EB9D6eDyMQ0BGjQkZotmCxI2hAOmcKDBtcrJCxGMrWfn+Ygsj30aNMRYpa7E0CloGQtbN3uqPI2HlobG+MAIMLMjEBo/cvcEGAW3wjyZH4LGh3JpHZ0Jl25yuMK2hgPkPV1lRJbKfjWVlNpqps69mkrNu2qv5X5UzV9ULVIOC4P1IYtHxzlOGXFEMJOwForeM8Rib8GjOdZD3Avz6Ae7QUiuWwhuYZtWEk/nuvrXp3+4cgpzw53f3SsePKjrLaHHpKSuTGG2opkJUUofm6Pc7O84FAHPL2e6ZTrPssZDWq5+O6GhVqPquLybyaF9gqKsbVTMpyXFZ1m8yVznJwxlCAJrEUsDBOrj8lopdkiDOvFk2k3f1OQPQkk4mRDEl2IX13yHJ1dSqQx6nYCWDqvEGmDPFkU/4+8/86qUbbNQWVw2lTFKABsrgwpN52+olQSWDUlsLe7VPm6Q6XlPdABspPS1imTihWcC8gUHR9yNHZQqCHniLnb+l7aKAuu/zsdC5soIHp5IPOZDLqdW/MtTNapqsL84ibCLt7cVi5Cyldb/njCYLYs+tS4e1R251bkz1EaK/Rz4IrbdfxEKEkDAdkWm4Sa9749LMbZ4y2y89epTgICEfnZrvb9yH3MZ9+BAAA//+bxjCThQUAAA
|
||||
// objectset.rio.cattle.io/id service
|
||||
// objectset.rio.cattle.io/owner-gvk rio.cattle.io/v1, Kind=Service
|
||||
// objectset.rio.cattle.io/owner-name cool-aryabhata-v0fnxq6
|
||||
// objectset.rio.cattle.io/owner-namespace default
|
||||
// rio.cattle.io/mesh true
|
||||
// Controlled By cool-aryabhata-v0fnxq6`;
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
@@ -144,6 +155,7 @@ class TableList extends React.Component {
|
||||
hasShowEdit2: false,
|
||||
traitItem: {},
|
||||
appName: '',
|
||||
envName: '',
|
||||
appDetailData: {},
|
||||
};
|
||||
}
|
||||
@@ -153,16 +165,27 @@ class TableList extends React.Component {
|
||||
}
|
||||
|
||||
async getInitialData() {
|
||||
const traitItem = _.get(this.props, 'location.state.traitItem', {});
|
||||
let traitItem = '';
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
traitItem = _.get(this.props, 'location.state.traitItem', '');
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('traitItem', traitItem);
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
traitItem = sessionStorage.getItem('traitItem');
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
traitItem,
|
||||
appName,
|
||||
envName,
|
||||
});
|
||||
const appName = _.get(this.props, 'location.state.appName', '');
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
if (appName && envName) {
|
||||
this.setState({
|
||||
appName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
payload: {
|
||||
@@ -203,17 +226,7 @@ class TableList extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { hasShowEdit, hasShowEdit2 } = this.state;
|
||||
// let finallyText;
|
||||
// if (demoText && demoText.length > 50) {
|
||||
// finallyText = (
|
||||
// <Tooltip placement="topRight" title={demoText}>
|
||||
// <Button>{`${demoText.substring(0, 50)}......`}</Button>
|
||||
// </Tooltip>
|
||||
// );
|
||||
// } else {
|
||||
// finallyText = <p>{demoText}</p>;
|
||||
// }
|
||||
const { hasShowEdit, hasShowEdit2, envName } = this.state;
|
||||
const status = _.get(this.state.appDetailData, 'Status', '');
|
||||
const { traitItem, appName } = this.state;
|
||||
const metadata = _.get(traitItem, 'metadata', '');
|
||||
@@ -225,340 +238,267 @@ class TableList extends React.Component {
|
||||
traitType = 2;
|
||||
}
|
||||
return (
|
||||
<PageContainer>
|
||||
<div className="card-container trait-detail">
|
||||
<h2>{traitName}</h2>
|
||||
<p style={{ marginBottom: '20px' }}>
|
||||
<i>
|
||||
{traitItem.apiVersion}1,Name={appName}
|
||||
</i>
|
||||
</p>
|
||||
<Tabs>
|
||||
<TabPane tab="Summary" key="1">
|
||||
<div>
|
||||
<Row>
|
||||
<Col span="12">
|
||||
<div className="hasBorder">
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: !hasShowEdit ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Configuration</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) => {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<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>
|
||||
<Link
|
||||
to={{
|
||||
pathname: '/ApplicationList/ApplicationListDetail',
|
||||
state: { appName, envName },
|
||||
}}
|
||||
>
|
||||
ApplicationListDetail
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>TraitDetail</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<PageContainer>
|
||||
<div className="card-container trait-detail">
|
||||
<h2>{traitName}</h2>
|
||||
<p style={{ marginBottom: '20px' }}>
|
||||
<i>
|
||||
{traitItem.apiVersion}1,Name={appName}
|
||||
</i>
|
||||
</p>
|
||||
<Tabs>
|
||||
<TabPane tab="Summary" key="1">
|
||||
<div>
|
||||
<Row>
|
||||
<Col span="12">
|
||||
<div className="hasBorder">
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: !hasShowEdit ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Configuration</p>
|
||||
<Row>
|
||||
{traitType === 2 ? (
|
||||
<Fragment>
|
||||
<Col span="8">
|
||||
<p>{currentKey}</p>
|
||||
<p>domain</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{spec[currentKey]}</p>
|
||||
<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>
|
||||
);
|
||||
})} */}
|
||||
</Row>
|
||||
{/* <Row>
|
||||
<Col span="10">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Deployment Strategy</p>
|
||||
<p>Rolling Update Strategy</p>
|
||||
<p>Selectors</p>
|
||||
<p>Min Ready Seconds</p>
|
||||
<p>Revision History Limit</p>
|
||||
<p>Replicas</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>RollingUpdate</p>
|
||||
<p>Max Surge 25%, Max Unavailable 25%</p>
|
||||
<p>
|
||||
<Tag color="orange">aryabhataapp:cool</Tag>
|
||||
<Tag color="orange">version:v0</Tag>
|
||||
</p>
|
||||
<p>0</p>
|
||||
<p>10</p>
|
||||
<p>1</p>
|
||||
</Col>
|
||||
</Row> */}
|
||||
</div>
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: hasShowEdit ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Deployment Editor</p>
|
||||
<Form
|
||||
labelAlign="left"
|
||||
{...layout}
|
||||
ef={this.formRefStep1}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep1}
|
||||
>
|
||||
<div className="relativeBox">
|
||||
<Form.Item name="Replicas" label="Replicas">
|
||||
<Input type="number" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit}>
|
||||
Cancle
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
|
||||
) : (
|
||||
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>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
<div
|
||||
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
className="textAlignLeft"
|
||||
type="link"
|
||||
block
|
||||
onClick={this.changeShowEdit}
|
||||
className="hasPadding"
|
||||
style={{ display: hasShowEdit ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Deployment Editor</p>
|
||||
<Form
|
||||
labelAlign="left"
|
||||
{...layout}
|
||||
ef={this.formRefStep1}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep1}
|
||||
>
|
||||
Edit
|
||||
<div className="relativeBox">
|
||||
<Form.Item name="Replicas" label="Replicas">
|
||||
<Input type="number" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit}>
|
||||
Cancle
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
|
||||
<div
|
||||
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
className="textAlignLeft"
|
||||
type="link"
|
||||
block
|
||||
onClick={this.changeShowEdit}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="1" />
|
||||
<Col span="10">
|
||||
<div className="hasBorder">
|
||||
<div className="hasPadding">
|
||||
<p className="title">Status</p>
|
||||
<p>{status}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title hasBG">Pods</p>
|
||||
<Table columns={columns} dataSource={data} pagination={false} />
|
||||
<p className="title hasBG">Conditions</p>
|
||||
<Table columns={columns1} dataSource={data1} pagination={false} />
|
||||
<p className="title hasBG">Pod Template</p>
|
||||
<div className="hasBorder">
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: !hasShowEdit2 ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Container cool-aryabhata-v0fnxq6</p>
|
||||
<Row>
|
||||
<Col span="2">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Image</p>
|
||||
<p>Args</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>secret</p>
|
||||
<p>['-h']</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: hasShowEdit2 ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Deployment Editor</p>
|
||||
<Form
|
||||
style={{ width: '50%' }}
|
||||
{...layout1}
|
||||
labelAlign="left"
|
||||
ef={this.formRefStep2}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep2}
|
||||
>
|
||||
<div className="relativeBox">
|
||||
<Form.Item name="Image" label="Image">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
|
||||
Cancle
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="1" />
|
||||
<Col span="10">
|
||||
<div className="hasBorder">
|
||||
<div className="hasPadding">
|
||||
<p className="title">Status</p>
|
||||
<p>{status}</p>
|
||||
{/* <Row>
|
||||
<Col span="10">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Avaliable Replicas</p>
|
||||
<p>Ready Replicas</p>
|
||||
<p>Total Replicas</p>
|
||||
<p>Unavaliable Replicas</p>
|
||||
<p>Updated Replicas</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>0</p>
|
||||
<p>0</p>
|
||||
<p>1</p>
|
||||
<p>1</p>
|
||||
<p>1</p>
|
||||
</Col>
|
||||
</Row> */}
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title" style={{ marginTop: '16px' }}>
|
||||
Pods
|
||||
</p>
|
||||
<Table columns={columns} dataSource={data} />
|
||||
<p className="title">Conditions</p>
|
||||
<Table columns={columns1} dataSource={data1} />
|
||||
<p className="title">Pod Template</p>
|
||||
<div className="hasBorder">
|
||||
<div className="hasPadding" style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
|
||||
<p className="title">Container cool-aryabhata-v0fnxq6</p>
|
||||
<Row>
|
||||
<Col span="2">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Image</p>
|
||||
<p>Args</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>secret</p>
|
||||
<p>['-h']</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<div className="hasPadding" style={{ display: hasShowEdit2 ? 'block' : 'none' }}>
|
||||
<p className="title">Deployment Editor</p>
|
||||
<Form
|
||||
style={{ width: '50%' }}
|
||||
{...layout1}
|
||||
labelAlign="left"
|
||||
ef={this.formRefStep2}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep2}
|
||||
>
|
||||
<div className="relativeBox">
|
||||
<Form.Item name="Image" label="Image">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
|
||||
Cancle
|
||||
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
|
||||
<div style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }} />
|
||||
<div>
|
||||
<Button
|
||||
className="textAlignLeft"
|
||||
type="link"
|
||||
block
|
||||
onClick={this.changeShowEdit2}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
|
||||
<div style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }} />
|
||||
<div>
|
||||
<Button
|
||||
className="textAlignLeft"
|
||||
type="link"
|
||||
block
|
||||
onClick={this.changeShowEdit2}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tab="Metadata" key="2">
|
||||
<div className="hasBorder">
|
||||
<div className="hasPadding">
|
||||
<p className="title">Metadata</p>
|
||||
{Object.keys(metadata).map((currentKey8) => {
|
||||
if (currentKey8 === 'annotations') {
|
||||
</TabPane>
|
||||
<TabPane tab="Metadata" key="2">
|
||||
<div className="hasBorder">
|
||||
<div className="hasPadding">
|
||||
<p className="title">Metadata</p>
|
||||
{Object.keys(metadata).map((currentKey8) => {
|
||||
if (currentKey8 === 'annotations') {
|
||||
return (
|
||||
<Row key={currentKey8}>
|
||||
<Col span="4">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>{currentKey8}</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="20">
|
||||
{Object.keys(metadata[currentKey8]).map((currentKey9) => {
|
||||
return (
|
||||
<Row key={currentKey9}>
|
||||
<Col span="8">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>{currentKey9}</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>{metadata[currentKey8][currentKey9]}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Row key={currentKey8}>
|
||||
<Col span="4">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>{currentKey8}</p>
|
||||
</div>
|
||||
<p>{currentKey8}</p>
|
||||
</Col>
|
||||
<Col span="20">
|
||||
{Object.keys(metadata[currentKey8]).map((currentKey9) => {
|
||||
return (
|
||||
<Row key={currentKey9}>
|
||||
<Col span="8">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>{currentKey9}</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>{metadata[currentKey8][currentKey9]}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
<Col>
|
||||
<p>{metadata[currentKey8]}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Row key={currentKey8}>
|
||||
<Col span="4">
|
||||
<p>{currentKey8}</p>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>{metadata[currentKey8]}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
{/* <Row>
|
||||
<Col span="4">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Annotations</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="20">
|
||||
<Row>
|
||||
<Col span="8">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>deployment.kubernetes.io/revision</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>1</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span="8">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>objectset.rio.cattle.io/applied</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="16">{finallyText}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span="8">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>objectset.rio.cattle.io/id</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>service</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span="4">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Controlled By</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>cool-aryabhata-v0fnxq6</p>
|
||||
</Col>
|
||||
</Row> */}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tab="Resource Viewer" key="3">
|
||||
<p>Resource Viewer</p>
|
||||
</TabPane>
|
||||
<TabPane tab="YAML" key="4">
|
||||
<p>YAML</p>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</PageContainer>
|
||||
</TabPane>
|
||||
<TabPane tab="Resource Viewer" key="3">
|
||||
<p>Resource Viewer</p>
|
||||
</TabPane>
|
||||
<TabPane tab="YAML" key="4">
|
||||
<p>YAML</p>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,19 @@
|
||||
.hasBorder {
|
||||
border: 1px solid #eee;
|
||||
.hasPadding {
|
||||
padding: 16px 16px 0;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
color: black;
|
||||
font-size: 18px;
|
||||
}
|
||||
.hasBG {
|
||||
margin-top: 50px;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 16px;
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.textAlignLeft {
|
||||
padding-left: 16px;
|
||||
text-align: left;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table, Spin } from 'antd';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table, Spin, Breadcrumb, Space } from 'antd';
|
||||
import { CheckCircleOutlined } from '@ant-design/icons';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
import _ from 'lodash';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
@@ -27,7 +29,12 @@ const columns = [
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (text) => <a>{text}</a>,
|
||||
render: (text) => (
|
||||
<div>
|
||||
<CheckCircleOutlined style={{ fontSize: '20px', color: '#4CAF51' }} />
|
||||
<a style={{ marginLeft: '6px' }}>{text}</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Ready',
|
||||
@@ -54,6 +61,17 @@ const columns = [
|
||||
dataIndex: 'Age',
|
||||
key: 'Age',
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: () => (
|
||||
<Space>
|
||||
<Button>logs</Button>
|
||||
<Button>exec</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const data = [
|
||||
@@ -120,13 +138,6 @@ const data1 = [
|
||||
LastTransition: '2d',
|
||||
},
|
||||
];
|
||||
// const demoText = `H4sIAAAAAAAA/6xUwW7bOhD8lYc9U4lsWfazgB4C5Na0DZK0lyKHNbmyWVMkQ66UGob/vSBdt3GCxEXRmwjujmZnZrmFtbYKGrgkb9ymI8sgAL3+QiFqZ6EB9D6eDyMQ0BGjQkZotmCxI2hAOmcKDBtcrJCxGMrWfn+Ygsj30aNMRYpa7E0CloGQtbN3uqPI2HlobG+MAIMLMjEBo/cvcEGAW3wjyZH4LGh3JpHZ0Jl25yuMK2hgPkPV1lRJbKfjWVlNpqps69mkrNu2qv5X5UzV9ULVIOC4P1IYtHxzlOGXFEMJOwForeM8Rib8GjOdZD3Avz6Ae7QUiuWwhuYZtWEk/nuvrXp3+4cgpzw53f3SsePKjrLaHHpKSuTGG2opkJUUofm6Pc7O84FAHPL2e6ZTrPssZDWq5+O6GhVqPquLybyaF9gqKsbVTMpyXFZ1m8yVznJwxlCAJrEUsDBOrj8lopdkiDOvFk2k3f1OQPQkk4mRDEl2IX13yHJ1dSqQx6nYCWDqvEGmDPFkU/4+8/86qUbbNQWVw2lTFKABsrgwpN52+olQSWDUlsLe7VPm6Q6XlPdABspPS1imTihWcC8gUHR9yNHZQqCHniLnb+l7aKAuu/zsdC5soIHp5IPOZDLqdW/MtTNapqsL84ibCLt7cVi5Cyldb/njCYLYs+tS4e1R251bkz1EaK/Rz4IrbdfxEKEkDAdkWm4Sa9749LMbZ4y2y89epTgICEfnZrvb9yH3MZ9+BAAA//+bxjCThQUAAA
|
||||
// objectset.rio.cattle.io/id service
|
||||
// objectset.rio.cattle.io/owner-gvk rio.cattle.io/v1, Kind=Service
|
||||
// objectset.rio.cattle.io/owner-name cool-aryabhata-v0fnxq6
|
||||
// objectset.rio.cattle.io/owner-namespace default
|
||||
// rio.cattle.io/mesh true
|
||||
// Controlled By cool-aryabhata-v0fnxq6`;
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
@@ -143,6 +154,8 @@ class TableList extends React.PureComponent {
|
||||
hasShowEdit: false,
|
||||
hasShowEdit2: false,
|
||||
appDetailData: {},
|
||||
appName: '',
|
||||
envName: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,8 +164,21 @@ class TableList extends React.PureComponent {
|
||||
}
|
||||
|
||||
async getInitialData() {
|
||||
const appName = _.get(this.props, 'location.state.appName', '');
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
appName,
|
||||
envName,
|
||||
});
|
||||
if (appName && envName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
@@ -194,338 +220,246 @@ class TableList extends React.PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { hasShowEdit, hasShowEdit2 } = this.state;
|
||||
// let finallyText;
|
||||
// if (demoText && demoText.length > 50) {
|
||||
// finallyText = (
|
||||
// <Tooltip placement="topRight" title={demoText}>
|
||||
// <Button>{`${demoText.substring(0, 50)}......`}</Button>
|
||||
// </Tooltip>
|
||||
// );
|
||||
// } else {
|
||||
// finallyText = <p>{demoText}</p>;
|
||||
// }
|
||||
const { hasShowEdit, hasShowEdit2, appName, envName } = this.state;
|
||||
const status = _.get(this.state.appDetailData, 'Status', '');
|
||||
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
|
||||
const metadata = _.get(Workload, 'metadata', {});
|
||||
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]', {});
|
||||
let { loadingAll } = this.props;
|
||||
loadingAll = loadingAll || false;
|
||||
return (
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="card-container workload-detail">
|
||||
<h2>{Workload.kind}</h2>
|
||||
<p style={{ marginBottom: '20px' }}>
|
||||
<i>
|
||||
{Workload.apiVersion},Name={_.get(Workload, 'metadata.name', '')}
|
||||
</i>
|
||||
</p>
|
||||
<Tabs>
|
||||
<TabPane tab="Summary" key="1">
|
||||
<div>
|
||||
<Row>
|
||||
<Col span="12">
|
||||
<div className="hasBorder">
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: !hasShowEdit ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Configuration</p>
|
||||
<Row>
|
||||
{Object.keys(containers).map((currentKey) => {
|
||||
if (currentKey === 'ports') {
|
||||
<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>
|
||||
<Link
|
||||
to={{
|
||||
pathname: '/ApplicationList/ApplicationListDetail',
|
||||
state: { appName, envName },
|
||||
}}
|
||||
>
|
||||
ApplicationListDetail
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>WorkloadDetail</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<PageContainer>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="card-container workload-detail">
|
||||
<h2>{Workload.kind}</h2>
|
||||
<p style={{ marginBottom: '20px' }}>
|
||||
<i>
|
||||
{Workload.apiVersion},Name={_.get(Workload, 'metadata.name', '')}
|
||||
</i>
|
||||
</p>
|
||||
<Tabs>
|
||||
<TabPane tab="Summary" key="1">
|
||||
<div>
|
||||
<Row>
|
||||
<Col span="12">
|
||||
<div className="hasBorder">
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: !hasShowEdit ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Configuration</p>
|
||||
<Row>
|
||||
{Object.keys(containers).map((currentKey) => {
|
||||
if (currentKey === 'ports') {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>port</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>
|
||||
{_.get(containers[currentKey], '[0].containerPort', '')}
|
||||
</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
// eslint-disable-next-line no-else-return
|
||||
} else if (currentKey === 'name') {
|
||||
return <Fragment key={currentKey} />;
|
||||
}
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>port</p>
|
||||
<p>{currentKey}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>
|
||||
{_.get(containers[currentKey], '[0].containerPort', '')}
|
||||
</p>
|
||||
<p>{containers[currentKey]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
// eslint-disable-next-line no-else-return
|
||||
} else if (currentKey === 'name') {
|
||||
return <Fragment key={currentKey} />;
|
||||
}
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>{currentKey}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{containers[currentKey]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
{/* <Row>
|
||||
<Col span="10">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Deployment Strategy</p>
|
||||
<p>Rolling Update Strategy</p>
|
||||
<p>Selectors</p>
|
||||
<p>Min Ready Seconds</p>
|
||||
<p>Revision History Limit</p>
|
||||
<p>Replicas</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>RollingUpdate</p>
|
||||
<p>Max Surge 25%, Max Unavailable 25%</p>
|
||||
<p>
|
||||
<Tag color="orange">aryabhataapp:cool</Tag>
|
||||
<Tag color="orange">version:v0</Tag>
|
||||
</p>
|
||||
<p>0</p>
|
||||
<p>10</p>
|
||||
<p>1</p>
|
||||
</Col>
|
||||
</Row> */}
|
||||
</div>
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: hasShowEdit ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Deployment Editor</p>
|
||||
<Form
|
||||
labelAlign="left"
|
||||
{...layout}
|
||||
ef={this.formRefStep1}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep1}
|
||||
>
|
||||
<div className="relativeBox">
|
||||
<Form.Item name="Replicas" label="Replicas">
|
||||
<Input type="number" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit}>
|
||||
Cancle
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
<div
|
||||
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
className="textAlignLeft"
|
||||
type="link"
|
||||
block
|
||||
onClick={this.changeShowEdit}
|
||||
className="hasPadding"
|
||||
style={{ display: hasShowEdit ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Deployment Editor</p>
|
||||
<Form
|
||||
labelAlign="left"
|
||||
{...layout}
|
||||
ef={this.formRefStep1}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep1}
|
||||
>
|
||||
Edit
|
||||
<div className="relativeBox">
|
||||
<Form.Item name="Replicas" label="Replicas">
|
||||
<Input type="number" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button
|
||||
style={{ marginLeft: '16px' }}
|
||||
onClick={this.changeShowEdit}
|
||||
>
|
||||
Cancle
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
|
||||
<div
|
||||
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
className="textAlignLeft"
|
||||
type="link"
|
||||
block
|
||||
onClick={this.changeShowEdit}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="1" />
|
||||
<Col span="10">
|
||||
<div className="hasBorder">
|
||||
<div className="hasPadding">
|
||||
<p className="title">Status</p>
|
||||
<p>{status}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title hasBG">Pods</p>
|
||||
<Table columns={columns} dataSource={data} pagination={false} />
|
||||
<p className="title hasBG">Conditions</p>
|
||||
<Table columns={columns1} dataSource={data1} pagination={false} />
|
||||
<p className="title hasBG">Pod Template</p>
|
||||
<div className="hasBorder">
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: !hasShowEdit2 ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Container cool-aryabhata-v0fnxq6</p>
|
||||
<Row>
|
||||
<Col span="2">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Image</p>
|
||||
<p>Args</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>secret</p>
|
||||
<p>['-h']</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: hasShowEdit2 ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Deployment Editor</p>
|
||||
<Form
|
||||
style={{ width: '50%' }}
|
||||
{...layout1}
|
||||
labelAlign="left"
|
||||
ef={this.formRefStep2}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep2}
|
||||
>
|
||||
<div className="relativeBox">
|
||||
<Form.Item name="Image" label="Image">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
|
||||
Cancle
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
|
||||
<div
|
||||
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
className="textAlignLeft"
|
||||
type="link"
|
||||
block
|
||||
onClick={this.changeShowEdit2}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="1" />
|
||||
<Col span="10">
|
||||
<div className="hasBorder">
|
||||
<div className="hasPadding">
|
||||
<p className="title">Status</p>
|
||||
<p>{status}</p>
|
||||
{/* <Row>
|
||||
<Col span="10">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Avaliable Replicas</p>
|
||||
<p>Ready Replicas</p>
|
||||
<p>Total Replicas</p>
|
||||
<p>Unavaliable Replicas</p>
|
||||
<p>Updated Replicas</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tab="Metadata" key="2">
|
||||
<div className="hasBorder">
|
||||
<div className="hasPadding">
|
||||
<p className="title">Metadata</p>
|
||||
{Object.keys(metadata).map((currentKey6) => {
|
||||
return (
|
||||
<Row key={currentKey6}>
|
||||
<Col span="4">
|
||||
<p>{currentKey6}</p>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>0</p>
|
||||
<p>0</p>
|
||||
<p>1</p>
|
||||
<p>1</p>
|
||||
<p>1</p>
|
||||
<p>{metadata[currentKey6]}</p>
|
||||
</Col>
|
||||
</Row> */}
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title" style={{ marginTop: '16px' }}>
|
||||
Pods
|
||||
</p>
|
||||
<Table columns={columns} dataSource={data} />
|
||||
<p className="title">Conditions</p>
|
||||
<Table columns={columns1} dataSource={data1} />
|
||||
<p className="title">Pod Template</p>
|
||||
<div className="hasBorder">
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: !hasShowEdit2 ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Container cool-aryabhata-v0fnxq6</p>
|
||||
<Row>
|
||||
<Col span="2">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Image</p>
|
||||
<p>Args</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>secret</p>
|
||||
<p>['-h']</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<div
|
||||
className="hasPadding"
|
||||
style={{ display: hasShowEdit2 ? 'block' : 'none' }}
|
||||
>
|
||||
<p className="title">Deployment Editor</p>
|
||||
<Form
|
||||
style={{ width: '50%' }}
|
||||
{...layout1}
|
||||
labelAlign="left"
|
||||
ef={this.formRefStep2}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep2}
|
||||
>
|
||||
<div className="relativeBox">
|
||||
<Form.Item name="Image" label="Image">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
|
||||
Cancle
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
|
||||
<div style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }} />
|
||||
<div>
|
||||
<Button
|
||||
className="textAlignLeft"
|
||||
type="link"
|
||||
block
|
||||
onClick={this.changeShowEdit2}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tab="Metadata" key="2">
|
||||
<div className="hasBorder">
|
||||
<div className="hasPadding">
|
||||
<p className="title">Metadata</p>
|
||||
{Object.keys(metadata).map((currentKey6) => {
|
||||
return (
|
||||
<Row key={currentKey6}>
|
||||
<Col span="4">
|
||||
<p>{currentKey6}</p>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>{metadata[currentKey6]}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
{/* <Row>
|
||||
<Col span="4">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Age</p>
|
||||
<p>Labels</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>2d</p>
|
||||
<p>
|
||||
<Tag color="orange">aryabhataapp:cool</Tag>
|
||||
<Tag color="orange">version:v0</Tag>
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span="4">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Annotations</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="20">
|
||||
<Row>
|
||||
<Col span="8">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>deployment.kubernetes.io/revision</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>1</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span="8">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>objectset.rio.cattle.io/applied</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="16">{finallyText}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span="8">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>objectset.rio.cattle.io/id</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>service</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span="4">
|
||||
<div style={{ color: 'black' }}>
|
||||
<p>Controlled By</p>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<p>cool-aryabhata-v0fnxq6</p>
|
||||
</Col>
|
||||
</Row> */}
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tab="Resource Viewer" key="3">
|
||||
<p>Resource Viewer</p>
|
||||
</TabPane>
|
||||
<TabPane tab="YAML" key="4">
|
||||
<p>YAML</p>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
</TabPane>
|
||||
<TabPane tab="Resource Viewer" key="3">
|
||||
<p>Resource Viewer</p>
|
||||
</TabPane>
|
||||
<TabPane tab="YAML" key="4">
|
||||
<p>YAML</p>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</Spin>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,19 @@
|
||||
.hasBorder {
|
||||
border: 1px solid #eee;
|
||||
.hasPadding {
|
||||
padding: 16px 16px 0;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
color: black;
|
||||
font-size: 18px;
|
||||
}
|
||||
.hasBG {
|
||||
margin-top: 50px;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 16px;
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.textAlignLeft {
|
||||
padding-left: 16px;
|
||||
text-align: left;
|
||||
|
||||
@@ -11,7 +11,6 @@ export async function getapplist({ url }) {
|
||||
export async function createApp({ params, url }) {
|
||||
return request(url, {
|
||||
method: 'POST',
|
||||
// body:JSON.stringify(params),
|
||||
data: {
|
||||
...params,
|
||||
},
|
||||
|
||||
@@ -11,7 +11,6 @@ export async function getCapabilityCenterlist() {
|
||||
export async function createCapabilityCenter({ params }) {
|
||||
return request('/api/capability-centers/', {
|
||||
method: 'put',
|
||||
// body:JSON.stringify(params),
|
||||
data: {
|
||||
...params,
|
||||
},
|
||||
@@ -32,10 +31,10 @@ export async function syncCapability({ capabilityCenterName }) {
|
||||
});
|
||||
}
|
||||
/*
|
||||
* Delete /api/capabilities/:capabilityName (删除一个 capability)
|
||||
* DELETE /capability-centers/:capabilityCenterName/ (Capability Center delete) (删除一个 capabilityCenter)
|
||||
*/
|
||||
export async function deleteCapability({ capabilityName }) {
|
||||
return request(`/api/capabilities/${capabilityName}`, {
|
||||
export async function deleteCapability({ capabilityCenterName }) {
|
||||
return request(`/api/capability-centers/${capabilityCenterName}`, {
|
||||
method: 'delete',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,11 +35,6 @@ const errorHandler = async (error) => {
|
||||
} else if (!response) {
|
||||
message.error('网络异常: 您的网络发生异常,无法连接服务器');
|
||||
}
|
||||
|
||||
// throw error; // 如果throw. 错误将继续抛出.
|
||||
// 如果return, 则将值作为返回. 'return;' 相当于return undefined, 在处理结果时判断response是否有值即可.
|
||||
// return {some: 'data'};
|
||||
// return response;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -47,9 +42,7 @@ const errorHandler = async (error) => {
|
||||
*/
|
||||
const request = extend({
|
||||
errorHandler, // 默认错误处理
|
||||
credentials: 'include', // 默认请求是否带上cookie,
|
||||
// prefix: '/api/v1',
|
||||
// timeout: 1000,
|
||||
credentials: 'include', // 默认请求是否带上cookie
|
||||
});
|
||||
|
||||
/*
|
||||
@@ -75,6 +68,5 @@ request.interceptors.response.use(async (response) => {
|
||||
}
|
||||
// eslint-disable-next-line consistent-return
|
||||
return data.data;
|
||||
// return response;
|
||||
});
|
||||
export default request;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
kubevela.io
|
||||
@@ -1,2 +0,0 @@
|
||||
theme: jekyll-theme-cayman
|
||||
source: website
|
||||
@@ -1,25 +0,0 @@
|
||||
## Overview
|
||||
|
||||
For developers and operators, KubeVela, as a out-of-box Cloud Native Application Management Platform, provides numerous
|
||||
workloads and operation toolings for application defining, deployment, scaling, traffic, rollout, routing, monitoring,
|
||||
logging, alerting, CICD and so on.
|
||||
|
||||
For platform builders, KubeVela, as a highly extensible PaaS/Serverless Core, provides pluginable capabilities, an elegant
|
||||
way to integrate any Workloads and Traits.
|
||||
|
||||

|
||||
|
||||
## Get Started
|
||||
|
||||
Check out [Get Started](https://github.com/oam-dev/kubevela) to try KubeVela.
|
||||
|
||||
Explore [Cli docs](https://github.com/oam-dev/kubevela/tree/master/documentation/cli) to get a quick glance
|
||||
of the power of KubeVela.
|
||||
|
||||
## Community
|
||||
|
||||
|
||||
|
||||
## Support or Contact
|
||||
|
||||
Having trouble with KubeVela? File an [issue](https://github.com/oam-dev/kubevela/issues), or contact us directly [Twitter](https://twitter.com/oam_dev) or [Gitter](https://gitter.im/oam-dev/)
|
||||
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 131 KiB |