Compare commits

..

36 Commits

Author SHA1 Message Date
Sun Jianbo
2ffd56a993 Merge pull request #291 from wonderflow/zip
use compressed file for release
2020-09-17 15:07:00 +08:00
天元
e0d7eed9d3 update readme 2020-09-17 14:52:33 +08:00
Sun Jianbo
b0ec26bca0 Merge pull request #290 from wonderflow/image
update image tag to latest and change image pull policy
2020-09-17 14:44:19 +08:00
天元
2116c9ad0e use compressed file for release 2020-09-17 14:42:00 +08:00
天元
d02d6675ab update image tag to latest and change image pull policy 2020-09-17 14:08:33 +08:00
Sun Jianbo
1a43c0e540 Merge pull request #276 from wonderflow/routetrait
add route trait as vela api gateway
2020-09-17 14:06:00 +08:00
Sun Jianbo
e6b46c6c1b Merge pull request #288 from mosesyou/bugfix-comp-run
fix `vela comp run` panic when args empty
2020-09-17 11:57:27 +08:00
mosesyou
387afaa2c2 fix vela comp run panic when args empty 2020-09-17 11:08:55 +08:00
Ryan Zhang
f9fea8b53a Merge pull request #285 from wonderflow/dockerpackage
Add Image upload for Docker Hub and Github Docker package
2020-09-16 19:32:07 -07:00
Hongchao Deng
21c58c0aa2 Merge pull request #286 from oam-dev/warning
Add warning for not play around it for now
2020-09-16 16:43:43 -07:00
Lei Zhang (Harry)
d5a8b54503 Add warning for not play around it for now 2020-09-16 16:39:06 -07:00
天元
c390928368 split release and daily build 2020-09-16 22:57:56 +08:00
天元
c4897008dc add docker image build 2020-09-16 21:50:56 +08:00
天元
fe09a85e53 add docker build 2020-09-16 21:34:20 +08:00
天元
1b9ee5c882 add route trait as vela apigate 2020-09-16 19:42:01 +08:00
Sun Jianbo
cc5e3dc6a2 Merge pull request #281 from oam-dev/install-dependency
vela core install dependencies
2020-09-16 17:29:02 +08:00
Ryan Zhang
8a9470b9b3 fix build error 2020-09-16 01:12:50 -07:00
Ryan Zhang
e0a21b2bd4 Merge pull request #268 from wonderflow/template
update chart and refactor default workload type
2020-09-15 22:52:32 -07:00
Ryan Zhang
dfeff25a31 install dependencies 2020-09-15 22:04:52 -07:00
天元
9510db0ace update chart and refactor default workload 2020-09-16 12:09:29 +08:00
Sun Jianbo
1576d1ff2b Merge pull request #280 from hanxie-crypto/feature03
Update code and page optimization ,fix bug
2020-09-16 11:37:03 +08:00
hanxie
764e1bbb79 Update code and page optimization ,fix bug 2020-09-16 11:03:16 +08:00
Sun Jianbo
83e718c4cd Merge pull request #261 from hanxie-crypto/feature01
update dashboard ux
2020-09-16 10:50:13 +08:00
Ryan Zhang
16fd51a213 adjust the helm setting and README (#269)
* add dependancy installer and fix e2e

* fix build

* add kubebuilder
2020-09-15 15:08:17 +08:00
Sun Jianbo
1744b4752c Merge pull request #270 from zzxwill/website
Remove website related files
2020-09-13 11:44:31 +08:00
zzxwill
7ad7848856 Remove website related files
Removed website related files and planning moving
the website to oam-dev/kubevela.io
2020-09-13 11:29:44 +08:00
Sun Jianbo
6df4e192f9 Merge pull request #267 from zzxwill/env
Rename `vela env switch` to `vela env set`
2020-09-11 21:40:36 +08:00
zzxwill
bc3169a1b5 Rename vela env switch to vela env set
Renaming and update cli documentation
Fix #235
2020-09-11 19:46:45 +08:00
Sun Jianbo
97f4d516fb Merge pull request #260 from wonderflow/cleanup
Clean Up Projects
2020-09-11 18:24:28 +08:00
天元
5328a7d6b9 update readme 2020-09-11 18:15:38 +08:00
天元
1e4a1159b5 clean up 2020-09-11 17:52:14 +08:00
hanxie
b026cf20f4 update dashboard ux 2020-09-11 14:43:08 +08:00
天元
044df63770 change go package to oam-dev/kubevela 2020-09-11 12:29:12 +08:00
天元
edce904df6 fix create release workflow 2020-09-11 00:30:39 +08:00
Sun Jianbo
159ca172bf Merge pull request #258 from wonderflow/fixci
fix release workflow
2020-09-11 00:12:20 +08:00
天元
f1497c3fc2 fix release workflow 2020-09-11 00:10:46 +08:00
286 changed files with 5473 additions and 4052 deletions

25
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Publish to Registry
on:
push:
branches:
- master
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Publish to Github Docker Package Registry
uses: elgohr/Publish-Docker-Github-Action@2.21
with:
name: oam-dev/kubevela/vela-core
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: docker.pkg.github.com
tags: "latest"
- name: Publish to Docker Hub Registry
uses: elgohr/Publish-Docker-Github-Action@2.21
with:
name: oamdev/vela-core
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tags: "latest"

View File

@@ -34,9 +34,15 @@ jobs:
with:
version: "v0.7.0"
- name: install Kubebuilder
uses: RyanSiu1995/kubebuilder-action@v1
- name: Run Make
run: make
- name: Run Make Manager
run: make manager
- name: Run e2e tests
run: |
make e2e-setup

View File

@@ -30,5 +30,8 @@ jobs:
with:
version: "v0.7.0"
- name: install Kubebuilder
uses: RyanSiu1995/kubebuilder-action@v1
- name: Run Make test
run: make test

View File

@@ -1,19 +1,17 @@
name: Release
on:
release:
types: [published]
branches:
- master
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
- "v*"
jobs:
build:
name: Build
runs-on: ubuntu-latest
env:
VELA_VERSION: ${{ github.ref }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
@@ -22,40 +20,90 @@ jobs:
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
with:
ref: 'master'
- name: Use Node.js 10.x
uses: actions/setup-node@v1
with:
node-version: 10.x
- run: npm install
working-directory: ./dashboard
- name: Run Make Realse
run: make release
- name: Run npm install
run: make npm-install
- name: Run npm build
run: make npm-build
- name: Run generate-source
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
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
- name: Upload Linux
- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}
- 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-${{ github.event.release.tag_name }}-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-${{ github.event.release.tag_name }}-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
asset_name: vela-${{ github.event.release.tag_name }}-windows-amd64
asset_content_type: binary/octet-stream
asset_path: ./_bin/vela-darwin-amd64.tar.gz
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-darwin-amd64.tar.gz
asset_content_type: binary/octet-stream
- name: Upload MacOS zip
uses: actions/upload-release-asset@v1.0.2
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./_bin/vela-darwin-amd64.zip
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-darwin-amd64.zip
asset_content_type: binary/octet-stream
- name: Upload Windows tar.gz
uses: actions/upload-release-asset@v1.0.2
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./_bin/vela-windows-amd64.tar.gz
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-windows-amd64.tar.gz
asset_content_type: binary/octet-stream
- name: Upload Windows zip
uses: actions/upload-release-asset@v1.0.2
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./_bin/vela-windows-amd64.zip
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-windows-amd64.zip
asset_content_type: binary/octet-stream
- name: Upload Checksums
uses: actions/upload-release-asset@v1.0.2
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./_bin/sha256sums.txt
asset_name: sha256sums.txt
asset_content_type: text/plain
- name: Publish to Github Docker Package Registry
uses: elgohr/Publish-Docker-Github-Action@2.21
with:
name: oam-dev/kubevela/vela-core
username: $GITHUB_ACTOR
password: ${{ secrets.GITHUB_TOKEN }}
registry: docker.pkg.github.com
tags: "${{ steps.get_version.outputs.VERSION }}"
- name: Publish to Docker Hub Registry
uses: elgohr/Publish-Docker-Github-Action@2.21
with:
name: oamdev/vela-core
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tags: "${{ steps.get_version.outputs.VERSION }}"

2
.gitignore vendored
View File

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

View File

@@ -1,7 +1,7 @@
# CONTRIBUTING
This doc explains how to set up a development environment, so you can get started
contributing to `RudrX` or build a PoC (Proof of Concept).
contributing to `kubevela` or build a PoC (Proof of Concept).
## Prerequisites
@@ -9,14 +9,14 @@ contributing to `RudrX` or build a PoC (Proof of Concept).
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/cloud-native-application/RudrX/blob/master/DEVELOPMENT.md#e2e-test))
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.
## Build
* Clone this project
```shell script
git clone git@github.com:cloud-native-application/RudrX.git
git clone git@github.com:oam-dev/kubevela.git
```
* Install Template CRD into your cluster
@@ -76,33 +76,33 @@ Will run 5 of 5 specs
Trait env init
should print env initiation successful message
/Users/zhouzhengxi/Programming/golang/src/github.com/zzxwill/RudrX/e2e/commonContext.go:14
kubevela/e2e/commonContext.go:14
Create env succeed, current env is default
------------------------------
Trait env switch
should show env switch message
/Users/zhouzhengxi/Programming/golang/src/github.com/zzxwill/RudrX/e2e/commonContext.go:40
Switch 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
/Users/zhouzhengxi/Programming/golang/src/github.com/zzxwill/RudrX/e2e/commonContext.go:76
kubevela/e2e/commonContext.go:76
Creating AppConfig app-trait-basic
SUCCEED
------------------------------
Trait rudr attach trait
Trait kubevela attach trait
should print successful attached information
/Users/zhouzhengxi/Programming/golang/src/github.com/zzxwill/RudrX/e2e/trait/trait_test.go:24
kubevela/e2e/trait/trait_test.go:24
Applying trait for app
Succeeded!
------------------------------
Trait delete
should print successful deletion information
/Users/zhouzhengxi/Programming/golang/src/github.com/zzxwill/RudrX/e2e/commonContext.go:85
kubevela/e2e/commonContext.go:85
Deleting AppConfig "app-trait-basic"
DELETE SUCCEED

View File

@@ -2,12 +2,13 @@
VELA_VERSION ?= 0.1.0
# Repo info
GIT_COMMIT ?= git-$(shell git rev-parse --short HEAD)
VELA_VERSION_VAR := github.com/cloud-native-application/rudrx/version.VelaVersion
VELA_GITVERSION_VAR := github.com/cloud-native-application/rudrx/version.GitRevision
LDFLAGS ?= "-X $(VELA_VERSION_VAR)=$(VELA_VERSION) -X $(VELA_GITVERSION_VAR)=$(GIT_COMMIT)"
VELA_VERSION_VAR := github.com/oam-dev/kubevela/version.VelaVersion
VELA_GITVERSION_VAR := github.com/oam-dev/kubevela/version.GitRevision
LDFLAGS ?= "-X $(VELA_VERSION_VAR)=$(VELA_VERSION) -X $(VELA_GITVERSION_VAR)=$(GIT_COMMIT) -X main.chartTGZSource=$$(cat -) -s -w"
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))
@@ -24,13 +25,29 @@ test: fmt vet lint
# Build manager binary
build: fmt vet lint
go build -o bin/vela -ldflags ${LDFLAGS} cmd/vela/main.go
go run hack/chart/generate.go | go build -o bin/vela -ldflags ${LDFLAGS} cmd/vela/main.go
release: fmt vet lint
# TODO: build vela core chart into vela binary
npm-build:
cd dashboard && npm run build && cd ./..
npm-install:
cd dashboard && npm install && cd ./..
generate-source:
go run hack/frontend/source.go
GO111MODULE=on CGO_ENABLED=0 $(GOX) -ldflags $(LDFLAGS) -parallel=3 -output="bin/vela-{{.OS}}-{{.Arch}}" -osarch='$(TARGETS)' cmd/vela/main.go
cross-build:
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
@@ -45,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
@@ -82,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
@@ -104,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

View File

@@ -1,3 +1,7 @@
domain: oam.dev
repo: github.com/cloud-native-application/rudrx
repo: github.com/oam-dev/kubevela
resources:
- group: standard
kind: Route
version: v1alpha1
version: "2"

151
README.md
View File

@@ -2,140 +2,67 @@
The Open Application Platform based on Kubernetes and OAM.
## Install `vela` binary
:rotating_light: **Warning: The project is still under heavy development, its UI/UX is also for demo purpose, please don't look inside unless you know what you are doing** Please contact @wonderflow if you are interested in its full story or becoming one of the boostrap contributors/maintainers. :rotating_light:
## Install
### Prerequisites
- Kubernetes cluster running Kubernetes v1.15.0 or greater
- kubectl current context is configured for the target cluster install
- ```kubectl config current-context```
### Get the Vela CLI
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.
```shell
sudo mv ./vela /usr/local/bin/vela
```
### Install Vela Core
```shell script
git clone git@github.com:cloud-native-application/RudrX.git
cd RudrX
make
mv bin/vela /usr/local/bin
$ vela install
```
## Vela commands
This command will install vela core controller into your K8s cluster, along with built-in workloads and traits.
```shell script
$ vela
✈️ A Micro App Platform for Kubernetes.
## Demos
Usage:
vela [flags]
vela [command]
Available Commands:
Getting Started:
env Manage application environments
delete Delete environment
init <envName> Create environment and switch to it
ls List all environments
switch switch to another environment
version Prints out build version information
Applications:
app Manage applications with ls, show, delete, run
delete <APPLICATION_NAME> Delete Applications
ls List applications with workloads, traits, status and created time
run <APPLICATION_BUNDLE_NAME> [args] Run a bundle of OAM Applications
show <APPLICATION-NAME> get details of your app, including its workload and trait
status <APPLICATION-NAME> get status of an application, including its workload and trait
comp Manage Components
delete <ComponentName> Delete Component From Application
ls List applications with workloads, traits, status and created time
run [args] Init and Run workloads
show <COMPONENT-NAME> get component detail, including arguments of workload and trait
Traits:
rollout <appname> [args] Attach rollout trait to an app
route <appname> [args] Attach route trait to an app
scale <appname> [args] Attach scale trait to an app
Want more? < install more capabilities by `vela cap` >
Others:
cap Capability Management with config, list, add, remove capabilities
add <center>/<name> Add capability into cluster
center <command> Manage Capability Center with config, sync, list
ls [centerName] List all capabilities in center
remove <name> Remove capability from cluster
System:
completion < bash | zsh > Output shell completion code for the specified shell (bash or zsh...
bash Generate the autocompletion script for Vela for the bash shell....
zsh Generate the autocompletion script for Vela for the zsh shell.
T...
dashboard Setup API Server and launch Dashboard
system system management utilities
info show vela client and cluster version
init Install OAM runtime and vela builtin capabilities.
update Refresh and sync definition files from cluster
```
#### env
#### Check workloads
```
$ vela env init test --namespace test
Create env succeed, current env is test
$ vela env ls
NAME CURRENT NAMESPACE
default default
test * test
$ vela env switch default
Switch env succeed, current env is default
$ vela env delete test
test deleted
$ vela env delete default
Error: you can't delete current using default
$ vela workloads
NAME DEFINITION
backend containerizeds.standard.oam.dev
task jobs
webservice containerizeds.standard.oam.dev
```
#### workload run
```shell script
$ vela comp run -t deployment app123 -p 80 --image nginx:1.9.4
$ vela comp run -t webservice app123 -p 80 --image nginx:1.9.4
Creating AppConfig app123
SUCCEED
```
#### app
```
$ vela app ls
app123
poc08032042
poc1039
$ vela comp status app123
$ vela comp ls
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
ccc ccc deployment Deployed 2020-08-27 10:56:41 +0800 CST
com1 com1 Deployed 2020-08-26 16:45:50 +0800 CST
com2 com1 Deployed 2020-08-26 16:45:50 +0800 CST
myapp myapp route,scale Deployed 2020-08-19 15:11:17 +0800 CST
app123 app123 deployment Deployed 2020-08-27 10:56:41 +0800 CST
```
#### app
```
$ vela app ls
app123
$ vela app delete app123
Deleting AppConfig "app123"
DELETE SUCCEED
```
#### WorkloadDefinitions/TraitDefinitions
```shell script
$ vela traits
NAME ALIAS DEFINITION APPLIES TO STATUS
manualscalertraits.core.oam.dev manualscalertraits.core.oam.dev core.oam.dev/v1alpha2.ContainerizedWorkload -
simplerollouttraits.extend.oam.dev simplerollouttraits.extend.oam.dev core.oam.dev/v1alpha2.ContainerizedWorkload, deployments.... -
$ vela workloads
NAME SHORT DEFINITION
containerizedworkloads.core.oam.dev containerizedworkloads.core.oam.dev
deployments.apps deployments.apps
```
#### Auto-Completion
##### bash
@@ -164,8 +91,8 @@ $ vela completion zsh > "${fpath[1]}/_vela"
### Clean your environment
```shell script
$ helm uninstall core-runtime -n oam-system
release "core-runtime" uninstalled
$ helm uninstall vela-core -n oam-system
release "vela-core" uninstalled
```
```shell script

View File

@@ -20,10 +20,8 @@ import (
"encoding/json"
"fmt"
"github.com/spf13/pflag"
"cuelang.org/go/cue"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -58,10 +56,11 @@ type Capability struct {
}
type Chart struct {
Repo string `json:"repo"`
URL string `json:"url"`
Name string `json:"name"`
Version string `json:"version"`
Repo string `json:"repo"`
URL string `json:"url"`
Name string `json:"name"`
Namespace string `json:"namespace,omitempty"`
Version string `json:"version"`
}
type Installation struct {

View File

@@ -2,10 +2,8 @@ package types
const (
DefaultOAMNS = "oam-system"
DefaultOAMReleaseName = "core-runtime"
DefaultOAMRuntimeChartName = "oam-kubernetes-runtime"
DefaultOAMRepoName = "crossplane-master"
DefaultOAMRepoURL = "https://charts.crossplane.io/master"
DefaultOAMReleaseName = "vela-core"
DefaultOAMRuntimeChartName = "vela-core"
DefaultOAMVersion = ">0.0.0-0"
DefaultEnvName = "default"

138
api/v1alpha1/route_types.go Normal file
View 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
}

View File

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

2
charts/third_party/cert-manager/download-cert-manager.sh vendored Executable file → Normal file
View File

@@ -17,7 +17,7 @@
#!/usr/bin/env bash
# Download and unpack cert-manager
CERT_MANAGER_VERSION=0.16.1
CERT_MANAGER_VERSION=1.0.0
ARCHIVE_DOWNLOAD_URL=https://github.com/jetstack/cert-manager/archive/v${CERT_MANAGER_VERSION}.tar.gz
YAML_URL=https://github.com/jetstack/cert-manager/releases/download/v${CERT_MANAGER_VERSION}/cert-manager.yaml

View File

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

View File

@@ -0,0 +1,465 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
creationTimestamp: null
name: servicemonitors.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
kind: ServiceMonitor
listKind: ServiceMonitorList
plural: servicemonitors
singular: servicemonitor
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: ServiceMonitor defines monitoring for a set of services.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Specification of desired Service selection for target discovery
by Prometheus.
properties:
endpoints:
description: A list of endpoints allowed as part of this ServiceMonitor.
items:
description: Endpoint defines a scrapeable endpoint serving Prometheus
metrics.
properties:
basicAuth:
description: 'BasicAuth allow an endpoint to authenticate over
basic authentication More info: https://prometheus.io/docs/operating/configuration/#endpoints'
properties:
password:
description: The secret in the service monitor namespace
that contains the password for authentication.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
username:
description: The secret in the service monitor namespace
that contains the username for authentication.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
type: object
bearerTokenFile:
description: File to read bearer token for scraping targets.
type: string
bearerTokenSecret:
description: Secret to mount to read bearer token for scraping
targets. The secret needs to be in the same namespace as the
service monitor and accessible by the Prometheus Operator.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
honorLabels:
description: HonorLabels chooses the metric's labels on collisions
with target labels.
type: boolean
honorTimestamps:
description: HonorTimestamps controls whether Prometheus respects
the timestamps present in scraped data.
type: boolean
interval:
description: Interval at which metrics should be scraped
type: string
metricRelabelings:
description: MetricRelabelConfigs to apply to samples before
ingestion.
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
description: Action to perform based on regex matching.
Default is 'replace'
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
params:
additionalProperties:
items:
type: string
type: array
description: Optional HTTP URL parameters
type: object
path:
description: HTTP path to scrape for metrics.
type: string
port:
description: Name of the service port this endpoint refers to.
Mutually exclusive with targetPort.
type: string
proxyUrl:
description: ProxyURL eg http://proxyserver:2195 Directs scrapes
to proxy through this endpoint.
type: string
relabelings:
description: 'RelabelConfigs to apply to samples before scraping.
More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
description: Action to perform based on regex matching.
Default is 'replace'
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
scheme:
description: HTTP scheme to use for scraping.
type: string
scrapeTimeout:
description: Timeout after which the scrape is ended
type: string
targetPort:
anyOf:
- type: integer
- type: string
description: Name or number of the pod port this endpoint refers
to. Mutually exclusive with port.
x-kubernetes-int-or-string: true
tlsConfig:
description: TLS configuration to use when scraping the endpoint
properties:
ca:
description: Stuct containing the CA cert to use for the
targets.
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
caFile:
description: Path to the CA cert in the Prometheus container
to use for the targets.
type: string
cert:
description: Struct containing the client cert file for
the targets.
properties:
configMap:
description: ConfigMap containing data to use for the
targets.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap or its
key must be defined
type: boolean
required:
- key
type: object
secret:
description: Secret containing data to use for the targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret or its key
must be defined
type: boolean
required:
- key
type: object
type: object
certFile:
description: Path to the client cert file in the Prometheus
container for the targets.
type: string
insecureSkipVerify:
description: Disable target certificate validation.
type: boolean
keyFile:
description: Path to the client key file in the Prometheus
container for the targets.
type: string
keySecret:
description: Secret containing the client key file for the
targets.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
serverName:
description: Used to verify the hostname for the targets.
type: string
type: object
type: object
type: array
jobLabel:
description: The label to use to retrieve the job name from.
type: string
namespaceSelector:
description: Selector to select which namespaces the Endpoints objects
are discovered from.
properties:
any:
description: Boolean describing whether all namespaces are selected
in contrast to a list restricting them.
type: boolean
matchNames:
description: List of namespace names.
items:
type: string
type: array
type: object
podTargetLabels:
description: PodTargetLabels transfers labels on the Kubernetes Pod
onto the target.
items:
type: string
type: array
sampleLimit:
description: SampleLimit defines per-scrape limit on number of scraped
samples that will be accepted.
format: int64
type: integer
selector:
description: Selector to select Endpoints objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that
contains values, a key, and an operator that relates the key
and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to
a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
type: string
values:
description: values is an array of string values. If the
operator is In or NotIn, the values array must be non-empty.
If the operator is Exists or DoesNotExist, the values
array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator
is "In", and the values array contains only "value". The requirements
are ANDed.
type: object
type: object
targetLabels:
description: TargetLabels transfers labels on the Kubernetes Service
onto the target.
items:
type: string
type: array
required:
- endpoints
- selector
type: object
required:
- spec
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View 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: []

View File

@@ -0,0 +1,36 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: backend
annotations:
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
definition.oam.dev/kind: "Containerized"
spec:
definitionRef:
name: containerizeds.standard.oam.dev
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
- apiVersion: v1
kind: Service
extension:
template: |
#Template: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "Containerized"
metadata:
name: backend.name
spec: {
containers: [{
image: backend.image
name: backend.name
}]
}
}
backend: {
name: string
// +usage=specify app image
// +short=i
image: string
}

View File

@@ -1,42 +0,0 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: backend-service
annotations:
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
definition.oam.dev/kind: "ContainerizedWorkload"
spec:
definitionRef:
name: containerizeds.standard.oam.dev
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
- apiVersion: v1
kind: Service
extension:
template: |
#Template: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ContainerizedWorkload"
metadata: name: containerized.name
spec: {
containers: [{
image: containerized.image
name: containerized.name
ports: [{
containerPort: containerized.port
protocol: "TCP"
name: "default"
}]
}]
}
}
containerized: {
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}

View File

@@ -15,7 +15,6 @@ metadata:
app: cainjector
app.kubernetes.io/name: cainjector
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
---
@@ -30,7 +29,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
---
@@ -44,7 +42,6 @@ metadata:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
---
# Source: cert-manager/templates/cainjector-rbac.yaml
@@ -56,7 +53,6 @@ metadata:
app: cainjector
app.kubernetes.io/name: cainjector
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
- apiGroups: ["cert-manager.io"]
@@ -86,7 +82,6 @@ metadata:
app: cainjector
app.kubernetes.io/name: cainjector
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -108,7 +103,6 @@ metadata:
app: cainjector
app.kubernetes.io/name: cainjector
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
# Used for leader election by the controller
@@ -130,7 +124,6 @@ metadata:
app: cainjector
app.kubernetes.io/name: cainjector
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -155,7 +148,6 @@ metadata:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -181,7 +173,6 @@ metadata:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -203,7 +194,6 @@ metadata:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
- apiGroups:
@@ -226,7 +216,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
# Used for leader election by the controller
@@ -248,7 +237,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -271,7 +259,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
- apiGroups: ["cert-manager.io"]
@@ -298,7 +285,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
- apiGroups: ["cert-manager.io"]
@@ -325,7 +311,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
- apiGroups: ["cert-manager.io"]
@@ -361,7 +346,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
- apiGroups: ["acme.cert-manager.io"]
@@ -400,7 +384,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
# Use to update challenge resource status
@@ -452,7 +435,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rules:
- apiGroups: ["cert-manager.io"]
@@ -484,7 +466,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -505,7 +486,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -526,7 +506,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -547,7 +526,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -568,7 +546,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -589,7 +566,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
roleRef:
apiGroup: rbac.authorization.k8s.io
@@ -610,7 +586,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rbac.authorization.k8s.io/aggregate-to-view: "true"
rbac.authorization.k8s.io/aggregate-to-edit: "true"
@@ -630,7 +605,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rbac.authorization.k8s.io/aggregate-to-admin: "true"
@@ -651,7 +625,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
spec:
type: ClusterIP
@@ -674,7 +647,6 @@ metadata:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
spec:
type: ClusterIP
@@ -686,7 +658,6 @@ spec:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
---
# Source: cert-manager/templates/cainjector-deployment.yaml
apiVersion: apps/v1
@@ -698,7 +669,6 @@ metadata:
app: cainjector
app.kubernetes.io/name: cainjector
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
spec:
replicas: 1
@@ -707,14 +677,12 @@ spec:
app: cainjector
app.kubernetes.io/name: cainjector
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
template:
metadata:
labels:
app: cainjector
app.kubernetes.io/name: cainjector
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
annotations:
spec:
@@ -746,7 +714,6 @@ metadata:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
spec:
replicas: 1
@@ -755,14 +722,12 @@ spec:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
template:
metadata:
labels:
app: cert-manager
app.kubernetes.io/name: cert-manager
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
annotations:
prometheus.io/path: "/metrics"
@@ -808,7 +773,6 @@ metadata:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
spec:
replicas: 1
@@ -817,14 +781,12 @@ spec:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
template:
metadata:
labels:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
annotations:
spec:
@@ -873,7 +835,6 @@ metadata:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
annotations:
cert-manager.io/inject-ca-from-secret: "cert-manager/cert-manager-webhook-tls"
@@ -947,7 +908,6 @@ metadata:
app: webhook
app.kubernetes.io/name: webhook
app.kubernetes.io/instance: cert-manager
app.kubernetes.io/managed-by: Tiller
helm.sh/chart: cert-manager-v0.12.0
annotations:
cert-manager.io/inject-ca-from-secret: "cert-manager/cert-manager-webhook-tls"

View File

@@ -2,6 +2,10 @@ apiVersion: core.oam.dev/v1alpha2
kind: ScopeDefinition
metadata:
name: healthscopes.core.oam.dev
annotations:
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
definition.oam.dev/kind: HealthScope
namespace: default
spec:
workloadRefsPath: spec.workloadRefs
allowComponentOverlap: true

View File

@@ -5,6 +5,7 @@ metadata:
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
definition.oam.dev/kind: ManualScalerTrait
name: manualscalertraits.core.oam.dev
namespace: default
spec:
appliesToWorkloads:
- core.oam.dev/v1alpha2.ContainerizedWorkload

View File

@@ -2,6 +2,10 @@ apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: metricstraits.standard.oam.dev
namespace: default
annotations:
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
definition.oam.dev/kind: MetricsTrait
spec:
appliesToWorkloads:
- containerizedworkloads.core.oam.dev
@@ -10,32 +14,4 @@ spec:
- statefulsets.apps
definitionRef:
name: metricstraits.standard.oam.dev
workloadRefPath: spec.workloadRef
extension:
template: |
#Template: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "MetricsTrait"
metadata:
name: metricstraits.name
spec: {
containers: [{
image: containerized.image
name: containerized.name
ports: [{
containerPort: containerized.port
protocol: "TCP"
name: "default"
}]
}]
}
}
containerized: {
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}
workloadRefPath: spec.workloadRef

View File

@@ -14,34 +14,34 @@ spec:
- logging
template: |
#Template: {
apiVersion: "v1"
kind: "Job"
metadata: name: task
spec: {
parallelism: taskSpec.count
completions: taskSpec.count
template:
spec:
containers: [{
image: taskSpec.image
name: taskSpec.name
ports: [{
containerPort: taskSpec.port
protocol: "TCP"
name: "default"
}]
}]
}
}
taskSpec: {
// +usage=specify number of tasks to run in parallel
// +short=c
count: *1 | int
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}
apiVersion: "v1"
kind: "Job"
metadata: name: task.name
spec: {
parallelism: task.count
completions: task.count
template:
spec:
containers: [{
image: task.image
name: task.name
ports: [{
containerPort: task.port
protocol: "TCP"
name: "default"
}]
}]
}
}
task: {
// +usage=specify number of tasks to run in parallel
// +short=c
count: *1 | int
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: vela-config
namespace: default
data:
certificates.cert-manager.io: |
{
"repo": "jetstack",
"urL": "https://charts.jetstack.io",
"name": "cert-manager",
"namespace": "cert-manager",
"version": "1.0.0"
}

View File

@@ -1,10 +1,10 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: web-service
name: webservice
annotations:
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
definition.oam.dev/kind: "ContainerizedWorkload"
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
definition.oam.dev/kind: "Containerized"
spec:
definitionRef:
name: containerizeds.standard.oam.dev
@@ -16,23 +16,26 @@ spec:
extension:
template: |
#Template: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ContainerizedWorkload"
apiVersion: "standard.oam.dev/v1alpha1"
kind: "Containerized"
metadata:
name: containerized.name
name: webservice.name
spec: {
containers: [{
image: containerized.image
name: containerized.name
ports: [{
containerPort: containerized.port
protocol: "TCP"
name: "default"
}]
}]
replicas: 1
podSpec: {
containers: [{
image: webservice.image
name: webservice.name
ports: [{
containerPort: webservice.port
protocol: "TCP"
name: "default"
}]
}]
}
}
}
containerized: {
webservice: {
name: string
// +usage=specify app image
// +short=i
@@ -40,4 +43,4 @@ spec:
// +usage=specify port for container
// +short=p
port: *6379 | int
}
}

View File

@@ -6,8 +6,8 @@ replicaCount: 1
useWebhook: true
image:
repository: oamdev/vela-core
tag: 0.1
pullPolicy: IfNotPresent
tag: latest
pullPolicy: Always
imagePullSecrets: []
nameOverride: ""

View File

@@ -6,8 +6,11 @@ import (
"os"
"strconv"
monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
velacore "github.com/oam-dev/kubevela/api/v1alpha1"
velacontroller "github.com/oam-dev/kubevela/pkg/controller"
"github.com/oam-dev/kubevela/pkg/controller/dependency"
velawebhook "github.com/oam-dev/kubevela/pkg/webhook"
oamcore "github.com/crossplane/oam-kubernetes-runtime/apis/core"
oamcontroller "github.com/crossplane/oam-kubernetes-runtime/pkg/controller"
oamv1alpha2 "github.com/crossplane/oam-kubernetes-runtime/pkg/controller/v1alpha2"
@@ -16,26 +19,31 @@ import (
injectorcontroller "github.com/oam-dev/trait-injector/controllers"
"github.com/oam-dev/trait-injector/pkg/injector"
"github.com/oam-dev/trait-injector/pkg/plugin"
monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
velacore "github.com/cloud-native-application/rudrx/api/v1alpha1"
velacontroller "github.com/cloud-native-application/rudrx/pkg/controller"
velawebhook "github.com/cloud-native-application/rudrx/pkg/webhook"
)
var scheme = runtime.NewScheme()
func init() {
_ = clientgoscheme.AddToScheme(scheme)
_ = crdv1.AddToScheme(scheme)
_ = oamcore.AddToScheme(scheme)
_ = monitoring.AddToScheme(scheme)
_ = velacore.AddToScheme(scheme)
_ = injectorv1alpha1.AddToScheme(scheme)
_ = certmanager.AddToScheme(scheme)
// +kubebuilder:scaffold:scheme
}
@@ -93,6 +101,17 @@ func main() {
os.Exit(1)
}
k8sClient, err := client.New(ctrl.GetConfigOrDie(), client.Options{Scheme: scheme})
if err != nil {
setupLog.Error(err, "unable to create a kubernetes client")
os.Exit(1)
}
if err = dependency.Install(k8sClient); err != nil {
setupLog.Error(err, "unable to install the dependency")
os.Exit(1)
}
if useWebhook {
setupLog.Info("vela webhook enabled, will serving at :" + strconv.Itoa(webhookPort))
oamwebhook.Add(mgr)

View File

@@ -1,3 +1,5 @@
package fake
// FrontendSource is a base64-encoded, gzipped tarball of the default Frontend Static files.
// This whole file will be rewrite at build time.
var FrontendSource string

View File

@@ -8,34 +8,30 @@ import (
"runtime"
"time"
"github.com/cloud-native-application/rudrx/cmd/vela/fake"
"github.com/cloud-native-application/rudrx/version"
"github.com/gosuri/uitable"
"k8s.io/klog"
"github.com/cloud-native-application/rudrx/api/types"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"github.com/cloud-native-application/rudrx/pkg/utils/system"
"github.com/oam-dev/kubevela/api/types"
"github.com/oam-dev/kubevela/cmd/vela/fake"
"github.com/oam-dev/kubevela/pkg/commands"
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
"github.com/oam-dev/kubevela/pkg/utils/system"
"github.com/oam-dev/kubevela/version"
"github.com/crossplane/oam-kubernetes-runtime/apis/core"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"github.com/cloud-native-application/rudrx/pkg/cmd"
cmdutil "github.com/cloud-native-application/rudrx/pkg/cmd/util"
"github.com/cloud-native-application/rudrx/pkg/utils/logs"
"k8s.io/klog"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)
var (
scheme = k8sruntime.NewScheme()
)
// chartTGZSource is a base64-encoded, gzipped tarball of the default Helm chart.
// Its value is initialized at build time.
var chartTGZSource string
func init() {
_ = clientgoscheme.AddToScheme(scheme)
@@ -47,8 +43,6 @@ func main() {
rand.Seed(time.Now().UnixNano())
command := newCommand()
logs.InitLogs()
defer logs.FlushLogs()
if err := command.Execute(); err != nil {
os.Exit(1)
@@ -98,34 +92,35 @@ func newCommand() *cobra.Command {
cmds.AddCommand(
// Getting Start
cmd.NewEnvCommand(commandArgs, ioStream),
commands.NewInstallCommand(commandArgs, chartTGZSource, ioStream),
commands.NewEnvCommand(commandArgs, ioStream),
// Getting Start
NewVersionCommand(),
// Apps
cmd.NewAppsCommand(commandArgs, ioStream),
commands.NewAppsCommand(commandArgs, ioStream),
// Workloads
cmd.AddCompCommands(commandArgs, ioStream),
commands.AddCompCommands(commandArgs, ioStream),
// Capability Systems
cmd.CapabilityCommandGroup(commandArgs, ioStream),
commands.CapabilityCommandGroup(commandArgs, ioStream),
// System
cmd.SystemCommandGroup(commandArgs, ioStream),
cmd.NewCompletionCommand(),
commands.SystemCommandGroup(commandArgs, ioStream),
commands.NewCompletionCommand(),
cmd.NewTraitsCommand(ioStream),
cmd.NewWorkloadsCommand(ioStream),
commands.NewTraitsCommand(ioStream),
commands.NewWorkloadsCommand(ioStream),
cmd.NewDashboardCommand(commandArgs, ioStream, fake.FrontendSource),
commands.NewDashboardCommand(commandArgs, ioStream, fake.FrontendSource),
cmd.NewLogsCommand(commandArgs, ioStream),
commands.NewLogsCommand(commandArgs, ioStream),
)
// Traits
if err = cmd.AddTraitCommands(cmds, commandArgs, ioStream); err != nil {
if err = commands.AddTraitCommands(cmds, commandArgs, ioStream); err != nil {
fmt.Println("Add trait commands from traitDefinition err", err)
os.Exit(1)
}
@@ -134,6 +129,7 @@ func newCommand() *cobra.Command {
fset := flag.NewFlagSet("logs", flag.ContinueOnError)
klog.InitFlags(fset)
_ = fset.Set("v", "-1")
return cmds
}

View File

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

View File

@@ -1,42 +1,33 @@
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
backend: {
port: route.port
}
}
}
route: {
domain: string
port: *80 | int
service: string
domain: string
path: *"" | string
port: *443 | int
}

View File

@@ -3,8 +3,8 @@ kind: WorkloadDefinition
metadata:
name: containerizeds.standard.oam.dev
annotations:
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
definition.oam.dev/kind: "ContainerizedWorkload"
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
definition.oam.dev/kind: "Containerized"
spec:
definitionRef:
name: containerizeds.standard.oam.dev
@@ -16,23 +16,26 @@ spec:
extension:
template: |
#Template: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ContainerizedWorkload"
apiVersion: "standard.oam.dev/v1alpha1"
kind: "Containerized"
metadata:
name: containerized.name
name: webservice.name
spec: {
containers: [{
image: containerized.image
name: containerized.name
ports: [{
containerPort: containerized.port
protocol: "TCP"
name: "default"
}]
}]
replicas: 1
podSpec: {
containers: [{
image: webservice.image
name: webservice.name
ports: [{
containerPort: webservice.port
protocol: "TCP"
name: "default"
}]
}]
}
}
}
containerized: {
webservice: {
name: string
// +usage=specify app image
// +short=i
@@ -40,4 +43,4 @@ spec:
// +usage=specify port for container
// +short=p
port: *6379 | int
}
}

View File

@@ -12,9 +12,7 @@ export default defineConfig({
hmr: true,
},
locale: {
// default zh-CN
default: 'en-US',
// default true, when it is true, will use `navigator.language` overwrite default
antd: false,
baseNavigator: false,
},
@@ -36,13 +34,11 @@ export default defineConfig({
routes: [
{
path: '/',
// redirect: `/${envname}/ApplicationList`,
redirect: `/ApplicationList`,
},
{
name: 'ApplicationList',
icon: 'table',
// path: `/${envname}/ApplicationList`,
path: `/ApplicationList`,
component: './ApplicationList',
},
@@ -77,25 +73,6 @@ export default defineConfig({
icon: 'table',
path: '/Workload',
routes: [
// {
// name: 'Deployment',
// icon: 'table',
// path: '/Workload/Deployment',
// component: './Workload/Deployment',
// },
// {
// name: 'Containerized',
// icon: 'smile',
// path: '/Workload/Containerized',
// component: './Workload/Containerized',
// },
// {
// name: 'Detail',
// icon: 'smile',
// path: '/Workload/Detail',
// component: './Workload/Detail',
// hideInMenu: true,
// },
{
name: 'WorkloadItem',
icon: 'smile',
@@ -109,25 +86,6 @@ export default defineConfig({
name: 'Traits',
icon: 'table',
routes: [
// {
// name: 'Scale',
// icon: 'table',
// path: '/Traits/Scale',
// component: './Traits/Scale',
// },
// {
// name: 'Rollout',
// icon: 'smile',
// path: '/Traits/Rollout',
// component: './Traits/Rollout',
// },
// {
// name: 'Detail',
// icon: 'smile',
// path: '/Traits/Detail',
// component: './Traits/Detail',
// hideInMenu: true,
// },
{
name: 'TraitItem',
icon: 'smile',
@@ -136,12 +94,6 @@ export default defineConfig({
},
],
},
// {
// name: 'Release',
// icon: 'table',
// path: '/Release',
// component: './Release',
// },
{
name: 'Capability',
icon: 'table',
@@ -183,8 +135,13 @@ export default defineConfig({
],
// Theme for antd: https://ant.design/docs/react/customize-theme-cn
theme: {
// ...darkTheme,
// 主题配置
'primary-color': defaultSettings.primaryColor,
'link-color': defaultSettings.linkColor,
'link-hover-color': defaultSettings.linkHoverColor,
'disabled-bg': defaultSettings.disabledBg,
'disabled-color': defaultSettings.disabledColor,
'btn-disable-color': defaultSettings.btnDisableColor,
},
// @ts-ignore
title: false,

View File

@@ -1,14 +1,19 @@
const proSettings = {
navTheme: 'dark',
// 拂晓蓝
primaryColor: '#1890ff',
// 主题颜色配置
primaryColor: '#1B58F4', // 全局主色
linkColor: '#1B58F4', // 链接色
linkHoverColor: '#1B58F4',
disabledBg: '#EBEBEB', // 失效背景色,
disabledColor: '#BEBEBE', // 失效文本色,
btnDisableColor: '#A4A4A4', // 禁用btn文字颜色
layout: 'side',
contentWidth: 'Fluid',
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
menu: {
locale: true,
locale: false,
},
title: 'Micro App Engine',
pwa: false,

View File

@@ -8,11 +8,8 @@
export default {
dev: {
'/api': {
target: 'http://123.56.222.218:8081/',
target: 'http://30.11.171.17:38081/',
changeOrigin: true,
// pathRewrite: {
// "^/api": "",
// },
},
},
test: {

View File

@@ -35,34 +35,40 @@ export default class CreateTraitItem extends React.PureComponent {
return this.formRefStep2.current.getFieldsValue();
};
validateFields = () => {
return this.formRefStep2.current.validateFields();
};
setDefaultValue = (traitType) => {
this.formRefStep2.current.setFieldsValue({ name: traitType });
this.traitSelectChange(traitType);
};
traitSelectChange = async (value, isType = 1) => {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: value,
},
});
this.setState({
parameters: res.parameters,
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else if (isType) {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {
const initialObj = {};
parameters.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep2.current.setFieldsValue(initialObj);
if (value) {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: value,
},
});
this.setState({
parameters: res.parameters,
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else if (isType) {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {
const initialObj = {};
parameters.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep2.current.setFieldsValue(initialObj);
}
}
}
};
@@ -77,8 +83,12 @@ export default class CreateTraitItem extends React.PureComponent {
name="control-ref"
className="traitItem"
>
<Form.Item name="name" label="Trait">
<Select placeholder="Select a Trait" allowClear onChange={this.traitSelectChange}>
<Form.Item
name="name"
label="Trait"
rules={[{ required: true, message: 'Please Select a Trait!' }]}
>
<Select placeholder="Select a Trait" onChange={this.traitSelectChange}>
{availableTraitList.map((item) => {
return (
<Option value={item.name} key={item.name}>
@@ -92,8 +102,34 @@ export default class CreateTraitItem extends React.PureComponent {
<div className="relativeBox">
{this.state.parameters ? (
this.state.parameters.map((item) => {
return (
<Form.Item name={item.name} label={item.name} key={item.name}>
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
</Form.Item>
);

View File

@@ -23,9 +23,8 @@ export default class WorkSpaceDropDown extends React.Component {
return env.current === '*';
});
this.setState({
envs: envs,
workSpaceName: envName,
namespace: namespace,
namespace,
});
this.props.dispatch({
type: 'globalData/currentEnv',

View File

@@ -1,18 +1,30 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Row, Col, Modal, Select, message } from 'antd';
import { Button, Row, Col, Modal, Select, message, Breadcrumb, Form, Input } from 'antd';
import './index.less';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
const { Option } = Select;
const layout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
@connect(({ loading, applist, globalData }) => ({
loadingAll: loading.models.applist,
currentEnv: globalData.currentEnv,
returnObj: applist.returnObj,
}))
class Trait extends React.Component {
formRefStep2 = React.createRef();
constructor(props) {
super(props);
this.state = {
@@ -37,28 +49,61 @@ class Trait extends React.Component {
};
showModal = () => {
this.setState({
visible: true,
});
this.setState(
{
visible: true,
},
() => {
if (this.formRefStep2.current) {
this.formRefStep2.current.resetFields();
}
},
);
};
handleOk = () => {
const { selectValue } = this.state;
if (selectValue) {
this.setState({
visible: false,
handleOk = async () => {
await this.formRefStep2.current.validateFields();
const { title } = this.props.propsObj;
if (title) {
const submitObj = {
name: title,
flags: [],
};
const submitData = this.formRefStep2.current.getFieldValue();
Object.keys(submitData).forEach((currentKey) => {
if (currentKey !== 'name' && currentKey !== 'appName' && submitData[currentKey]) {
submitObj.flags.push({
name: currentKey,
value: submitData[currentKey].toString(),
});
}
});
const { history } = this.props.propsObj;
history.push({
pathname: '/ApplicationList/ApplicationListDetail',
state: {
appName: selectValue,
envName: this.props.currentEnv,
traitType: this.props.propsObj.title,
},
});
} else {
message.warn('please select a application');
const { currentEnv: envName } = this.props;
const { appName } = submitData;
if (envName && appName) {
const res = await this.props.dispatch({
type: 'trait/attachOneTraits',
payload: {
envName,
appName,
params: submitObj,
},
});
if (res) {
this.setState({
visible: false,
});
message.success(res);
const { history } = this.props.propsObj;
history.push({
pathname: '/ApplicationList/ApplicationListDetail',
state: {
appName,
envName,
},
});
}
}
}
};
@@ -77,98 +122,175 @@ class Trait extends React.Component {
onSearch = () => {};
render() {
const { btnValue, title, settings, btnIsShow, crdInfo, appliesTo } = this.props.propsObj;
const { btnValue, title, settings = [], btnIsShow, crdInfo, appliesTo } = this.props.propsObj;
const initialObj = {};
if (settings.length) {
settings.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
}
const appList = _.get(this.props, 'returnObj', []);
return (
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<Row>
<Col span="22">
<p className="title">Applies To</p>
<p>{Array.isArray(appliesTo) ? appliesTo.join(', ') : appliesTo}</p>
</Col>
</Row>
<p className="title">Configurable Properties:</p>
{settings.map((item, index) => {
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
<Button
type="primary"
className="create-button"
onClick={this.showModal}
style={{ display: btnIsShow ? 'block' : 'none' }}
>
{btnValue}
</Button>
<Modal
title="Select a Application"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Next
</Button>,
]}
>
<Select
showSearch
allowClear
value={this.state.selectValue}
style={{ width: '100%' }}
placeholder="Select a Application"
optionFilterProp="children"
onChange={this.onChange}
onSearch={this.onSearch}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Traits</Breadcrumb.Item>
<Breadcrumb.Item>{title}</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<Row>
<Col span="22">
<p className="title">Applies To:</p>
<p>{Array.isArray(appliesTo) ? appliesTo.join(', ') : appliesTo}</p>
</Col>
</Row>
<p className="title">Configurable Properties:</p>
{settings.map((item, index) => {
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
<Button
type="primary"
className="create-button"
onClick={this.showModal}
style={{ display: btnIsShow ? 'block' : 'none' }}
>
{appList.length ? (
appList.map((item) => {
return (
<Option key={item.name} value={item.name}>
{item.name}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Modal>
</Col>
</Row>
</PageContainer>
{btnValue}
</Button>
<Modal
title="Attach Trait"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Submit
</Button>,
]}
>
<Form
labelAlign="left"
{...layout}
ref={this.formRefStep2}
name="control-ref"
className="traitItem"
initialValues={initialObj}
>
<Form.Item
label="Target"
name="appName"
rules={[{ required: true, message: 'Please Select a Application!' }]}
>
<Select
showSearch
allowClear
value={this.state.selectValue}
style={{ width: '100%' }}
placeholder="Select a Application"
optionFilterProp="children"
onChange={this.onChange}
onSearch={this.onSearch}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{appList.length ? (
appList.map((item) => {
return (
<Option key={item.name} value={item.name}>
{item.name}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Form.Item>
<div className="relativeBox">
<Form.Item label="Properties" />
{settings ? (
settings.map((item) => {
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{
pattern: /^[0-9]*$/,
message: `${item.name} only use digits(0-9).`,
},
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
</Form.Item>
);
})
) : (
<></>
)}
</div>
</Form>
</Modal>
</Col>
</Row>
</PageContainer>
</div>
);
}
}

View File

@@ -1,69 +1,70 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Row, Col } from 'antd';
import { Button, Row, Col, Breadcrumb } from 'antd';
import { Link } from 'umi';
import './index.less';
export default class Workload extends React.PureComponent {
render() {
const {
btnValue,
pathname,
title,
crdInfo,
state,
settings,
hrefAddress,
btnIsShow,
} = this.props.propsObj;
const { btnValue, pathname, title, crdInfo, state, settings, btnIsShow } = this.props.propsObj;
return (
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<a href={hrefAddress}>?</a>
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<p className="title">Configurable Settings:</p>
{settings.map((item, index) => {
if (item.name === 'name') {
return <Fragment key={index.toString()} />;
}
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
{
// eslint-disable-next-line consistent-return
}
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
<Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
<Button type="primary" className="create-button">
{btnValue}
</Button>
</Link>
</Col>
</Row>
</PageContainer>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Workloads</Breadcrumb.Item>
<Breadcrumb.Item>{title}</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<p className="title">Configurable Settings:</p>
{settings.map((item, index) => {
if (item.name === 'name') {
return <Fragment key={index.toString()} />;
}
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
{
// eslint-disable-next-line consistent-return
}
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
<Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
<Button type="primary" className="create-button">
{btnValue}
</Button>
</Link>
</Col>
</Row>
</PageContainer>
</div>
);
}
}

View File

@@ -29,13 +29,6 @@ beforeEach(async () => {
describe('Ant Design Pro E2E test', () => {
const testPage = (path) => async () => {
await page.goto(`${BASE_URL}${path}`);
await page.waitForSelector('footer', {
timeout: 2000,
});
const haveFooter = await page.evaluate(
() => document.getElementsByTagName('footer').length > 0,
);
expect(haveFooter).toBeTruthy();
};
const routers = formatter(RouterConfig);
@@ -46,12 +39,5 @@ describe('Ant Design Pro E2E test', () => {
it('topmenu should have footer', async () => {
const params = '?navTheme=light&layout=topmenu';
await page.goto(`${BASE_URL}${params}`);
await page.waitForSelector('footer', {
timeout: 2000,
});
const haveFooter = await page.evaluate(
() => document.getElementsByTagName('footer').length > 0,
);
expect(haveFooter).toBeTruthy();
});
});

View File

@@ -69,3 +69,19 @@ ol {
.ant-page-header-heading {
display: none !important;
}
.ant-pro-page-container-warp {
display: none;
}
.ant-pro-basicLayout-content {
margin: 0 !important;
}
.ant-pro-basicLayout-content .ant-pro-page-container {
margin: 0 !important;
}
.breadCrumb {
padding: 12px 24px;
background: #fff;
}
.ant-breadcrumb a:hover {
color: #1b58f4 !important;
}

View File

@@ -4,7 +4,7 @@
* https://github.com/ant-design/ant-design-pro-layout
*/
import ProLayout from '@ant-design/pro-layout';
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { Link, useIntl, connect, history } from 'umi';
import RightContent from '@/components/GlobalHeader/RightContent';
import {
@@ -16,15 +16,6 @@ import {
} from '@ant-design/icons';
import _ from 'lodash';
// const menuDataRender = (menuList) => {
// return menuList.map((item) => {
// const localItem = {
// ...item,
// children: item.children ? menuDataRender(item.children) : undefined,
// };
// return localItem;
// });
// };
const AddIcon = (menuData) => {
return menuData.map((item) => {
const name = _.get(item, 'name', '');
@@ -52,18 +43,37 @@ const AddIcon = (menuData) => {
const BasicLayout = (props) => {
const { settings, dispatch, menus } = props;
const [currentSelectKeys, setCurrentSelectedKeys] = useState('');
const getCurrentSelectKeys = () => {
const pathnameCur = props.history.location.pathname;
if (pathnameCur) {
if (pathnameCur.includes('Application')) {
setCurrentSelectedKeys(['applist']);
} else if (pathnameCur.includes('Capability')) {
setCurrentSelectedKeys(['Capability']);
} else if (pathnameCur.includes('System/Env')) {
setCurrentSelectedKeys(['Env']);
} else if (pathnameCur.includes('Workload')) {
const arr = pathnameCur.split('/');
const key = arr[arr.length - 1];
setCurrentSelectedKeys([key]);
} else if (pathnameCur.includes('Traits')) {
const arr = pathnameCur.split('/');
const key = arr[arr.length - 1];
setCurrentSelectedKeys([key]);
}
}
};
useEffect(() => {
if (dispatch) {
// dispatch({
// type: 'user/fetchCurrent',
// });
// dispatch({
// type: 'settings/getSetting',
// });
dispatch({
type: 'menus/getMenuData',
});
}
props.history.listen((route) => {
getCurrentSelectKeys(route.pathname);
});
// setCurrentSelectedKeys('applist')
}, []);
const { formatMessage } = useIntl();
@@ -75,9 +85,19 @@ const BasicLayout = (props) => {
if (menuItemProps.isUrl || !menuItemProps.path) {
return defaultDom;
}
return <Link to={menuItemProps.path}>{defaultDom}</Link>;
// return <Link to={menuItemProps.path}>{defaultDom}</Link>;
return (
<div
onClick={() => {
setCurrentSelectedKeys([menuItemProps.key]);
history.push(menuItemProps.path);
}}
>
{defaultDom}
</div>
);
}}
selectedKeys={currentSelectKeys}
breadcrumbRender={(routers = []) => [
{
path: '/',

View File

@@ -2,17 +2,15 @@ import { getapplist, createApp, getAppDetail, deleteApp } from '@/services/appli
const TestModel = {
namespace: 'applist',
state: {
// initailState: '8880'
},
state: {},
effects: {
*getList({ payload }, { call, put }) {
const res = yield call(getapplist, payload);
// getlist是引入services层那个js文件的getlist方法payload是后台要求传递的参数res就是后台返过来的数据
yield put({
type: 'addList', // 这就是reducer的addNum方法put用来触发reducer中的方法payload是传过去的参数。同时也能触发同等级effects中的方法
type: 'addList',
payload: {
returnObj: res, // 把后台返回的数据赋值给num,假如哪个reducer中的方法是由这里effects去触发的哪个num名必须是这里的名字num如果reducer中的方法不是这触发那名字可以随意取
returnObj: res,
},
});
},

View File

@@ -10,9 +10,7 @@ import {
const TestModel = {
namespace: 'capability',
state: {
// initailState: '8880'
},
state: {},
effects: {
*getCapabilityCenterlist({ payload }, { call }) {
const res = yield call(getCapabilityCenterlist, payload);

View File

@@ -6,7 +6,7 @@ const globalModel = {
effects: {
*currentEnv({ payload }, { put }) {
yield put({
type: 'setCurrentEnv', // 这就是reducer的addNum方法put用来触发reducer中的方法payload是传过去的参数。同时也能触发同等级effects中的方法
type: 'setCurrentEnv',
payload,
});
},

View File

@@ -1,36 +1,31 @@
import { capabilityList } from '@/services/capability.js';
import { getTraits } from '@/services/trait.js';
import { getWorkload } from '@/services/workload.js';
function getMenuList(response) {
function getMenuList(workload, trait) {
let workloadList = [];
let traitList = [];
// eslint-disable-next-line no-param-reassign
response = response.filter((item) => {
return item.status === 'installed';
});
response.forEach((item) => {
if (item.type === 'workload') {
workloadList.push(item.name);
} else if (item.type === 'trait') {
traitList.push(item.name);
}
});
// 在此之前要对workloadList和traitList进行一次去重操作
workloadList = workloadList.map((item) => {
// eslint-disable-next-line no-param-reassign
item = item.charAt(0).toUpperCase() + item.slice(1);
return {
name: item,
path: `/Workload/${item}`,
};
});
traitList = traitList.map((item) => {
// eslint-disable-next-line no-param-reassign
item = item.charAt(0).toUpperCase() + item.slice(1);
return {
name: item,
path: `/Traits/${item}`,
};
});
if (workload) {
workloadList = workload.map((item) => {
let name1 = item.name;
name1 = name1.charAt(0).toUpperCase() + name1.slice(1);
return {
name: name1,
path: `/Workload/${name1}`,
key: name1,
};
});
}
if (trait) {
traitList = trait.map((item) => {
let name1 = item.name;
name1 = name1.charAt(0).toUpperCase() + name1.slice(1);
return {
name: name1,
path: `/Traits/${name1}`,
key: name1,
};
});
}
// 只是动态生成侧边栏(name,path,icon)路由还是config.js里面配置的路由
const menuList = [
{
@@ -41,6 +36,7 @@ function getMenuList(response) {
name: 'ApplicationList',
icon: 'Table',
path: `/ApplicationList`,
key: 'applist',
},
{
name: 'ApplicationList.ApplicationListDetail',
@@ -76,13 +72,10 @@ function getMenuList(response) {
},
],
},
// {
// name: 'Release',
// path: '/Release',
// },
{
name: 'Capability',
path: '/Capability',
key: 'Capability',
},
{
path: '/System',
@@ -91,6 +84,7 @@ function getMenuList(response) {
{
name: 'Env',
path: '/System/Env',
key: 'Env',
},
],
},
@@ -110,8 +104,9 @@ const TestModel = {
},
effects: {
*getMenuData({ payload }, { call, put }) {
let response = yield call(capabilityList, payload);
response = getMenuList(response);
const workloadList = yield call(getWorkload, payload);
const traitList = yield call(getTraits, payload);
const response = getMenuList(workloadList, traitList);
yield put({
type: 'saveMenuData',
payload: response,

View File

@@ -2,9 +2,7 @@ import { getTraitByName, getTraits, attachOneTraits, deleteOneTrait } from '@/se
const TestModel = {
namespace: 'trait',
state: {
// initailState: '8880'
},
state: {},
effects: {
*getTraitByName({ payload }, { call }) {
const res = yield call(getTraitByName, payload);

View File

@@ -2,9 +2,7 @@ import { createWorkload, getWorkload, getWorkloadByName } from '@/services/workl
const TestModel = {
namespace: 'workload',
state: {
// initailState: '8880'
},
state: {},
effects: {
*createWorkload({ payload }, { call }) {
const res = yield call(createWorkload, payload);

View File

@@ -43,8 +43,6 @@ const Topology = () => {
],
};
// const width = document.getElementById('container').scrollWidth;
// const height = document.getElementById('container').scrollHeight || 500;
const width = 1000;
const height = 400;

View File

@@ -1,11 +1,22 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Button, Row, Col, Tabs, Popconfirm, message, Tooltip, Modal, Spin } from 'antd';
import {
Button,
Row,
Col,
Tabs,
Popconfirm,
message,
Tooltip,
Modal,
Spin,
Breadcrumb,
} from 'antd';
import { connect } from 'dva';
import _ from 'lodash';
import { Link } from 'umi';
import CreateTraitItem from '../../../components/AttachOneTrait/index.jsx';
import Topology from './Topology.jsx';
const { TabPane } = Tabs;
@@ -54,18 +65,18 @@ class TableList extends React.Component {
const traits = await this.props.dispatch({
type: 'trait/getTraits',
});
this.setState({
traitList: traits,
});
if (traits) {
this.setState({
traitList: traits,
});
}
const workloadType = _.get(res, 'Workload.workload.kind', '');
if (workloadType && workloadType === 'ContainerizedWorkload') {
this.getAcceptTrait('containerized');
} else if (workloadType && workloadType === 'Deployment') {
this.getAcceptTrait('deployment');
}
// 如果traitType存在是从特定trait跳转来新增单个trait的
if (traitType && times === 1) {
// this.createTrait(traitType)
await this.setState({
visible: true,
});
@@ -88,7 +99,7 @@ class TableList extends React.Component {
deleteApp = async (e) => {
e.stopPropagation();
const { currentEnv: envName } = this.props;
const { envName } = this.state;
const { appDetailData } = this.state;
const appName = _.get(appDetailData, 'Workload.workload.metadata.name', '');
if (appName && envName) {
@@ -138,6 +149,7 @@ class TableList extends React.Component {
};
handleOk = async () => {
await this.child.validateFields();
const submitData = this.child.getSelectValue();
if (submitData.name) {
const submitObj = {
@@ -187,26 +199,10 @@ class TableList extends React.Component {
gotoWorkloadDetail = (e) => {
e.stopPropagation();
const appName = _.get(this.props, 'location.state.appName', '');
const envName = _.get(this.props, 'location.state.envName', '');
if (appName && envName) {
this.props.history.push({
pathname: '/ApplicationList/WorkloadDetail',
state: { appName, envName },
});
}
};
gotoTraitDetail = (e, traitItem) => {
gotoTraitDetail = (e) => {
e.stopPropagation();
const appName = _.get(this.props, 'location.state.appName', '');
const envName = _.get(this.props, 'location.state.envName', '');
if (appName && envName) {
this.props.history.push({
pathname: '/ApplicationList/TraitDetail',
state: { traitItem, appName, envName },
});
}
};
render() {
@@ -214,277 +210,277 @@ class TableList extends React.Component {
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
const Traits = _.get(this.state.appDetailData, 'Traits', []);
let containers = {};
// if (Workload.kind === 'ContainerizedWorkload') {
// containers = _.get(Workload, 'spec.containers[0]', {});
// } else if (Workload.kind === 'Deployment') {
// containers = _.get(Workload, 'spec.template.spec.containers[0]', {});
// }
containers = _.get(Workload, 'spec.containers[0]', {});
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const colorObj = {
Deployed: '#4CAF51',
Staging: '#F44337',
UNKNOWN: '#1890ff',
};
return (
<PageContainer>
<Spin spinning={loadingAll}>
<div className="card-container app-detial">
<h2>{_.get(Workload, 'metadata.name')}</h2>
<p style={{ marginBottom: '20px' }}>
{Workload.apiVersion}, Kind={Workload.kind}
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<Row>
<Col span="11">
<div className="summaryBox1" onClick={(e) => this.gotoWorkloadDetail(e)}>
{/* <div className="summaryBox1"> */}
<Row>
<Col span="22">
<p className="title">{Workload.kind}</p>
<p>{Workload.apiVersion}</p>
</Col>
<Col span="2">
{/* <a href="JavaScript:;">?</a> */}
<p className="title hasCursor" onClick={this.hrefClick}>
?
</p>
</Col>
</Row>
<p className="title">
Name:<span>{_.get(Workload, 'metadata.name')}</span>
</p>
<p className="title">Settings:</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>{_.get(containers[currentKey], '[0].containerPort', '')}</p>
</Col>
</Fragment>
);
// eslint-disable-next-line no-else-return
} else if (currentKey === 'name') {
return <Fragment key={currentKey} />;
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{containers[currentKey]}</p>
</Col>
</Fragment>
);
})}
</Row>
</div>
<div className="summaryBox2">
<p className="title">Status:</p>
<p>{status}</p>
{/* <Row>
<Col span="8">
<p>Available Replicas</p>
<p>Ready Replicas</p>
</Col>
<Col span="16">
<p>1</p>
<p>1</p>
</Col>
</Row> */}
</div>
<Popconfirm
title="Are you sure delete this app?"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button danger>Delete</Button>
</Popconfirm>
</Col>
<Col span="1" />
<Col span="10">
{Traits.length ? (
Traits.map((item, index) => {
const traitItem = _.get(item, 'trait', {});
const annotations = _.get(traitItem, 'metadata.annotations', {});
let traitType = 1;
const spec = _.get(traitItem, 'spec', {});
if (traitItem.kind === 'Ingress') {
traitType = 2;
}
return (
<div
className="summaryBox"
onClick={(e) => this.gotoTraitDetail(e, traitItem)}
key={index.toString()}
>
<Row>
<Col span="22">
<p className="title">{traitItem.kind}</p>
<p>{traitItem.apiVersion}</p>
</Col>
<Col span="2">
<p
className="title hasCursor"
onClick={(e) => {
e.stopPropagation();
}}
>
?
</p>
</Col>
</Row>
<Row>
{Object.keys(annotations).map((currentKey3) => {
return (
<Fragment key={currentKey3}>
<Col span="8">
<p>{currentKey3}:</p>
</Col>
<Col span="8">
<p>{annotations[currentKey3]}</p>
</Col>
</Fragment>
);
})}
</Row>
<p className="title">Properties:</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>domain</p>
</Col>
<Col span="16">
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.serviceName',
'',
)}
</p>
</Col>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>ApplicationListDetail</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingAll}>
<div className="card-container app-detial">
<h2>{_.get(Workload, 'metadata.name')}</h2>
<p style={{ marginBottom: '20px' }}>
{Workload.apiVersion}, Kind={Workload.kind}
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<Row>
<Col span="11">
<div
className="summaryBox1"
onClick={(e) => this.gotoWorkloadDetail(e)}
style={{ background: colorObj[status] || '#1890ff' }}
>
<Row>
<Col span="22">
<p className="title">{Workload.kind}</p>
<p>{Workload.apiVersion}</p>
</Col>
<Col span="2">
<p className="title hasCursor" onClick={this.hrefClick}>
?
</p>
</Col>
</Row>
<p className="title">
Name:<span>{_.get(Workload, 'metadata.name')}</span>
</p>
<p className="title">Settings:</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.servicePort',
'',
)}
</p>
<p>{_.get(containers[currentKey], '[0].containerPort', '')}</p>
</Col>
</Fragment>
) : (
Object.keys(spec).map((currentKey) => {
);
// eslint-disable-next-line no-else-return
} else if (currentKey === 'name') {
return <Fragment key={currentKey} />;
// eslint-disable-next-line no-else-return
} else if (currentKey === 'env') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>env</p>
</Col>
<Col span="16">
<p>{_.get(containers[currentKey], '[0].value', '')}</p>
</Col>
</Fragment>
);
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{containers[currentKey]}</p>
</Col>
</Fragment>
);
})}
</Row>
</div>
<Popconfirm
title="Are you sure delete this app?"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button danger>Delete</Button>
</Popconfirm>
</Col>
<Col span="1" />
<Col span="10">
{Traits.length ? (
Traits.map((item, index) => {
const traitItem = _.get(item, 'trait', {});
const annotations = _.get(traitItem, 'metadata.annotations', {});
let traitType = 1;
const spec = _.get(traitItem, 'spec', {});
if (traitItem.kind === 'Ingress') {
traitType = 2;
}
return (
<div
className="summaryBox"
onClick={(e) => this.gotoTraitDetail(e, traitItem)}
key={index.toString()}
>
<Row>
<Col span="22">
<p className="title">{traitItem.kind}</p>
<p>{traitItem.apiVersion}</p>
</Col>
<Col span="2">
<p
className="title hasCursor"
onClick={(e) => {
e.stopPropagation();
}}
>
?
</p>
</Col>
</Row>
<Row>
{Object.keys(annotations).map((currentKey3) => {
return (
<Fragment key={currentKey}>
<Fragment key={currentKey3}>
<Col span="8">
<p>{currentKey}</p>
<p>{currentKey3}:</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
<Col span="8">
<p>{annotations[currentKey3]}</p>
</Col>
</Fragment>
);
})
)}
{/* {Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
})}
</Row>
<p className="title">Properties:</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>{currentKey}</p>
<p>domain</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.serviceName',
'',
)}
</p>
</Col>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.servicePort',
'',
)}
</p>
</Col>
</Fragment>
);
})} */}
</Row>
<div style={{ clear: 'both', height: '32px' }}>
<Popconfirm
title="Are you sure delete this trait?"
onConfirm={(e) => this.deleteTrait(e, item)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button
danger
className="floatRight"
onClick={(e) => {
e.stopPropagation();
}}
) : (
Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})
)}
</Row>
<div style={{ clear: 'both', height: '32px' }}>
<Popconfirm
title="Are you sure delete this trait?"
onConfirm={(e) => this.deleteTrait(e, item)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
Delete
</Button>
</Popconfirm>
<Button
danger
className="floatRight"
onClick={(e) => {
e.stopPropagation();
}}
>
Delete
</Button>
</Popconfirm>
</div>
</div>
</div>
);
})
) : (
<Fragment />
)}
<Tooltip placement="top" title="Attach Trait">
<p
className="hasCursor"
style={{
fontSize: '30px',
display: 'inline-flex',
}}
onClick={this.createTrait}
>
+
</p>
</Tooltip>
</Col>
</Row>
</TabPane>
<TabPane tab="Topology" key="2">
{/* <p>Topology</p> */}
<Topology />
</TabPane>
</Tabs>
</div>
<Modal
title="attach a trait"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Confirm
</Button>,
]}
>
<CreateTraitItem
onRef={(ref) => {
this.child = ref;
}}
availableTraitList={this.state.availableTraitList}
initialValues={{}}
/>
</Modal>
</Spin>
</PageContainer>
);
})
) : (
<Fragment />
)}
<Tooltip placement="top" title="Attach Trait">
<p
className="hasCursor"
style={{
fontSize: '30px',
display: 'inline-flex',
}}
onClick={this.createTrait}
>
+
</p>
</Tooltip>
</Col>
</Row>
</TabPane>
<TabPane tab="Topology" key="2">
<p>Topology</p>
</TabPane>
</Tabs>
</div>
<Modal
title="Attach a Trait"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Confirm
</Button>,
]}
>
<CreateTraitItem
onRef={(ref) => {
this.child = ref;
}}
availableTraitList={this.state.availableTraitList}
initialValues={{}}
/>
</Modal>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -19,7 +19,6 @@
font-weight: 500;
font-size: 16px;
line-height: 36px;
// color: #fff;
}
p {
margin: 0;

View File

@@ -1,7 +1,7 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Button, Row, Col, Form, Input, Select, Steps, message } from 'antd';
import { Button, Row, Col, Form, Input, Select, Steps, message, Breadcrumb } from 'antd';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
@@ -73,12 +73,8 @@ class TableList extends React.Component {
this.setState({
traitList: traits,
});
// 如果直接跳转到第二步,需要设置值
const traitType = _.get(this.props, 'location.state.TraitType', '');
if (traitType) {
// let availableTraitList = traits.filter((item)=>{
// return item.name === traitType
// })
this.setState({
availableTraitList: traits,
traitNum: [
@@ -117,7 +113,12 @@ class TableList extends React.Component {
});
};
onFinishStep2 = () => {
onFinishStep2 = async () => {
const asyncValidateArray = [];
this.state.traitNum.forEach((item) => {
asyncValidateArray.push(item.refname.validateFields());
});
await Promise.all(asyncValidateArray);
const newTraitNum = this.state.traitNum.map((item) => {
// eslint-disable-next-line no-param-reassign
item.initialData = item.refname.getSelectValue();
@@ -163,7 +164,11 @@ class TableList extends React.Component {
};
createApp = async () => {
const { step1SubmitObj, traitNum } = this.state;
const { traitNum } = this.state;
const { step1SubmitObj } = this.state;
if (step1SubmitObj.env_name !== this.props.currentEnv) {
step1SubmitObj.env_name = this.props.currentEnv;
}
const submitObj = _.cloneDeep(step1SubmitObj);
const { workload_name: workloadName } = step1SubmitObj;
submitObj.flags.push({
@@ -289,9 +294,6 @@ class TableList extends React.Component {
this.state.traitNum = this.state.traitNum.filter((item) => {
return item.uniq !== uniq;
});
// this.setState(()=>({
// traitNum: this.state.traitNum
// }));
this.setState((prev) => ({
traitNum: prev.traitNum,
}));
@@ -320,6 +322,11 @@ class TableList extends React.Component {
name="workload_name"
label="Name"
rules={[
{
pattern: /^[a-z0-9-_]+$/,
message:
'Names can only use digits(0-9),lowercase letters(a-z),and dashes(-),Underline.',
},
{
required: true,
message: 'Please input name!',
@@ -356,8 +363,11 @@ class TableList extends React.Component {
)}
</Select>
</Form.Item>
<Form.Item label="Settings" />
</div>
<Form.Item
label="Settings"
style={{ background: 'rgba(0, 0, 0, 0.04)', paddingLeft: '16px' }}
/>
<div className="relativeBox">
<p className="hasMore">?</p>
{Array.isArray(workloadSettings) && workloadSettings.length ? (
@@ -365,7 +375,7 @@ class TableList extends React.Component {
if (item.name === 'name') {
return <Fragment key={item.name} />;
}
return (
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
@@ -375,6 +385,22 @@ class TableList extends React.Component {
required: item.required,
message: `Please input ${item.name}!`,
},
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required,
message: `Please input ${item.name}!`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
@@ -548,11 +574,6 @@ class TableList extends React.Component {
</Row>
</div>
<div className="buttonBox">
{/* <Link to="/ApplicationList">
<Button type="primary" className="floatRight">
Confirm
</Button>
</Link> */}
<Button
type="primary"
className="floatRight"
@@ -570,16 +591,29 @@ class TableList extends React.Component {
);
}
return (
<PageContainer>
<div className="create-container create-app">
<Steps current={current}>
<Step title="Step 1" description="Choose Workload" />
<Step title="Step 2" description="Attach Trait" />
<Step title="Step 3" description="Review and confirm" />
</Steps>
{currentDetail}
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>CreateApplication</Breadcrumb.Item>
</Breadcrumb>
</div>
</PageContainer>
<PageContainer>
<div className="create-container create-app">
<Steps current={current}>
<Step title="Step 1" description="Choose Workload" />
<Step title="Step 2" description="Attach Trait" />
<Step title="Step 3" description="Review and confirm" />
</Steps>
{currentDetail}
</div>
</PageContainer>
</div>
);
}
}

View File

@@ -27,7 +27,6 @@
}
.relativeBox {
position: relative;
// border: 1px solid #eee;
padding: 0 48px 0 16px;
.hasMore {
position: absolute;
@@ -53,7 +52,7 @@
}
.summaryBox1 {
color: #fff;
background: #0097a7;
background: rgb(24, 144, 255);
}
.summaryBox2 {
background: yellow;

View File

@@ -35,29 +35,35 @@ export default class CreateTraitItem extends React.PureComponent {
return this.formRefStep2.current.getFieldsValue();
};
validateFields = () => {
return this.formRefStep2.current.validateFields();
};
traitSelectChange = async (value, isType = 1) => {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: value,
},
});
this.setState({
parameters: res.parameters,
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {
const initialObj = {};
parameters.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep2.current.setFieldsValue(initialObj);
if (value) {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: value,
},
});
this.setState({
parameters: res.parameters,
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {
const initialObj = {};
parameters.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep2.current.setFieldsValue(initialObj);
}
}
}
};
@@ -78,8 +84,12 @@ export default class CreateTraitItem extends React.PureComponent {
>
<div style={{ border: '1px solid #eee', margin: '16px 0px 8px' }}>
<div style={{ padding: '16px 48px 0px 16px' }}>
<Form.Item name="name" label="Trait">
<Select placeholder="Select a Trait" allowClear onChange={this.traitSelectChange}>
<Form.Item
name="name"
label="Trait"
rules={[{ required: true, message: 'Please Select a Trait!' }]}
>
<Select placeholder="Select a Trait" onChange={this.traitSelectChange}>
{availableTraitList.map((item) => {
return (
<Option value={item.name} key={item.name}>
@@ -92,11 +102,36 @@ export default class CreateTraitItem extends React.PureComponent {
<Form.Item label="Properties" />
</div>
<div className="relativeBox">
{/* <p className="hasMore">?</p> */}
{this.state.parameters ? (
this.state.parameters.map((item) => {
return (
<Form.Item name={item.name} label={item.name} key={item.name}>
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
</Form.Item>
);

View File

@@ -1,19 +1,14 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { SearchOutlined, BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
import { Button, Card, Row, Col, Form, Select, DatePicker, Spin, Empty } from 'antd';
import { BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
import { Button, Card, Row, Col, Form, Spin, Empty, Breadcrumb } from 'antd';
import { connect } from 'dva';
import moment from 'moment';
import './index.less';
import { Link } from 'umi';
const { Option } = Select;
@connect(({ loading, applist, globalData }) => ({
loadingAll: loading.models.applist,
// 当applist这个models有数据请求行为的时候loading为true没有请求的时候为false
// loadingList: loading.effects['applist/getList'],
// 当applist的effects中的getList有异步请求行为时为true没有请求行为时为false
returnObj: applist.returnObj,
currentEnv: globalData.currentEnv,
}))
@@ -27,7 +22,7 @@ class TableList extends React.Component {
const { currentEnv } = this.props;
if (currentEnv) {
this.props.dispatch({
type: 'applist/getList', // applist对应models层的命名空间namespace
type: 'applist/getList',
payload: {
url: `/api/envs/${currentEnv}/apps/`,
},
@@ -40,29 +35,24 @@ class TableList extends React.Component {
return true;
}
this.props.dispatch({
type: 'applist/getList', // applist对应models层的命名空间namespace
type: 'applist/getList',
payload: {
url: `/api/envs/${nextProps.currentEnv}/apps/`,
},
});
return true;
// return true;
}
onFinish = () => {
// const data = moment(values.createTime).format('YYYY-MM-DD')
};
onFinish = () => {};
handleChange = () => {};
handleAdd = () => {};
onSelect = () => {
// console.log("selected", selectedKeys, info);
};
onSelect = () => {};
getHeight = (num) => {
return `${num * 55}px`;
return `${num * 43}px`;
};
getFormatDate = (time) => {
@@ -80,98 +70,91 @@ class TableList extends React.Component {
UNKNOWN: 'first3',
};
return (
<PageContainer>
<Spin spinning={loadingAll}>
<div className="applist">
<Form name="horizontal_login" layout="inline" onFinish={this.onFinish}>
<Form.Item name="createTime">
<DatePicker placeholder="createTime" />
</Form.Item>
<Form.Item name="status">
<Select
placeholder="status"
style={{ width: 120 }}
onChange={this.handleChange}
allowClear
>
<Option value="True">True</Option>
<Option value="False">False</Option>
<Option value="UNKNOWN">UNKNOWN</Option>
</Select>
</Form.Item>
<Form.Item>
<Button icon={<SearchOutlined />} htmlType="submit">
Search
</Button>
</Form.Item>
<Form.Item>
<Link to="/ApplicationList/CreateApplication">
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
create
</Button>
</Link>
</Form.Item>
</Form>
</div>
<Row gutter={16}>
{Array.isArray(returnObj) && returnObj.length ? (
returnObj.map((item, index) => {
const { traits = [] } = item;
return (
<Col span={6} onClick={this.gotoDetail} key={index.toString()}>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName: item.name, envName: currentEnv },
}}
>
<Card
title={item.name}
bordered={false}
extra={this.getFormatDate(item.created)}
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Applications</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingAll}>
<div className="applist">
<Form name="horizontal_login" layout="inline" onFinish={this.onFinish}>
<Form.Item>
<Link to="/ApplicationList/CreateApplication">
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
create
</Button>
</Link>
</Form.Item>
</Form>
</div>
<Row gutter={16}>
{Array.isArray(returnObj) && returnObj.length ? (
returnObj.map((item, index) => {
const { traits = [] } = item;
return (
<Col span={6} onClick={this.gotoDetail} key={index.toString()}>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName: item.name, envName: currentEnv },
}}
>
<div className="cardContent">
<div className="box2" style={{ height: this.getHeight(traits.length) }} />
<div className="box1">
{traits.length ? (
<div className="box3" style={{ width: '40px' }} />
) : (
''
)}
<Card
title={item.name}
bordered={false}
extra={this.getFormatDate(item.created)}
>
<div className="cardContent">
<div
className={['hasPadding', colorObj[item.status] || 'first3'].join(
' ',
className="box2"
style={{ height: this.getHeight(traits.length) }}
/>
<div className="box1">
{traits.length ? (
<div className="box3" style={{ width: '30px' }} />
) : (
''
)}
>
<ApartmentOutlined style={{ marginRight: '10px' }} />
{item.workload}
</div>
</div>
{traits.map((item1, index1) => {
return (
<div className="box1" key={index1.toString()}>
<div className="box3" style={{ width: '80px' }} />
<div className="other hasPadding">
<BranchesOutlined style={{ marginRight: '10px' }} />
{item1}
</div>
<div
className={['hasPadding', colorObj[item.status] || 'first3'].join(
' ',
)}
>
<ApartmentOutlined style={{ marginRight: '4px' }} />
{item.workload}
</div>
);
})}
</div>
</Card>
</Link>
</Col>
);
})
) : (
<div style={{ width: '100%', height: '80%' }}>
<Empty />
</div>
)}
</Row>
</Spin>
</PageContainer>
</div>
{traits.map((item1, index1) => {
return (
<div className="box1" key={index1.toString()}>
<div className="box3" style={{ width: '50px' }} />
<div className="other hasPadding">
<BranchesOutlined style={{ marginRight: '4px' }} />
{item1}
</div>
</div>
);
})}
</div>
</Card>
</Link>
</Col>
);
})
) : (
<div style={{ width: '100%', height: '80%' }}>
<Empty />
</div>
)}
</Row>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -15,38 +15,38 @@
}
.box2 {
position: absolute;
top: 21px;
top: 15px;
width: 0;
padding: 0;
border-left: 1px solid black;
}
.box3 {
position: absolute;
top: 21px;
top: 15px;
height: 0;
border-bottom: 1px solid black;
}
.hasPadding {
padding: 10px;
padding: 4px;
color: #fff;
}
div {
margin-bottom: 10px;
}
.first1 {
margin-left: 40px;
background: #1890ff;
margin-left: 30px;
background: #4caf51;
}
.first2 {
margin-left: 40px;
background: red;
margin-left: 30px;
background: #f44337;
}
.first3 {
margin-left: 40px;
background: #92c47c;
margin-left: 30px;
background: #1890ff;
}
.other {
margin-left: 80px;
background: #ee9611;
margin-left: 50px;
background: #ffc105;
}
}

View File

@@ -1,14 +1,11 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Space, Button, Row, Col, message, Spin } from 'antd';
// import { Space, Modal, Button, Row, Col, message, Spin } from 'antd';
// import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Space, Button, Row, Col, message, Spin, Breadcrumb } from 'antd';
import { Link } from 'umi';
import './index.less';
import { connect } from 'dva';
import _ from 'lodash';
// const { confirm } = Modal;
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.capability,
currentEnv: globalData.currentEnv,
@@ -53,8 +50,7 @@ class TableList extends React.PureComponent {
};
gotoOtherPage = () => {
// window.location.href = 'https://github.com/oam-dev/catalog/blob/master/workloads/cloneset/README.md';
window.open('https://github.com/oam-dev/catalog/blob/master/workloads/cloneset/README.md');
// window.open('https://github.com/oam-dev/catalog/blob/master/workloads/cloneset/README.md');
};
installSignle = async (e, name) => {
@@ -69,25 +65,21 @@ class TableList extends React.PureComponent {
});
if (res) {
message.success(res);
// this.getInitialData();
window.location.reload();
}
};
uninstallSignle = async (e, name) => {
e.stopPropagation();
// const capabilityCenterName = _.get(this.props, 'location.state.name', '');
if (name) {
const res = await this.props.dispatch({
type: 'capability/deleteOneCapability',
payload: {
// capabilityCenterName,
capabilityName: name,
},
});
if (res) {
message.success(res);
// this.getInitialData();
window.location.reload();
}
}
@@ -104,7 +96,6 @@ class TableList extends React.PureComponent {
});
if (res) {
message.success(res);
// this.getInitialData();
window.location.reload();
}
}
@@ -112,48 +103,6 @@ class TableList extends React.PureComponent {
showDeleteConfirm = () => {
message.info('正在开发中...');
// // eslint-disable-next-line
// const _this = this;
// const capabilityCenterName = _.get(this.props, 'location.state.name', '');
// if (capabilityCenterName) {
// confirm({
// title: `Are you sure delete ${capabilityCenterName}?`,
// icon: <ExclamationCircleOutlined />,
// width: 500,
// content: (
// <div>
// <p style={{ margin: '0px' }}>您本次移除 {capabilityCenterName},将会删除的应用列表:</p>
// <Space>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// </Space>
// <p style={{ margin: '0px' }}>
// 确认后,移除 {capabilityCenterName},并且删除相应的应用?
// </p>
// </div>
// ),
// okText: 'Yes',
// okType: 'danger',
// cancelText: 'No',
// async onOk() {
// const res = await _this.props.dispatch({
// type: 'capability/deleteCapability',
// payload: {
// capabilityName: capabilityCenterName,
// },
// });
// if (res) {
// message.success(res);
// _this.props.history.push({ pathname: '/Capability' });
// }
// },
// onCancel() {
// // console.log('Cancel');
// },
// });
// }
};
render() {
@@ -161,82 +110,101 @@ class TableList extends React.PureComponent {
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
return (
<PageContainer>
<Spin spinning={loadingAll}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.syncAllSignle}>
Install all
</Button>
<Button type="default" onClick={this.showDeleteConfirm}>
Remove
</Button>
</Space>
</div>
<div>
<h3>Workloads</h3>
<Row>
{workloadList.length ? (
workloadList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<img
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
alt="workload"
/>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的workload</div>
</Fragment>
)}
</Row>
</div>
<div>
<h3>Traits</h3>
<Row>
{traitList.length ? (
traitList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<img
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
alt="workload"
/>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的trait</div>
</Fragment>
)}
</Row>
</div>
</Spin>
</PageContainer>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/Capability">Capability</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Detail</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingAll}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.syncAllSignle}>
Install all
</Button>
<Button type="default" onClick={this.showDeleteConfirm}>
Remove
</Button>
</Space>
</div>
<div>
<h3>Workloads</h3>
<Row>
{workloadList.length ? (
workloadList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<div className="title">{item.name.substr(0, 3).toUpperCase()}</div>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button
onClick={(e) => this.installSignle(e, item.name)}
type="primary"
ghost
>
install
</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的workload</div>
</Fragment>
)}
</Row>
</div>
<div>
<h3>Traits</h3>
<Row>
{traitList.length ? (
traitList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<div className="title">{item.name.substr(0, 3).toUpperCase()}</div>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button
onClick={(e) => this.installSignle(e, item.name)}
type="primary"
ghost
>
install
</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的trait</div>
</Fragment>
)}
</Row>
</div>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -3,7 +3,7 @@
flex-direction: column;
align-items: center;
justify-content: center;
margin: 10px;
margin: 10px 16px 10px 0;
padding: 10px 0;
background-color: #fff;
cursor: pointer;
@@ -11,6 +11,12 @@
width: 60%;
margin: 10px auto;
}
.title {
width: 60%;
margin: 10px auto;
font-size: 30px;
text-align: center;
}
p {
margin: 0;
margin-bottom: 10px;

View File

@@ -1,13 +1,12 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Table, Space, Modal, Form, Input, message, Spin } from 'antd';
// import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Button, Table, Space, Modal, Form, Input, message, Spin, Breadcrumb } from 'antd';
import { CopyOutlined } from '@ant-design/icons';
import { Link } from 'umi';
import './index.less';
import { connect } from 'dva';
import _ from 'lodash';
// const { confirm } = Modal;
const { Column } = Table;
const layout = {
@@ -85,13 +84,6 @@ class TableList extends React.PureComponent {
}
};
// handleTest = async () => {
// await this.formRef.current.validateFields();
// this.setState({
// visible: false,
// });
// };
handleCancel = () => {
this.setState({
visible: false,
@@ -113,7 +105,6 @@ class TableList extends React.PureComponent {
});
if (res) {
message.success(res);
// this.getInitialData();
}
const newList1 = _.cloneDeep(this.state.capabilityList);
newList1[index].btnSyncLoading = false;
@@ -124,47 +115,19 @@ class TableList extends React.PureComponent {
}
};
copyURL = (text) => {
const oInput = document.createElement('input');
oInput.value = text;
document.body.appendChild(oInput);
oInput.select();
document.execCommand('Copy');
oInput.className = 'oInput';
oInput.style.display = 'none';
message.success('copy success');
};
showDeleteConfirm = () => {
message.info('正在开发中...');
// if (record) {
// // eslint-disable-next-line
// const _this = this;
// confirm({
// title: `Are you sure delete ${record}?`,
// icon: <ExclamationCircleOutlined />,
// width: 500,
// content: (
// <div>
// <p>您本次移除 {record},将会删除的应用列表:</p>
// <Space>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// </Space>
// <p>确认后,移除{record},并且删除相应的应用?</p>
// </div>
// ),
// okText: 'Yes',
// okType: 'danger',
// cancelText: 'No',
// async onOk() {
// const res = await _this.props.dispatch({
// type: 'capability/deleteCapability',
// payload: {
// capabilityName: record,
// },
// });
// if (res) {
// message.success(res);
// _this.getInitialData();
// }
// },
// onCancel() {
// // console.log('Cancel');
// },
// });
// }
};
render() {
@@ -173,106 +136,123 @@ class TableList extends React.PureComponent {
loadingList = loadingList || false;
capabilityList = Array.isArray(capabilityList) ? capabilityList : [];
return (
<PageContainer>
<Spin spinning={loadingList}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.showModal}>
Create
</Button>
{/* <Button type="default">Sync All</Button> */}
</Space>
</div>
<Modal
title="Create Capability Center"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
// <Button key="test" onClick={this.handleTest}>
// Test
// </Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Create
</Button>,
]}
>
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
<Form.Item
name="Name"
label="Name"
rules={[
{
required: true,
message: 'Please input name!',
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="Address"
label="URL"
rules={[
// { pattern: '/^((https|http|ftp|rtsp|mms){0,1}(:\/\/){0,1})\.(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/',
// message: 'please input correct URL'
// },
{
required: true,
message: 'Please input URL!',
},
]}
>
<Input />
</Form.Item>
</Form>
</Modal>
<Table dataSource={capabilityList} pagination={false} rowKey={(record) => record.name}>
<Column
title="Name"
dataIndex="name"
key="name"
render={(text, record) => {
return (
<Link to={{ pathname: '/Capability/Detail', state: { name: record.name } }}>
{text}
</Link>
);
}}
/>
<Column
title="URL"
dataIndex="url"
key="url"
render={(text) => {
return (
<a href={text} target="_blank" rel="noreferrer">
{text}
</a>
);
}}
/>
<Column
title="Operations"
dataIndex="name"
key="name"
render={(text, record, index) => {
return (
<Space>
<Button
loading={record.btnSyncLoading}
onClick={() => this.syncSignle(text, index)}
>
sync
</Button>
<Button onClick={() => this.showDeleteConfirm(text)}>remove</Button>
</Space>
);
}}
/>
</Table>
</Spin>
</PageContainer>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Capability</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingList}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.showModal}>
Create
</Button>
</Space>
</div>
<Modal
title="Create Capability Center"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="submit" type="primary" onClick={this.handleOk}>
Create
</Button>,
]}
>
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
<Form.Item
name="Name"
label="Name"
rules={[
{
required: true,
message: 'Please input name!',
},
{
pattern: new RegExp('^[0-9a-zA-Z_]{1,60}$', 'g'),
message:
'The maximum length is 60,should be combination of numbers,alphabets,underline!',
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="Address"
label="URL"
rules={[
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
{
required: true,
message: 'Please input URL!',
},
]}
>
<Input />
</Form.Item>
</Form>
</Modal>
<Table dataSource={capabilityList} pagination={false} rowKey={(record) => record.name}>
<Column
title="Name"
dataIndex="name"
key="name"
render={(text, record) => {
return (
<Link to={{ pathname: '/Capability/Detail', state: { name: record.name } }}>
{text}
</Link>
);
}}
/>
<Column
title="URL"
dataIndex="url"
key="url"
width="60%"
render={(text) => {
return (
<div className="hoverItem">
<a href={text} target="_blank" rel="noreferrer">
{text}
</a>
<div className="copyIcon" onClick={() => this.copyURL(text)}>
<CopyOutlined />
</div>
</div>
);
}}
/>
<Column
title="Operations"
dataIndex="name"
key="name"
render={(text, record, index) => {
return (
<Space>
<Button
loading={record.btnSyncLoading}
onClick={() => this.syncSignle(text, index)}
type="primary"
ghost
>
sync
</Button>
<Button onClick={() => this.showDeleteConfirm(text)}>remove</Button>
</Space>
);
}}
/>
</Table>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -1,3 +1,14 @@
p {
margin: 0;
}
.hoverItem {
.copyIcon {
display: none;
margin-left: 10px;
font-size: 16px;
cursor: pointer;
}
}
.hoverItem:hover .copyIcon {
display: inline-block;
}

View File

@@ -178,7 +178,8 @@ const TableList = (props) => {
},
{
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
message: 'The maximum length is 63,should be combination of numbers,alphabets,underline!',
message:
'The maximum length is 63,should be combination of numbers,alphabets,underline!',
},
]}
>

View File

@@ -1,9 +1,11 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Form, Input, Button, Row, Col, Tabs, Table } from 'antd';
import { Form, Input, Button, Row, Col, Tabs, Table, Breadcrumb } from 'antd';
import { CheckCircleOutlined } from '@ant-design/icons';
import _ from 'lodash';
import { connect } from 'dva';
import { Link } from 'umi';
const { TabPane } = Tabs;
const layout = {
@@ -27,7 +29,12 @@ const columns = [
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text) => <a>{text}</a>,
render: (text) => (
<div>
<CheckCircleOutlined style={{ fontSize: '20px', color: '#4CAF51' }} />
<a style={{ marginLeft: '6px' }}>{text}</a>
</div>
),
},
{
title: 'Ready',
@@ -120,13 +127,6 @@ const data1 = [
LastTransition: '2d',
},
];
// const demoText = `H4sIAAAAAAAA/6xUwW7bOhD8lYc9U4lsWfazgB4C5Na0DZK0lyKHNbmyWVMkQ66UGob/vSBdt3GCxEXRmwjujmZnZrmFtbYKGrgkb9ymI8sgAL3+QiFqZ6EB9D6eDyMQ0BGjQkZotmCxI2hAOmcKDBtcrJCxGMrWfn+Ygsj30aNMRYpa7E0CloGQtbN3uqPI2HlobG+MAIMLMjEBo/cvcEGAW3wjyZH4LGh3JpHZ0Jl25yuMK2hgPkPV1lRJbKfjWVlNpqps69mkrNu2qv5X5UzV9ULVIOC4P1IYtHxzlOGXFEMJOwForeM8Rib8GjOdZD3Avz6Ae7QUiuWwhuYZtWEk/nuvrXp3+4cgpzw53f3SsePKjrLaHHpKSuTGG2opkJUUofm6Pc7O84FAHPL2e6ZTrPssZDWq5+O6GhVqPquLybyaF9gqKsbVTMpyXFZ1m8yVznJwxlCAJrEUsDBOrj8lopdkiDOvFk2k3f1OQPQkk4mRDEl2IX13yHJ1dSqQx6nYCWDqvEGmDPFkU/4+8/86qUbbNQWVw2lTFKABsrgwpN52+olQSWDUlsLe7VPm6Q6XlPdABspPS1imTihWcC8gUHR9yNHZQqCHniLnb+l7aKAuu/zsdC5soIHp5IPOZDLqdW/MtTNapqsL84ibCLt7cVi5Cyldb/njCYLYs+tS4e1R251bkz1EaK/Rz4IrbdfxEKEkDAdkWm4Sa9749LMbZ4y2y89epTgICEfnZrvb9yH3MZ9+BAAA//+bxjCThQUAAA
// objectset.rio.cattle.io/id service
// objectset.rio.cattle.io/owner-gvk rio.cattle.io/v1, Kind=Service
// objectset.rio.cattle.io/owner-name cool-aryabhata-v0fnxq6
// objectset.rio.cattle.io/owner-namespace default
// rio.cattle.io/mesh true
// Controlled By cool-aryabhata-v0fnxq6`;
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.applist,
@@ -204,16 +204,6 @@ class TableList extends React.Component {
render() {
const { hasShowEdit, hasShowEdit2 } = this.state;
// let finallyText;
// if (demoText && demoText.length > 50) {
// finallyText = (
// <Tooltip placement="topRight" title={demoText}>
// <Button>{`${demoText.substring(0, 50)}......`}</Button>
// </Tooltip>
// );
// } else {
// finallyText = <p>{demoText}</p>;
// }
const status = _.get(this.state.appDetailData, 'Status', '');
const { traitItem, appName } = this.state;
const metadata = _.get(traitItem, 'metadata', '');
@@ -224,341 +214,269 @@ class TableList extends React.Component {
if (traitItem.kind === 'Ingress') {
traitType = 2;
}
const envName = _.get(this.props, 'location.state.envName', '');
return (
<PageContainer>
<div className="card-container trait-detail">
<h2>{traitName}</h2>
<p style={{ marginBottom: '20px' }}>
<i>
{traitItem.apiVersion}1,Name={appName}
</i>
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<div>
<Row>
<Col span="12">
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Configuration</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>domain</p>
</Col>
<Col span="16">
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(spec, 'rules[0].http.paths[0].backend.serviceName', '')}
</p>
</Col>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(spec, 'rules[0].http.paths[0].backend.servicePort', '')}
</p>
</Col>
</Fragment>
) : (
Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})
)}
{/* {Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName, envName },
}}
>
ApplicationListDetail
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>TraitDetail</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<div className="card-container trait-detail">
<h2>{traitName}</h2>
<p style={{ marginBottom: '20px' }}>
<i>
{traitItem.apiVersion}1,Name={appName}
</i>
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<div>
<Row>
<Col span="12">
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Configuration</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>{currentKey}</p>
<p>domain</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(spec, 'rules[0].http.paths[0].backend.serviceName', '')}
</p>
</Col>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(spec, 'rules[0].http.paths[0].backend.servicePort', '')}
</p>
</Col>
</Fragment>
);
})} */}
</Row>
{/* <Row>
<Col span="10">
<div style={{ color: 'black' }}>
<p>Deployment Strategy</p>
<p>Rolling Update Strategy</p>
<p>Selectors</p>
<p>Min Ready Seconds</p>
<p>Revision History Limit</p>
<p>Replicas</p>
</div>
</Col>
<Col>
<p>RollingUpdate</p>
<p>Max Surge 25%, Max Unavailable 25%</p>
<p>
<Tag color="orange">aryabhataapp:cool</Tag>
<Tag color="orange">version:v0</Tag>
</p>
<p>0</p>
<p>10</p>
<p>1</p>
</Col>
</Row> */}
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
labelAlign="left"
{...layout}
ef={this.formRefStep1}
name="control-ref"
onFinish={this.onFinishStep1}
>
<div className="relativeBox">
<Form.Item name="Replicas" label="Replicas">
<Input type="number" />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
) : (
Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})
)}
</Row>
</div>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit}
className="hasPadding"
style={{ display: hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
labelAlign="left"
{...layout}
ef={this.formRefStep1}
name="control-ref"
onFinish={this.onFinishStep1}
>
Edit
<div className="relativeBox">
<Form.Item name="Replicas" label="Replicas">
<Input type="number" />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit}
>
Edit
</Button>
</div>
</div>
</div>
</Col>
<Col span="1" />
<Col span="10">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Status</p>
<p>{status}</p>
</div>
</div>
</Col>
</Row>
<p className="title hasBG">Pods</p>
<Table columns={columns} dataSource={data} pagination={false} />
<p className="title hasBG">Conditions</p>
<Table columns={columns1} dataSource={data1} pagination={false} />
<p className="title hasBG">Pod Template</p>
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Container cool-aryabhata-v0fnxq6</p>
<Row>
<Col span="2">
<div style={{ color: 'black' }}>
<p>Image</p>
<p>Args</p>
</div>
</Col>
<Col>
<p>secret</p>
<p>[&apos;-h&apos;]</p>
</Col>
</Row>
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
style={{ width: '50%' }}
{...layout1}
labelAlign="left"
ef={this.formRefStep2}
name="control-ref"
onFinish={this.onFinishStep2}
>
<div className="relativeBox">
<Form.Item name="Image" label="Image">
<Input />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
Cancle
</Button>
</div>
</div>
</Form>
</div>
</Col>
<Col span="1" />
<Col span="10">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Status</p>
<p>{status}</p>
{/* <Row>
<Col span="10">
<div style={{ color: 'black' }}>
<p>Avaliable Replicas</p>
<p>Ready Replicas</p>
<p>Total Replicas</p>
<p>Unavaliable Replicas</p>
<p>Updated Replicas</p>
</div>
</Col>
<Col>
<p>0</p>
<p>0</p>
<p>1</p>
<p>1</p>
<p>1</p>
</Col>
</Row> */}
</div>
</div>
</Col>
</Row>
<p className="title" style={{ marginTop: '16px' }}>
Pods
</p>
<Table columns={columns} dataSource={data} />
<p className="title">Conditions</p>
<Table columns={columns1} dataSource={data1} />
<p className="title">Pod Template</p>
<div className="hasBorder">
<div className="hasPadding" style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<p className="title">Container cool-aryabhata-v0fnxq6</p>
<Row>
<Col span="2">
<div style={{ color: 'black' }}>
<p>Image</p>
<p>Args</p>
</div>
</Col>
<Col>
<p>secret</p>
<p>[&apos;-h&apos;]</p>
</Col>
</Row>
</div>
<div className="hasPadding" style={{ display: hasShowEdit2 ? 'block' : 'none' }}>
<p className="title">Deployment Editor</p>
<Form
style={{ width: '50%' }}
{...layout1}
labelAlign="left"
ef={this.formRefStep2}
name="control-ref"
onFinish={this.onFinishStep2}
>
<div className="relativeBox">
<Form.Item name="Image" label="Image">
<Input />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
Cancle
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<div style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }} />
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit2}
>
Edit
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<div style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }} />
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit2}
>
Edit
</Button>
</div>
</div>
</div>
</div>
</TabPane>
<TabPane tab="Metadata" key="2">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Metadata</p>
{Object.keys(metadata).map((currentKey8) => {
if (currentKey8 === 'annotations') {
</TabPane>
<TabPane tab="Metadata" key="2">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Metadata</p>
{Object.keys(metadata).map((currentKey8) => {
if (currentKey8 === 'annotations') {
return (
<Row key={currentKey8}>
<Col span="4">
<div style={{ color: 'black' }}>
<p>{currentKey8}</p>
</div>
</Col>
<Col span="20">
{Object.keys(metadata[currentKey8]).map((currentKey9) => {
return (
<Row key={currentKey9}>
<Col span="8">
<div style={{ color: 'black' }}>
<p>{currentKey9}</p>
</div>
</Col>
<Col>
<p>{metadata[currentKey8][currentKey9]}</p>
</Col>
</Row>
);
})}
</Col>
</Row>
);
}
return (
<Row key={currentKey8}>
<Col span="4">
<div style={{ color: 'black' }}>
<p>{currentKey8}</p>
</div>
<p>{currentKey8}</p>
</Col>
<Col span="20">
{Object.keys(metadata[currentKey8]).map((currentKey9) => {
return (
<Row key={currentKey9}>
<Col span="8">
<div style={{ color: 'black' }}>
<p>{currentKey9}</p>
</div>
</Col>
<Col>
<p>{metadata[currentKey8][currentKey9]}</p>
</Col>
</Row>
);
})}
<Col>
<p>{metadata[currentKey8]}</p>
</Col>
</Row>
);
}
return (
<Row key={currentKey8}>
<Col span="4">
<p>{currentKey8}</p>
</Col>
<Col>
<p>{metadata[currentKey8]}</p>
</Col>
</Row>
);
})}
{/* <Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Annotations</p>
</div>
</Col>
<Col span="20">
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>deployment.kubernetes.io/revision</p>
</div>
</Col>
<Col>
<p>1</p>
</Col>
</Row>
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>objectset.rio.cattle.io/applied</p>
</div>
</Col>
<Col span="16">{finallyText}</Col>
</Row>
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>objectset.rio.cattle.io/id</p>
</div>
</Col>
<Col span="16">
<p>service</p>
</Col>
</Row>
</Col>
</Row>
<Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Controlled By</p>
</div>
</Col>
<Col>
<p>cool-aryabhata-v0fnxq6</p>
</Col>
</Row> */}
})}
</div>
</div>
</div>
</TabPane>
<TabPane tab="Resource Viewer" key="3">
<p>Resource Viewer</p>
</TabPane>
<TabPane tab="YAML" key="4">
<p>YAML</p>
</TabPane>
</Tabs>
</div>
</PageContainer>
</TabPane>
<TabPane tab="Resource Viewer" key="3">
<p>Resource Viewer</p>
</TabPane>
<TabPane tab="YAML" key="4">
<p>YAML</p>
</TabPane>
</Tabs>
</div>
</PageContainer>
</div>
);
}
}

View File

@@ -9,13 +9,19 @@
.hasBorder {
border: 1px solid #eee;
.hasPadding {
padding: 16px 16px 0;
padding: 16px;
}
}
.title {
color: black;
font-size: 18px;
}
.hasBG {
margin-top: 50px;
margin-bottom: 8px;
padding-left: 16px;
background: rgba(0, 0, 0, 0.04);
}
.textAlignLeft {
padding-left: 16px;
text-align: left;

View File

@@ -1,8 +1,10 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Form, Input, Button, Row, Col, Tabs, Table, Spin } from 'antd';
import { Form, Input, Button, Row, Col, Tabs, Table, Spin, Breadcrumb } from 'antd';
import { CheckCircleOutlined } from '@ant-design/icons';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
const { TabPane } = Tabs;
@@ -27,7 +29,12 @@ const columns = [
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text) => <a>{text}</a>,
render: (text) => (
<div>
<CheckCircleOutlined style={{ fontSize: '20px', color: '#4CAF51' }} />
<a style={{ marginLeft: '6px' }}>{text}</a>
</div>
),
},
{
title: 'Ready',
@@ -120,13 +127,6 @@ const data1 = [
LastTransition: '2d',
},
];
// const demoText = `H4sIAAAAAAAA/6xUwW7bOhD8lYc9U4lsWfazgB4C5Na0DZK0lyKHNbmyWVMkQ66UGob/vSBdt3GCxEXRmwjujmZnZrmFtbYKGrgkb9ymI8sgAL3+QiFqZ6EB9D6eDyMQ0BGjQkZotmCxI2hAOmcKDBtcrJCxGMrWfn+Ygsj30aNMRYpa7E0CloGQtbN3uqPI2HlobG+MAIMLMjEBo/cvcEGAW3wjyZH4LGh3JpHZ0Jl25yuMK2hgPkPV1lRJbKfjWVlNpqps69mkrNu2qv5X5UzV9ULVIOC4P1IYtHxzlOGXFEMJOwForeM8Rib8GjOdZD3Avz6Ae7QUiuWwhuYZtWEk/nuvrXp3+4cgpzw53f3SsePKjrLaHHpKSuTGG2opkJUUofm6Pc7O84FAHPL2e6ZTrPssZDWq5+O6GhVqPquLybyaF9gqKsbVTMpyXFZ1m8yVznJwxlCAJrEUsDBOrj8lopdkiDOvFk2k3f1OQPQkk4mRDEl2IX13yHJ1dSqQx6nYCWDqvEGmDPFkU/4+8/86qUbbNQWVw2lTFKABsrgwpN52+olQSWDUlsLe7VPm6Q6XlPdABspPS1imTihWcC8gUHR9yNHZQqCHniLnb+l7aKAuu/zsdC5soIHp5IPOZDLqdW/MtTNapqsL84ibCLt7cVi5Cyldb/njCYLYs+tS4e1R251bkz1EaK/Rz4IrbdfxEKEkDAdkWm4Sa9749LMbZ4y2y89epTgICEfnZrvb9yH3MZ9+BAAA//+bxjCThQUAAA
// objectset.rio.cattle.io/id service
// objectset.rio.cattle.io/owner-gvk rio.cattle.io/v1, Kind=Service
// objectset.rio.cattle.io/owner-name cool-aryabhata-v0fnxq6
// objectset.rio.cattle.io/owner-namespace default
// rio.cattle.io/mesh true
// Controlled By cool-aryabhata-v0fnxq6`;
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.applist,
@@ -195,337 +195,247 @@ class TableList extends React.PureComponent {
render() {
const { hasShowEdit, hasShowEdit2 } = this.state;
// let finallyText;
// if (demoText && demoText.length > 50) {
// finallyText = (
// <Tooltip placement="topRight" title={demoText}>
// <Button>{`${demoText.substring(0, 50)}......`}</Button>
// </Tooltip>
// );
// } else {
// finallyText = <p>{demoText}</p>;
// }
const status = _.get(this.state.appDetailData, 'Status', '');
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
const metadata = _.get(Workload, 'metadata', {});
let containers = {};
// if (Workload.kind === 'ContainerizedWorkload') {
// containers = _.get(Workload, 'spec.containers[0]', {});
// } else if (Workload.kind === 'Deployment') {
// containers = _.get(Workload, 'spec.template.spec.containers[0]', {});
// }
containers = _.get(Workload, 'spec.containers[0]', {});
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const appName = _.get(this.props, 'location.state.appName', '');
const envName = _.get(this.props, 'location.state.envName', '');
return (
<PageContainer>
<Spin spinning={loadingAll}>
<div className="card-container workload-detail">
<h2>{Workload.kind}</h2>
<p style={{ marginBottom: '20px' }}>
<i>
{Workload.apiVersion},Name={_.get(Workload, 'metadata.name', '')}
</i>
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<div>
<Row>
<Col span="12">
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Configuration</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName, envName },
}}
>
ApplicationListDetail
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>WorkloadDetail</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Spin spinning={loadingAll}>
<div className="card-container workload-detail">
<h2>{Workload.kind}</h2>
<p style={{ marginBottom: '20px' }}>
<i>
{Workload.apiVersion},Name={_.get(Workload, 'metadata.name', '')}
</i>
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<div>
<Row>
<Col span="12">
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Configuration</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(containers[currentKey], '[0].containerPort', '')}
</p>
</Col>
</Fragment>
);
// eslint-disable-next-line no-else-return
} else if (currentKey === 'name') {
return <Fragment key={currentKey} />;
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>port</p>
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>
{_.get(containers[currentKey], '[0].containerPort', '')}
</p>
<p>{containers[currentKey]}</p>
</Col>
</Fragment>
);
// eslint-disable-next-line no-else-return
} else if (currentKey === 'name') {
return <Fragment key={currentKey} />;
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{containers[currentKey]}</p>
</Col>
</Fragment>
);
})}
</Row>
{/* <Row>
<Col span="10">
<div style={{ color: 'black' }}>
<p>Deployment Strategy</p>
<p>Rolling Update Strategy</p>
<p>Selectors</p>
<p>Min Ready Seconds</p>
<p>Revision History Limit</p>
<p>Replicas</p>
</div>
</Col>
<Col>
<p>RollingUpdate</p>
<p>Max Surge 25%, Max Unavailable 25%</p>
<p>
<Tag color="orange">aryabhataapp:cool</Tag>
<Tag color="orange">version:v0</Tag>
</p>
<p>0</p>
<p>10</p>
<p>1</p>
</Col>
</Row> */}
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
labelAlign="left"
{...layout}
ef={this.formRefStep1}
name="control-ref"
onFinish={this.onFinishStep1}
>
<div className="relativeBox">
<Form.Item name="Replicas" label="Replicas">
<Input type="number" />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
})}
</Row>
</div>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit}
className="hasPadding"
style={{ display: hasShowEdit ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
labelAlign="left"
{...layout}
ef={this.formRefStep1}
name="control-ref"
onFinish={this.onFinishStep1}
>
Edit
<div className="relativeBox">
<Form.Item name="Replicas" label="Replicas">
<Input type="number" />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button
style={{ marginLeft: '16px' }}
onClick={this.changeShowEdit}
>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit ? 'block' : 'none' }}>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit}
>
Edit
</Button>
</div>
</div>
</div>
</Col>
<Col span="1" />
<Col span="10">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Status</p>
<p>{status}</p>
</div>
</div>
</Col>
</Row>
<p className="title hasBG">Pods</p>
<Table columns={columns} dataSource={data} pagination={false} />
<p className="title hasBG">Conditions</p>
<Table columns={columns1} dataSource={data1} pagination={false} />
<p className="title hasBG">Pod Template</p>
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Container cool-aryabhata-v0fnxq6</p>
<Row>
<Col span="2">
<div style={{ color: 'black' }}>
<p>Image</p>
<p>Args</p>
</div>
</Col>
<Col>
<p>secret</p>
<p>[&apos;-h&apos;]</p>
</Col>
</Row>
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
style={{ width: '50%' }}
{...layout1}
labelAlign="left"
ef={this.formRefStep2}
name="control-ref"
onFinish={this.onFinishStep2}
>
<div className="relativeBox">
<Form.Item name="Image" label="Image">
<Input />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<div
style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }}
/>
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit2}
>
Edit
</Button>
</div>
</div>
</Col>
<Col span="1" />
<Col span="10">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Status</p>
<p>{status}</p>
{/* <Row>
<Col span="10">
<div style={{ color: 'black' }}>
<p>Avaliable Replicas</p>
<p>Ready Replicas</p>
<p>Total Replicas</p>
<p>Unavaliable Replicas</p>
<p>Updated Replicas</p>
</div>
</div>
</div>
</TabPane>
<TabPane tab="Metadata" key="2">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Metadata</p>
{Object.keys(metadata).map((currentKey6) => {
return (
<Row key={currentKey6}>
<Col span="4">
<p>{currentKey6}</p>
</Col>
<Col>
<p>0</p>
<p>0</p>
<p>1</p>
<p>1</p>
<p>1</p>
<p>{metadata[currentKey6]}</p>
</Col>
</Row> */}
</div>
</div>
</Col>
</Row>
<p className="title" style={{ marginTop: '16px' }}>
Pods
</p>
<Table columns={columns} dataSource={data} />
<p className="title">Conditions</p>
<Table columns={columns1} dataSource={data1} />
<p className="title">Pod Template</p>
<div className="hasBorder">
<div
className="hasPadding"
style={{ display: !hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Container cool-aryabhata-v0fnxq6</p>
<Row>
<Col span="2">
<div style={{ color: 'black' }}>
<p>Image</p>
<p>Args</p>
</div>
</Col>
<Col>
<p>secret</p>
<p>[&apos;-h&apos;]</p>
</Col>
</Row>
</div>
<div
className="hasPadding"
style={{ display: hasShowEdit2 ? 'block' : 'none' }}
>
<p className="title">Deployment Editor</p>
<Form
style={{ width: '50%' }}
{...layout1}
labelAlign="left"
ef={this.formRefStep2}
name="control-ref"
onFinish={this.onFinishStep2}
>
<div className="relativeBox">
<Form.Item name="Image" label="Image">
<Input />
</Form.Item>
</div>
<div style={{ marginBottom: '16px' }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button style={{ marginLeft: '16px' }} onClick={this.changeShowEdit2}>
Cancle
</Button>
</div>
</Form>
</div>
<div style={{ display: !hasShowEdit2 ? 'block' : 'none' }}>
<div style={{ width: '100%', borderTop: '1px solid #eee', height: '0px' }} />
<div>
<Button
className="textAlignLeft"
type="link"
block
onClick={this.changeShowEdit2}
>
Edit
</Button>
</div>
</Row>
);
})}
</div>
</div>
</div>
</TabPane>
<TabPane tab="Metadata" key="2">
<div className="hasBorder">
<div className="hasPadding">
<p className="title">Metadata</p>
{Object.keys(metadata).map((currentKey6) => {
return (
<Row key={currentKey6}>
<Col span="4">
<p>{currentKey6}</p>
</Col>
<Col>
<p>{metadata[currentKey6]}</p>
</Col>
</Row>
);
})}
{/* <Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Age</p>
<p>Labels</p>
</div>
</Col>
<Col>
<p>2d</p>
<p>
<Tag color="orange">aryabhataapp:cool</Tag>
<Tag color="orange">version:v0</Tag>
</p>
</Col>
</Row>
<Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Annotations</p>
</div>
</Col>
<Col span="20">
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>deployment.kubernetes.io/revision</p>
</div>
</Col>
<Col>
<p>1</p>
</Col>
</Row>
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>objectset.rio.cattle.io/applied</p>
</div>
</Col>
<Col span="16">{finallyText}</Col>
</Row>
<Row>
<Col span="8">
<div style={{ color: 'black' }}>
<p>objectset.rio.cattle.io/id</p>
</div>
</Col>
<Col span="16">
<p>service</p>
</Col>
</Row>
</Col>
</Row>
<Row>
<Col span="4">
<div style={{ color: 'black' }}>
<p>Controlled By</p>
</div>
</Col>
<Col>
<p>cool-aryabhata-v0fnxq6</p>
</Col>
</Row> */}
</div>
</div>
</TabPane>
<TabPane tab="Resource Viewer" key="3">
<p>Resource Viewer</p>
</TabPane>
<TabPane tab="YAML" key="4">
<p>YAML</p>
</TabPane>
</Tabs>
</div>
</Spin>
</PageContainer>
</TabPane>
<TabPane tab="Resource Viewer" key="3">
<p>Resource Viewer</p>
</TabPane>
<TabPane tab="YAML" key="4">
<p>YAML</p>
</TabPane>
</Tabs>
</div>
</Spin>
</PageContainer>
</div>
);
}
}

View File

@@ -9,13 +9,19 @@
.hasBorder {
border: 1px solid #eee;
.hasPadding {
padding: 16px 16px 0;
padding: 16px;
}
}
.title {
color: black;
font-size: 18px;
}
.hasBG {
margin-top: 50px;
margin-bottom: 8px;
padding-left: 16px;
background: rgba(0, 0, 0, 0.04);
}
.textAlignLeft {
padding-left: 16px;
text-align: left;

View File

@@ -11,7 +11,6 @@ export async function getapplist({ url }) {
export async function createApp({ params, url }) {
return request(url, {
method: 'POST',
// body:JSON.stringify(params),
data: {
...params,
},

View File

@@ -11,7 +11,6 @@ export async function getCapabilityCenterlist() {
export async function createCapabilityCenter({ params }) {
return request('/api/capability-centers/', {
method: 'put',
// body:JSON.stringify(params),
data: {
...params,
},

View File

@@ -35,11 +35,6 @@ const errorHandler = async (error) => {
} else if (!response) {
message.error('网络异常: 您的网络发生异常,无法连接服务器');
}
// throw error; // 如果throw. 错误将继续抛出.
// 如果return, 则将值作为返回. 'return;' 相当于return undefined, 在处理结果时判断response是否有值即可.
// return {some: 'data'};
// return response;
};
/**
@@ -47,9 +42,7 @@ const errorHandler = async (error) => {
*/
const request = extend({
errorHandler, // 默认错误处理
credentials: 'include', // 默认请求是否带上cookie,
// prefix: '/api/v1',
// timeout: 1000,
credentials: 'include', // 默认请求是否带上cookie
});
/*
@@ -75,6 +68,5 @@ request.interceptors.response.use(async (response) => {
}
// eslint-disable-next-line consistent-return
return data.data;
// return response;
});
export default request;

View File

@@ -1 +0,0 @@
kubevela.io

View File

@@ -1,2 +0,0 @@
theme: jekyll-theme-cayman
source: website

View File

@@ -1,25 +0,0 @@
## Overview
For developers and operators, KubeVela, as a out-of-box Cloud Native Application Management Platform, provides numerous
workloads and operation toolings for application defining, deployment, scaling, traffic, rollout, routing, monitoring,
logging, alerting, CICD and so on.
For platform builders, KubeVela, as a highly extensible PaaS/Serverless Core, provides pluginable capabilities, an elegant
way to integrate any Workloads and Traits.
![](./resource/3-minutes-demo.jpg)
## Get Started
Check out [Get Started](https://github.com/cloud-native-application/RudrX) to try KubeVela.
Explore [Cli docs](https://github.com/cloud-native-application/RudrX/tree/master/documentation/cli) to get a quick glance
of the power of KubeVela.
## Community
## Support or Contact
Having trouble with KubeVela? File an [issue](https://github.com/cloud-native-application/RudrX/issues), or contact us directly [Twitter](https://twitter.com/oam_dev) or [Gitter](https://gitter.im/oam-dev/)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Some files were not shown because too many files have changed in this diff Show More