mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-28 00:33:56 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bce8bfd40f | ||
|
|
66855265c8 | ||
|
|
63f52ea556 | ||
|
|
90fd0a5055 | ||
|
|
3af41f6515 | ||
|
|
fa37bf0284 | ||
|
|
ee39054537 | ||
|
|
b5218d371a | ||
|
|
7f5298a802 | ||
|
|
2ffd56a993 | ||
|
|
e0d7eed9d3 | ||
|
|
b0ec26bca0 | ||
|
|
2116c9ad0e | ||
|
|
d02d6675ab | ||
|
|
1a43c0e540 | ||
|
|
e6b46c6c1b | ||
|
|
387afaa2c2 | ||
|
|
1b9ee5c882 |
3
.github/workflows/e2e.yml
vendored
3
.github/workflows/e2e.yml
vendored
@@ -40,6 +40,9 @@ jobs:
|
||||
- name: Run Make
|
||||
run: make
|
||||
|
||||
- name: Run Make Manager
|
||||
run: make manager
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
make e2e-setup
|
||||
|
||||
48
.github/workflows/release.yml
vendored
48
.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,27 +43,55 @@ 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_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:
|
||||
|
||||
2
.gitignore
vendored
2
.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/
|
||||
|
||||
145
CONTRIBUTING.md
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 set
|
||||
should show env set message
|
||||
kubevela/e2e/commonContext.go:40
|
||||
Set 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
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
4
PROJECT
@@ -1,3 +1,7 @@
|
||||
domain: oam.dev
|
||||
repo: github.com/oam-dev/kubevela
|
||||
resources:
|
||||
- group: standard
|
||||
kind: Route
|
||||
version: v1alpha1
|
||||
version: "2"
|
||||
|
||||
82
README.md
82
README.md
@@ -13,64 +13,70 @@ 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
|
||||
|
||||
#### Check workloads
|
||||
|
||||
```
|
||||
$ vela workloads
|
||||
NAME DEFINITION
|
||||
backend containerizeds.standard.oam.dev
|
||||
task jobs
|
||||
webservice containerizeds.standard.oam.dev
|
||||
```
|
||||
|
||||
#### workload run
|
||||
* Create ENV
|
||||
|
||||
```shell script
|
||||
$ vela comp run -t webservice app123 -p 80 --image nginx:1.9.4
|
||||
Creating AppConfig app123
|
||||
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
|
||||
$ vela comp run mycomp -t webservice --image crccheck/hello-world --port 8000
|
||||
Creating AppConfig appcomp
|
||||
SUCCEED
|
||||
|
||||
$ vela comp status app123
|
||||
|
||||
$ vela comp ls
|
||||
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
|
||||
app123 app123 deployment Deployed 2020-08-27 10:56:41 +0800 CST
|
||||
```
|
||||
|
||||
#### app
|
||||
* Add Trait
|
||||
|
||||
```shell script
|
||||
$ vela route mycomp
|
||||
Adding route for app abc
|
||||
Succeeded!
|
||||
```
|
||||
|
||||
* 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
|
||||
app123
|
||||
abc
|
||||
|
||||
$ vela app delete app123
|
||||
Deleting AppConfig "app123"
|
||||
DELETE SUCCEED
|
||||
$ vela app delete abc
|
||||
Deleting Application "abc"
|
||||
delete apps succeed abc from t2
|
||||
```
|
||||
|
||||
#### 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"`
|
||||
|
||||
@@ -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
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
|
||||
}
|
||||
|
||||
@@ -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: ""
|
||||
|
||||
245
charts/vela-core/crds/standard.oam.dev_routes.yaml
Normal file
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: []
|
||||
@@ -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
|
||||
|
||||
46
charts/vela-core/templates/containerized.yaml
Normal file
46
charts/vela-core/templates/containerized.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: containerizeds.standard.oam.dev
|
||||
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
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
extension:
|
||||
template: |
|
||||
#Template: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Containerized"
|
||||
metadata:
|
||||
name: webservice.name
|
||||
spec: {
|
||||
replicas: 1
|
||||
podSpec: {
|
||||
containers: [{
|
||||
image: webservice.image
|
||||
name: webservice.name
|
||||
ports: [{
|
||||
containerPort: webservice.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
webservice: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=specify port for container
|
||||
// +short=p
|
||||
port: *6379 | int
|
||||
}
|
||||
@@ -4,7 +4,7 @@ metadata:
|
||||
name: metricstraits.standard.oam.dev
|
||||
namespace: default
|
||||
annotations:
|
||||
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
|
||||
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
|
||||
definition.oam.dev/kind: MetricsTrait
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
|
||||
37
charts/vela-core/templates/routetrait.yaml
Normal file
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
|
||||
}
|
||||
@@ -21,18 +21,20 @@ spec:
|
||||
metadata:
|
||||
name: webservice.name
|
||||
spec: {
|
||||
containers: [{
|
||||
image: webservice.image
|
||||
name: webservice.name
|
||||
ports: [{
|
||||
containerPort: webservice.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
replicas: 1
|
||||
podSpec: {
|
||||
containers: [{
|
||||
image: webservice.image
|
||||
name: webservice.name
|
||||
ports: [{
|
||||
containerPort: webservice.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
webservice: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
|
||||
@@ -6,8 +6,8 @@ replicaCount: 1
|
||||
useWebhook: true
|
||||
image:
|
||||
repository: oamdev/vela-core
|
||||
tag: 0.3
|
||||
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,6 +19,10 @@ 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"
|
||||
@@ -24,11 +31,6 @@ import (
|
||||
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"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/dependency"
|
||||
velawebhook "github.com/oam-dev/kubevela/pkg/webhook"
|
||||
)
|
||||
|
||||
var scheme = runtime.NewScheme()
|
||||
@@ -40,6 +42,8 @@ func init() {
|
||||
_ = monitoring.AddToScheme(scheme)
|
||||
_ = velacore.AddToScheme(scheme)
|
||||
_ = injectorv1alpha1.AddToScheme(scheme)
|
||||
_ = certmanager.AddToScheme(scheme)
|
||||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
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
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,18 @@ 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',
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
export default {
|
||||
dev: {
|
||||
'/api': {
|
||||
target: 'http://30.11.171.17:38081/',
|
||||
target: 'http://30.11.171.29:38081/',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* https://github.com/ant-design/ant-design-pro-layout
|
||||
*/
|
||||
import ProLayout from '@ant-design/pro-layout';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Link, useIntl, connect, history } from 'umi';
|
||||
import RightContent from '@/components/GlobalHeader/RightContent';
|
||||
import {
|
||||
@@ -44,6 +44,7 @@ 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) {
|
||||
@@ -70,9 +71,14 @@ const BasicLayout = (props) => {
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
props.history.listen((route) => {
|
||||
timerRef.current = props.history.listen((route) => {
|
||||
getCurrentSelectKeys(route.pathname);
|
||||
});
|
||||
return () => {
|
||||
if (timerRef.current) {
|
||||
timerRef.current = null;
|
||||
}
|
||||
};
|
||||
// setCurrentSelectedKeys('applist')
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -38,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: {
|
||||
@@ -76,12 +84,6 @@ class TableList extends React.Component {
|
||||
} else if (workloadType && workloadType === 'Deployment') {
|
||||
this.getAcceptTrait('deployment');
|
||||
}
|
||||
if (traitType && times === 1) {
|
||||
await this.setState({
|
||||
visible: true,
|
||||
});
|
||||
this.child.setDefaultValue(traitType);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
108
dashboard/src/pages/ApplicationList/ComponentDetail/Topology.jsx
Normal file
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
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
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
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;
|
||||
}
|
||||
}
|
||||
@@ -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,20 +66,6 @@ class TableList extends React.Component {
|
||||
this.setState({
|
||||
traitList: traits,
|
||||
});
|
||||
const traitType = _.get(this.props, 'location.state.TraitType', '');
|
||||
if (traitType) {
|
||||
this.setState({
|
||||
availableTraitList: traits,
|
||||
traitNum: [
|
||||
{
|
||||
refname: null,
|
||||
initialData: { name: traitType },
|
||||
uniq: new Date().valueOf(),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(res) && res.length) {
|
||||
this.setState(
|
||||
() => ({
|
||||
@@ -94,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,
|
||||
});
|
||||
|
||||
642
dashboard/src/pages/ApplicationList/CreateComponent/index.jsx
Normal file
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;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,34 @@
|
||||
import React from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Row, Col, Form, Spin, Empty, Breadcrumb } from 'antd';
|
||||
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 layout = {
|
||||
labelCol: {
|
||||
span: 6,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 18,
|
||||
},
|
||||
};
|
||||
|
||||
@connect(({ loading, applist, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
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() {
|
||||
@@ -43,6 +56,23 @@ class TableList extends React.Component {
|
||||
return true;
|
||||
}
|
||||
|
||||
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 = () => {};
|
||||
@@ -153,6 +183,40 @@ class TableList extends React.Component {
|
||||
)}
|
||||
</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="description" label="Description">
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Space, Button, Row, Col, message, Spin, Breadcrumb } from 'antd';
|
||||
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;
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.capability,
|
||||
currentEnv: globalData.currentEnv,
|
||||
@@ -16,6 +19,7 @@ class TableList extends React.PureComponent {
|
||||
this.state = {
|
||||
workloadList: [],
|
||||
traitList: [],
|
||||
capabilityCenterName: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,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') {
|
||||
@@ -55,7 +68,7 @@ class TableList extends React.PureComponent {
|
||||
|
||||
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: {
|
||||
@@ -65,7 +78,10 @@ class TableList extends React.PureComponent {
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
window.location.reload();
|
||||
this.getInitialData();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,13 +96,16 @@ class TableList extends React.PureComponent {
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
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',
|
||||
@@ -96,13 +115,51 @@ class TableList extends React.PureComponent {
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
window.location.reload();
|
||||
this.getInitialData();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
showDeleteConfirm = () => {
|
||||
message.info('正在开发中...');
|
||||
// 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() {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import React from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Button, Table, Space, Modal, Form, Input, message, Spin, Breadcrumb } from 'antd';
|
||||
import { CopyOutlined } from '@ant-design/icons';
|
||||
import { CopyOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import { Link } from 'umi';
|
||||
import './index.less';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
|
||||
const { Column } = Table;
|
||||
const { confirm } = Modal;
|
||||
|
||||
const layout = {
|
||||
labelCol: {
|
||||
@@ -31,6 +32,7 @@ class TableList extends React.PureComponent {
|
||||
this.state = {
|
||||
visible: false,
|
||||
capabilityList: [],
|
||||
isCreateLoading: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,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: {
|
||||
@@ -111,7 +118,9 @@ class TableList extends React.PureComponent {
|
||||
this.setState(() => ({
|
||||
capabilityList: newList1,
|
||||
}));
|
||||
window.location.reload();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -126,8 +135,40 @@ class TableList extends React.PureComponent {
|
||||
message.success('copy success');
|
||||
};
|
||||
|
||||
showDeleteConfirm = () => {
|
||||
message.info('正在开发中...');
|
||||
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() {
|
||||
@@ -135,6 +176,7 @@ class TableList extends React.PureComponent {
|
||||
let { loadingList } = this.props;
|
||||
loadingList = loadingList || false;
|
||||
capabilityList = Array.isArray(capabilityList) ? capabilityList : [];
|
||||
const { isCreateLoading } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
@@ -160,7 +202,12 @@ class TableList extends React.PureComponent {
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={[
|
||||
<Button key="submit" type="primary" onClick={this.handleOk}>
|
||||
<Button
|
||||
loading={isCreateLoading}
|
||||
key="submit"
|
||||
type="primary"
|
||||
onClick={this.handleOk}
|
||||
>
|
||||
Create
|
||||
</Button>,
|
||||
]}
|
||||
|
||||
@@ -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,65 +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,7 +1,7 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table, Breadcrumb } 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';
|
||||
@@ -61,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 = [
|
||||
@@ -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,7 +226,7 @@ class TableList extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { hasShowEdit, hasShowEdit2 } = this.state;
|
||||
const { hasShowEdit, hasShowEdit2, envName } = this.state;
|
||||
const status = _.get(this.state.appDetailData, 'Status', '');
|
||||
const { traitItem, appName } = this.state;
|
||||
const metadata = _.get(traitItem, 'metadata', '');
|
||||
@@ -214,7 +237,6 @@ class TableList extends React.Component {
|
||||
if (traitItem.kind === 'Ingress') {
|
||||
traitType = 2;
|
||||
}
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
return (
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table, Spin, Breadcrumb } 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';
|
||||
@@ -61,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 = [
|
||||
@@ -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,7 +220,7 @@ class TableList extends React.PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { hasShowEdit, hasShowEdit2 } = this.state;
|
||||
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', {});
|
||||
@@ -202,8 +228,6 @@ class TableList extends React.PureComponent {
|
||||
containers = _.get(Workload, 'spec.containers[0]', {});
|
||||
let { loadingAll } = this.props;
|
||||
loadingAll = loadingAll || false;
|
||||
const appName = _.get(this.props, 'location.state.appName', '');
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
return (
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
|
||||
@@ -31,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',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ var (
|
||||
cli := fmt.Sprintf("vela env init %s --namespace %s", envName, envName)
|
||||
output, err := Exec(cli)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
expectedOutput := fmt.Sprintf("Create env succeed, current env is %s", envName)
|
||||
expectedOutput := fmt.Sprintf("ENV %s CREATED,", envName)
|
||||
gomega.Expect(output).To(gomega.ContainSubstring(expectedOutput))
|
||||
})
|
||||
})
|
||||
@@ -129,7 +129,7 @@ var (
|
||||
cli := fmt.Sprintf("vela app delete %s", applicationName)
|
||||
output, err := Exec(cli)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("DELETE SUCCEED"))
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("delete apps succeed"))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -214,7 +214,7 @@ var (
|
||||
err = json.Unmarshal(result, &r)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(http.StatusOK).Should(gomega.Equal(r.Code))
|
||||
gomega.Expect(r.Data.(string)).To(gomega.ContainSubstring("Create env succeed"))
|
||||
gomega.Expect(r.Data.(string)).To(gomega.ContainSubstring("CREATED"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
3
go.mod
3
go.mod
@@ -17,13 +17,13 @@ require (
|
||||
github.com/google/go-cmp v0.5.2
|
||||
github.com/google/go-github/v32 v32.1.0
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/jetstack/cert-manager v0.14.3
|
||||
github.com/mholt/archiver/v3 v3.3.0
|
||||
github.com/oam-dev/trait-injector v0.0.0-20200331033130-0a27b176ffc4
|
||||
github.com/onsi/ginkgo v1.13.0
|
||||
github.com/onsi/gomega v1.10.1
|
||||
github.com/openservicemesh/osm v0.3.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/xid v1.2.1 // indirect
|
||||
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
@@ -43,7 +43,6 @@ require (
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29 // indirect
|
||||
k8s.io/kubectl v0.18.6 // indirect
|
||||
k8s.io/utils v0.0.0-20200414100711-2df71ebbae66
|
||||
rsc.io/letsencrypt v0.0.3 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.6.0
|
||||
)
|
||||
|
||||
|
||||
60
go.sum
60
go.sum
@@ -41,6 +41,7 @@ github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiU
|
||||
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v34.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v36.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v41.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
@@ -133,6 +134,7 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/Venafi/vcert v0.0.0-20200310111556-eba67a23943f/go.mod h1:9EegQjmRoMqVT/ydgd54mJj5rTd7ym0qMgEfhnPsce0=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
@@ -166,6 +168,7 @@ github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:o
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||
github.com/aws/aws-sdk-go v1.24.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.30.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
@@ -224,6 +227,7 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.8.5/go.mod h1:8KhU6K+zHUEWOSU++mEQYf7D9UZOcQcibUoSm6vCUz4=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@@ -265,6 +269,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbp
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/prometheus-operator v0.41.1 h1:MEhY9syliPlQg+VlFRUfNodUEVXRXJ2n1pFG0aBp+mI=
|
||||
github.com/coreos/prometheus-operator v0.41.1/go.mod h1:LhLfEBydppl7nvfEA1jIqlF3xJ9myHCnzrU+HHDxRd4=
|
||||
github.com/cpu/goacmedns v0.0.0-20180701200144-565ecf2a84df/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
@@ -277,8 +282,6 @@ github.com/crossplane/crossplane-runtime v0.8.0/go.mod h1:gNY/21MLBaz5KNP7hmfXbB
|
||||
github.com/crossplane/crossplane-runtime v0.9.0 h1:K6/tLhXKzhsEUUddTvEWWnQLLrawWyw1ptNK7NBDpDU=
|
||||
github.com/crossplane/crossplane-runtime v0.9.0/go.mod h1:gNY/21MLBaz5KNP7hmfXbBXp8reYRbwY5B/97Kp4tgM=
|
||||
github.com/crossplane/crossplane-tools v0.0.0-20200219001116-bb8b2ce46330/go.mod h1:C735A9X0x0lR8iGVOOxb49Mt70Ua4EM2b7PGaRPBLd4=
|
||||
github.com/crossplane/oam-kubernetes-runtime v0.0.9 h1:cZMT7p1jZ6MsJqAuzVIZwvOxVdD+PGEQgCYHHuwR7Pc=
|
||||
github.com/crossplane/oam-kubernetes-runtime v0.0.9/go.mod h1:f5xqmo0B2WtaOTZh8jhP+0f0XuzqhJG2xRtxfMZR3jA=
|
||||
github.com/crossplane/oam-kubernetes-runtime v0.1.1-0.20200909070723-78b84f2c4799 h1:424LLFb7C8Qvy3wFZZ7HzmawlCeF32PNRTXXK5rKOk0=
|
||||
github.com/crossplane/oam-kubernetes-runtime v0.1.1-0.20200909070723-78b84f2c4799/go.mod h1:UZ4eXkl/e4lKrAhK81Pz1sR90wqeuE9PgdwVXr8kDgI=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
|
||||
@@ -303,6 +306,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
||||
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/digitalocean/godo v1.29.0/go.mod h1:iJnN9rVu6K5LioLxLimlq0uRI+y/eAQjROUmeU/r0hY=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 h1:FwssHbCDJD025h+BchanCwE1Q8fyMgqDr2mOQAWOLGw=
|
||||
@@ -535,6 +539,7 @@ github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598
|
||||
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||
github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
|
||||
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
|
||||
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
|
||||
@@ -751,6 +756,7 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@@ -775,6 +781,7 @@ github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoI
|
||||
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
|
||||
@@ -802,6 +809,8 @@ github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mq
|
||||
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
|
||||
github.com/jessevdk/go-flags v0.0.0-20180331124232-1c38ed7ad0cc/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jetstack/cert-manager v0.14.3 h1:SSp/aTA3s+Nr8lRH5d9Kg7vF8HT4sf7Q6idHMnRQ+vs=
|
||||
github.com/jetstack/cert-manager v0.14.3/go.mod h1:xuOMzt8X2TVad3gRzRDBI88JUfuP78YEp7+mCLWWyWM=
|
||||
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
|
||||
@@ -867,6 +876,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
@@ -910,6 +920,7 @@ github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kN
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/matm/gocov-html v0.0.0-20200509184451-71874e2e203b/go.mod h1:zha4ZSIA/qviBBKx3j6tJG/Lx6aIdjOXPWuKAcJchQM=
|
||||
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
@@ -945,10 +956,9 @@ github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4f
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mholt/archiver v1.1.2 h1:xukR55YIrnhDHp10lrNtRSsAK5THpWrOCuviweNSBw4=
|
||||
github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU=
|
||||
github.com/mholt/archiver/v3 v3.3.0 h1:vWjhY8SQp5yzM9P6OJ/eZEkmi3UAbRrxCq48MxjAzig=
|
||||
github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao=
|
||||
github.com/miekg/dns v0.0.0-20170721150254-0f3adef2e220/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
@@ -970,6 +980,7 @@ github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
|
||||
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
||||
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||
@@ -997,6 +1008,7 @@ github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISe
|
||||
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto=
|
||||
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/munnerz/crd-schema-fuzz v0.0.0-20191114184610-fbd148d44a0a/go.mod h1:fVs1Mso4ZxhlygBEUDgOcyLtp5/DnLuCb8H5GI3CzS4=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@@ -1012,6 +1024,7 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nishanths/exhaustive v0.0.0-20200708172631-8866003e3856/go.mod h1:wBEpHwM2OdmeNpdCvRPUlkEbBuaFmcK4Wv8Q7FuGW3c=
|
||||
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
||||
@@ -1083,6 +1096,7 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
|
||||
github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible/go.mod h1:xlUlxe/2ItGlQyMTstqeDv9r3U4obH7xYd26TbDQutY=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||
@@ -1319,6 +1333,7 @@ github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.12.0/go.mod h1:229t1eWu9UXTPmoUkbpN/fctKPBY4IJoFXQnxHGXy6E=
|
||||
@@ -1406,6 +1421,7 @@ golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -1466,6 +1482,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1516,6 +1533,7 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrS
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1554,6 +1572,7 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190425045458-9f0b1ff7b46a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1706,6 +1725,7 @@ golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200630223951-c138986dd9b9 h1:CrrjeBLnUL9WPTAjIqIPdfEAAs5XNY+8A4/CNxRqmbI=
|
||||
golang.org/x/tools v0.0.0-20200630223951-c138986dd9b9/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200701041122-1837592efa10/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305 h1:yaM5S0KcY0lIoZo7Fl+oi91b/DdlU2zuWpfHrpWbCS0=
|
||||
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1820,6 +1840,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
@@ -1836,8 +1857,10 @@ gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
|
||||
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
@@ -1878,12 +1901,17 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||
k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN7Vh59Bw0vh9jhoX+V58=
|
||||
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48=
|
||||
k8s.io/api v0.0.0-20191016110408-35e52d86657a/go.mod h1:/L5qH+AD540e7Cetbui1tuJeXdmNhO8jM6VkXeDdDhQ=
|
||||
k8s.io/api v0.0.0-20191115095533-47f6de673b26/go.mod h1:iA/8arsvelvo4IDqIhX4IbjTEKBGgvsf2OraTuRtLFU=
|
||||
k8s.io/api v0.0.0-20191122220107-b5267f2975e0/go.mod h1:vYpRfxYkMrmPPSesoHEkGNHxNKTk96REAwqm/inQbs0=
|
||||
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
|
||||
k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4=
|
||||
k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0=
|
||||
k8s.io/api v0.17.5/go.mod h1:0zV5/ungglgy2Rlm3QK8fbxkXVs+BSJWpJP/+8gUVLY=
|
||||
k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
|
||||
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
|
||||
@@ -1892,13 +1920,21 @@ k8s.io/api v0.18.5/go.mod h1:tN+e/2nbdGKOAH55NMV8oGrMG+3uRlA9GaRfvnCCSNk=
|
||||
k8s.io/api v0.18.6 h1:osqrAXbOQjkKIWDTjrqxWQ3w0GkKb1KA1XkUGHHYpeE=
|
||||
k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65/go.mod h1:5BINdGqggRXXKnDgpwoJ7PyQH8f+Ypp02fvVNcIFy9s=
|
||||
k8s.io/apiextensions-apiserver v0.17.0/go.mod h1:XiIFUakZywkUl54fVXa7QTEHcqQz9HG55nHd1DCoHj8=
|
||||
k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs=
|
||||
k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY=
|
||||
k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo=
|
||||
k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8=
|
||||
k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
|
||||
k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8=
|
||||
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4=
|
||||
k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ=
|
||||
k8s.io/apimachinery v0.0.0-20191115015347-3c7067801da2/go.mod h1:dXFS2zaQR8fyzuvRdJDHw2Aerij/yVGJSre0bZQSVJA=
|
||||
k8s.io/apimachinery v0.0.0-20191121175448-79c2a76c473a/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
|
||||
k8s.io/apimachinery v0.17.5/go.mod h1:ioIo1G/a+uONV7Tv+ZmCbMG1/a3kVw5YcDdncd8ugQ0=
|
||||
k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
@@ -1907,7 +1943,11 @@ k8s.io/apimachinery v0.18.5/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCk
|
||||
k8s.io/apimachinery v0.18.6 h1:RtFHnfGNfd1N0LeSrKCUznz5xtUP1elRGvHJbL3Ntag=
|
||||
k8s.io/apimachinery v0.18.6/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
|
||||
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
|
||||
k8s.io/apiserver v0.0.0-20191016112112-5190913f932d/go.mod h1:7OqfAolfWxUM/jJ/HBLyE+cdaWFBUoo5Q5pHgJVj2ws=
|
||||
k8s.io/apiserver v0.0.0-20191122221311-9d521947b1e1/go.mod h1:RbsZY5zzBIWnz4KbctZsTVjwIuOpTp4Z8oCgFHN4kZQ=
|
||||
k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
|
||||
k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo=
|
||||
k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY=
|
||||
k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw=
|
||||
k8s.io/apiserver v0.18.2 h1:fwKxdTWwwYhxvtjo0UUfX+/fsitsNtfErPNegH2x9ic=
|
||||
k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
|
||||
@@ -1918,12 +1958,20 @@ k8s.io/cli-runtime v0.18.6/go.mod h1:+G/WTNqHgUv636e5y7rhOQ7epUbRXnwmPnhOhD6t9uM
|
||||
k8s.io/client-go v0.18.6 h1:I+oWqJbibLSGsZj8Xs8F0aWVXJVIoUHWaaJV3kUN/Zw=
|
||||
k8s.io/client-go v0.18.6/go.mod h1:/fwtGLjYMS1MaM5oi+eXhKwG+1UHidUEXRh6cNsdO0Q=
|
||||
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
|
||||
k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894/go.mod h1:mJUgkl06XV4kstAnLHAIzJPVCOzVR+ZcfPIv4fUsFCY=
|
||||
k8s.io/code-generator v0.17.0/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
|
||||
k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
|
||||
k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ=
|
||||
k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
|
||||
k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
|
||||
k8s.io/code-generator v0.18.5/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
|
||||
k8s.io/code-generator v0.18.6/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
|
||||
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA=
|
||||
k8s.io/component-base v0.0.0-20191016111319-039242c015a9/go.mod h1:SuWowIgd/dtU/m/iv8OD9eOxp3QZBBhTIiWMsBQvKjI=
|
||||
k8s.io/component-base v0.0.0-20191122220729-2684fb322cb9/go.mod h1:NFuUusy/X4Tk21m21tcNUihnmp4OI7lXU7/xA+rYXkc=
|
||||
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
|
||||
k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs=
|
||||
k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8=
|
||||
k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c=
|
||||
k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM=
|
||||
k8s.io/component-base v0.18.5/go.mod h1:RSbcboNk4B+S8Acs2JaBOVW3XNz1+A637s2jL+QQrlU=
|
||||
@@ -1940,6 +1988,7 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/kube-aggregator v0.17.3/go.mod h1:1dMwMFQbmH76RKF0614L7dNenMl3dwnUJuOOyZ3GMXA=
|
||||
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
|
||||
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
@@ -1982,9 +2031,11 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
|
||||
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
|
||||
sigs.k8s.io/controller-runtime v0.5.1-0.20200307095134-d0de78d9f1c1/go.mod h1:Uojny7gvg55YLQnEGnPzRE3dC4ik2tRlZJgOUCWXAV4=
|
||||
sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM=
|
||||
sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
|
||||
sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA=
|
||||
sigs.k8s.io/controller-tools v0.2.5/go.mod h1:+t0Hz6tOhJQCdd7IYO0mNzimmiM9sqMU0021u6UCF2o=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
@@ -2001,6 +2052,7 @@ sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||
|
||||
@@ -320,7 +320,7 @@ func EvalToObject(capName string, data map[string]interface{}) (*unstructured.Un
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (app *Application) GetComponentTraits(componentName string) ([]v1alpha2.ComponentTrait, error) {
|
||||
func (app *Application) GetComponentTraits(componentName string, env *types.EnvMeta) ([]v1alpha2.ComponentTrait, error) {
|
||||
var traits []v1alpha2.ComponentTrait
|
||||
rawTraits, err := app.GetTraits(componentName)
|
||||
if err != nil {
|
||||
@@ -338,6 +338,14 @@ func (app *Application) GetComponentTraits(componentName string) ([]v1alpha2.Com
|
||||
return traits, nil
|
||||
}
|
||||
|
||||
func (app *Application) VelaCoreInjection(obj *unstructured.Unstructured, env *types.EnvMeta, traitType string) {
|
||||
switch traitType {
|
||||
case "route":
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func FormatDefaultHealthScopeName(appName string) string {
|
||||
return appName + "-default-health"
|
||||
}
|
||||
@@ -387,7 +395,7 @@ func (app *Application) OAM(env *types.EnvMeta) ([]v1alpha2.Component, v1alpha2.
|
||||
}})
|
||||
|
||||
//TODO(wonderflow): handle component data input/output here
|
||||
compTraits, err := app.GetComponentTraits(name)
|
||||
compTraits, err := app.GetComponentTraits(name, env)
|
||||
if err != nil {
|
||||
return nil, v1alpha2.ApplicationConfiguration{}, nil, err
|
||||
}
|
||||
|
||||
@@ -58,8 +58,7 @@ func NewCompRunCommands(c types.Args, ioStreams util.IOStreams) *cobra.Command {
|
||||
Long: "Init and Run workloads",
|
||||
Example: "vela comp run -t <workload-type>",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if args[0] == "-h" {
|
||||
if len(args) == 0 || args[0] == "-h" {
|
||||
err := cmd.Help()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -43,7 +43,7 @@ func NewDeleteCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command
|
||||
o.AppName = args[0]
|
||||
|
||||
ioStreams.Infof("Deleting Application \"%s\"\n", o.AppName)
|
||||
_, err = o.DeleteApp()
|
||||
info, err := o.DeleteApp()
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
ioStreams.Info("Already deleted")
|
||||
@@ -51,7 +51,7 @@ func NewDeleteCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command
|
||||
}
|
||||
return err
|
||||
}
|
||||
ioStreams.Info("DELETE SUCCEED")
|
||||
ioStreams.Info(info)
|
||||
return nil
|
||||
}
|
||||
return cmd
|
||||
|
||||
@@ -57,7 +57,7 @@ func NewEnvInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Create environments",
|
||||
Long: "Create environment and set the currently using environment",
|
||||
Example: `vela env init test --namespace test`,
|
||||
Example: `vela env init test --namespace test --email my@email.com`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
|
||||
if err != nil {
|
||||
@@ -71,6 +71,8 @@ func NewEnvInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command
|
||||
}
|
||||
cmd.SetOut(ioStreams.Out)
|
||||
cmd.Flags().StringVar(&envArgs.Namespace, "namespace", "default", "specify K8s namespace for env")
|
||||
cmd.Flags().StringVar(&envArgs.Email, "email", "", "specify email for production TLS Certificate notification")
|
||||
cmd.Flags().StringVar(&envArgs.Domain, "domain", "", "specify domain your applications")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -94,7 +96,6 @@ func NewEnvDeleteCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
}
|
||||
|
||||
func NewEnvSetCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
ctx := context.Background()
|
||||
cmd := &cobra.Command{
|
||||
Use: "set",
|
||||
Aliases: []string{"sw"},
|
||||
@@ -103,7 +104,7 @@ func NewEnvSetCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
Long: "Set an environment as the current using one",
|
||||
Example: `vela env set test`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return SetEnv(ctx, args, ioStreams)
|
||||
return SetEnv(args, ioStreams)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
types.TagCommandType: types.TypeStart,
|
||||
@@ -149,8 +150,8 @@ func CreateOrUpdateEnv(ctx context.Context, c client.Client, envArgs *types.EnvM
|
||||
return fmt.Errorf("you must specify env name for vela env init command")
|
||||
}
|
||||
envName := args[0]
|
||||
namespace := envArgs.Namespace
|
||||
msg, err := oam.CreateOrUpdateEnv(ctx, c, envName, namespace)
|
||||
envArgs.Name = envName
|
||||
msg, err := oam.CreateOrUpdateEnv(ctx, c, envName, envArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -158,7 +159,7 @@ func CreateOrUpdateEnv(ctx context.Context, c client.Client, envArgs *types.EnvM
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetEnv(ctx context.Context, args []string, ioStreams cmdutil.IOStreams) error {
|
||||
func SetEnv(args []string, ioStreams cmdutil.IOStreams) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("you must specify env name for vela env command")
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func TestENV(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
|
||||
// set as default env
|
||||
err = SetEnv(ctx, []string{"default"}, ioStream)
|
||||
err = SetEnv([]string{"default"}, ioStream)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// check env set success
|
||||
@@ -93,10 +93,10 @@ func TestENV(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
// can not set as a non-exist env
|
||||
err = SetEnv(ctx, []string{"env1"}, ioStream)
|
||||
err = SetEnv([]string{"env1"}, ioStream)
|
||||
assert.Error(t, err)
|
||||
|
||||
// set success
|
||||
err = SetEnv(ctx, []string{"default"}, ioStream)
|
||||
err = SetEnv([]string{"default"}, ioStream)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func AddTraitCommands(parentCmd *cobra.Command, c types.Args, ioStreams cmdutil.
|
||||
Long: "Attach " + name + " trait to an app",
|
||||
Example: "vela " + name + " frontend",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o := &commandOptions{IOStreams: ioStreams}
|
||||
o := &commandOptions{IOStreams: ioStreams, traitType: name}
|
||||
o.Template = tmp
|
||||
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
|
||||
if err != nil {
|
||||
@@ -103,7 +103,9 @@ func (o *commandOptions) AddOrUpdateTrait(cmd *cobra.Command, args []string) err
|
||||
if err = o.Prepare(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
if o.app, err = oam.AddOrUpdateTrait(o.Env.Name, o.appName, o.workloadName, cmd.Flags(), o.Template); err != nil {
|
||||
flags := cmd.Flags()
|
||||
|
||||
if o.app, err = oam.AddOrUpdateTrait(o.Env, o.appName, o.workloadName, flags, o.Template); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -138,6 +140,7 @@ func (o *commandOptions) Run(ctx context.Context, cmd *cobra.Command) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Info(msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
7
pkg/controller/common/error.go
Normal file
7
pkg/controller/common/error.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package common
|
||||
|
||||
const (
|
||||
ErrLocatingWorkload = "failed to locate the workload"
|
||||
ErrLocatingService = "failed to locate any the services"
|
||||
ErrCreatingService = "failed to create the services"
|
||||
)
|
||||
11
pkg/controller/common/types.go
Normal file
11
pkg/controller/common/types.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
var ServiceKind = reflect.TypeOf(v1.Service{}).Name()
|
||||
|
||||
var ServiceAPIVersion = v1.SchemeGroupVersion.String()
|
||||
@@ -21,12 +21,13 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/controller/v1alpha1/containerized"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/v1alpha1/metrics"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/v1alpha1/routes"
|
||||
)
|
||||
|
||||
// Setup workload controllers.
|
||||
func Setup(mgr ctrl.Manager) error {
|
||||
for _, setup := range []func(ctrl.Manager) error{
|
||||
metrics.Setup, containerized.Setup,
|
||||
metrics.Setup, containerized.Setup, routes.Setup,
|
||||
} {
|
||||
if err := setup(mgr); err != nil {
|
||||
return err
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/controller/common"
|
||||
|
||||
monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
|
||||
cpv1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/event"
|
||||
@@ -42,17 +44,12 @@ import (
|
||||
|
||||
const (
|
||||
errApplyServiceMonitor = "failed to apply the service monitor"
|
||||
errLocatingWorkload = "failed to locate the workload"
|
||||
errLocatingService = "failed to locate any the services"
|
||||
errCreatingService = "failed to create the services"
|
||||
servicePort = 4848
|
||||
)
|
||||
|
||||
var (
|
||||
serviceMonitorKind = reflect.TypeOf(monitoring.ServiceMonitor{}).Name()
|
||||
serviceMonitorAPIVersion = monitoring.SchemeGroupVersion.String()
|
||||
serviceKind = reflect.TypeOf(corev1.Service{}).Name()
|
||||
serviceAPIVersion = corev1.SchemeGroupVersion.String()
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -115,27 +112,27 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
if err != nil {
|
||||
mLog.Error(err, "Error while fetching the workload", "workload reference",
|
||||
metricsTrait.GetWorkloadReference())
|
||||
r.record.Event(eventObj, event.Warning(errLocatingWorkload, err))
|
||||
r.record.Event(eventObj, event.Warning(common.ErrLocatingWorkload, err))
|
||||
return oamutil.ReconcileWaitResult,
|
||||
oamutil.PatchCondition(ctx, r, &metricsTrait,
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, errLocatingWorkload)))
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, common.ErrLocatingWorkload)))
|
||||
}
|
||||
// try to see if the workload already has services as child resources
|
||||
serviceLabel, err := r.fetchServicesLabel(ctx, mLog, workload, metricsTrait.Spec.ScrapeService.TargetPort)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
r.record.Event(eventObj, event.Warning(errLocatingService, err))
|
||||
r.record.Event(eventObj, event.Warning(common.ErrLocatingService, err))
|
||||
return oamutil.ReconcileWaitResult,
|
||||
oamutil.PatchCondition(ctx, r, &metricsTrait,
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, errLocatingService)))
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, common.ErrLocatingService)))
|
||||
} else if serviceLabel == nil {
|
||||
// TODO: use podMonitor instead?
|
||||
// no service with the targetPort found, we will create a service that talks to the targetPort
|
||||
serviceLabel, err = r.createService(ctx, mLog, workload, &metricsTrait)
|
||||
if err != nil {
|
||||
r.record.Event(eventObj, event.Warning(errCreatingService, err))
|
||||
r.record.Event(eventObj, event.Warning(common.ErrCreatingService, err))
|
||||
return oamutil.ReconcileWaitResult,
|
||||
oamutil.PatchCondition(ctx, r, &metricsTrait,
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, errCreatingService)))
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, common.ErrCreatingService)))
|
||||
}
|
||||
}
|
||||
// construct the serviceMonitor that hooks the service to the prometheus server
|
||||
@@ -171,7 +168,7 @@ func (r *Reconciler) fetchServicesLabel(ctx context.Context, mLog logr.Logger,
|
||||
}
|
||||
// find the service that has the port
|
||||
for _, childRes := range resources {
|
||||
if childRes.GetAPIVersion() == serviceAPIVersion && childRes.GetKind() == serviceKind {
|
||||
if childRes.GetAPIVersion() == common.ServiceAPIVersion && childRes.GetKind() == common.ServiceKind {
|
||||
ports, _, _ := unstructured.NestedSlice(childRes.Object, "spec", "ports")
|
||||
for _, port := range ports {
|
||||
servicePort, _ := port.(corev1.ServicePort)
|
||||
@@ -189,8 +186,8 @@ func (r *Reconciler) createService(ctx context.Context, mLog logr.Logger, worklo
|
||||
metricsTrait *v1alpha1.MetricsTrait) (map[string]string, error) {
|
||||
oamService := &corev1.Service{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: serviceKind,
|
||||
APIVersion: serviceAPIVersion,
|
||||
Kind: common.ServiceKind,
|
||||
APIVersion: common.ServiceAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "oam-" + workload.GetName(),
|
||||
|
||||
470
pkg/controller/v1alpha1/routes/route_controller.go
Normal file
470
pkg/controller/v1alpha1/routes/route_controller.go
Normal file
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
|
||||
|
||||
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 routes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/oam-dev/kubevela/api/v1alpha1"
|
||||
standardv1alpha1 "github.com/oam-dev/kubevela/api/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/common"
|
||||
|
||||
cpv1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||||
runtimev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/event"
|
||||
oamutil "github.com/crossplane/oam-kubernetes-runtime/pkg/oam/util"
|
||||
"github.com/go-logr/logr"
|
||||
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/networking/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/utils/pointer"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
const (
|
||||
errApplyNginxIngress = "failed to apply the ingress"
|
||||
errCreateIssuer = "failed to create cert-manager Issuer"
|
||||
)
|
||||
|
||||
var (
|
||||
// oamServiceLabel is the pre-defined labels for any serviceMonitor
|
||||
// created by the RouteTrait
|
||||
oamServiceLabel = map[string]string{
|
||||
"k8s-app": "oam",
|
||||
"controller": "routeTrait",
|
||||
}
|
||||
)
|
||||
|
||||
// Reconciler reconciles a Route object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
Log logr.Logger
|
||||
record event.Recorder
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=standard.oam.dev,resources=routes,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=standard.oam.dev,resources=routes/status,verbs=get;update;patch
|
||||
|
||||
func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
ctx := context.Background()
|
||||
mLog := r.Log.WithValues("route", req.NamespacedName)
|
||||
|
||||
mLog.Info("Reconcile route trait")
|
||||
// fetch the trait
|
||||
var routeTrait standardv1alpha1.Route
|
||||
if err := r.Get(ctx, req.NamespacedName, &routeTrait); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
mLog.Info("Get the route trait",
|
||||
"host", routeTrait.Spec.Host,
|
||||
"path", routeTrait.Spec.Path,
|
||||
"workload reference", routeTrait.Spec.WorkloadReference,
|
||||
"labels", routeTrait.GetLabels())
|
||||
|
||||
// find the resource object to record the event to, default is the parent appConfig.
|
||||
eventObj, err := oamutil.LocateParentAppConfig(ctx, r.Client, &routeTrait)
|
||||
if eventObj == nil {
|
||||
// fallback to workload itself
|
||||
mLog.Error(err, "add events to route trait itself", "name", routeTrait.Name)
|
||||
eventObj = &routeTrait
|
||||
}
|
||||
|
||||
// Fetch the workload instance to which we want to do routes
|
||||
workload, err := oamutil.FetchWorkload(ctx, r, mLog, &routeTrait)
|
||||
if err != nil {
|
||||
mLog.Error(err, "Error while fetching the workload", "workload reference",
|
||||
routeTrait.GetWorkloadReference())
|
||||
r.record.Event(eventObj, event.Warning(common.ErrLocatingWorkload, err))
|
||||
return oamutil.ReconcileWaitResult,
|
||||
oamutil.PatchCondition(ctx, r, &routeTrait,
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, common.ErrLocatingWorkload)))
|
||||
}
|
||||
|
||||
// try to see if the workload already has services as child resources, and match for our route
|
||||
svc, exist, svcPort, err := r.fetchService(ctx, mLog, workload, &routeTrait)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
r.record.Event(eventObj, event.Warning(common.ErrLocatingService, err))
|
||||
return oamutil.ReconcileWaitResult,
|
||||
oamutil.PatchCondition(ctx, r, &routeTrait,
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, common.ErrLocatingService)))
|
||||
}
|
||||
|
||||
// Create Services
|
||||
if !exist {
|
||||
// no service found, we will create service according to rule
|
||||
svc, svcPort, err = r.createService(ctx, mLog, workload, &routeTrait)
|
||||
if err != nil {
|
||||
r.record.Event(eventObj, event.Warning(common.ErrCreatingService, err))
|
||||
return oamutil.ReconcileWaitResult,
|
||||
oamutil.PatchCondition(ctx, r, &routeTrait,
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, common.ErrCreatingService)))
|
||||
}
|
||||
r.record.Event(eventObj, event.Normal("Service created",
|
||||
fmt.Sprintf("successfully automatically created a service `%s`", svc.Name)))
|
||||
} else {
|
||||
mLog.Info("workload already has service as child resource, will not create new", "workloadName", workload.GetName())
|
||||
}
|
||||
|
||||
// Create Issuers
|
||||
var issuer standardv1alpha1.TLS
|
||||
if routeTrait.Spec.TLS == nil || routeTrait.Spec.TLS.IssuerName == "" {
|
||||
issuerName, err := r.createSelfsignedIssuer(ctx, &routeTrait)
|
||||
if err != nil {
|
||||
r.record.Event(eventObj, event.Warning(errCreateIssuer, err))
|
||||
return oamutil.ReconcileWaitResult,
|
||||
oamutil.PatchCondition(ctx, r, &routeTrait,
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, errCreateIssuer)))
|
||||
}
|
||||
r.record.Event(eventObj, event.Normal("Issuer created",
|
||||
fmt.Sprintf("successfully automatically created a Issuer for route TLS `%s`", issuerName)))
|
||||
issuer.Type = standardv1alpha1.NamespaceIssuer
|
||||
issuer.IssuerName = issuerName
|
||||
} else {
|
||||
issuer = *routeTrait.Spec.TLS
|
||||
}
|
||||
|
||||
// Create Ingress
|
||||
// construct the serviceMonitor that hooks the service to the prometheus server
|
||||
ingress := constructNginxIngress(&routeTrait, issuer, svc, svcPort)
|
||||
// server side apply the serviceMonitor, only the fields we set are touched
|
||||
applyOpts := []client.PatchOption{client.ForceOwnership, client.FieldOwner(routeTrait.GetUID())}
|
||||
if err := r.Patch(ctx, ingress, client.Apply, applyOpts...); err != nil {
|
||||
mLog.Error(err, "Failed to apply to ingress")
|
||||
r.record.Event(eventObj, event.Warning(errApplyNginxIngress, err))
|
||||
return oamutil.ReconcileWaitResult,
|
||||
oamutil.PatchCondition(ctx, r, &routeTrait,
|
||||
cpv1alpha1.ReconcileError(errors.Wrap(err, errApplyNginxIngress)))
|
||||
}
|
||||
r.record.Event(eventObj, event.Normal("Nginx Ingress created",
|
||||
fmt.Sprintf("successfully server side patched a route trait `%s`", routeTrait.Name)))
|
||||
|
||||
// TODO(wonderflow): GC mechanism for no used ingress, service, issuer
|
||||
|
||||
routeTrait.Status.Service = svc
|
||||
routeTrait.Status.Ingress = &runtimev1alpha1.TypedReference{
|
||||
APIVersion: v1beta1.SchemeGroupVersion.String(),
|
||||
Kind: reflect.TypeOf(v1beta1.Ingress{}).Name(),
|
||||
Name: ingress.Name,
|
||||
UID: routeTrait.UID,
|
||||
}
|
||||
return ctrl.Result{}, oamutil.PatchCondition(ctx, r, &routeTrait)
|
||||
}
|
||||
|
||||
// create a service that targets the exposed workload pod
|
||||
func (r *Reconciler) createService(ctx context.Context, mLog logr.Logger, workload *unstructured.Unstructured,
|
||||
routeTrait *v1alpha1.Route) (*runtimev1alpha1.TypedReference, int32, error) {
|
||||
|
||||
oamService := &corev1.Service{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: common.ServiceKind,
|
||||
APIVersion: common.ServiceAPIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "route-" + workload.GetName(),
|
||||
Namespace: workload.GetNamespace(),
|
||||
Labels: oamServiceLabel,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: routeTrait.GetObjectKind().GroupVersionKind().GroupVersion().String(),
|
||||
Kind: routeTrait.GetObjectKind().GroupVersionKind().Kind,
|
||||
UID: routeTrait.GetUID(),
|
||||
Name: routeTrait.GetName(),
|
||||
Controller: pointer.BoolPtr(true),
|
||||
BlockOwnerDeletion: pointer.BoolPtr(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Type: corev1.ServiceTypeClusterIP,
|
||||
},
|
||||
}
|
||||
// assign selector
|
||||
if routeTrait.Spec.Backend != nil && len(routeTrait.Spec.Backend.SelectLabels) != 0 {
|
||||
oamService.Spec.Selector = routeTrait.Spec.Backend.SelectLabels
|
||||
}
|
||||
|
||||
port, labels, err := DiscoverPortLabel(ctx, mLog, workload, r)
|
||||
if err == nil {
|
||||
if len(oamService.Spec.Selector) == 0 {
|
||||
oamService.Spec.Selector = labels
|
||||
}
|
||||
if routeTrait.Spec.Backend == nil {
|
||||
routeTrait.Spec.Backend = &standardv1alpha1.Backend{Port: port}
|
||||
} else if routeTrait.Spec.Backend.Port.String() == "0" {
|
||||
routeTrait.Spec.Backend.Port = port
|
||||
}
|
||||
} else {
|
||||
mLog.Info("[WARN] fail to discovery port and label", "err", err)
|
||||
}
|
||||
|
||||
var servicePort int32 = 443
|
||||
oamService.Spec.Ports = []corev1.ServicePort{
|
||||
{
|
||||
Port: servicePort,
|
||||
TargetPort: routeTrait.Spec.Backend.Port,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
}
|
||||
// server side apply the service, only the fields we set are touched
|
||||
applyOpts := []client.PatchOption{client.ForceOwnership, client.FieldOwner(routeTrait.GetUID())}
|
||||
if err := r.Patch(ctx, oamService, client.Apply, applyOpts...); err != nil {
|
||||
mLog.Error(err, "Failed to apply to service")
|
||||
return nil, servicePort, err
|
||||
}
|
||||
return &runtimev1alpha1.TypedReference{
|
||||
APIVersion: common.ServiceAPIVersion,
|
||||
Kind: common.ServiceKind,
|
||||
Name: oamService.Name,
|
||||
UID: routeTrait.UID,
|
||||
}, servicePort, nil
|
||||
}
|
||||
|
||||
// Assume the workload or it's childResource will always having spec.template as PodTemplate if discoverable
|
||||
func DiscoverPortLabel(ctx context.Context, mLog logr.Logger, workload *unstructured.Unstructured, r client.Reader) (intstr.IntOrString, map[string]string, error) {
|
||||
var resources = []*unstructured.Unstructured{workload}
|
||||
// Fetch the child resources list from the corresponding workload
|
||||
childResources, err := oamutil.FetchWorkloadChildResources(ctx, mLog, r, workload)
|
||||
if err == nil {
|
||||
resources = append(resources, childResources...)
|
||||
} else {
|
||||
mLog.Info("[WARN] fail to fetch workload child resource", "name", workload.GetName(), "err", err)
|
||||
}
|
||||
var gatherErrs []error
|
||||
for _, w := range resources {
|
||||
port, labels, err := discoveryFromObject(w)
|
||||
if err == nil {
|
||||
return port, labels, nil
|
||||
}
|
||||
gatherErrs = append(gatherErrs, err)
|
||||
}
|
||||
return intstr.IntOrString{}, nil, fmt.Errorf("can't discovery port from workload %v %v.%v and it's child resource, errorList: %v", workload.GetName(), workload.GetAPIVersion(), workload.GetKind(), gatherErrs)
|
||||
}
|
||||
|
||||
func discoveryFromObject(w *unstructured.Unstructured) (intstr.IntOrString, map[string]string, error) {
|
||||
obj, found, _ := unstructured.NestedMap(w.Object, "spec", "template")
|
||||
if !found {
|
||||
return intstr.IntOrString{}, nil, fmt.Errorf("not have spec.template in workload %v", w.GetName())
|
||||
}
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return intstr.IntOrString{}, nil, fmt.Errorf("workload %v convert object err %v", w.GetName(), err)
|
||||
}
|
||||
var template corev1.PodTemplate
|
||||
err = json.Unmarshal(data, &template)
|
||||
if err != nil {
|
||||
return intstr.IntOrString{}, nil, fmt.Errorf("workload %v convert object to PodTemplate err %v", w.GetName(), err)
|
||||
}
|
||||
port := getFirstPort(template.Template.Spec.Containers)
|
||||
if port == 0 {
|
||||
return intstr.IntOrString{}, nil, fmt.Errorf("no port found in workload %v", w.GetName())
|
||||
}
|
||||
return intstr.FromInt(int(port)), template.Labels, nil
|
||||
}
|
||||
|
||||
func getFirstPort(cs []corev1.Container) int32 {
|
||||
//TODO(wonderflow): exclude some sidecars
|
||||
for _, container := range cs {
|
||||
for _, p := range container.Ports {
|
||||
return p.ContainerPort
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *Reconciler) createSelfsignedIssuer(ctx context.Context, routeTrait *v1alpha1.Route) (string, error) {
|
||||
var selfSigned = "selfsigned"
|
||||
var issuer = certmanager.Issuer{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: certmanager.SchemeGroupVersion.String(),
|
||||
Kind: certmanager.IssuerKind,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: selfSigned,
|
||||
Namespace: routeTrait.Namespace,
|
||||
},
|
||||
}
|
||||
err := r.Client.Get(ctx, client.ObjectKey{Name: selfSigned, Namespace: routeTrait.Namespace}, &issuer)
|
||||
if err == nil {
|
||||
return selfSigned, nil
|
||||
}
|
||||
issuer.Spec.SelfSigned = &certmanager.SelfSignedIssuer{}
|
||||
if apierrors.IsNotFound(err) {
|
||||
return selfSigned, r.Client.Create(ctx, &issuer)
|
||||
}
|
||||
return selfSigned, fmt.Errorf("get %s err %v", selfSigned, err)
|
||||
}
|
||||
|
||||
func constructNginxIngress(routeTrait *standardv1alpha1.Route, issuer standardv1alpha1.TLS, service *runtimev1alpha1.TypedReference, port int32) *v1beta1.Ingress {
|
||||
|
||||
var annotations = make(map[string]string)
|
||||
|
||||
// Use nginx-ingress as implementation
|
||||
annotations["kubernetes.io/ingress.class"] = "nginx"
|
||||
|
||||
// SSL
|
||||
var issuerAnn = "cert-manager.io/issuer"
|
||||
if issuer.Type == standardv1alpha1.ClusterIssuer {
|
||||
issuerAnn = "cert-manager.io/cluster-issuer"
|
||||
}
|
||||
annotations[issuerAnn] = issuer.IssuerName
|
||||
|
||||
// Rewrite
|
||||
if routeTrait.Spec.RewriteTarget != "" {
|
||||
annotations["ingress.kubernetes.io/rewrite-target"] = routeTrait.Spec.RewriteTarget
|
||||
}
|
||||
|
||||
// Custom headers
|
||||
var headerSnippet string
|
||||
for k, v := range routeTrait.Spec.CustomHeaders {
|
||||
headerSnippet += fmt.Sprintf("more_set_headers \"%s: %s\";\n", k, v)
|
||||
}
|
||||
if headerSnippet != "" {
|
||||
annotations["nginx.ingress.kubernetes.io/configuration-snippet"] = headerSnippet
|
||||
}
|
||||
backend := routeTrait.Spec.Backend
|
||||
if backend != nil {
|
||||
// Backend protocol
|
||||
if backend.Protocol != "" {
|
||||
annotations["nginx.ingress.kubernetes.io/backend-protocol"] = backend.Protocol
|
||||
}
|
||||
|
||||
//Send timeout
|
||||
if backend.SendTimeout != 0 {
|
||||
annotations["nginx.ingress.kubernetes.io/proxy-send-timeout"] = strconv.Itoa(backend.SendTimeout)
|
||||
}
|
||||
|
||||
//Read timeout
|
||||
if backend.ReadTimeout != 0 {
|
||||
annotations["nginx.ingress.kubernetes.io/proxy‑read‑timeout"] = strconv.Itoa(backend.ReadTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
ingress := &v1beta1.Ingress{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: reflect.TypeOf(v1beta1.Ingress{}).Name(),
|
||||
APIVersion: v1beta1.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: routeTrait.Name,
|
||||
Namespace: routeTrait.Namespace,
|
||||
Annotations: annotations,
|
||||
Labels: oamServiceLabel,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: routeTrait.GetObjectKind().GroupVersionKind().GroupVersion().String(),
|
||||
Kind: routeTrait.GetObjectKind().GroupVersionKind().Kind,
|
||||
UID: routeTrait.GetUID(),
|
||||
Name: routeTrait.GetName(),
|
||||
Controller: pointer.BoolPtr(true),
|
||||
BlockOwnerDeletion: pointer.BoolPtr(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
ingress.Spec.TLS = []v1beta1.IngressTLS{
|
||||
{
|
||||
Hosts: []string{routeTrait.Spec.Host},
|
||||
SecretName: routeTrait.Name + "-cert",
|
||||
},
|
||||
}
|
||||
if routeTrait.Spec.DefaultBackend != nil {
|
||||
ingress.Spec.Backend = routeTrait.Spec.DefaultBackend
|
||||
}
|
||||
ingress.Spec.Rules = []v1beta1.IngressRule{
|
||||
{
|
||||
Host: routeTrait.Spec.Host,
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: routeTrait.Spec.Path,
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: service.Name,
|
||||
ServicePort: intstr.FromInt(int(port)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
return ingress
|
||||
}
|
||||
|
||||
// fetch the service that is associated with the workload
|
||||
func (r *Reconciler) fetchService(ctx context.Context, mLog logr.Logger,
|
||||
workload *unstructured.Unstructured, routeTrait *v1alpha1.Route) (*runtimev1alpha1.TypedReference, bool, int32, error) {
|
||||
// Fetch the child resources list from the corresponding workload
|
||||
resources, err := oamutil.FetchWorkloadChildResources(ctx, mLog, r, workload)
|
||||
if err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
mLog.Error(err, "Error while fetching the workload child resources", "workload kind", workload.GetKind(),
|
||||
"workload name", workload.GetName())
|
||||
}
|
||||
return nil, false, 0, err
|
||||
}
|
||||
|
||||
// find the service that has the port
|
||||
for _, childRes := range resources {
|
||||
if childRes.GetAPIVersion() == corev1.SchemeGroupVersion.String() && childRes.GetKind() == reflect.TypeOf(corev1.Service{}).Name() {
|
||||
svc := &runtimev1alpha1.TypedReference{
|
||||
APIVersion: common.ServiceAPIVersion,
|
||||
Kind: common.ServiceKind,
|
||||
Name: childRes.GetName(),
|
||||
UID: childRes.GetUID(),
|
||||
}
|
||||
ports, _, _ := unstructured.NestedSlice(childRes.Object, "spec", "ports")
|
||||
for _, port := range ports {
|
||||
data, _ := json.Marshal(port)
|
||||
var servicePort corev1.ServicePort
|
||||
_ = json.Unmarshal(data, &servicePort)
|
||||
if routeTrait.Spec.Backend == nil || routeTrait.Spec.Backend.Port.IntValue() == 0 || servicePort.TargetPort == routeTrait.Spec.Backend.Port {
|
||||
return svc, true, servicePort.Port, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false, 0, nil
|
||||
}
|
||||
|
||||
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
r.record = event.NewAPIRecorder(mgr.GetEventRecorderFor("Route")).
|
||||
WithAnnotations("controller", "route")
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&standardv1alpha1.Route{}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
// Setup adds a controller that reconciles MetricsTrait.
|
||||
func Setup(mgr ctrl.Manager) error {
|
||||
reconciler := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Log: ctrl.Log.WithName("Route"),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}
|
||||
return reconciler.SetupWithManager(mgr)
|
||||
}
|
||||
83
pkg/controller/v1alpha1/routes/suite_test.go
Normal file
83
pkg/controller/v1alpha1/routes/suite_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
|
||||
|
||||
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 routes
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
standardv1alpha1 "github.com/oam-dev/kubevela/api/v1alpha1"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
|
||||
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
|
||||
|
||||
var cfg *rest.Config
|
||||
var k8sClient client.Client
|
||||
var testEnv *envtest.Environment
|
||||
|
||||
func TestAPIs(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
RunSpecsWithDefaultAndCustomReporters(t,
|
||||
"Controller Suite",
|
||||
[]Reporter{printer.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
|
||||
|
||||
By("bootstrapping test environment")
|
||||
useExistCluster := true
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
|
||||
UseExistingCluster: &useExistCluster,
|
||||
}
|
||||
|
||||
var err error
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cfg).ToNot(BeNil())
|
||||
|
||||
err = standardv1alpha1.AddToScheme(scheme.Scheme)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
By("tearing down the test environment")
|
||||
err := testEnv.Stop()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
@@ -137,7 +137,7 @@ func (o *DeleteOptions) DeleteApp() (string, error) {
|
||||
err := o.Client.Get(ctx, client.ObjectKey{Name: o.AppName, Namespace: o.Env.Namespace}, &appConfig)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return "", err
|
||||
return "", nil
|
||||
}
|
||||
return "", fmt.Errorf("delete appconfig err %s", err)
|
||||
}
|
||||
@@ -155,7 +155,6 @@ func (o *DeleteOptions) DeleteApp() (string, error) {
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return "", fmt.Errorf("delete appconfig err %s", err)
|
||||
}
|
||||
|
||||
var healthscope corev1alpha2.HealthScope
|
||||
healthscope.Name = application.FormatDefaultHealthScopeName(o.AppName)
|
||||
healthscope.Namespace = o.Env.Namespace
|
||||
|
||||
@@ -9,6 +9,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha2"
|
||||
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
|
||||
v1 "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -36,15 +39,40 @@ func GetEnvByName(name string) (*types.EnvMeta, error) {
|
||||
//Create or update env.
|
||||
//If it does not exist, create it and set to the new env.
|
||||
//If it exists, update it and set to the new env.
|
||||
func CreateOrUpdateEnv(ctx context.Context, c client.Client, envName string, namespace string) (string, error) {
|
||||
func CreateOrUpdateEnv(ctx context.Context, c client.Client, envName string, envArgs *types.EnvMeta) (string, error) {
|
||||
var message = ""
|
||||
var envArgs = types.EnvMeta{
|
||||
Name: envName,
|
||||
Namespace: namespace,
|
||||
}
|
||||
// Create Namespace
|
||||
if err := c.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: envArgs.Namespace}}); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return message, err
|
||||
}
|
||||
|
||||
// Create Issuer For SSL
|
||||
if envArgs.Email != "" {
|
||||
issuerName := "oam-env-" + envArgs.Name
|
||||
if err := c.Create(ctx, &certmanager.Issuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: issuerName, Namespace: envArgs.Namespace},
|
||||
Spec: certmanager.IssuerSpec{
|
||||
IssuerConfig: certmanager.IssuerConfig{
|
||||
ACME: &v1alpha2.ACMEIssuer{
|
||||
Email: envArgs.Email,
|
||||
Server: "https://acme-v02.api.letsencrypt.org/directory",
|
||||
PrivateKey: v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{Name: "oam-env-" + envArgs.Name + ".key"},
|
||||
},
|
||||
Solvers: []v1alpha2.ACMEChallengeSolver{{
|
||||
HTTP01: &v1alpha2.ACMEChallengeSolverHTTP01{
|
||||
Ingress: &v1alpha2.ACMEChallengeSolverHTTP01Ingress{Class: GetStringPointer("nginx")},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return message, err
|
||||
}
|
||||
envArgs.Issuer = issuerName
|
||||
}
|
||||
|
||||
data, err := json.Marshal(envArgs)
|
||||
if err != nil {
|
||||
return message, err
|
||||
@@ -67,42 +95,22 @@ func CreateOrUpdateEnv(ctx context.Context, c client.Client, envName string, nam
|
||||
if err = ioutil.WriteFile(curEnvPath, []byte(envName), 0644); err != nil {
|
||||
return message, err
|
||||
}
|
||||
message = fmt.Sprintf("Create env succeed, current env is " + envName + " namespace is " + envArgs.Namespace + ", use --namespace=<namespace> to specify namespace with env init")
|
||||
message = fmt.Sprintf("ENV %s CREATED, Namespace: %s, Email: %s.", envName, envArgs.Namespace, envArgs.Email)
|
||||
return message, nil
|
||||
}
|
||||
|
||||
//Create env. If env already exists, return error
|
||||
func CreateEnv(ctx context.Context, c client.Client, envName string, namespace string) (string, error) {
|
||||
var message = ""
|
||||
var envArgs = types.EnvMeta{
|
||||
Name: envName,
|
||||
Namespace: namespace,
|
||||
}
|
||||
data, err := json.Marshal(envArgs)
|
||||
if err != nil {
|
||||
return message, err
|
||||
}
|
||||
_, err = GetEnvByName(envName)
|
||||
func GetStringPointer(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
// CreateEnv will only create. If env already exists, return error
|
||||
func CreateEnv(ctx context.Context, c client.Client, envName string, envArgs *types.EnvMeta) (string, error) {
|
||||
_, err := GetEnvByName(envName)
|
||||
if err == nil {
|
||||
message = fmt.Sprintf("Env %s already exist", envName)
|
||||
message := fmt.Sprintf("Env %s already exist", envName)
|
||||
return message, errors.New(message)
|
||||
}
|
||||
if err := c.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: envArgs.Namespace}}); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return message, err
|
||||
}
|
||||
envdir, err := system.GetEnvDir()
|
||||
if err != nil {
|
||||
return message, err
|
||||
}
|
||||
subEnvDir := filepath.Join(envdir, envName)
|
||||
if _, err := system.CreateIfNotExist(subEnvDir); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
if err = ioutil.WriteFile(filepath.Join(subEnvDir, system.EnvConfigName), data, 0644); err != nil {
|
||||
return message, err
|
||||
}
|
||||
message = "Create env succeed"
|
||||
return message, err
|
||||
return CreateOrUpdateEnv(ctx, c, envName, envArgs)
|
||||
}
|
||||
|
||||
//Update Env, if env does not exist, return error
|
||||
|
||||
@@ -148,12 +148,47 @@ func SimplifyCapabilityStruct(capabilityList []types.Capability) []apis.TraitMet
|
||||
return traitList
|
||||
}
|
||||
|
||||
func ValidateAndMutateForCore(traitType, workloadName string, flags *pflag.FlagSet, env *types.EnvMeta) error {
|
||||
switch traitType {
|
||||
case "route":
|
||||
domain, _ := flags.GetString("domain")
|
||||
if domain == "" {
|
||||
if env.Domain == "" {
|
||||
return fmt.Errorf("--domain is required if not set in env")
|
||||
}
|
||||
if strings.HasPrefix(env.Domain, "https://") {
|
||||
env.Domain = strings.TrimPrefix(env.Domain, "https://")
|
||||
}
|
||||
if strings.HasPrefix(env.Domain, "http://") {
|
||||
env.Domain = strings.TrimPrefix(env.Domain, "http://")
|
||||
}
|
||||
if err := flags.Set("domain", workloadName+"."+env.Domain); err != nil {
|
||||
return fmt.Errorf("set flag for vela-core trait('route') err %v, please make sure your template is right", err)
|
||||
}
|
||||
}
|
||||
issuer, _ := flags.GetString("issuer")
|
||||
if issuer == "" {
|
||||
if env.Issuer == "" {
|
||||
return fmt.Errorf("--issuer is required, you can also set email in env and let it generate automatically")
|
||||
}
|
||||
if err := flags.Set("issuer", env.Issuer); err != nil {
|
||||
return fmt.Errorf("set flag for vela-core trait('route') err %v, please make sure your template is right", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//AddOrUpdateTrait attach trait to workload
|
||||
func AddOrUpdateTrait(envName string, appName string, workloadName string, flagSet *pflag.FlagSet, template types.Capability) (*application.Application, error) {
|
||||
func AddOrUpdateTrait(env *types.EnvMeta, appName string, workloadName string, flagSet *pflag.FlagSet, template types.Capability) (*application.Application, error) {
|
||||
err := ValidateAndMutateForCore(template.Name, workloadName, flagSet, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if appName == "" {
|
||||
appName = workloadName
|
||||
}
|
||||
app, err := application.Load(envName, appName)
|
||||
app, err := application.Load(env.Name, appName)
|
||||
if err != nil {
|
||||
return app, err
|
||||
}
|
||||
@@ -181,7 +216,7 @@ func AddOrUpdateTrait(envName string, appName string, workloadName string, flagS
|
||||
if err = app.SetTrait(workloadName, traitAlias, traitData); err != nil {
|
||||
return app, err
|
||||
}
|
||||
return app, app.Save(envName)
|
||||
return app, app.Save(env.Name)
|
||||
}
|
||||
|
||||
func AttachTrait(c *gin.Context, body apis.TraitBody) (string, error) {
|
||||
@@ -204,12 +239,13 @@ func AttachTrait(c *gin.Context, body apis.TraitBody) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
appObj, err = AddOrUpdateTrait(body.EnvName, body.AppGroup, body.WorkloadName, fs, template)
|
||||
// Run step
|
||||
env, err := GetEnvByName(body.EnvName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Run step
|
||||
env, err := GetEnvByName(body.EnvName)
|
||||
|
||||
appObj, err = AddOrUpdateTrait(env, body.AppGroup, body.WorkloadName, fs, template)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/oam-dev/kubevela/api/types"
|
||||
@@ -100,12 +101,28 @@ func HandleTemplate(in *runtime.RawExtension, name, syncDir string) (types.Capab
|
||||
if err != nil {
|
||||
return types.Capability{}, err
|
||||
}
|
||||
if tmp.CueTemplate == "" {
|
||||
return types.Capability{}, errors.New("template not exist in definition")
|
||||
|
||||
var cueTemplate string
|
||||
if tmp.CueTemplateURI != "" {
|
||||
res, err := http.Get(tmp.CueTemplateURI)
|
||||
if err != nil {
|
||||
return types.Capability{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
b, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return types.Capability{}, err
|
||||
}
|
||||
cueTemplate = string(b)
|
||||
} else {
|
||||
if tmp.CueTemplate == "" {
|
||||
return types.Capability{}, errors.New("template not exist in definition")
|
||||
}
|
||||
cueTemplate = tmp.CueTemplate
|
||||
}
|
||||
_, _ = system.CreateIfNotExist(syncDir)
|
||||
filePath := filepath.Join(syncDir, name+".cue")
|
||||
err = ioutil.WriteFile(filePath, []byte(tmp.CueTemplate), 0644)
|
||||
err = ioutil.WriteFile(filePath, []byte(cueTemplate), 0644)
|
||||
if err != nil {
|
||||
return types.Capability{}, err
|
||||
}
|
||||
|
||||
@@ -64,6 +64,37 @@ var _ = Describe("DefinitionFiles", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
websvc := types.Capability{
|
||||
Name: "webservice",
|
||||
Type: types.TypeWorkload,
|
||||
CueTemplateURI: "https://raw.githubusercontent.com/oam-dev/kubevela/master/vela-templates/web-service.cue",
|
||||
Parameters: []types.Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Required: true,
|
||||
Default: "",
|
||||
Type: cue.StringKind,
|
||||
},
|
||||
{
|
||||
Name: "image",
|
||||
Type: cue.StringKind,
|
||||
Default: "",
|
||||
Short: "i",
|
||||
Required: true,
|
||||
Usage: "specify app image",
|
||||
},
|
||||
{
|
||||
Name: "port",
|
||||
Type: cue.IntKind,
|
||||
Short: "p",
|
||||
Default: int64(6379),
|
||||
Usage: "specify port for container",
|
||||
},
|
||||
},
|
||||
CrdName: "webservice.testapps",
|
||||
}
|
||||
|
||||
req, _ := labels.NewRequirement("usecase", selection.Equals, []string{"forplugintest"})
|
||||
selector := labels.NewSelector().Add(*req)
|
||||
|
||||
@@ -89,7 +120,7 @@ var _ = Describe("DefinitionFiles", func() {
|
||||
workloadDefs[i].CueTemplate = ""
|
||||
workloadDefs[i].DefinitionPath = ""
|
||||
}
|
||||
Expect(workloadDefs).Should(Equal([]types.Capability{deployment}))
|
||||
Expect(workloadDefs).Should(Equal([]types.Capability{deployment, websvc}))
|
||||
})
|
||||
It("getall", func() {
|
||||
alldef, err := GetCapabilitiesFromCluster(context.Background(), DefinitionNamespace, k8sClient, definitionDir, selector)
|
||||
@@ -99,6 +130,6 @@ var _ = Describe("DefinitionFiles", func() {
|
||||
alldef[i].CueTemplate = ""
|
||||
alldef[i].DefinitionPath = ""
|
||||
}
|
||||
Expect(alldef).Should(Equal([]types.Capability{deployment, route}))
|
||||
Expect(alldef).Should(Equal([]types.Capability{deployment, websvc, route}))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -37,7 +37,7 @@ var k8sClient client.Client
|
||||
var testEnv *envtest.Environment
|
||||
var definitionDir string
|
||||
var td v1alpha2.TraitDefinition
|
||||
var wd v1alpha2.WorkloadDefinition
|
||||
var wd, websvcWD v1alpha2.WorkloadDefinition
|
||||
|
||||
func TestAPIs(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
@@ -138,6 +138,14 @@ var _ = BeforeSuite(func(done Done) {
|
||||
logf.Log.Info("Creating workload definition", "data", wd)
|
||||
Expect(k8sClient.Create(ctx, &wd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
websvcWorkloadData, err := ioutil.ReadFile("testdata/websvcWorkloadDef.yaml")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
Expect(yaml.Unmarshal(websvcWorkloadData, &websvcWD)).Should(BeNil())
|
||||
websvcWD.Namespace = DefinitionNamespace
|
||||
logf.Log.Info("Creating workload definition whose CUE template from remote", "data", &websvcWD)
|
||||
Expect(k8sClient.Create(ctx, &websvcWD)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
|
||||
11
pkg/plugins/testdata/websvcWorkloadDef.yaml
vendored
Normal file
11
pkg/plugins/testdata/websvcWorkloadDef.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: webservice.testapps
|
||||
labels:
|
||||
usecase: forplugintest
|
||||
spec:
|
||||
definitionRef:
|
||||
name: webservice.testapps
|
||||
extension:
|
||||
templateURI: "https://raw.githubusercontent.com/oam-dev/kubevela/master/vela-templates/web-service.cue"
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
type Environment struct {
|
||||
EnvName string `json:"envName" binding:"required,min=1,max=32"`
|
||||
Namespace string `json:"namespace" binding:"required,min=1,max=32"`
|
||||
Email string `json:"email"`
|
||||
Domain string `json:"domain"`
|
||||
Current string `json:"current,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/oam-dev/kubevela/api/types"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
@@ -23,9 +24,16 @@ func CreateEnv(c *gin.Context) {
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
ctx := util.GetContext(c)
|
||||
kubeClient := c.MustGet("KubeClient")
|
||||
message, err := oam.CreateEnv(ctx, kubeClient.(client.Client), name, namespace)
|
||||
message, err := oam.CreateEnv(ctx, kubeClient.(client.Client), name, &types.EnvMeta{
|
||||
Name: name,
|
||||
Current: environment.Current,
|
||||
Namespace: namespace,
|
||||
Email: environment.Email,
|
||||
Domain: environment.Domain,
|
||||
})
|
||||
util.AssembleResponse(c, message, err)
|
||||
}
|
||||
|
||||
|
||||
20
vela-templates/route.cue
Normal file
20
vela-templates/route.cue
Normal file
@@ -0,0 +1,20 @@
|
||||
#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
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
#Template: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ContainerizedWorkload"
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Containerized"
|
||||
metadata:
|
||||
name: webservice.name
|
||||
spec: {
|
||||
containers: [{
|
||||
image: webservice.image
|
||||
name: webservice.name
|
||||
ports: [{
|
||||
containerPort: webservice.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
replicas: 1
|
||||
podSpec: {
|
||||
containers: [{
|
||||
image: webservice.image
|
||||
name: webservice.name
|
||||
ports: [{
|
||||
containerPort: webservice.port
|
||||
protocol: "TCP"
|
||||
name: "default"
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
webservice: {
|
||||
name: string
|
||||
// +usage=specify app image
|
||||
|
||||
Reference in New Issue
Block a user