Compare commits

..

6 Commits

Author SHA1 Message Date
Sun Jianbo
17ac119f9b Merge pull request #297 from wonderflow/readme
add more readme
2020-09-18 23:11:21 +08:00
天元
829b813b1a add move readme 2020-09-18 23:02:10 +08:00
Sun Jianbo
bce8bfd40f Merge pull request #296 from wonderflow/con
temporarily add containerized to chart
2020-09-18 22:12:01 +08:00
天元
66855265c8 temporarily add containerized to chart 2020-09-18 21:59:48 +08:00
Sun Jianbo
63f52ea556 Merge pull request #294 from hanxie-crypto/feature04
Components static page,bugfix,capability delete
2020-09-18 21:56:38 +08:00
hanxie
fa37bf0284 Components static page,bugfix,capability delete 2020-09-18 17:47:39 +08:00
293 changed files with 27527 additions and 35028 deletions

View File

@@ -34,9 +34,6 @@ jobs:
with:
version: "v0.7.0"
- name: Load Image to kind cluster
run: make kind-load
- name: install Kubebuilder
uses: RyanSiu1995/kubebuilder-action@v1

View File

@@ -35,11 +35,3 @@ jobs:
- name: Run Make test
run: make test
- name: Upload coverage report
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.txt
flags: unittests
name: codecov-umbrella

6
.gitignore vendored
View File

@@ -13,7 +13,6 @@ e2e/vela
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
coverage.txt
# Kubernetes Generated files - skip generated files, except for vendored files
@@ -37,13 +36,10 @@ config/crd/bases
tmp/
cmd/vela/fake/source.go
cmd/vela/fake/chart_source.go
charts/vela-core/crds/_.yaml
.vela/
# Dashboard
node_modules/
dashboard/node_modules/
.eslintcache
dashboard/dist/
dashboard/package-lock.json

View File

@@ -1,3 +0,0 @@
# Code of Conduct
KubeVela follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).

View File

@@ -11,7 +11,6 @@ contributing to `kubevela` or build a PoC (Proof of Concept).
4. golangci-lint 1.31.0+, it will install automatically if you run `make`, you can [install it manually](https://golangci-lint.run/usage/install/#local-installation) if the installation is too slow.
## Build
* Clone this project
```shell script
@@ -32,34 +31,15 @@ after build, make will create `vela` binary to `bin/`, Set this path to PATH.
export PATH=$PATH:/your/path/to/project/kubevela/bin
```
Then you can use `vela` command directly.
* Build Vela Core
* Install Vela Core
```shell script
make manager
vela install
```
* Run Vela Core
Firstly make sure your cluster has CRDs.
```shell script
make core-install
```
Run locally:
```shell script
make core-run
```
This command will run controller locally, it will use your local KubeConfig which means you need to have a k8s cluster
locally. If you don't have a one, we suggest that you could setup up a cluster with [kind](https://kind.sigs.k8s.io/).
## Use
* Create environment
* Create ENV
```shell script
vela env init myenv --namespace myenv --email my@email.com --domain kubevela.io
@@ -70,7 +50,7 @@ vela env init myenv --namespace myenv --email my@email.com --domain kubevela.io
For example, use the following command to create and run an application.
```shell script
$ vela comp deploy mycomp -t webservice --image crccheck/hello-world --port 8000
$ vela comp run mycomp -t webservice --image crccheck/hello-world --port 8000
Creating AppConfig appcomp
SUCCEED
```
@@ -89,7 +69,7 @@ Succeeded!
$ vela comp status abc
Showing status of Component abc deployed in Environment t2
Component Status:
Name: abc PodSpecWorkload(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind PodSpecWorkload workload is unknown for HealthScope
Name: abc Containerized(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind Containerized workload is unknown for HealthScope
Traits
└─Trait/route
@@ -118,15 +98,6 @@ make test
```
### E2E test
** Before e2e test start, make sure you have vela-core running.**
```shell script
make core-run
```
Start to test.
```
make e2e-test
```

192
DESIGN.md
View File

@@ -1,192 +0,0 @@
# KubeVela Design
This document is the detailed design and architecture of the KubeVela being built in this repository.
> All the diagram in this documentation could be found in [this slides](https://docs.google.com/presentation/d/1Y3gnKrd7fUZGgee7Ia9vBsRIYhcZLQwMUCDkk1RJvQc/edit?usp=sharing).
## Overview
KubeVela is a simple, complete, but highly extensible cloud native application platform based on Kubernetes and Open Application Model (OAM). KubeVela intends to bring application-centric experience to its end users and democratize building cloud native application platforms for platform engineers.
## User Stories
As a end user (e.g. application developers, operators etc) of the platform, I only want to focus on coding my business logic and ship them to various environments at ease. Let's say:
- Here's my source code.
- Here's my application configuration (described in end user PoV).
- Deploy it in test environment.
- Deploy it in production environment.
- Monitoring, debugging, rollout/rollback the application.
- Dockerfile is fine, but please keep it simple.
As a platform engineer, I want to build a easy-to-use platform for my end users. In detail, the platform should be:
- Heroku-like (_in terms of both user experience and functionality_).
- and I prefer to build my own version with OSS tools, particularly, with Kubernetes (for obvious reason).
- Easy to build.
- I am too busy to reinvent any wheel, I want to reuse every capability in Kubernetes community as possible, with minimal effort. Writing some simple CRD and controllers is fine, but please, just the simple ones like copy-paste.
- Powerful and highly extensible.
- I don't want to lock my users with restricted abstractions and capabilities like traditional PaaS or FaaS/Serverless. I love Kubernetes and what it has enabled. So in terms of capability, I hope my platform is fully open and has unlimited possibilities like Kubernetes itself, rather than another opinionated close system like traditional PaaS.
## Core Principles
In nutshell, the principles for KubeVela project are:
- For end users, it out-of-the-box ships a fully featured PaaS-like experience, nothing special.
- For platform builders, it works like a special Kubernetes "distro" or extensible PaaS core that could be used to build something more complex. It allows platform builders to ship any existing capabilities in cloud native ecosystem to end users with minimal effort, or develop a new capability at ease in a standardized and Kubernetes-native approach.
## Design Details
### 1. Application Centric
The design of KubeVela intends to make users think in terms of application, not containers or infrastructure.
Lacking application context impacts the user experience and significantly raised the bar to adopt cloud native stack. We believe "application" is the natural mindset of developers and it's the core concept an application platform should expose.
![alt](resources/app-centric.png)
KubeVela intends to let developers push code, define application in developer facing primitives, and make daily operations as configurations of the application.
Thus, KubeVela choose to:
1. Introduce "application" as first class citizen and main API.
2. Build the whole system around "application", i.e. model capabilities of Kubernetes as application configuration, with clarity and manageability.
#### Solution
Instead of creating a in-house "application CRD", KubeVela adopts [Open Application Model (OAM)](https://github.com/oam-dev/spec) as its application definition, since OAM:
1. Defines micro-services application by default.
2. Can model operations as part of the application (i.e. `Trait`).
2. Highly extensible: every workload and trait in OAM is a independent definition, no abstraction or capability lock-in.
### 2. Capability Oriented Architecture
To enable platform builders use KubeVela as the extensible "PaaS core", KubeVela intends to make its every capability a standalone "plug-in".
![alt](resources/coa.png)
For example, there are several "built-in" workload types in KubeVela such as `Web Service` or `Task`. It is by design that they are all independent CRD controllers that abstract Kubernetes built-in workloads and create Service automatically if needed. KubeVela itself is **NOT** aware of the specification or implementation of these workload types.
This means platform builders are free to bring their own workload types by simply install a CRD controller, or even just reference a k8s built-in resource like StatefulSet as new workload type.
Similarly, all the "built-in" operations such as `scaling` or `rollout` (i.e. "traits" in KubeVela) are also independent CRD controllers which are **NOT** bound with specific workload types. Platform builders are free to bring their own traits implementations by simply providing a CRD controller, reference a k8s built-in resource like `HPA` or `NetworkPolicy` as trait is also possible.
This loosely coupled design of KubeVela adopts the idea of Capability Oriented Architecture (COA), i.e. instead of creating a close system like traditional PaaS, KubeVela intends to become an application-centric framework to connect end users with underlying infrastructure capabilities.
#### Solution
We decide to build KubeVela core with [OAM Kubernetes runtime](https://github.com/crossplane/oam-kubernetes-runtime) which already implemented the building block features such as standalone workload type and trait controllers. Whether a given trait could work with certain workload types is also clarified as `appliesTo` field of trait definition. For platform builders, OAM Kubernetes Runtime also provided a [lightweight library](https://github.com/crossplane/oam-kubernetes-runtime/blob/2be3900a3817aed570de9ec353e6ab0b50e100f0/pkg/controller/v1alpha2/core/traits/manualscalertrait/manualscalertrait_controller.go#L42) to implement trait controller so it doesn't need to care about workload kinds.
##### Capability Register and Discovery
KubeVela leverages [OAM definition objects](https://github.com/oam-dev/spec/blob/master/4.workload_definitions.md) to register and discover workloads and traits:
```console
$ kubectl apply -f workload-definition.yaml # register a new workload type
$ kubectl apply -f trait-definition.yaml # register a new trait
```
Note that OAM definition objects only care about API resource, not including the controllers. Thus KubeVela intends to include a **CRD registry** so whenever a new API resource is installed as workload or trait, KubeVela could install its controller automatically from the registry. That of course means we envision the CRD registry could register a CRD and Helm chart (which contains the manifest of the controller). In practice, we are currently evaluating RedHat's Operator Lifecycle Manager (OLM) but no the final conclusion yet.
##### Cloud Services Integration
For capabilities like cloud services, KubeVela intends to leverage Kubernetes as the universal control plane so [Crossplane](https://github.com/crossplane/crossplane) core will be used to register cloud services as workload types.
### 3. Extensible User Interface
KubeVela is built with Kubernetes and OAM also adopts Kubernetes API resource model. So in nutshell, **ALL** functionalities of KubeVela core are able to be consumed by simple `kubectl`, for example:
```yaml
$ kubectl apply -f frontend-component.yaml # create frontend component
$ kubectl apply -f backend-component.yaml # create backend component
$ kubectl apply -f application-config.yaml # assign operational traits to components
```
However, Kubernetes API should not be the end game. Declarative YAML objects are great to build platforms like KubeVela with but when directly exposed to end users, it creates heavy mental burden and high learning curve. For example, even with highest level abstraction, the default [containerized workload schema](https://github.com/oam-dev/spec/blob/master/core/workloads/schema/containerized_workload.md) of OAM still has dozens fields to learn with many rough edges to be aware.
Thus KubeVela intends to introduce a lightweight user facing layer on top of its Kubernetes API objects with following goals in mind:
- Shorten the learning curve of new developers. Most capabilities in KubeVela are developed by big
companies that run very complex workloads. However, for the bigger developer community, the new user facing layer will provide a much simpler path to on-board KubeVela.
- Developers can describe their applications and behavior of their components without making assumptions on availability of specific Kubernetes API. For instance, a developer will be able to model auto-scaling needs without referring to the CRD of auto-scaling trait.
- Provides a single source of truth of the application description. The user facing layer allows developers to work with a single artifact to capture the application definition. This artifact is the definitive truth of how the application is supposed to look like. It simplifies administrative tasks such as change management. It also serves as an anchor for application truth to avoid configuration drifts during operation.
- Highly extensible. For example, when a new workload type or trait is installed, the end users could access this new capability directly from user interface layer, no re-compile or re-deploy of KubeVela is required.
#### Solution
We concluded above requirements of the user interface layer as introducing a modeling language on top of the existing model objects. With this context, we decide to adopt [CUElang](https://github.com/cuelang/cue) as the modeling language of KubeVela. In detail, every workload or trait definition will inline a cue template as the contract between user and Kubernetes API object.
However, we don't assume users need to learn or write CUElang directly. Instead, the reason we choose CUElang is it's just perfect to help us create developer facing tools based on it. Thus, KubeVela introduced three most common tools based to CUElang for developers to use:
1. A command line tool.
2. A GUI dashboard.
3. A Docker Compose style `appfile`.
An example for `vela cli`:
```console
$ vela comp deploy frontend -t webservice --image oamdev/testapp:v1 --port 80 --app helloworld
```
The `-t webservice --image oamdev/testapp:v1 --port 80` arguments are not hard coded, they are schema defined by in-line CUE template of `WebService` workload definition.
The `appfile` is essentially a YAML version of command line tool so we can support more complex and serious scenarios by simply running `$ vela up hello-world.yaml`:
```yaml
version: "1.0-alpha.1"
name: helloworld
services:
express-server:
type: webservice # workload type
build:
docker:
file: Dockerfile
context: .
image: oamdev/testapp:v1
cmd: ["node", "server.js"]
ports:
- 8080:80
env:
- FOO=bar
scale: # scaling trait
replica: 2
auto:
range: "1-10"
cpu: 80
qps: 1000
canary: # canary trait
step: 5
headers:
- "foo:bar.*"
redis:
image: oamdev/redis
secrets:
my-secret: /local-path/my-secret # load local file into k8s secret
```
The schema of above `appfile` is not hard coded, they are defined by in-line CUE templates of `WebService` workload definition, `Scaling` trait definition and `Canary` trait definition.
We will skip the example of dashboard, but similarly, the schema of GUI forms are defined by in-lined CUE template of definition objects.
## Architecture
![alt](resources/arch.png)
From highest level, KubeVela is composed by only two components:
### 1. User interface layer
Including: `cli`, `dashboard`, `appfile`, they are all client side tools to provide developer facing abstractions by leveraging CUElang based parametering and templating.
### 2. KubeVela core
Including:
- [OAM Kubernetes runtime](https://github.com/crossplane/oam-kubernetes-runtime) to provide application level building blocks such as `Component` and `Application` etc.
- [Built-in workload and trait controllers](https://github.com/oam-dev/kubevela/tree/master/pkg/controller/v1alpha1) to implement core capabilities such as `webservice`, `route` and `rollout` etc.
- Capability Management: manage features of KubeVela following Capability Oriented Architecture.
- Every feature of KubeVela is a "addon", and it is registered by Kubernetes API resource (including CRD) leveraging OAM definition objects.
- CRD Registry: register controllers of Kubernetes add-ons and discover them by CRD. This will enable automatically install controllers/operators when CRD is missing in the cluster.

View File

@@ -4,7 +4,7 @@ VELA_VERSION ?= 0.1.0
GIT_COMMIT ?= git-$(shell git rev-parse --short HEAD)
VELA_VERSION_VAR := github.com/oam-dev/kubevela/version.VelaVersion
VELA_GITVERSION_VAR := github.com/oam-dev/kubevela/version.GitRevision
LDFLAGS ?= "-X $(VELA_VERSION_VAR)=$(VELA_VERSION) -X $(VELA_GITVERSION_VAR)=$(GIT_COMMIT)"
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
@@ -21,13 +21,11 @@ all: build
# Run tests
test: fmt vet lint
./hack/unit_test.sh
go test ./pkg/... -coverprofile cover.out
# Build manager binary
build: fmt vet lint
go run hack/chart/generate.go
go build -o bin/vela -ldflags ${LDFLAGS} cmd/vela/main.go
git checkout cmd/vela/fake/chart_source.go
go run hack/chart/generate.go | go build -o bin/vela -ldflags ${LDFLAGS} cmd/vela/main.go
npm-build:
cd dashboard && npm run build && cd ./..
@@ -35,16 +33,11 @@ npm-build:
npm-install:
cd dashboard && npm install && cd ./..
generate-doc:
rm -r documentation/cli/*
go run hack/docgen/gen.go
generate-source:
go run hack/frontend/source.go
cross-build:
go run hack/chart/generate.go
GO111MODULE=on CGO_ENABLED=0 $(GOX) -ldflags $(LDFLAGS) -parallel=3 -output="_bin/{{.OS}}-{{.Arch}}/vela" -osarch='$(TARGETS)' ./cmd/vela/
go run hack/chart/generate.go | GO111MODULE=on CGO_ENABLED=0 $(GOX) -ldflags $(LDFLAGS) -parallel=3 -output="_bin/{{.OS}}-{{.Arch}}/vela" -osarch='$(TARGETS)' ./cmd/vela/
compress:
( \
@@ -80,10 +73,8 @@ docker-push:
docker push ${IMG}
e2e-setup:
bin/vela install --image-pull-policy IfNotPresent --image-repo vela-core-test --image-tag $(GIT_COMMIT)
ginkgo version
ginkgo -v -r e2e/setup
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=vela-core,app.kubernetes.io/instance=kubevela -n vela-system --timeout=600s
bin/vela dashboard &
e2e-test:
@@ -97,10 +88,6 @@ e2e-api-test:
e2e-cleanup:
# Clean up
# load docker image to the kind cluster
kind-load:
docker build -t vela-core-test:$(GIT_COMMIT) .
kind load docker-image vela-core-test:$(GIT_COMMIT) || { echo >&2 "kind not installed or error loading image: $(IMAGE)"; exit 1; }
# Image URL to use all building/pushing image targets
IMG ?= vela-core:latest
@@ -119,22 +106,22 @@ manager: generate fmt vet lint manifests
core-run: generate fmt vet manifests
go run ./cmd/core/main.go
# Install CRDs and Definitions of Vela Core into a cluster, this is for develop convenient.
# Install CRDs into a cluster
core-install: manifests
kubectl apply -f charts/vela-core/crds/
kubectl apply -f charts/vela-core/templates/definitions/
kubectl apply -f charts/third_party/prometheus
kustomize build config/crd | kubectl apply -f -
# Uninstall CRDs and Definitions of Vela Core from a cluster, this is for develop convenient.
# Uninstall CRDs from a cluster
core-uninstall: manifests
kubectl delete -f charts/vela-core/crds/
kubectl delete -f charts/vela-core/templates/definitions/
kubectl delete -f charts/third_party/prometheus
kustomize build config/crd | kubectl delete -f -
# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
core-deploy: manifests
cd config/manager && kustomize edit set image controller=${IMG}
kustomize build config/default | kubectl apply -f -
# 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-core/crds
rm charts/vela-core/crds/_.yaml
# Generate code
generate: controller-gen

267
README.md
View File

@@ -1,27 +1,8 @@
# KubeVela
The Open Application Platform based on Kubernetes and Open Application Model (OAM).
The Open Application Platform based on Kubernetes and OAM.
## Project Status
:rotating_light: **Warning: this project is still a work in progress with lots of rough edges, please don't look inside unless you know what you are doing.**
KubeVela project is initialized and maintained by the cloud native community since day 0 with [bootstrapping contributors from 8+ different organizations](https://github.com/oam-dev/kubevela/graphs/contributors). We intend for KubeVela to have a open governance since the very beginning and donate the project to neutral foundation as soon as it's released.
## Purpose and Goal
- For developers and operators
- KubeVela, as an out-of-box Cloud Native Application Management Platform, provides numerous workloads and operation tooling for application defining, deployment, scaling, traffic, rollout, routing, monitoring, logging, alerting, CI/CD and so on.
- For platform builders
- KubeVela, as a highly extensible PaaS/Serverless Core, provides pluggable capabilities, an elegant way to integrate any workloads and operational capabilities (i.e. traits).
## Design and Architecture
Read more about [KubeVela's high level design and architecture](DESIGN.md).
## Demo Instructions
See the demo instructions below get a sense of what we've accomplished and are working on.
: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
@@ -32,7 +13,7 @@ See the demo instructions below get a sense of what we've accomplished and are w
### 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.
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
@@ -40,154 +21,159 @@ sudo mv ./vela /usr/local/bin/vela
### Install Vela Core
```console
```shell script
$ vela install
```
This command will install vela core controller into your K8s cluster, along with built-in workloads and traits.
## Using KubeVela
## Demos
After `vela install` you will see available workloads and traits.
After `vela install` you will have workloads and traits locally, and available to use by vela cli.
```console
```shell script
$ vela workloads
NAME DEFINITION
backend podspecworkloads.standard.oam.dev
task jobs.batch.k8s.io
webservice podspecworkloads.standard.oam.dev
backend containerizeds.standard.oam.dev
containerized containerizedworkloads.core.oam.dev
task jobs
webservice containerizeds.standard.oam.dev
```
```console
```shell script
$ vela traits
NAME DEFINITION APPLIES TO
route routes.standard.oam.dev webservice,backend
scale manualscalertraits.core.oam.dev webservice,backend
route routes.standard.oam.dev webservice
backend
scale manualscalertraits.core.oam.dev webservice
backend
```
### Create environment
### Create ENV
Before working with your application, you should prepare an deploy environment for it (e.g. test, staging, prod etc).
Before working with your application, you should create an env for it.
```console
$ vela env init demo --namespace demo --email my@email.com --domain kubevela.io
ENVIROMENT demo CREATED, Namespace: demo, Email: my@email.com.
```shell script
$ vela env init myenv --namespace myenv --email my@email.com --domain kubevela.io
ENV myenv CREATED, Namespace: myenv, Email: my@email.com.
```
Vela will create a Kubernetes namespace called `demo` , with namespace level issuer for certificate generation using the email you provided.
It will create a namespace called myenv
You could check the environment metadata in your local:
```shell script
$ kubectl get ns
NAME STATUS AGE
myenv Active 40s
```
```console
$ cat ~/.vela/envs/demo/config.json
{"name":"demo","namespace":"demo","email":"my@email.com","domain":"kubevela.io","issuer":"oam-env-demo"}
A namespace level issuer for certificate generation with email.
```shell script
$ kubectl get issuers.cert-manager.io -n myenv
NAME READY AGE
oam-env-myenv True 40s
```
A env metadata in your local:
```shell script
$ cat ~/.vela/envs/myenv/config.json
{"name":"myenv","namespace":"myenv","email":"my@email.com","domain":"kubevela.io","issuer":"oam-env-myenv"}
```
### Create simple component
### Create Component
Then let's create application, we will use the `demo` environment.
Then let's create application, we will use our env created by default.
```console
$ vela comp deploy mycomp -t webservice --image crccheck/hello-world --port 8000 --app myapp
```shell script
$ vela comp run mycomp -t webservice --image crccheck/hello-world --port 8000 --app myapp
Creating AppConfig appcomp
SUCCEED
```
### Create micro-services application
It will create component named `mycomp`.
Vela supports micro-services application by default thanks to Open Application Model.
```shell script
$ kubectl get components -n myenv
NAME WORKLOAD-KIND AGE
mycomp Containerized 10s
```
```console
$ vela comp deploy db -t backend --image crccheck/hello-world --app myapp
And an AppConfig named myapp.
```shell script
$ kubectl get appconfig -n myenv
NAME AGE
myapp 24s
```
Vela Core will work for AppConfig and create K8s deployment and service.
```shell script
$ kubectl get deployment -n myenv
NAME READY UP-TO-DATE AVAILABLE AGE
mycomp 1/1 1 1 38s
```
```shell script
$ kubectl get svc -n myenv
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mycomp ClusterIP 172.21.4.228 <none> 8080/TCP 49s
```
### Multiple Component
Creating a new component in the same application is easy, just use the `--app` flag.
```shell script
$ vela comp run db -t backend --image crccheck/hello-world --app myapp
Creating App myapp
SUCCEED
```
```console
```shell script
$ vela comp ls
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
db myapp backend Deployed 2020-09-18 22:42:04 +0800 CST
mycomp myapp webservice Deployed 2020-09-18 22:42:04 +0800 CST
```
#### Under the hood
Now we can see the application deployed, let's add route trait for visiting.
In Kubernetes, vela creates an OAM application configuration named `myapp` to manage all related components.
### Add Trait
```console
$ kubectl get appconfig -n demo
NAME AGE
myapp 24s
```
```console
$ kubectl get components -n demo
NAME AGE
mycomp 24s
db 10s
```
Vela Core is responsible for managing the underlying Kubernetes resources linked with the components and application configuration above.
```console
$ kubectl get deployment -n demo
NAME READY UP-TO-DATE AVAILABLE AGE
mycomp 1/1 1 1 38s
db 1/1 1 1 20s
```
```console
$ kubectl get svc -n demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mycomp ClusterIP 172.21.4.228 <none> 8080/TCP 49s
```
### Manage operational configurations of the application
Vela leverages OAM trait system to manage operational configurations such as `scale`, `route`, `canary`, `autocale`etc in application centric approach.
Let's take `route` as example.
### `route`
If you want to use `route`, please make sure you have [nginx-ingress controller[https://kubernetes.github.io/ingress-nginx/deploy/] in your cluster.
```console
```shell script
$ vela route mycomp --app myapp
Adding route for app mycomp
Succeeded!
```
For now you have to check the public address manually (this will be fixed soon so `vela route` will return visiting URL as result):
It will create route trait for this component.
```console
$ kubectl get ingress -n demo
NAME HOSTS ADDRESS PORTS AGE
mycomp-trait-5b576c4fc mycomp.kubevela.io 123.57.10.233 80, 443 73s
```
And after you configure the `kubevela.io` domain pointing to the public address above.
Your application will be reached by `https://mycomp.kubevela.io` with `mTLS` automatically enabled.
### Under the hood
Vela will manage the underlying Kubernetes resource which implements the `route` trait.
```console
$ kubectl get routes.standard.oam.dev -n demo
```shell script
$ kubeclt get routes.standard.oam.dev -n myenv
NAME AGE
mycomp-trait-5b576c4fc 18s
```
`routes.standard.oam.dev` is a CRD controller which will manage ingress, domain, certificate etc for your application.
Controller of route trait which is part of vela core will create an ingress for it.
### Check status
```shell script
$ kubectl get ingress -n myenv
NAME HOSTS ADDRESS PORTS AGE
mycomp-trait-5b576c4fc mycomp.kubevela.io 123.57.10.233 80, 443 73s
```
Please configure your domain pointing to the public address.
Then you will be able to visit it by `https://mycomp.kubevela.io`, `mTLS` is automatically enabled.
Check the application:
### Check Status
```console
App level:
```shell script
$ vela app show myapp
About:
@@ -198,7 +184,7 @@ $ vela app show myapp
Environment:
Namespace: demo
Namespace: myenv
Components:
@@ -207,9 +193,9 @@ $ vela app show myapp
mycomp webservice route
```
Check specific component:
Component Level:
```console
```shell script
$ vela comp show mycomp
About:
@@ -219,7 +205,7 @@ $ vela comp show mycomp
Environment:
Namespace: demo
Namespace: myenv
Arguments:
@@ -232,15 +218,15 @@ $ vela comp show mycomp
route:
domain: mycomp.kubevela.io
issuer: oam-env-demo
issuer: oam-env-myenv
name: route
```
```
$ vela comp status mycomp
Showing status of Component mycomp deployed in Environment demo
Showing status of Component mycomp deployed in Environment myenv
Component Status:
Name: mycomp PodSpecWorkload(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind PodSpecWorkload workload is unknown for HealthScope
Name: mycomp Containerized(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind Containerized workload is unknown for HealthScope
Traits
└─Trait/route
@@ -249,50 +235,50 @@ $ vela comp status mycomp
Updated at: 2020-09-18T22:51:11+08:00
```
### Delete application or component
### Delete App or Component
```console
```shell script
$ vela app ls
myapp
```
```console
```shell script
$ vela comp ls
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
db myapp backend Deployed 2020-09-18 22:42:04 +0800 CST
mycomp myapp webservice route Deployed 2020-09-18 22:42:04 +0800 CST
```
```console
```shell script
$ vela comp delete db
Deleting Component 'db' from Application 'db'
```
```console
```shell script
$ vela comp ls
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
mycomp myapp webservice route Deployed 2020-09-18 22:42:04 +0800 CST
```
```console
```shell script
$ vela app delete myapp
Deleting Application "myapp"
delete apps succeed myapp from demo
delete apps succeed myapp from myenv
```
## Dashboard
Vela has a simple client side dashboard for you to interact with (note it's still under development). The functionality is equivalent to the vela cli.
We also prepared a dashboard for you, but it's still in heavily development.
```console
```shell script
$ vela dashboard
```
#### Auto-completion
#### Auto-Completion
##### bash
```console
```shell script
To load completions in your current shell session:
$ source <(vela completion bash)
@@ -305,7 +291,7 @@ MacOS:
##### zsh
```console
```shell script
To load completions in your current shell session:
$ source <(vela completion zsh)
@@ -315,24 +301,21 @@ $ vela completion zsh > "${fpath[1]}/_vela"
### Clean your environment
```console
$ helm uninstall kubevela -n vela-system
release "kubevela" uninstalled
```shell script
$ helm uninstall vela-core -n oam-system
release "vela-core" uninstalled
```
```console
$ kubectl delete crd workloaddefinitions.core.oam.dev traitdefinitions.core.oam.dev scopedefinitions.core.oam.dev
```shell script
$ kubectl delete crd workloaddefinitions.core.oam.dev traitdefinitions.core.oam.dev
customresourcedefinition.apiextensions.k8s.io "workloaddefinitions.core.oam.dev" deleted
customresourcedefinition.apiextensions.k8s.io "traitdefinitions.core.oam.dev" deleted
```
```console
```shell script
$ rm -r ~/.vela
```
## Contributing
## CONTRIBUTING
Check out [CONTRIBUTING.md](./CONTRIBUTING.md) to see how to develop with KubeVela.
## Code of Conduct
This project has adopted the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for further details.

View File

@@ -1,59 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
runtimev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// ApplicationDeploymentSpec defines the desired state of ApplicationDeployment
type ApplicationDeploymentSpec struct {
//TODO add spec here
}
// ApplicationDeploymentStatus defines the observed state of ApplicationDeployment
type ApplicationDeploymentStatus struct {
//TODO add status field here
runtimev1alpha1.ConditionedStatus `json:",inline"`
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:categories={oam}
// +kubebuilder:subresource:status
// ApplicationDeployment is the Schema for the ApplicationDeployment API
type ApplicationDeployment struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ApplicationDeploymentSpec `json:"spec,omitempty"`
Status ApplicationDeploymentStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// ApplicationDeploymentList contains a list of ApplicationDeployment
type ApplicationDeploymentList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ApplicationDeployment `json:"items"`
}
func init() {
SchemeBuilder.Register(&ApplicationDeployment{}, &ApplicationDeploymentList{})
}

View File

@@ -1,35 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package v1alpha2 contains API Schema definitions for the core.oam.dev v1alpha2 API group
// +kubebuilder:object:generate=true
// +groupName=core.oam.dev
package v1alpha2
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "core.oam.dev", Version: "v1alpha2"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)

View File

@@ -1,115 +0,0 @@
// +build !ignore_autogenerated
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha2
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationDeployment) DeepCopyInto(out *ApplicationDeployment) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDeployment.
func (in *ApplicationDeployment) DeepCopy() *ApplicationDeployment {
if in == nil {
return nil
}
out := new(ApplicationDeployment)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ApplicationDeployment) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationDeploymentList) DeepCopyInto(out *ApplicationDeploymentList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ApplicationDeployment, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDeploymentList.
func (in *ApplicationDeploymentList) DeepCopy() *ApplicationDeploymentList {
if in == nil {
return nil
}
out := new(ApplicationDeploymentList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ApplicationDeploymentList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationDeploymentSpec) DeepCopyInto(out *ApplicationDeploymentSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDeploymentSpec.
func (in *ApplicationDeploymentSpec) DeepCopy() *ApplicationDeploymentSpec {
if in == nil {
return nil
}
out := new(ApplicationDeploymentSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationDeploymentStatus) DeepCopyInto(out *ApplicationDeploymentStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDeploymentStatus.
func (in *ApplicationDeploymentStatus) DeepCopy() *ApplicationDeploymentStatus {
if in == nil {
return nil
}
out := new(ApplicationDeploymentStatus)
in.DeepCopyInto(out)
return out
}

View File

@@ -46,7 +46,6 @@ type Capability struct {
CrdName string `json:"crdName,omitempty"`
Center string `json:"center,omitempty"`
Status string `json:"status,omitempty"`
Description string `json:"description,omitempty"`
//trait only
AppliesTo []string `json:"appliesTo,omitempty"`

View File

@@ -1,8 +1,8 @@
package types
const (
DefaultOAMNS = "vela-system"
DefaultOAMReleaseName = "kubevela"
DefaultOAMNS = "oam-system"
DefaultOAMReleaseName = "vela-core"
DefaultOAMRuntimeChartName = "vela-core"
DefaultOAMVersion = ">0.0.0-0"
@@ -11,11 +11,13 @@ const (
)
const (
AnnAPIVersion = "definition.oam.dev/apiVersion"
AnnKind = "definition.oam.dev/kind"
AnnDescription = "definition.oam.dev/description"
AnnAPIVersion = "definition.oam.dev/apiVersion"
AnnKind = "definition.oam.dev/kind"
LabelPodSpecable = "workload.oam.dev/podspecable"
// Indicate which workloadDefinition generate from
AnnWorkloadDef = "workload.oam.dev/name"
// Indicate which traitDefinition generate from
AnnTraitDef = "trait.oam.dev/name"
)
const (

View File

@@ -23,20 +23,20 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PodSpecWorkloadSpec defines the desired state of PodSpecWorkload
type PodSpecWorkloadSpec struct {
// ContainerizedSpec defines the desired state of Containerized
type ContainerizedSpec struct {
// Replicas is the desired number of replicas of the given podSpec.
// These are replicas in the sense that they are instantiations of the same podSpec.
// If unspecified, defaults to 1.
Replicas *int32 `json:"replicas"`
// PodSpec describes the pods that will be created,
// we omit the meta part as it will be exactly the same as the PodSpecWorkload
// we omit the meta part as it will be exactly the same as the containerized
PodSpec v1.PodSpec `json:"podSpec"`
}
// PodSpecWorkloadStatus defines the observed state of PodSpecWorkload
type PodSpecWorkloadStatus struct {
// ContainerizedStatus defines the observed state of Containerized
type ContainerizedStatus struct {
cpv1alpha1.ConditionedStatus `json:",inline"`
// Resources managed by this workload.
@@ -45,36 +45,36 @@ type PodSpecWorkloadStatus struct {
// +kubebuilder:object:root=true
// PodSpecWorkload is the Schema for the PodSpec API
// Containerized is the Schema for the containerizeds API
// +kubebuilder:resource:categories={oam}
// +kubebuilder:subresource:status
type PodSpecWorkload struct {
type Containerized struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec PodSpecWorkloadSpec `json:"spec,omitempty"`
Status PodSpecWorkloadStatus `json:"status,omitempty"`
Spec ContainerizedSpec `json:"spec,omitempty"`
Status ContainerizedStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// PodSpecWorkloadList contains a list of PodSpecWorkload
type PodSpecWorkloadList struct {
// ContainerizedList contains a list of Containerized
type ContainerizedList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []PodSpecWorkload `json:"items"`
Items []Containerized `json:"items"`
}
func init() {
SchemeBuilder.Register(&PodSpecWorkload{}, &PodSpecWorkloadList{})
SchemeBuilder.Register(&Containerized{}, &ContainerizedList{})
}
var _ oam.Workload = &PodSpecWorkload{}
var _ oam.Workload = &Containerized{}
func (in *PodSpecWorkload) SetConditions(c ...cpv1alpha1.Condition) {
func (in *Containerized) SetConditions(c ...cpv1alpha1.Condition) {
in.Status.SetConditions(c...)
}
func (in *PodSpecWorkload) GetCondition(c cpv1alpha1.ConditionType) cpv1alpha1.Condition {
func (in *Containerized) GetCondition(c cpv1alpha1.ConditionType) cpv1alpha1.Condition {
return in.Status.GetCondition(c)
}

View File

@@ -19,6 +19,7 @@ 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"
)
@@ -33,24 +34,15 @@ type RouteSpec struct {
// 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"`
// Rules contain multiple rules of route
Rules []Rule `json:"rules,omitempty"`
// Provider indicate which ingress controller implementation the route trait will use, by default it's nginx-ingress
Provider string `json:"provider,omitempty"`
}
// Rule defines to route rule
type Rule struct {
// Name will become the suffix of underlying ingress created by this rule, if not, will use index as suffix.
Name string `json:"name,omitempty"`
// Path is location Path, default for "/"
Path string `json:"path,omitempty"`
// DefaultBackend uses serviceName
DefaultBackend *v1beta1.IngressBackend `json:"defaultBackend,omitempty"`
// RewriteTarget will rewrite request from Path to RewriteTarget path.
RewriteTarget string `json:"rewriteTarget,omitempty"`
@@ -58,9 +50,6 @@ type Rule struct {
// CustomHeaders pass a custom list of headers to the backend service.
CustomHeaders map[string]string `json:"customHeaders,omitempty"`
// DefaultBackend will become the ingress default backend if the backend is not available
DefaultBackend *runtimev1alpha1.TypedReference `json:"defaultBackend,omitempty"`
// Backend indicate how to connect backend service
// If it's nil, will auto discovery
Backend *Backend `json:"backend,omitempty"`
@@ -69,8 +58,7 @@ type Rule struct {
type TLS struct {
IssuerName string `json:"issuerName,omitempty"`
// Type indicate the issuer is ClusterIssuer or Issuer(namespace issuer), by default, it's Issuer
// +kubebuilder:default:=Issuer
// Type indicate the issuer is ClusterIssuer or NamespaceIssuer
Type IssuerType `json:"type,omitempty"`
}
@@ -81,30 +69,27 @@ const (
NamespaceIssuer IssuerType = "Issuer"
)
// Route will automatically discover podSpec and label for BackendService.
// If BackendService is already set, discovery won't work.
// If BackendService is not set, the discovery mechanism will work.
// 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"`
// BackendService specifies the backend K8s service and port, it's optional
BackendService *BackendServiceRef `json:"backendService,omitempty"`
}
// BackendServiceRef specifies the backend K8s service and port, if specified, the two fields are all required
type BackendServiceRef struct {
// Port allow you direct specify backend service port.
Port intstr.IntOrString `json:"port"`
// ServiceName allow you direct specify K8s service for backend service.
ServiceName string `json:"serviceName"`
// 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 {
Ingresses []runtimev1alpha1.TypedReference `json:"ingresses,omitempty"`
Service *runtimev1alpha1.TypedReference `json:"service,omitempty"`
Ingress *runtimev1alpha1.TypedReference `json:"ingress,omitempty"`
Service *runtimev1alpha1.TypedReference `json:"service,omitempty"`
runtimev1alpha1.ConditionedStatus `json:",inline"`
}

View File

@@ -22,16 +22,20 @@ 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
if in.BackendService != nil {
in, out := &in.BackendService, &out.BackendService
*out = new(BackendServiceRef)
**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
}
}
}
@@ -46,17 +50,102 @@ func (in *Backend) DeepCopy() *Backend {
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackendServiceRef) DeepCopyInto(out *BackendServiceRef) {
func (in *Containerized) DeepCopyInto(out *Containerized) {
*out = *in
out.Port = in.Port
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 BackendServiceRef.
func (in *BackendServiceRef) DeepCopy() *BackendServiceRef {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Containerized.
func (in *Containerized) DeepCopy() *Containerized {
if in == nil {
return nil
}
out := new(BackendServiceRef)
out := new(Containerized)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Containerized) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerizedList) DeepCopyInto(out *ContainerizedList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Containerized, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerizedList.
func (in *ContainerizedList) DeepCopy() *ContainerizedList {
if in == nil {
return nil
}
out := new(ContainerizedList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ContainerizedList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerizedSpec) DeepCopyInto(out *ContainerizedSpec) {
*out = *in
if in.Replicas != nil {
in, out := &in.Replicas, &out.Replicas
*out = new(int32)
**out = **in
}
in.PodSpec.DeepCopyInto(&out.PodSpec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerizedSpec.
func (in *ContainerizedSpec) DeepCopy() *ContainerizedSpec {
if in == nil {
return nil
}
out := new(ContainerizedSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerizedStatus) DeepCopyInto(out *ContainerizedStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
if in.Resources != nil {
in, out := &in.Resources, &out.Resources
*out = make([]corev1alpha1.TypedReference, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerizedStatus.
func (in *ContainerizedStatus) DeepCopy() *ContainerizedStatus {
if in == nil {
return nil
}
out := new(ContainerizedStatus)
in.DeepCopyInto(out)
return out
}
@@ -158,107 +247,6 @@ func (in *MetricsTraitStatus) DeepCopy() *MetricsTraitStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodSpecWorkload) DeepCopyInto(out *PodSpecWorkload) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpecWorkload.
func (in *PodSpecWorkload) DeepCopy() *PodSpecWorkload {
if in == nil {
return nil
}
out := new(PodSpecWorkload)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PodSpecWorkload) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodSpecWorkloadList) DeepCopyInto(out *PodSpecWorkloadList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]PodSpecWorkload, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpecWorkloadList.
func (in *PodSpecWorkloadList) DeepCopy() *PodSpecWorkloadList {
if in == nil {
return nil
}
out := new(PodSpecWorkloadList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PodSpecWorkloadList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodSpecWorkloadSpec) DeepCopyInto(out *PodSpecWorkloadSpec) {
*out = *in
if in.Replicas != nil {
in, out := &in.Replicas, &out.Replicas
*out = new(int32)
**out = **in
}
in.PodSpec.DeepCopyInto(&out.PodSpec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpecWorkloadSpec.
func (in *PodSpecWorkloadSpec) DeepCopy() *PodSpecWorkloadSpec {
if in == nil {
return nil
}
out := new(PodSpecWorkloadSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodSpecWorkloadStatus) DeepCopyInto(out *PodSpecWorkloadStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
if in.Resources != nil {
in, out := &in.Resources, &out.Resources
*out = make([]corev1alpha1.TypedReference, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSpecWorkloadStatus.
func (in *PodSpecWorkloadStatus) DeepCopy() *PodSpecWorkloadStatus {
if in == nil {
return nil
}
out := new(PodSpecWorkloadStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Route) DeepCopyInto(out *Route) {
*out = *in
@@ -327,13 +315,23 @@ func (in *RouteSpec) DeepCopyInto(out *RouteSpec) {
*out = new(TLS)
**out = **in
}
if in.Rules != nil {
in, out := &in.Rules, &out.Rules
*out = make([]Rule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
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.
@@ -349,10 +347,10 @@ func (in *RouteSpec) DeepCopy() *RouteSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RouteStatus) DeepCopyInto(out *RouteStatus) {
*out = *in
if in.Ingresses != nil {
in, out := &in.Ingresses, &out.Ingresses
*out = make([]corev1alpha1.TypedReference, len(*in))
copy(*out, *in)
if in.Ingress != nil {
in, out := &in.Ingress, &out.Ingress
*out = new(corev1alpha1.TypedReference)
**out = **in
}
if in.Service != nil {
in, out := &in.Service, &out.Service
@@ -372,38 +370,6 @@ func (in *RouteStatus) DeepCopy() *RouteStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Rule) DeepCopyInto(out *Rule) {
*out = *in
if in.CustomHeaders != nil {
in, out := &in.CustomHeaders, &out.CustomHeaders
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.DefaultBackend != nil {
in, out := &in.DefaultBackend, &out.DefaultBackend
*out = new(corev1alpha1.TypedReference)
**out = **in
}
if in.Backend != nil {
in, out := &in.Backend, &out.Backend
*out = new(Backend)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
func (in *Rule) DeepCopy() *Rule {
if in == nil {
return nil
}
out := new(Rule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ScapeServiceEndPoint) DeepCopyInto(out *ScapeServiceEndPoint) {
*out = *in

View File

@@ -0,0 +1,44 @@
#!/bin/bash
# Copyright 2019 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#!/usr/bin/env bash
# Download and unpack cert-manager
CERT_MANAGER_VERSION=1.0.0
ARCHIVE_DOWNLOAD_URL=https://github.com/jetstack/cert-manager/archive/v${CERT_MANAGER_VERSION}.tar.gz
YAML_URL=https://github.com/jetstack/cert-manager/releases/download/v${CERT_MANAGER_VERSION}/cert-manager.yaml
wget $ARCHIVE_DOWNLOAD_URL
tar xzf v${CERT_MANAGER_VERSION}.tar.gz
(
# subshell in downloaded directory
cd cert-manager-${CERT_MANAGER_VERSION} || exit
# Copy the CRD yaml file
cp deploy/manifests/00-crds.yaml ../cert-manager-crds.yaml
)
# Download the cert-manager yaml file
wget $YAML_URL
# Clean up.
rm -rf cert-manager-${CERT_MANAGER_VERSION}
rm v${CERT_MANAGER_VERSION}.tar.gz
# Add enable-certificate-owner-ref option to cert-manager's controller.
# The option is to cleans up secret(certificate) by adding ownerref.
patch -l cert-manager.yaml owner-ref.patch

View File

@@ -0,0 +1,4 @@
#!/bin/bash
# Download and unpack operator Lifecycle Manager (coreos)
curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/0.15.1/install.sh | bash -s 0.15.1

View File

@@ -0,0 +1,24 @@
apiVersion: v1
kind: Namespace
metadata:
name: my-grafana-operator
---
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
name: operatorgroup
namespace: my-grafana-operator
spec:
targetNamespaces:
- my-grafana-operator
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: my-grafana-operator
namespace: my-grafana-operator
spec:
channel: alpha
name: grafana-operator
source: operatorhubio-catalog
sourceNamespace: olm

View File

@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
mornitoring: oam
name: monitoring

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,265 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
creationTimestamp: null
name: podmonitors.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
kind: PodMonitor
listKind: PodMonitorList
plural: podmonitors
singular: podmonitor
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: PodMonitor defines monitoring for a set of pods.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Specification of desired Pod selection for target discovery
by Prometheus.
properties:
jobLabel:
description: The label to use to retrieve the job name from.
type: string
namespaceSelector:
description: Selector to select which namespaces the Endpoints objects
are discovered from.
properties:
any:
description: Boolean describing whether all namespaces are selected
in contrast to a list restricting them.
type: boolean
matchNames:
description: List of namespace names.
items:
type: string
type: array
type: object
podMetricsEndpoints:
description: A list of endpoints allowed as part of this PodMonitor.
items:
description: PodMetricsEndpoint defines a scrapeable endpoint of
a Kubernetes Pod serving Prometheus metrics.
properties:
honorLabels:
description: HonorLabels chooses the metric's labels on collisions
with target labels.
type: boolean
honorTimestamps:
description: HonorTimestamps controls whether Prometheus respects
the timestamps present in scraped data.
type: boolean
interval:
description: Interval at which metrics should be scraped
type: string
metricRelabelings:
description: MetricRelabelConfigs to apply to samples before
ingestion.
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
description: Action to perform based on regex matching.
Default is 'replace'
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
params:
additionalProperties:
items:
type: string
type: array
description: Optional HTTP URL parameters
type: object
path:
description: HTTP path to scrape for metrics.
type: string
port:
description: Name of the pod port this endpoint refers to. Mutually
exclusive with targetPort.
type: string
proxyUrl:
description: ProxyURL eg http://proxyserver:2195 Directs scrapes
to proxy through this endpoint.
type: string
relabelings:
description: 'RelabelConfigs to apply to samples before ingestion.
More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
items:
description: 'RelabelConfig allows dynamic rewriting of the
label set, being applied to samples before ingestion. It
defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
description: Action to perform based on regex matching.
Default is 'replace'
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex replace
is performed if the regular expression matches. Regex
capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
scheme:
description: HTTP scheme to use for scraping.
type: string
scrapeTimeout:
description: Timeout after which the scrape is ended
type: string
targetPort:
anyOf:
- type: integer
- type: string
description: 'Deprecated: Use ''port'' instead.'
x-kubernetes-int-or-string: true
type: object
type: array
podTargetLabels:
description: PodTargetLabels transfers labels on the Kubernetes Pod
onto the target.
items:
type: string
type: array
sampleLimit:
description: SampleLimit defines per-scrape limit on number of scraped
samples that will be accepted.
format: int64
type: integer
selector:
description: Selector to select Pod objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that
contains values, a key, and an operator that relates the key
and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to
a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
type: string
values:
description: values is an array of string values. If the
operator is In or NotIn, the values array must be non-empty.
If the operator is Exists or DoesNotExist, the values
array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator
is "In", and the values array contains only "value". The requirements
are ANDed.
type: object
type: object
required:
- podMetricsEndpoints
- selector
type: object
required:
- spec
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,212 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
creationTimestamp: null
name: probes.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
kind: Probe
listKind: ProbeList
plural: probes
singular: probe
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: Probe defines monitoring for a set of static targets or ingresses.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Specification of desired Ingress selection for target discovery
by Prometheus.
properties:
interval:
description: Interval at which targets are probed using the configured
prober. If not specified Prometheus' global scrape interval is used.
type: string
jobName:
description: The job name assigned to scraped metrics by default.
type: string
module:
description: 'The module to use for probing specifying how to probe
the target. Example module configuring in the blackbox exporter:
https://github.com/prometheus/blackbox_exporter/blob/master/example.yml'
type: string
prober:
description: Specification for the prober to use for probing targets.
The prober.URL parameter is required. Targets cannot be probed if
left empty.
properties:
path:
description: Path to collect metrics from. Defaults to `/probe`.
type: string
scheme:
description: HTTP scheme to use for scraping. Defaults to `http`.
type: string
url:
description: Mandatory URL of the prober.
type: string
required:
- url
type: object
scrapeTimeout:
description: Timeout for scraping metrics from the Prometheus exporter.
type: string
targets:
description: Targets defines a set of static and/or dynamically discovered
targets to be probed using the prober.
properties:
ingress:
description: Ingress defines the set of dynamically discovered
ingress objects which hosts are considered for probing.
properties:
namespaceSelector:
description: Select Ingress objects by namespace.
properties:
any:
description: Boolean describing whether all namespaces
are selected in contrast to a list restricting them.
type: boolean
matchNames:
description: List of namespace names.
items:
type: string
type: array
type: object
relabelingConfigs:
description: 'RelabelConfigs to apply to samples before ingestion.
More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config'
items:
description: 'RelabelConfig allows dynamic rewriting of
the label set, being applied to samples before ingestion.
It defines `<metric_relabel_configs>`-section of Prometheus
configuration. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs'
properties:
action:
description: Action to perform based on regex matching.
Default is 'replace'
type: string
modulus:
description: Modulus to take of the hash of the source
label values.
format: int64
type: integer
regex:
description: Regular expression against which the extracted
value is matched. Default is '(.*)'
type: string
replacement:
description: Replacement value against which a regex
replace is performed if the regular expression matches.
Regex capture groups are available. Default is '$1'
type: string
separator:
description: Separator placed between concatenated source
label values. default is ';'.
type: string
sourceLabels:
description: The source labels select values from existing
labels. Their content is concatenated using the configured
separator and matched against the configured regular
expression for the replace, keep, and drop actions.
items:
type: string
type: array
targetLabel:
description: Label to which the resulting value is written
in a replace action. It is mandatory for replace actions.
Regex capture groups are available.
type: string
type: object
type: array
selector:
description: Select Ingress objects by labels.
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In, NotIn,
Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists or
DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field is
"key", the operator is "In", and the values array contains
only "value". The requirements are ANDed.
type: object
type: object
type: object
staticConfig:
description: 'StaticConfig defines static targets which are considers
for probing. More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#static_config.'
properties:
labels:
additionalProperties:
type: string
description: Labels assigned to all metrics scraped from the
targets.
type: object
static:
description: Targets is a list of URLs to probe using the
configured prober.
items:
type: string
type: array
type: object
type: object
type: object
required:
- spec
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,94 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.4
creationTimestamp: null
name: prometheusrules.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
kind: PrometheusRule
listKind: PrometheusRuleList
plural: prometheusrules
singular: prometheusrule
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: PrometheusRule defines alerting rules for a Prometheus instance
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Specification of desired alerting rule definitions for Prometheus.
properties:
groups:
description: Content of Prometheus rule file
items:
description: 'RuleGroup is a list of sequentially evaluated recording
and alerting rules. Note: PartialResponseStrategy is only used
by ThanosRuler and will be ignored by Prometheus instances. Valid
values for this field are ''warn'' or ''abort''. More info: https://github.com/thanos-io/thanos/blob/master/docs/components/rule.md#partial-response'
properties:
interval:
type: string
name:
type: string
partial_response_strategy:
type: string
rules:
items:
description: Rule describes an alerting or recording rule.
properties:
alert:
type: string
annotations:
additionalProperties:
type: string
type: object
expr:
anyOf:
- type: integer
- type: string
x-kubernetes-int-or-string: true
for:
type: string
labels:
additionalProperties:
type: string
type: object
record:
type: string
required:
- expr
type: object
type: array
required:
- name
- rules
type: object
type: array
type: object
required:
- spec
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: prometheus-operator
app.kubernetes.io/version: v0.41.1
name: prometheus-operator
rules:
- apiGroups:
- monitoring.coreos.com
resources:
- alertmanagers
- alertmanagers/finalizers
- prometheuses
- prometheuses/finalizers
- thanosrulers
- thanosrulers/finalizers
- servicemonitors
- podmonitors
- probes
- prometheusrules
verbs:
- '*'
- apiGroups:
- apps
resources:
- statefulsets
verbs:
- '*'
- apiGroups:
- ""
resources:
- configmaps
- secrets
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods
verbs:
- list
- delete
- apiGroups:
- ""
resources:
- services
- services/finalizers
- endpoints
verbs:
- get
- create
- update
- delete
- apiGroups:
- ""
resources:
- nodes
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- list
- watch
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create

View File

@@ -0,0 +1,34 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: prometheus-operator
app.kubernetes.io/version: v0.41.1
name: prometheus-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus-operator
subjects:
- kind: ServiceAccount
name: prometheus-operator
namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: prometheus-operator
app.kubernetes.io/version: v0.41.1
name: prometheus-operator-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: "cluster-admin"
subjects:
- kind: ServiceAccount
name: prometheus-operator
namespace: monitoring

View File

@@ -0,0 +1,60 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: prometheus-operator
app.kubernetes.io/version: v0.41.1
name: prometheus-operator
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: prometheus-operator
template:
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: prometheus-operator
app.kubernetes.io/version: v0.41.1
spec:
containers:
- args:
- --kubelet-service=kube-system/kubelet
- --logtostderr=true
- --config-reloader-image=jimmidyson/configmap-reload:v0.4.0
- --prometheus-config-reloader=quay.io/coreos/prometheus-config-reloader:v0.41.1
image: quay.io/coreos/prometheus-operator:v0.41.1
name: prometheus-operator
ports:
- containerPort: 8080
name: http
resources:
limits:
cpu: 200m
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
securityContext:
allowPrivilegeEscalation: false
- args:
- --logtostderr
- --secure-listen-address=:8443
- --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- --upstream=http://127.0.0.1:8080/
image: quay.io/coreos/kube-rbac-proxy:v0.4.1
name: kube-rbac-proxy
ports:
- containerPort: 8443
name: https
securityContext:
runAsUser: 65534
nodeSelector:
beta.kubernetes.io/os: linux
securityContext:
runAsNonRoot: true
runAsUser: 65534
serviceAccountName: prometheus-operator

View File

@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: prometheus-operator
app.kubernetes.io/version: v0.41.1
name: prometheus-operator
namespace: monitoring
spec:
clusterIP: None
ports:
- name: https
port: 8443
targetPort: https
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/name: prometheus-operator

View File

@@ -0,0 +1,9 @@
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: prometheus-operator
app.kubernetes.io/version: v0.41.1
name: prometheus-operator
namespace: monitoring

View File

@@ -1,6 +1,6 @@
apiVersion: v2
name: vela-core
description: A Helm chart for Kube Vela core
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#

File diff suppressed because it is too large Load Diff

View File

@@ -101,29 +101,16 @@ spec:
to match a value.
properties:
fieldPath:
description: FieldPath specifies got value from
workload/trait object
type: string
op:
description: ConditionOperator specifies the operator
to match a value.
type: string
value:
description: Value specifies an expected value This
is mutually exclusive with ValueFrom
type: string
valueFrom:
description: ValueFrom specifies expected value
from AppConfig This is mutually exclusive with
Value
properties:
fieldPath:
type: string
required:
- fieldPath
type: object
required:
- op
- value
type: object
type: array
fieldPath:
@@ -246,29 +233,16 @@ spec:
requirement to match a value.
properties:
fieldPath:
description: FieldPath specifies got value
from workload/trait object
type: string
op:
description: ConditionOperator specifies the
operator to match a value.
type: string
value:
description: Value specifies an expected value
This is mutually exclusive with ValueFrom
type: string
valueFrom:
description: ValueFrom specifies expected
value from AppConfig This is mutually exclusive
with Value
properties:
fieldPath:
type: string
required:
- fieldPath
type: object
required:
- op
- value
type: object
type: array
fieldPath:
@@ -364,8 +338,6 @@ spec:
- kind
- name
type: object
reason:
type: string
to:
description: DependencyToObject represents the object that
dependency data goes to.
@@ -393,47 +365,10 @@ spec:
type: object
required:
- from
- reason
- to
type: object
type: array
type: object
historyWorkloads:
description: HistoryWorkloads will record history but still working
revision workloads.
items:
description: HistoryWorkload contain the old component revision
that are still running
properties:
revision:
description: component revision of this workload
type: string
workloadRef:
description: Reference to running workload.
properties:
apiVersion:
description: APIVersion of the referenced object.
type: string
kind:
description: Kind of the referenced object.
type: string
name:
description: Name of the referenced object.
type: string
uid:
description: UID of the referenced object.
type: string
required:
- apiVersion
- kind
- name
type: object
type: object
type: array
observedGeneration:
description: The generation observed by the appConfig controller.
format: int64
type: integer
status:
description: Status is a place holder for a customized controller
to fill if it needs a single place to summarize the status of the
@@ -547,6 +482,8 @@ spec:
type: object
type: object
type: array
required:
- dependency
type: object
type: object
served: true

View File

@@ -1,90 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
creationTimestamp: null
name: applicationdeployments.core.oam.dev
spec:
group: core.oam.dev
names:
categories:
- oam
kind: ApplicationDeployment
listKind: ApplicationDeploymentList
plural: applicationdeployments
singular: applicationdeployment
scope: Namespaced
versions:
- name: v1alpha2
schema:
openAPIV3Schema:
description: ApplicationDeployment is the Schema for the ApplicationDeployment
API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ApplicationDeploymentSpec defines the desired state of ApplicationDeployment
type: object
status:
description: ApplicationDeploymentStatus defines the observed state of
ApplicationDeployment
properties:
conditions:
description: Conditions of the resource.
items:
description: A Condition that may apply to a resource.
properties:
lastTransitionTime:
description: LastTransitionTime is the last time this condition
transitioned from one status to another.
format: date-time
type: string
message:
description: A Message containing details about this condition's
last transition from one status to another, if any.
type: string
reason:
description: A Reason for this condition's last transition from
one status to another.
type: string
status:
description: Status of this condition; is it currently True,
False, or Unknown?
type: string
type:
description: Type of this condition. At most one of each condition
type may apply to a resource at any point in time.
type: string
required:
- lastTransitionTime
- reason
- status
- type
type: object
type: array
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -72,7 +72,6 @@ spec:
will specify parameter values using this name.
type: string
required:
default: false
description: Required specifies whether or not a value for this
parameter must be supplied when authoring an ApplicationConfiguration.
type: boolean
@@ -83,9 +82,7 @@ spec:
type: array
workload:
description: A Workload that will be created for each ApplicationConfiguration
that includes this Component. Workload is an instance of a workloadDefinition.
We either use the GVK info or a special "type" field in the workload
to associate the content of the workload with its workloadDefinition
that includes this Component. Workloads must be defined by a WorkloadDefinition.
type: object
x-kubernetes-embedded-resource: true
x-kubernetes-preserve-unknown-fields: true
@@ -140,10 +137,6 @@ spec:
- name
- revision
type: object
observedGeneration:
description: The generation observed by the component controller.
format: int64
type: integer
type: object
type: object
served: true

View File

@@ -11,190 +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.
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:
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
workloadStatus:
description: WorkloadStatus represents status of workloads whose
HealthStatus is UNKNOWN.
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:
healthStatus:
description: HealthStatus represents health status strings.
type: string
healthyWorkloads:
format: int64
type: integer
total:
format: int64
type: integer
unhealthyWorkloads:
format: int64
type: integer
unknownWorkloads:
format: int64
type: integer
required:
- healthStatus
- healthStatus
type: object
type: array
scopeHealthCondition:
description: ScopeHealthCondition represents health condition summary
of the scope
properties:
healthStatus:
description: HealthStatus represents health status strings.
type: string
healthyWorkloads:
format: int64
type: integer
total:
format: int64
type: integer
unhealthyWorkloads:
format: int64
type: integer
unknownWorkloads:
format: int64
type: integer
required:
- healthStatus
type: object
required:
- scopeHealthCondition
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

@@ -85,16 +85,6 @@ spec:
builders
type: object
x-kubernetes-preserve-unknown-fields: true
podSpecPath:
description: PodSpecPath indicates where/if this workload has K8s
podSpec field if one workload has podSpec, trait can do lot's of
assumption such as port, env, volume fields.
type: string
revisionLabel:
description: RevisionLabel indicates which label for underlying resources(e.g.
pods) of this workload can be used by trait to create resource selectors(e.g.
label selector for pods).
type: string
required:
- definitionRef
type: object

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

@@ -6,22 +6,22 @@ metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
creationTimestamp: null
name: podspecworkloads.standard.oam.dev
name: containerizeds.standard.oam.dev
spec:
group: standard.oam.dev
names:
categories:
- oam
kind: PodSpecWorkload
listKind: PodSpecWorkloadList
plural: podspecworkloads
singular: podspecworkload
kind: Containerized
listKind: ContainerizedList
plural: containerizeds
singular: containerized
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: PodSpecWorkload is the Schema for the PodSpec API
description: Containerized is the Schema for the containerizeds API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
@@ -36,11 +36,11 @@ spec:
metadata:
type: object
spec:
description: PodSpecWorkloadSpec defines the desired state of PodSpecWorkload
description: ContainerizedSpec defines the desired state of Containerized
properties:
podSpec:
description: PodSpec describes the pods that will be created, we omit
the meta part as it will be exactly the same as the PodSpecWorkload
the meta part as it will be exactly the same as the containerized
properties:
activeDeadlineSeconds:
description: Optional duration in seconds the pod may be active
@@ -5694,7 +5694,7 @@ spec:
- replicas
type: object
status:
description: PodSpecWorkloadStatus defines the observed state of PodSpecWorkload
description: ContainerizedStatus defines the observed state of Containerized
properties:
conditions:
description: Conditions of the resource.

View File

@@ -38,90 +38,84 @@ spec:
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
provider:
description: Provider indicate which ingress controller implementation
the route trait will use, by default it's nginx-ingress
path:
description: Path is location Path, default for "/"
type: string
rewriteTarget:
description: RewriteTarget will rewrite request from Path to RewriteTarget
path.
type: string
rules:
description: Rules contain multiple rules of route
items:
description: Rule defines to route rule
properties:
backend:
description: Backend indicate how to connect backend service
If it's nil, will auto discovery
properties:
backendService:
description: BackendService specifies the backend K8s service
and port, it's optional
properties:
port:
anyOf:
- type: integer
- type: string
description: Port allow you direct specify backend service
port.
x-kubernetes-int-or-string: true
serviceName:
description: ServiceName allow you direct specify K8s
service for backend service.
type: string
required:
- port
- serviceName
type: object
readTimeout:
description: ReadTimeout used for setting read timeout duration
for backend service, the unit is second.
type: integer
sendTimeout:
description: SendTimeout used for setting send timeout duration
for backend service, the unit is second.
type: integer
type: object
customHeaders:
additionalProperties:
type: string
description: CustomHeaders pass a custom list of headers to
the backend service.
type: object
defaultBackend:
description: DefaultBackend will become the ingress default
backend if the backend is not available
properties:
apiVersion:
description: APIVersion of the referenced object.
type: string
kind:
description: Kind of the referenced object.
type: string
name:
description: Name of the referenced object.
type: string
uid:
description: UID of the referenced object.
type: string
required:
- apiVersion
- kind
- name
type: object
name:
description: Name will become the suffix of underlying ingress
created by this rule, if not, will use index as suffix.
type: string
path:
description: Path is location Path, default for "/"
type: string
rewriteTarget:
description: RewriteTarget will rewrite request from Path to
RewriteTarget path.
type: string
type: object
type: array
tls:
description: TLS indicate route trait will create SSL secret using
cert-manager with specified issuer If this is nil, route trait will
@@ -130,9 +124,7 @@ spec:
issuerName:
type: string
type:
default: Issuer
description: Type indicate the issuer is ClusterIssuer or Issuer(namespace
issuer), by default, it's Issuer
description: Type indicate the issuer is ClusterIssuer or NamespaceIssuer
type: string
type: object
workloadRef:
@@ -195,30 +187,28 @@ spec:
- type
type: object
type: array
ingresses:
items:
description: A TypedReference refers to an object by Name, Kind,
and APIVersion. It is commonly used to reference cluster-scoped
objects or objects where the namespace is already known.
properties:
apiVersion:
description: APIVersion of the referenced object.
type: string
kind:
description: Kind of the referenced object.
type: string
name:
description: Name of the referenced object.
type: string
uid:
description: UID of the referenced object.
type: string
required:
- apiVersion
- kind
- name
type: object
type: array
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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,13 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: podspecworkloads.standard.oam.dev
name: containerizeds.standard.oam.dev
annotations:
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
definition.oam.dev/kind: "PodSpecWorkload"
definition.oam.dev/kind: "Containerized"
spec:
definitionRef:
name: podspecworkloads.standard.oam.dev
name: containerizeds.standard.oam.dev
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
@@ -15,19 +15,19 @@ spec:
kind: Service
extension:
template: |
output: {
#Template: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "PodSpecWorkload"
kind: "Containerized"
metadata:
name: parameter.name
name: webservice.name
spec: {
replicas: 1
podSpec: {
containers: [{
image: parameter.image
name: parameter.name
image: webservice.image
name: webservice.name
ports: [{
containerPort: parameter.port
containerPort: webservice.port
protocol: "TCP"
name: "default"
}]
@@ -35,7 +35,7 @@ spec:
}
}
}
#webservice: {
webservice: {
name: string
// +usage=specify app image
// +short=i
@@ -44,5 +44,3 @@ spec:
// +short=p
port: *6379 | int
}
parameter: #webservice

View File

@@ -1,39 +0,0 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: backend
annotations:
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
definition.oam.dev/kind: "PodSpecWorkload"
definition.oam.dev/description: "Backend worker without ports exposed"
spec:
definitionRef:
name: podspecworkload.standard.oam.dev
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
extension:
template: |
output: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "PodSpecWorkload"
metadata:
name: parameter.name
spec: {
replicas: 1
podSpec: {
containers: [{
image: parameter.image
name: parameter.name
}]
}
}
}
#backend: {
name: string
// +usage=specify app image
// +short=i
image: string
}
parameter: #backend

View File

@@ -1,53 +0,0 @@
---
apiVersion: v1
kind: Namespace
metadata:
labels:
mornitoring: oam
name: monitoring
---
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: metric
namespace: default
annotations:
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
definition.oam.dev/kind: MetricsTrait
definition.oam.dev/description: "Add metric monitoring for workload"
spec:
appliesToWorkloads:
- webservice
- backend
- task
- containerizedworkloads.core.oam.dev
- clonesetworkloads.apps.kruise.io
- deployments.apps
- statefulsets.apps
definitionRef:
name: metricstraits.standard.oam.dev
workloadRefPath: spec.workloadRef
extension:
template: |-
#metrics: {
// +usage=format of the metrics, default as prometheus
// +short=f
format: *"prometheus" | string
path: *"/metrics" | string
scheme: *"http" | string
enabled: *true | bool
port: *8080 | >=1024 & uint16
// +usage= the label selector for the pods, default is the workload labels
selector?: [string]: string
}
spec: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "MetricsTrait"
spec: {
scrapeService: parameter
}
}
parameter: #metrics
output: {}

View File

@@ -1,35 +0,0 @@
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: route
annotations:
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
definition.oam.dev/kind: Route
definition.oam.dev/description: "Add a route for workload"
spec:
appliesToWorkloads:
- core.oam.dev/v1alpha2.ContainerizedWorkload
- standard.oam.dev/v1alpha1.PodSpecWorkload
- deployments.apps
- webservice
workloadRefPath: spec.workloadRef
definitionRef:
name: routes.standard.oam.dev
extension:
template: |
output: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "Route"
spec: {
host: parameter.domain
tls: {
issuerName: parameter.issuer
}
}
}
#route: {
domain: *"" | string
issuer: *"" | string
}
parameter: #route

View File

@@ -1,49 +0,0 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: task
annotations:
definition.oam.dev/apiVersion: "v1"
definition.oam.dev/kind: "Job"
definition.oam.dev/description: "One-time off task or job"
spec:
definitionRef:
name: jobs
extension:
defaultTraits:
- monitor
- logging
template: |
output: {
apiVersion: "v1"
kind: "Job"
metadata: name: parameter.name
spec: {
parallelism: parameter.count
completions: parameter.count
template:
spec:
containers: [{
image: parameter.image
name: parameter.name
ports: [{
containerPort: parameter.port
protocol: "TCP"
name: "default"
}]
}]
}
}
#task: {
// +usage=specify number of tasks to run in parallel
// +short=c
count: *1 | int
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}
parameter: #task

View File

@@ -1,48 +0,0 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: webservice
annotations:
definition.oam.dev/apiVersion: "standard.oam.dev/v1alpha1"
definition.oam.dev/kind: "PodSpecWorkload"
definition.oam.dev/description: "Long running service with ports exposed"
spec:
definitionRef:
name: podspecworkloads.standard.oam.dev
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
- apiVersion: v1
kind: Service
extension:
template: |
output: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "PodSpecWorkload"
metadata:
name: parameter.name
spec: {
replicas: 1
podSpec: {
containers: [{
image: parameter.image
name: parameter.name
ports: [{
containerPort: parameter.port
protocol: "TCP"
name: "default"
}]
}]
}
}
}
#webservice: {
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}
parameter: #webservice

View File

@@ -18,7 +18,7 @@ metadata:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "kubevela.fullname" . }}:manager-rolebinding
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
@@ -33,7 +33,7 @@ subjects:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "kubevela.fullname" . }}:leader-election-role
name: leader-election-role
rules:
- apiGroups:
- ""
@@ -66,11 +66,11 @@ rules:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "kubevela.fullname" . }}:leader-election-rolebinding
name: leader-election-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ include "kubevela.fullname" . }}:leader-election-role
name: leader-election-role
subjects:
- kind: ServiceAccount
name: {{ include "kubevela.serviceAccountName" . }}
@@ -80,7 +80,6 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "kubevela.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "kubevela.labels" . | nindent 4 }}
spec:
@@ -111,7 +110,6 @@ spec:
- "--use-webhook=true"
- "--webhook-port={{ .Values.webhookService.port }}"
- "--webhook-cert-dir={{ .Values.certificate.mountPath }}"
- "--health-addr=:{{ .Values.healthCheck.port }}"
{{ end }}
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ quote .Values.image.pullPolicy }}
@@ -122,21 +120,6 @@ spec:
- containerPort: {{ .Values.webhookService.port }}
name: webhook-server
protocol: TCP
- containerPort: {{ .Values.healthCheck.port }}
name: healthz
protocol: TCP
readinessProbe:
httpGet:
path: /readyz
port: healthz
initialDelaySeconds: 90
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz
port: healthz
initialDelaySeconds: 90
periodSeconds: 5
volumeMounts:
- mountPath: {{ .Values.certificate.mountPath }}
name: tls-cert-vol
@@ -158,4 +141,4 @@ spec:
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}

View File

@@ -12,18 +12,17 @@ spec:
- apps/v1.Deployment
definitionRef:
name: manualscalertraits.core.oam.dev
workloadRefPath: spec.workloadRef
extension:
template: |-
output: {
#Template: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ManualScalerTrait"
spec: {
replicaCount: parameter.replica
replicaCount: scale.replica
}
}
#scale: {
scale: {
//+short=r
replica: *2 | int
}
parameter: #scale
workloadRefPath: spec.workloadRef

View File

@@ -0,0 +1,17 @@
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
- clonesetworkloads.apps.kruise.io
- deployments.apps
- statefulsets.apps
definitionRef:
name: metricstraits.standard.oam.dev
workloadRefPath: spec.workloadRef

View File

@@ -0,0 +1,37 @@
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: routes.standard.oam.dev
annotations:
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
definition.oam.dev/kind: Route
spec:
appliesToWorkloads:
- core.oam.dev/v1alpha2.ContainerizedWorkload
- standard.oam.dev/v1alpha1.Containerized
- deployments.apps
workloadRefPath: spec.workloadRef
definitionRef:
name: routes.standard.oam.dev
extension:
template: |
#Template: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "Route"
spec: {
host: route.domain
path: route.path
tls: {
issuerName: route.issuer
}
backend: {
port: route.port
}
}
}
route: {
domain: *"" | string
path: *"" | string
port: *443 | int
issuer: *"" | string
}

View File

@@ -0,0 +1,47 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: task
annotations:
definition.oam.dev/apiVersion: "v1"
definition.oam.dev/kind: "Job"
spec:
definitionRef:
name: jobs
extension:
defaultTraits:
- monitor
- logging
template: |
#Template: {
apiVersion: "v1"
kind: "Job"
metadata: name: task.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

@@ -2,7 +2,7 @@ apiVersion: v1
kind: ConfigMap
metadata:
name: vela-config
namespace: {{ .Release.Namespace }}
namespace: default
data:
certificates.cert-manager.io: |
{
@@ -10,13 +10,5 @@ data:
"urL": "https://charts.jetstack.io",
"name": "cert-manager",
"namespace": "cert-manager",
"version": "1.0.3"
}
servicemonitors.monitoring.coreos.com: |
{
"repo": "prometheus-community",
"urL": "https://prometheus-community.github.io/helm-charts",
"name": "kube-prometheus-stack",
"namespace": "monitoring",
"version": "9.4.4"
"version": "1.0.0"
}

View File

@@ -3,8 +3,8 @@
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
creationTimestamp: null
name: mutating-webhook-configuration
namespace: {{ .Release.Namespace }}
annotations:
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Values.certificate.certificateName }}
webhooks:
@@ -31,7 +31,7 @@ webhooks:
service:
name: {{ template "kubevela.name" . }}-webhook
namespace: {{ .Release.Namespace }}
path: /mutate-standard-oam-dev-v1alpha1-podspecworkload
path: /mutate-standard-oam-dev-v1alpha1-containerized
failurePolicy: Fail
name: mcontainerized.kb.io
rules:
@@ -43,14 +43,14 @@ webhooks:
- CREATE
- UPDATE
resources:
- podspecworkloads
- Containerized
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
creationTimestamp: null
name: validating-webhook-configuration
namespace: {{ .Release.Namespace }}
annotations:
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Values.certificate.certificateName }}
webhooks:
@@ -78,7 +78,7 @@ webhooks:
service:
name: {{ template "kubevela.name" . }}-webhook
namespace: {{ .Release.Namespace }}
path: /validate-standard-oam-dev-v1alpha1-podspecworkload
path: /validate-standard-oam-dev-v1alpha1-containerized
failurePolicy: Fail
name: vcontainerized.kb.io
rules:
@@ -89,15 +89,15 @@ webhooks:
operations:
- CREATE
- UPDATE
- DELETE
resources:
- podspecworkloads
- Containerized
---
apiVersion: v1
kind: Service
metadata:
name: {{ template "kubevela.name" . }}-webhook
namespace: {{ .Release.Namespace }}
labels:
{{- include "kubevela.labels" . | nindent 4 }}
spec:
@@ -113,7 +113,7 @@ spec:
---
# The following manifests contain a self-signed issuer CR and a certificate CR.
# More document can be found at https://docs.cert-manager.io
apiVersion: cert-manager.io/v1
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: {{ .Values.certificate.issuerName | quote }}
@@ -121,7 +121,7 @@ spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: {{ .Values.certificate.certificateName }}

View File

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

View File

@@ -63,9 +63,6 @@ webhookService:
type: ClusterIP
port: 9443
healthCheck:
port: 9440
nodeSelector: {}
tolerations: []

View File

@@ -1,17 +1,16 @@
package main
import (
"errors"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"time"
monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
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"
@@ -20,7 +19,10 @@ import (
injectorcontroller "github.com/oam-dev/trait-injector/controllers"
"github.com/oam-dev/trait-injector/pkg/injector"
"github.com/oam-dev/trait-injector/pkg/plugin"
certmanager "github.com/wonderflow/cert-manager-api/pkg/apis/certmanager/v1"
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"
@@ -28,26 +30,10 @@ import (
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
velacoreoamdev "github.com/oam-dev/kubevela/api/core.oam.dev/v1alpha2"
velacore "github.com/oam-dev/kubevela/api/v1alpha1"
velacontroller "github.com/oam-dev/kubevela/pkg/controller"
"github.com/oam-dev/kubevela/pkg/controller/dependency"
velawebhook "github.com/oam-dev/kubevela/pkg/webhook"
)
const (
kubevelaName = "kubevela"
)
var (
setupLog = ctrl.Log.WithName(kubevelaName)
scheme = runtime.NewScheme()
waitSecretTimeout = 90 * time.Second
waitSecretInterval = 2 * time.Second
)
var scheme = runtime.NewScheme()
func init() {
_ = clientgoscheme.AddToScheme(scheme)
@@ -55,7 +41,6 @@ func init() {
_ = oamcore.AddToScheme(scheme)
_ = monitoring.AddToScheme(scheme)
_ = velacore.AddToScheme(scheme)
_ = velacoreoamdev.AddToScheme(scheme)
_ = injectorv1alpha1.AddToScheme(scheme)
_ = certmanager.AddToScheme(scheme)
@@ -70,7 +55,6 @@ func main() {
var webhookPort int
var useWebhook, useTraitInjector bool
var controllerArgs oamcontroller.Args
var healthAddr string
flag.BoolVar(&useWebhook, "use-webhook", false, "Enable Admission Webhook")
flag.BoolVar(&useTraitInjector, "use-trait-injector", false, "Enable TraitInjector")
@@ -84,7 +68,6 @@ func main() {
flag.BoolVar(&logCompress, "log-compress", true, "Enable compression on the rotated logs.")
flag.IntVar(&controllerArgs.RevisionLimit, "revision-limit", 50,
"RevisionLimit is the maximum number of revisions that will be maintained. The default value is 50.")
flag.StringVar(&healthAddr, "health-addr", ":9440", "The address the health endpoint binds to.")
flag.Parse()
// setup logging
@@ -104,33 +87,28 @@ func main() {
o.DestWritter = w
}))
// install dependency charts first
k8sClient, err := client.New(ctrl.GetConfigOrDie(), client.Options{Scheme: scheme})
if err != nil {
setupLog.Error(err, "unable to create a kubernetes client")
os.Exit(1)
}
if err = dependency.Install(k8sClient); err != nil {
setupLog.Error(err, "unable to install the dependency")
os.Exit(1)
}
setupLog := ctrl.Log.WithName("vela-runtime")
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: kubevelaName,
Port: webhookPort,
CertDir: certDir,
HealthProbeBindAddress: healthAddr,
Scheme: scheme,
MetricsBindAddress: metricsAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "vela-runtime",
Port: webhookPort,
CertDir: certDir,
})
if err != nil {
setupLog.Error(err, "unable to create a controller manager")
os.Exit(1)
}
if err := registerHealthChecks(mgr); err != nil {
setupLog.Error(err, "unable to register ready/health checks")
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)
}
@@ -138,10 +116,6 @@ func main() {
setupLog.Info("vela webhook enabled, will serving at :" + strconv.Itoa(webhookPort))
oamwebhook.Add(mgr)
velawebhook.Register(mgr)
if err := waitWebhookSecretVolume(certDir, waitSecretTimeout, waitSecretInterval); err != nil {
setupLog.Error(err, "unable to get webhook secret")
os.Exit(1)
}
}
if err = oamv1alpha2.Setup(mgr, controllerArgs, logging.NewLogrLogger(setupLog)); err != nil {
@@ -178,50 +152,3 @@ func main() {
os.Exit(1)
}
}
// registerHealthChecks is used to create readiness&liveness probes
func registerHealthChecks(mgr ctrl.Manager) error {
setupLog.Info("creating readiness/health check")
if err := mgr.AddReadyzCheck("ping", healthz.Ping); err != nil {
return err
}
if err := mgr.AddHealthzCheck("ping", healthz.Ping); err != nil {
return err
}
return nil
}
// waitWebhookSecretVolume waits for webhook secret ready to avoid mgr running crash
func waitWebhookSecretVolume(certDir string, timeout, interval time.Duration) error {
start := time.Now()
for {
time.Sleep(interval)
if time.Since(start) > timeout {
return fmt.Errorf("getting webhook secret timeout after %s", timeout.String())
}
setupLog.Info(fmt.Sprintf("waiting webhook secret, time consumed: %d/%d seconds ...",
int64(time.Since(start).Seconds()), int64(timeout.Seconds())))
if _, err := os.Stat(certDir); !os.IsNotExist(err) {
f, _ := os.Open(certDir)
defer f.Close()
// check if dir is empty
if _, err := f.Readdir(1); err == io.EOF {
continue
}
// check if secret files are empty
err := filepath.Walk(certDir, func(path string, info os.FileInfo, err error) error {
// even Cert dir is created, cert files are still empty for a while
if info.Size() == 0 {
return errors.New("secret is not ready")
}
return nil
})
if err == nil {
setupLog.Info(fmt.Sprintf("webhook secret is ready (time consumed: %d seconds)",
int64(time.Since(start).Seconds())))
return nil
}
}
}
}

View File

@@ -1,66 +0,0 @@
package main
import (
"io/ioutil"
"os"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var (
testdir = "testdir"
testTimeout = 2 * time.Second
testInterval = 1 * time.Second
)
func TestGinkgo(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "test main")
}
var _ = Describe("test waitSecretVolume", func() {
BeforeEach(func() {
err := os.MkdirAll(testdir, 0755)
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
os.RemoveAll(testdir)
})
When("dir not exist or empty", func() {
It("return timeout error", func() {
err := waitWebhookSecretVolume(testdir, testTimeout, testInterval)
Expect(err).To(HaveOccurred())
By("remove dir")
os.RemoveAll(testdir)
err = waitWebhookSecretVolume(testdir, testTimeout, testInterval)
Expect(err).To(HaveOccurred())
})
})
When("dir contains empty file", func() {
It("return timeout error", func() {
By("add empty file")
_, err := os.Create(testdir + "/emptyFile")
Expect(err).NotTo(HaveOccurred())
err = waitWebhookSecretVolume(testdir, testTimeout, testInterval)
Expect(err).To(HaveOccurred())
})
})
When("files in dir are not empty", func() {
It("return nil", func() {
By("add non-empty file")
_, err := os.Create(testdir + "/file")
Expect(err).NotTo(HaveOccurred())
err = ioutil.WriteFile(testdir+"/file", []byte("test"), os.ModeAppend)
Expect(err).NotTo(HaveOccurred())
err = waitWebhookSecretVolume(testdir, testTimeout, testInterval)
Expect(err).NotTo(HaveOccurred())
})
})
})

View File

@@ -1,5 +0,0 @@
package fake
// ChartSource is a base64-encoded, gzipped tarball of the default Helm chart(charts/vela-core).
// Its value is initialized at build time. This whole file will be rewrite at that time, please don't change this file.
var ChartSource string

View File

@@ -1,19 +1,176 @@
package main
import (
"flag"
"fmt"
"math/rand"
"os"
"runtime"
"time"
"github.com/oam-dev/kubevela/api/types"
"github.com/oam-dev/kubevela/cmd/vela/fake"
"github.com/oam-dev/kubevela/pkg/commands"
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
"github.com/oam-dev/kubevela/pkg/utils/system"
"github.com/oam-dev/kubevela/version"
"github.com/crossplane/oam-kubernetes-runtime/apis/core"
"github.com/gosuri/uitable"
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
"github.com/spf13/cobra"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"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)
_ = certmanager.AddToScheme(scheme)
_ = core.AddToScheme(scheme)
// +kubebuilder:scaffold:scheme
}
func main() {
rand.Seed(time.Now().UnixNano())
command := commands.NewCommand()
command := newCommand()
if err := command.Execute(); err != nil {
os.Exit(1)
}
}
func newCommand() *cobra.Command {
ioStream := cmdutil.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
cmds := &cobra.Command{
Use: "vela",
DisableFlagParsing: true,
Run: func(cmd *cobra.Command, args []string) {
allCommands := cmd.Commands()
cmd.Printf("✈️ A Micro App Platform for Kubernetes.\n\nUsage:\n vela [flags]\n vela [command]\n\nAvailable Commands:\n\n")
PrintHelpByTag(cmd, allCommands, types.TypeStart)
PrintHelpByTag(cmd, allCommands, types.TypeApp)
PrintHelpByTag(cmd, allCommands, types.TypeTraits)
PrintHelpByTag(cmd, allCommands, types.TypeOthers)
PrintHelpByTag(cmd, allCommands, types.TypeSystem)
cmd.Println("Flags:")
cmd.Println(" -h, --help help for vela")
cmd.Println()
cmd.Println(`Use "vela [command] --help" for more information about a command.`)
},
SilenceUsage: true,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveNoFileComp
},
}
cmds.PersistentFlags().StringP("env", "e", "", "specify env name for application")
restConf, err := config.GetConfig()
if err != nil {
fmt.Println("get kubeconfig err", err)
os.Exit(1)
}
commandArgs := types.Args{
Config: restConf,
Schema: scheme,
}
if err := system.InitDirs(); err != nil {
fmt.Println("InitDir err", err)
os.Exit(1)
}
cmds.AddCommand(
// Getting Start
commands.NewInstallCommand(commandArgs, chartTGZSource, ioStream),
commands.NewEnvCommand(commandArgs, ioStream),
// Getting Start
NewVersionCommand(),
// Apps
commands.NewAppsCommand(commandArgs, ioStream),
// Workloads
commands.AddCompCommands(commandArgs, ioStream),
// Capability Systems
commands.CapabilityCommandGroup(commandArgs, ioStream),
// System
commands.SystemCommandGroup(commandArgs, ioStream),
commands.NewCompletionCommand(),
commands.NewTraitsCommand(ioStream),
commands.NewWorkloadsCommand(ioStream),
commands.NewDashboardCommand(commandArgs, ioStream, fake.FrontendSource),
commands.NewLogsCommand(commandArgs, ioStream),
)
// Traits
if err = commands.AddTraitCommands(cmds, commandArgs, ioStream); err != nil {
fmt.Println("Add trait commands from traitDefinition err", err)
os.Exit(1)
}
// this is for mute klog
fset := flag.NewFlagSet("logs", flag.ContinueOnError)
klog.InitFlags(fset)
_ = fset.Set("v", "-1")
return cmds
}
func PrintHelpByTag(cmd *cobra.Command, all []*cobra.Command, tag string) {
cmd.Printf(" %s:\n\n", tag)
table := uitable.New()
for _, c := range all {
if val, ok := c.Annotations[types.TagCommandType]; ok && val == tag {
table.AddRow(" "+c.Use, c.Long)
for _, subcmd := range c.Commands() {
table.AddRow(" "+subcmd.Use, " "+subcmd.Long)
}
}
}
cmd.Println(table.String())
if tag == types.TypeTraits {
if len(table.Rows) > 0 {
cmd.Println()
}
cmd.Println(" Want more? < install more capabilities by `vela cap` >")
}
cmd.Println()
}
func NewVersionCommand() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Prints out build version information",
Long: "Prints out build version information",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf(`Version: %v
GitRevision: %v
GolangVersion: %v
`,
version.VelaVersion,
version.GitRevision,
runtime.Version())
},
Annotations: map[string]string{
types.TagCommandType: types.TypeStart,
},
}
}

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

@@ -0,0 +1,19 @@
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt-prod
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: wonderflow@icloud.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource that will be used to store the account's private key.
name: example-issuer-account-key
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
class: nginx

View File

@@ -1,31 +1,25 @@
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: manualscalertraits.core.oam.dev
annotations:
definition.oam.dev/apiVersion: core.oam.dev/v1alpha2
definition.oam.dev/kind: ManualScalerTrait
definition.oam.dev/description: "Scale replica for workload"
name: scaler
namespace: default
definition.oam.dev/apiVersion: "core.oam.dev/v1alpha2"
definition.oam.dev/kind: "ManualScalerTrait"
spec:
appliesToWorkloads:
- core.oam.dev/v1alpha2.ContainerizedWorkload
- apps/v1.Deployment
- webservice
definitionRef:
name: manualscalertraits.core.oam.dev
workloadRefPath: spec.workloadRef
extension:
template: |-
output: {
template: |
#Template: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ManualScalerTrait"
spec: {
replicaCount: parameter.replica
replicaCount: scale.replica
}
}
#scale: {
scale: {
//+short=r
replica: *2 | int
}
parameter: #scale

View File

@@ -0,0 +1,37 @@
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: routes.standard.oam.dev
annotations:
definition.oam.dev/apiVersion: standard.oam.dev/v1alpha1
definition.oam.dev/kind: Route
spec:
appliesToWorkloads:
- core.oam.dev/v1alpha2.ContainerizedWorkload
- standard.oam.dev/v1alpha1.Containerized
- deployments.apps
workloadRefPath: spec.workloadRef
definitionRef:
name: routes.standard.oam.dev
extension:
template: |
#Template: {
apiVersion: "standard.oam.dev/v1alpha1"
kind: "Route"
spec: {
host: route.domain
path: route.path
tls: {
issuerName: route.issuer
}
backend: {
port: route.port
}
}
}
route: {
domain: *"" | string
path: *"" | string
port: *443 | int
issuer: *"" | string
}

View File

@@ -0,0 +1,30 @@
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: simplerollouttraits.extend.oam.dev
annotations:
"definition.oam.dev/apiVersion": "extend.oam.dev/v1alpha2"
"definition.oam.dev/kind": "SimpleRolloutTrait"
spec:
revisionEnabled: true
appliesToWorkloads:
- core.oam.dev/v1alpha2.ContainerizedWorkload
- deployments.apps
definitionRef:
name: simplerollouttraits.extend.oam.dev
extension:
template: |
#Template: {
apiVersion: "extend.oam.dev/v1alpha2"
kind: "SimpleRolloutTrait"
spec: {
replica: rollout.replica
maxUnavailable: rollout.maxUnavailable
batch: rollout.batch
}
}
rollout: {
replica: *3 | int
maxUnavailable: *1 | int
batch: *2 | int
}

View File

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

View File

@@ -0,0 +1,52 @@
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: deployments.apps
annotations:
definition.oam.dev/apiVersion: "apps/v1"
definition.oam.dev/kind: "Deployment"
spec:
definitionRef:
name: deployments.apps
extension:
template: |
#Template: {
apiVersion: "apps/v1"
kind: "Deployment"
metadata: name: deployment.name
spec: {
selector:
matchLabels:
app: deployment.name
template: {
metadata:
labels:
app: deployment.name
spec: containers: [{
image: deployment.image
name: deployment.name
env: deployment.env
ports: [{
containerPort: deployment.port
protocol: "TCP"
name: "default"
}]
}]
}
}
}
deployment: {
name: string
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *8080 | int
env: [...{
name: string
value: string
}]
}

View File

@@ -29,9 +29,9 @@ webhooks:
service:
name: webhook-service
namespace: system
path: /mutate-standard-oam-dev-v1alpha1-podspecworkload
path: /mutate-standard-oam-dev-v1alpha1-containerized
failurePolicy: Fail
name: mpodspecworkload.kb.io
name: mcontainerized.kb.io
rules:
- apiGroups:
- standard.oam.dev
@@ -41,7 +41,7 @@ webhooks:
- CREATE
- UPDATE
resources:
- PodSpecWorkload
- Containerized
---
apiVersion: admissionregistration.k8s.io/v1beta1
@@ -74,9 +74,9 @@ webhooks:
service:
name: webhook-service
namespace: system
path: /validate-standard-oam-dev-v1alpha1-podspecworkload
path: /validate-standard-oam-dev-v1alpha1-containerized
failurePolicy: Fail
name: vpodspecworkload.kb.io
name: vcontainerized.kb.io
rules:
- apiGroups:
- standard.oam.dev
@@ -87,4 +87,4 @@ webhooks:
- UPDATE
- DELETE
resources:
- PodSpecWorkload
- Containerized

View File

@@ -42,6 +42,12 @@ export default defineConfig({
path: `/ApplicationList`,
component: './ApplicationList',
},
{
name: 'ApplicationList.ApplicationListDetail',
hideInMenu: true,
path: '/ApplicationList/ApplicationListDetail',
component: './ApplicationList/ApplicationListDetail',
},
{
name: 'ApplicationList.WorkloadDetail',
icon: 'smile',
@@ -56,6 +62,12 @@ export default defineConfig({
component: './Traits/Detail',
hideInMenu: true,
},
{
name: 'ApplicationList.CreateApplication',
hideInMenu: true,
path: '/ApplicationList/CreateApplication',
component: './ApplicationList/CreateApplication',
},
{
name: 'ApplicationList.Components',
hideInMenu: true,

View File

@@ -8,7 +8,7 @@
export default {
dev: {
'/api': {
target: 'http://127.0.0.1:38081/',
target: 'http://30.11.171.29:38081/',
changeOrigin: true,
},
},

View File

@@ -35,10 +35,6 @@ export default class CreateTraitItem extends React.PureComponent {
return this.formRefStep2.current.getFieldsValue();
};
resetFields = () => {
return this.formRefStep2.current.resetFields();
};
validateFields = () => {
return this.formRefStep2.current.validateFields();
};

View File

@@ -30,7 +30,6 @@ class Trait extends React.Component {
this.state = {
visible: false,
selectValue: null,
compList: [],
};
}
@@ -38,19 +37,6 @@ class Trait extends React.Component {
this.getInitialData();
}
shouldComponentUpdate(nextProps) {
if (nextProps.currentEnv === this.props.currentEnv) {
return true;
}
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${nextProps.currentEnv}/apps/`,
},
});
return true;
}
getInitialData = async () => {
if (this.props.currentEnv) {
await this.props.dispatch({
@@ -85,12 +71,7 @@ class Trait extends React.Component {
};
const submitData = this.formRefStep2.current.getFieldValue();
Object.keys(submitData).forEach((currentKey) => {
if (
currentKey !== 'name' &&
currentKey !== 'appName' &&
currentKey !== 'compName' &&
submitData[currentKey]
) {
if (currentKey !== 'name' && currentKey !== 'appName' && submitData[currentKey]) {
submitObj.flags.push({
name: currentKey,
value: submitData[currentKey].toString(),
@@ -98,14 +79,13 @@ class Trait extends React.Component {
}
});
const { currentEnv: envName } = this.props;
const { appName, compName } = submitData;
if (envName && appName && compName) {
const { appName } = submitData;
if (envName && appName) {
const res = await this.props.dispatch({
type: 'trait/attachOneTraits',
payload: {
envName,
appName,
compName,
params: submitObj,
},
});
@@ -116,8 +96,11 @@ class Trait extends React.Component {
message.success(res);
const { history } = this.props.propsObj;
history.push({
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
pathname: '/ApplicationList/ApplicationListDetail',
state: {
appName,
envName,
},
});
}
}
@@ -130,30 +113,10 @@ class Trait extends React.Component {
});
};
onChange = async (value) => {
onChange = (value) => {
this.setState({
selectValue: value,
compList: [],
});
const res = await this.props.dispatch({
type: 'applist/getAppDetail',
payload: {
envName: this.props.currentEnv,
appName: value,
},
});
if (res) {
const compData = _.get(res, 'components', []);
const compList = [];
compData.forEach((item) => {
compList.push({
compName: item.name,
});
});
this.setState({
compList,
});
}
};
onSearch = () => {};
@@ -168,11 +131,7 @@ class Trait extends React.Component {
}
});
}
let appList = _.get(this.props, 'returnObj', []);
if (!appList) {
appList = [];
}
const { compList = [] } = this.state;
const appList = _.get(this.props, 'returnObj', []);
return (
<div>
<div className="breadCrumb">
@@ -253,12 +212,13 @@ class Trait extends React.Component {
initialValues={initialObj}
>
<Form.Item
label="App"
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"
@@ -270,9 +230,9 @@ class Trait extends React.Component {
}
>
{appList.length ? (
appList.map((item, index) => {
appList.map((item) => {
return (
<Option key={index.toString()} value={item.name}>
<Option key={item.name} value={item.name}>
{item.name}
</Option>
);
@@ -282,30 +242,6 @@ class Trait extends React.Component {
)}
</Select>
</Form.Item>
<Form.Item
label="Component"
name="compName"
rules={[{ required: true, message: 'Please Select a Component!' }]}
>
<Select
allowClear
// value={this.state.selectValue}
style={{ width: '100%' }}
placeholder="Select a Component"
>
{compList.length ? (
compList.map((item) => {
return (
<Option key={item.compName} value={item.compName}>
{item.compName}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Form.Item>
<div className="relativeBox">
<Form.Item label="Properties" />
{settings ? (

View File

@@ -1,108 +1,12 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Row, Col, Modal, Select, Breadcrumb, Form } from 'antd';
import { connect } from 'dva';
import { Button, Row, Col, Breadcrumb } from 'antd';
import { Link } from 'umi';
import _ from 'lodash';
import './index.less';
const { Option } = Select;
const layout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
@connect(({ loading, applist, globalData }) => ({
loadingAll: loading.models.applist,
currentEnv: globalData.currentEnv,
returnObj: applist.returnObj,
}))
export default class Workload extends React.Component {
formRefStep2 = React.createRef();
constructor(props) {
super(props);
this.state = {
visible: false,
};
}
componentDidMount() {
this.getInitialData();
}
shouldComponentUpdate(nextProps) {
if (nextProps.currentEnv === this.props.currentEnv) {
return true;
}
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${nextProps.currentEnv}/apps/`,
},
});
return true;
}
getInitialData = async () => {
if (this.props.currentEnv) {
await this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${this.props.currentEnv}/apps/`,
},
});
}
};
showModal = () => {
this.setState(
{
visible: true,
},
() => {
if (this.formRefStep2.current) {
this.formRefStep2.current.resetFields();
}
},
);
};
handleOk = async () => {
const submitData = await this.formRefStep2.current.validateFields();
const { history } = this.props.propsObj;
history.push({
pathname: `/ApplicationList/${submitData.appName}/createComponent`,
state: {
...submitData,
isCreate: false,
envName: this.props.currentEnv,
WorkloadType: this.props.propsObj.title,
},
});
};
handleCancel = () => {
this.setState({
visible: false,
});
};
onChange = () => {};
onSearch = () => {};
export default class Workload extends React.PureComponent {
render() {
const { btnValue, title, crdInfo, settings, btnIsShow } = this.props.propsObj;
let appList = _.get(this.props, 'returnObj', []);
if (!appList) {
appList = [];
}
const { btnValue, pathname, title, crdInfo, state, settings, btnIsShow } = this.props.propsObj;
return (
<div>
<div className="breadCrumb">
@@ -152,73 +56,13 @@ export default class Workload extends React.Component {
);
})}
</div>
{/* <Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
<Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
<Button type="primary" className="create-button">
{btnValue}
</Button>
</Link> */}
<Button
type="primary"
className="create-button"
onClick={() => this.showModal()}
style={{ display: btnIsShow ? 'block' : 'none' }}
>
{btnValue}
</Button>
</Link>
</Col>
</Row>
<Modal
title="Add Component"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Next
</Button>,
]}
>
<Form
labelAlign="left"
{...layout}
ref={this.formRefStep2}
name="control-ref"
className="traitItem"
>
<Form.Item
label="App"
name="appName"
rules={[{ required: true, message: 'Please Select a Application!' }]}
>
<Select
showSearch
style={{ width: '100%' }}
placeholder="Select a Application"
optionFilterProp="children"
onChange={this.onChange}
onSearch={this.onSearch}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{appList.length ? (
appList.map((item, index) => {
return (
<Option key={index.toString()} value={item.name}>
{item.name}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Form.Item>
</Form>
</Modal>
</PageContainer>
</div>
);

View File

@@ -85,19 +85,3 @@ ol {
.ant-breadcrumb a:hover {
color: #1b58f4 !important;
}
// 对齐
.ant-form-item-label > label::before {
display: inline-block;
width: 7.09px;
height: 14px;
margin-right: 4px;
color: rgb(255, 77, 79);
font-size: 14px;
font-family: SimSun, sans-serif;
line-height: 1;
content: '';
}
.ant-spin-nested-loading {
height: calc(100% - 46px) !important;
}

View File

@@ -1,22 +0,0 @@
import { getComponentList, getComponentDetail, deleteComponent } from '@/services/components.js';
const TestModel = {
namespace: 'components',
state: {},
effects: {
*getComponentList({ payload }, { call }) {
const res = yield call(getComponentList, payload);
return res;
},
*getComponentDetail({ payload }, { call }) {
const res = yield call(getComponentDetail, payload);
return res;
},
*deleteComponent({ payload }, { call }) {
const res = yield call(deleteComponent, payload);
return res;
},
},
reducers: {},
};
export default TestModel;

View File

@@ -0,0 +1,108 @@
import React, { useEffect } from 'react';
import G6 from '@antv/g6';
const Topology = () => {
let graph = null;
const data = {
nodes: [
{
id: 'node1',
x: 150,
y: 50,
label: 'node1',
},
{
id: 'node2',
x: 250,
y: 200,
label: 'node2',
},
{
id: 'node3',
x: 100,
y: 350,
label: 'node3',
},
],
edges: [
{
source: 'node1',
target: 'node2',
label: 'edge 1',
},
{
source: 'node2',
target: 'node3',
label: 'edge 2',
},
{
source: 'node3',
target: 'node1',
label: 'edge 3',
},
],
};
const width = 1000;
const height = 400;
useEffect(() => {
if (!graph) {
graph = new G6.Graph({
container: 'container',
width,
height,
// translate the graph to align the canvas's center, support by v3.5.1
fitCenter: true,
defaultNode: {
type: 'circle',
size: [40],
color: '#5B8FF9',
style: {
fill: '#9EC9FF',
lineWidth: 3,
},
labelCfg: {
style: {
fill: '#1890ff',
fontSize: 14,
},
position: 'bottom',
},
},
defaultEdge: {
labelCfg: {
autoRotate: true,
style: {
background: {
fill: '#ffffff',
stroke: '#9EC9FF',
padding: [2, 2, 2, 2],
radius: 2,
},
},
},
},
modes: {
default: ['drag-canvas', 'drag-node'],
},
nodeStateStyles: {
// style configurations for hover state
hover: {
fillOpacity: 0.8,
},
// style configurations for selected state
selected: {
lineWidth: 5,
},
},
});
}
graph.data(data);
graph.render();
}, []);
return <div id="container" style={{ overflow: 'auto', width: '100%', height: '400px' }} />;
};
export default Topology;

View File

@@ -0,0 +1,490 @@
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,
Breadcrumb,
} from 'antd';
import { connect } from 'dva';
import _ from 'lodash';
import { Link } from 'umi';
import CreateTraitItem from '../../../components/AttachOneTrait/index.jsx';
const { TabPane } = Tabs;
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.applist,
currentEnv: globalData.currentEnv,
}))
class TableList extends React.Component {
constructor(props) {
super(props);
this.state = {
appDetailData: {},
visible: false,
traitList: [],
availableTraitList: [],
envName: '',
appName: '',
};
}
componentDidMount() {
this.getInitialData();
}
getInitialData = async () => {
let appName = '';
let envName = '';
if (this.props.location.state) {
appName = _.get(this.props, 'location.state.appName', '');
envName = _.get(this.props, 'location.state.envName', '');
sessionStorage.setItem('appName', appName);
sessionStorage.setItem('envName', envName);
} else {
appName = sessionStorage.getItem('appName');
envName = sessionStorage.getItem('envName');
}
this.setState({
appName,
envName,
});
if (appName && envName) {
const res = await this.props.dispatch({
type: 'applist/getAppDetail',
payload: {
envName,
appName,
},
});
if (res) {
this.setState({
appDetailData: res,
});
}
const traits = await this.props.dispatch({
type: 'trait/getTraits',
});
if (traits) {
this.setState({
traitList: traits,
});
}
const workloadType = _.get(res, 'Workload.workload.kind', '');
if (workloadType && workloadType === 'ContainerizedWorkload') {
this.getAcceptTrait('containerized');
} else if (workloadType && workloadType === 'Deployment') {
this.getAcceptTrait('deployment');
}
}
};
getAcceptTrait = (workloadType) => {
const res = this.state.traitList.filter((item) => {
if (item.appliesTo.indexOf(workloadType) !== -1) {
return true;
}
return false;
});
this.setState(() => ({
availableTraitList: res,
}));
};
deleteApp = async (e) => {
e.stopPropagation();
const { envName } = this.state;
const { appDetailData } = this.state;
const appName = _.get(appDetailData, 'Workload.workload.metadata.name', '');
if (appName && envName) {
const res = await this.props.dispatch({
type: 'applist/deleteApp',
payload: {
appName,
envName,
},
});
if (res) {
message.success(res);
this.props.history.push({ pathname: '/ApplicationList' });
}
}
};
deleteTrait = async (e, item) => {
e.stopPropagation();
const { appName, envName } = this.state;
const traitNameObj = _.get(item, 'trait.metadata.annotations', '');
const traitName = traitNameObj['vela.oam.dev/traitDef'] || traitNameObj['trait.oam.dev/name'];
if (traitName && appName && envName) {
const res = await this.props.dispatch({
type: 'trait/deleteOneTrait',
payload: {
envName,
appName,
traitName,
},
});
if (res) {
message.success(res);
this.getInitialData(2);
}
}
};
cancel = (e) => {
e.stopPropagation();
};
createTrait = async () => {
await this.setState({
visible: true,
});
};
handleOk = async () => {
await this.child.validateFields();
const submitData = this.child.getSelectValue();
if (submitData.name) {
const submitObj = {
name: submitData.name,
flags: [],
};
Object.keys(submitData).forEach((currentKey) => {
if (currentKey !== 'name' && submitData[currentKey]) {
submitObj.flags.push({
name: currentKey,
value: submitData[currentKey].toString(),
});
}
});
const { envName, appName } = this.state;
if (envName && appName) {
const res = await this.props.dispatch({
type: 'trait/attachOneTraits',
payload: {
envName,
appName,
params: submitObj,
},
});
if (res) {
this.setState({
visible: false,
});
message.success(res);
this.getInitialData(2);
}
}
} else {
message.warning('please select a trait type');
}
};
handleCancel = () => {
this.setState({
visible: false,
});
};
hrefClick = (e) => {
e.stopPropagation();
};
gotoWorkloadDetail = (e) => {
e.stopPropagation();
};
gotoTraitDetail = (e) => {
e.stopPropagation();
};
render() {
const status = _.get(this.state.appDetailData, 'Status', '');
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
const Traits = _.get(this.state.appDetailData, 'Traits', []);
let containers = {};
containers = _.get(Workload, 'spec.containers[0]', {});
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const colorObj = {
Deployed: '#4CAF51',
Staging: '#F44337',
UNKNOWN: '#1890ff',
};
return (
<div>
<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(containers[currentKey], '[0].containerPort', '')}</p>
</Col>
</Fragment>
);
// eslint-disable-next-line no-else-return
} else if (currentKey === 'name') {
return <Fragment key={currentKey} />;
// eslint-disable-next-line no-else-return
} else if (currentKey === 'env') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>env</p>
</Col>
<Col span="16">
<p>{_.get(containers[currentKey], '[0].value', '')}</p>
</Col>
</Fragment>
);
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{containers[currentKey]}</p>
</Col>
</Fragment>
);
})}
</Row>
</div>
<Popconfirm
title="Are you sure delete this app?"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button danger>Delete</Button>
</Popconfirm>
</Col>
<Col span="1" />
<Col span="10">
{Traits.length ? (
Traits.map((item, index) => {
const traitItem = _.get(item, 'trait', {});
const annotations = _.get(traitItem, 'metadata.annotations', {});
let traitType = 1;
const spec = _.get(traitItem, 'spec', {});
if (traitItem.kind === 'Ingress') {
traitType = 2;
}
return (
<div
className="summaryBox"
onClick={(e) => this.gotoTraitDetail(e, traitItem)}
key={index.toString()}
>
<Row>
<Col span="22">
<p className="title">{traitItem.kind}</p>
<p>{traitItem.apiVersion}</p>
</Col>
<Col span="2">
<p
className="title hasCursor"
onClick={(e) => {
e.stopPropagation();
}}
>
?
</p>
</Col>
</Row>
<Row>
{Object.keys(annotations).map((currentKey3) => {
return (
<Fragment key={currentKey3}>
<Col span="8">
<p>{currentKey3}:</p>
</Col>
<Col span="8">
<p>{annotations[currentKey3]}</p>
</Col>
</Fragment>
);
})}
</Row>
<p className="title">Properties:</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>domain</p>
</Col>
<Col span="16">
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.serviceName',
'',
)}
</p>
</Col>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.servicePort',
'',
)}
</p>
</Col>
</Fragment>
) : (
Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})
)}
</Row>
<div style={{ clear: 'both', height: '32px' }}>
<Popconfirm
title="Are you sure delete this trait?"
onConfirm={(e) => this.deleteTrait(e, item)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button
danger
className="floatRight"
onClick={(e) => {
e.stopPropagation();
}}
>
Delete
</Button>
</Popconfirm>
</div>
</div>
);
})
) : (
<Fragment />
)}
<Tooltip placement="top" title="Attach Trait">
<p
className="hasCursor"
style={{
fontSize: '30px',
display: 'inline-flex',
}}
onClick={this.createTrait}
>
+
</p>
</Tooltip>
</Col>
</Row>
</TabPane>
<TabPane tab="Topology" key="2">
<p>Topology</p>
</TabPane>
</Tabs>
</div>
<Modal
title="Attach a Trait"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Confirm
</Button>,
]}
>
<CreateTraitItem
onRef={(ref) => {
this.child = ref;
}}
availableTraitList={this.state.availableTraitList}
initialValues={{}}
/>
</Modal>
</Spin>
</PageContainer>
</div>
);
}
}
export default TableList;

View File

@@ -0,0 +1,49 @@
.app-detial {
.ant-tabs {
height: 800px;
min-height: 800px;
padding: 10px 20px;
overflow: auto;
background-color: #fff;
.ant-tabs-content-holder {
background: #fff;
}
}
.summaryBox1,
.summaryBox2,
.summaryBox {
clear: both;
margin-bottom: 20px;
padding: 0 10px 10px;
.title {
font-weight: 500;
font-size: 16px;
line-height: 36px;
}
p {
margin: 0;
font-size: 12px;
line-height: 20px;
}
}
.summaryBox1 {
color: #fff;
background: #0097a7;
cursor: pointer;
}
.summaryBox2 {
color: #fff;
background: #92c47c;
}
.summaryBox {
border: 1px solid black;
cursor: pointer;
}
.hasCursor {
font-size: 20px;
cursor: pointer;
}
.floatRight {
float: right;
}
}

View File

@@ -15,14 +15,12 @@ class TableList extends React.Component {
constructor(props) {
super(props);
this.state = {
compDetailData: {},
appDetailData: {},
visible: false,
traitList: [],
availableTraitList: [],
envName: '',
appName: '',
compName: '',
compList: [],
};
}
@@ -31,144 +29,98 @@ class TableList extends React.Component {
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.compName !== nextProps.compName) {
this.getInitialData(nextProps.compName);
if (this.props.appName !== nextProps.appName) {
this.getInitialData(nextProps.appName);
}
}
getInitialData = async (nextCompName) => {
const appName = _.get(this.props, 'appName', '');
getInitialData = async (nextAppName) => {
let appName = _.get(this.props, 'appName', '');
const envName = _.get(this.props, 'envName', '');
const compList = _.get(this.props, 'compList', []);
let compName = _.get(this.props, 'compName', '');
compName = nextCompName || compName;
this.setState({
compList,
});
if (appName && envName && compName) {
appName = nextAppName || appName;
if (appName && envName) {
this.setState({
envName,
appName,
compName,
});
const res = await this.props.dispatch({
type: 'components/getComponentDetail',
type: 'applist/getAppDetail',
payload: {
envName,
appName,
compName,
},
});
if (res) {
this.setState({
compDetailData: res,
appDetailData: res,
});
const traits = await this.props.dispatch({
type: 'trait/getTraits',
}
const traits = await this.props.dispatch({
type: 'trait/getTraits',
});
if (traits) {
this.setState({
traitList: traits,
});
if (traits) {
const checkedTrait = [];
if (res.traits) {
res.traits.forEach((item) => {
const traitNameObj = _.get(item, 'trait.metadata.annotations', '');
const traitName =
traitNameObj['vela.oam.dev/traitDef'] || traitNameObj['trait.oam.dev/name'];
checkedTrait.push(traitName);
});
}
const newTraits = traits.filter((item) => {
if (checkedTrait.includes(item.name)) {
return false;
}
return true;
});
this.setState({
traitList: newTraits,
});
}
const workloadType = _.get(res, 'workload.kind', '');
if (workloadType) {
this.getAcceptTrait(workloadType.toLowerCase());
}
// if (workloadType && workloadType === '') {
// this.getAcceptTrait('containerized');
// } else if (workloadType && workloadType === 'Deployment') {
// this.getAcceptTrait('deployment');
// }
}
const workloadType = _.get(res, 'Workload.workload.kind', '');
if (workloadType && workloadType === 'ContainerizedWorkload') {
this.getAcceptTrait('containerized');
} else if (workloadType && workloadType === 'Deployment') {
this.getAcceptTrait('deployment');
}
}
};
// getAcceptTrait = (workloadType) => {
// const res = this.state.traitList.filter((item) => {
// if (item.appliesTo) {
// if(item.appliesTo==='*'){
// return true;
// }
// if (item.appliesTo.indexOf(workloadType) !== -1) {
// return true;
// }
// return false;
// }
// return false;
// });
// this.setState(() => ({
// availableTraitList: res,
// }));
// };
getAcceptTrait = () => {
const res = this.state.traitList;
getAcceptTrait = (workloadType) => {
const res = this.state.traitList.filter((item) => {
if (item.appliesTo.indexOf(workloadType) !== -1) {
return true;
}
return false;
});
this.setState(() => ({
availableTraitList: res,
}));
};
deleteComp = async (e) => {
deleteApp = async (e) => {
e.stopPropagation();
const { envName, appName, compName, compList } = this.state;
if (appName && envName && compName) {
const { envName } = this.state;
const { appDetailData } = this.state;
const appName = _.get(appDetailData, 'Workload.workload.metadata.name', '');
if (appName && envName) {
const res = await this.props.dispatch({
type: 'components/deleteComponent',
type: 'applist/deleteApp',
payload: {
appName,
envName,
compName,
},
});
if (res) {
message.success(res);
if (compList.length === 1) {
// 只有一个组件时会同时删除应用
this.props.history.push({
pathname: `/ApplicationList`,
});
} else {
// 删除当前component成功后刷新当前页面
this.props.getInitCompList(compList.length);
}
this.props.history.push({ pathname: '/ApplicationList' });
}
}
};
deleteTrait = async (e, item) => {
e.stopPropagation();
const { appName, envName, compName } = this.state;
const { appName, envName } = this.state;
const traitNameObj = _.get(item, 'trait.metadata.annotations', '');
const traitName = traitNameObj['vela.oam.dev/traitDef'] || traitNameObj['trait.oam.dev/name'];
if (traitName && appName && envName && compName) {
if (traitName && appName && envName) {
const res = await this.props.dispatch({
type: 'trait/deleteOneTrait',
payload: {
envName,
appName,
traitName,
compName,
},
});
if (res) {
message.success(res);
this.getInitialData(compName);
this.getInitialData(2);
}
}
};
@@ -178,16 +130,9 @@ class TableList extends React.Component {
};
createTrait = async () => {
await this.setState(
{
visible: true,
},
() => {
if (this.child) {
this.child.resetFields();
}
},
);
await this.setState({
visible: true,
});
};
handleOk = async () => {
@@ -206,14 +151,13 @@ class TableList extends React.Component {
});
}
});
const { envName, appName, compName } = this.state;
if (envName && appName && compName) {
const { envName, appName } = this.state;
if (envName && appName) {
const res = await this.props.dispatch({
type: 'trait/attachOneTraits',
payload: {
envName,
appName,
compName,
params: submitObj,
},
});
@@ -222,7 +166,7 @@ class TableList extends React.Component {
visible: false,
});
message.success(res);
this.getInitialData(compName);
this.getInitialData(2);
}
}
} else {
@@ -249,16 +193,11 @@ class TableList extends React.Component {
};
render() {
const { compDetailData } = this.state;
const status = _.get(compDetailData, 'status', '');
const Workload = _.get(compDetailData, 'workload', {});
const Traits = _.get(compDetailData, 'traits', []);
const status = _.get(this.state.appDetailData, 'Status', '');
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
const Traits = _.get(this.state.appDetailData, 'Traits', []);
let containers = {};
if (_.get(Workload, 'kind', '') === 'Job') {
containers = _.get(Workload, 'spec.template.spec.containers[0]', {});
} else {
containers = _.get(Workload, 'spec.podSpec.containers[0]', {});
}
containers = _.get(Workload, 'spec.containers[0]', {});
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const colorObj = {
@@ -299,21 +238,6 @@ class TableList extends React.Component {
</p>
<p className="title">Settings:</p>
<Row>
{_.get(Workload, 'kind', '') === 'Job' ? (
<Fragment>
<Col span="8">
<p>count</p>
</Col>
<Col span="16">
<p>
{_.get(Workload, 'spec.completions', '') ||
_.get(Workload, 'spec.parallelism', '')}
</p>
</Col>
</Fragment>
) : (
<Fragment />
)}
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
return (
@@ -356,17 +280,8 @@ class TableList extends React.Component {
</Row>
</div>
<Popconfirm
title={
this.state.compList.length === 1 ? (
<div>
<p>There is only one component in the current application,</p>
<p>Do you want to delete the entire application?</p>
</div>
) : (
'Are you sure delete this component?'
)
}
onConfirm={(e) => this.deleteComp(e)}
title="Are you sure delete this app?"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
@@ -380,7 +295,11 @@ class TableList extends React.Component {
Traits.map((item, index) => {
const traitItem = _.get(item, 'trait', {});
const annotations = _.get(traitItem, 'metadata.annotations', {});
let traitType = 1;
const spec = _.get(traitItem, 'spec', {});
if (traitItem.kind === 'Ingress') {
traitType = 2;
}
return (
<div
className="summaryBox"
@@ -419,33 +338,53 @@ class TableList extends React.Component {
</Row>
<p className="title">Properties:</p>
<Row>
{Object.keys(spec).map((currentKey) => {
if (spec[currentKey].constructor === Object) {
const backend = _.get(spec, `${currentKey}`, {});
return Object.keys(backend).map((currentKey1) => {
return (
<Fragment key={currentKey1}>
<Col span="8">
<p>{currentKey1}</p>
</Col>
<Col span="16">
<p>{backend[currentKey1]}</p>
</Col>
</Fragment>
);
});
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})}
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>domain</p>
</Col>
<Col span="16">
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.serviceName',
'',
)}
</p>
</Col>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.servicePort',
'',
)}
</p>
</Col>
</Fragment>
) : (
Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})
)}
</Row>
<div style={{ clear: 'both', height: '32px' }}>
<Popconfirm

View File

@@ -1,117 +1,73 @@
import React from 'react';
import { Link } from 'umi';
import { Breadcrumb, Button, Menu, Spin, Popconfirm, message } from 'antd';
import { Breadcrumb, Button, Menu, Spin } from 'antd';
import { connect } from 'dva';
import _ from 'lodash';
import './index.less';
import ComponentDetail from '../ComponentDetail/index.jsx';
@connect(({ loading, globalData }) => ({
@connect(({ loading }) => ({
loadingAll: loading.models.applist,
currentEnv: globalData.currentEnv,
}))
class TableList extends React.Component {
class TableList extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
envName: '',
componentName: '',
defaultSelectedKeys: '',
appName: '',
compList: [],
defaultSelectedKeys: 'a1',
};
}
componentDidMount() {
this.getInitData();
}
// shouldComponentUpdate(nextProps) {
// if (nextProps.currentEnv === this.props.currentEnv) {
// return true;
// }
// this.getInitData(nextProps.currentEnv);
// return true;
// }
getInitData = async (changeEnvName) => {
UNSAFE_componentWillMount() {
let appName = '';
let description = '';
let envName = this.props.currentEnv;
let envName = '';
if (this.props.location.state) {
appName = _.get(this.props, 'location.state.appName', '');
description = _.get(this.props, 'location.state.description', '');
envName = _.get(this.props, 'location.state.envName', this.props.currentEnv);
envName = _.get(this.props, 'location.state.envName', '');
sessionStorage.setItem('appName', appName);
sessionStorage.setItem('description', description);
sessionStorage.setItem('envName', envName);
} else {
appName = sessionStorage.getItem('appName');
description = sessionStorage.getItem('description');
envName = sessionStorage.getItem('envName');
}
this.setState({
appName,
envName,
componentName: appName,
});
const res = await this.props.dispatch({
type: 'applist/getAppDetail',
payload: {
envName: changeEnvName || envName,
appName,
},
});
if (res) {
const compData = _.get(res, 'components', []);
const compList = [];
compData.forEach((item) => {
compList.push({
compName: item.name,
});
});
if (appName === 'test33') {
this.setState({
compList,
defaultSelectedKeys: 'a1',
});
} else if (appName === 'test01') {
this.setState({
defaultSelectedKeys: 'c2',
});
} else {
this.setState({
defaultSelectedKeys: 'c3',
});
if (compList.length) {
this.changeComponent({
key: compList[0].compName,
});
}
}
};
}
changeComponent = ({ key }) => {
this.setState({
componentName: key,
defaultSelectedKeys: key,
});
};
deleteApp = async (e) => {
e.stopPropagation();
const { envName } = this.state;
const { appName } = this.state;
if (appName && envName) {
const res = await this.props.dispatch({
type: 'applist/deleteApp',
payload: {
appName,
envName,
},
if (key === 'a1') {
this.setState({
componentName: 'test33',
});
} else if (key === 'c2') {
this.setState({
componentName: 'test01',
});
} else {
this.setState({
componentName: 'testoo',
});
if (res) {
message.success(res);
this.props.history.push({ pathname: '/ApplicationList' });
}
}
};
cancel = (e) => {
e.stopPropagation();
};
render() {
const { envName, componentName, defaultSelectedKeys, appName, compList } = this.state;
const { envName, componentName, defaultSelectedKeys } = this.state;
const { loadingAll } = this.props;
return (
<div style={{ height: '100%' }}>
@@ -123,7 +79,7 @@ class TableList extends React.Component {
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>{appName}</Breadcrumb.Item>
<Breadcrumb.Item>appname</Breadcrumb.Item>
<Breadcrumb.Item>Components</Breadcrumb.Item>
<Breadcrumb.Item>{componentName}</Breadcrumb.Item>
</Breadcrumb>
@@ -135,51 +91,31 @@ class TableList extends React.Component {
mode="inline"
onClick={this.changeComponent}
defaultSelectedKeys={[defaultSelectedKeys]}
selectedKeys={defaultSelectedKeys}
>
<Menu.ItemGroup key="g1" title="Components">
{compList.map((item) => {
return <Menu.Item key={item.compName}>{item.compName}</Menu.Item>;
})}
<Menu.Item key="a1">a1(containerized)</Menu.Item>
<Menu.Item key="c2">c2(deploy)</Menu.Item>
<Menu.Item key="c3">c3(webserver)</Menu.Item>
</Menu.ItemGroup>
</Menu>
<div className="addComp">
<Link
to={{
pathname: `/ApplicationList/${appName}/createComponent`,
state: { appName, envName, isCreate: false },
// pathname: '/ApplicationList/CreateApplication',
pathname: `/ApplicationList/${componentName}/createComponent`,
state: { appName: componentName, envName },
}}
>
add a new comp
</Link>
</div>
</div>
{defaultSelectedKeys ? (
<div className="right">
<div className="btn">
<Popconfirm
title="Are you sure delete this app?"
placement="bottom"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button type="primary">Delete App</Button>
</Popconfirm>
</div>
<ComponentDetail
appName={appName}
compName={componentName}
envName={envName}
getInitCompList={this.getInitData}
compList={compList}
history={this.props.history}
/>
<div className="right">
<div style={{ margin: '10px', float: 'right' }}>
<Button type="primary">Delete App</Button>
</div>
) : (
<div style={{ width: '100%', height: '100%', background: '#FFFFFF' }} />
)}
<ComponentDetail appName={componentName} envName={envName} />
</div>
</div>
</Spin>
</div>

View File

@@ -1,12 +1,6 @@
.ant-spin-nested-loading {
height: 100%;
}
.ant-spin-container {
height: 100%;
}
.appComponent {
display: flex;
height: 100%;
height: calc(100% - 46px);
.left {
width: 200px;
background: #fff;
@@ -30,19 +24,12 @@
text-decoration: underline;
background-color: #fff;
border-top: 1px solid #efefef;
border-right: 1px solid #efefef;
border-bottom: 1px solid #efefef;
cursor: pointer;
}
}
.right {
position: relative;
flex: 1;
.btn {
position: absolute;
top: 10px;
right: 10px;
z-index: 8;
}
// clear: both;
}
}

View File

@@ -0,0 +1,606 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Button, Row, Col, Form, Input, Select, Steps, message, Breadcrumb } from 'antd';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
import CreateTraitItem from '../createTrait/index.jsx';
const { Option } = Select;
const { Step } = Steps;
const layout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.workload,
currentEnv: globalData.currentEnv,
}))
class TableList extends React.Component {
formRefStep1 = React.createRef();
formRefStep2All = React.createRef();
constructor(props) {
super(props);
this.state = {
current: 0,
isShowMore: false,
traitNum: [
{
refname: null,
initialData: {},
uniq: new Date().valueOf(),
},
],
traitList: [],
availableTraitList: [],
workloadList: [],
workloadSettings: [],
step1SubmitObj: {},
step1InitialValues: {
workload_type: '',
},
step1Settings: [],
};
}
componentDidMount() {
this.getInitalData();
}
getInitalData = async () => {
const res = await this.props.dispatch({
type: 'workload/getWorkload',
});
const traits = await this.props.dispatch({
type: 'trait/getTraits',
});
this.setState({
traitList: traits,
});
if (Array.isArray(res) && res.length) {
this.setState(
() => ({
workloadList: res,
}),
() => {
if (this.state.current === 0) {
let WorkloadType = '';
if (this.props.location.state) {
WorkloadType = _.get(this.props, 'location.state.WorkloadType', '');
sessionStorage.setItem('WorkloadType', WorkloadType);
} else {
WorkloadType = sessionStorage.getItem('WorkloadType');
}
this.formRefStep1.current.setFieldsValue({
workload_type: WorkloadType || this.state.workloadList[0].name,
});
this.workloadTypeChange(this.state.workloadList[0].name);
}
},
);
}
};
onFinishStep1 = (values) => {
this.setState({
current: 1,
step1InitialValues: values,
isShowMore: false,
});
};
onFinishStep2 = async () => {
const asyncValidateArray = [];
this.state.traitNum.forEach((item) => {
asyncValidateArray.push(item.refname.validateFields());
});
await Promise.all(asyncValidateArray);
const newTraitNum = this.state.traitNum.map((item) => {
// eslint-disable-next-line no-param-reassign
item.initialData = item.refname.getSelectValue();
return item;
});
// 进行trait数据整理便于第三步展示
this.setState(() => ({
traitNum: newTraitNum,
current: 2,
}));
};
gotoStep2 = () => {
this.setState({
current: 1,
isShowMore: false,
});
};
gotoStep1 = () => {
this.setState({
current: 0,
});
};
changeShowMore = () => {
this.setState({
isShowMore: true,
});
};
addMore = (e) => {
e.preventDefault();
this.setState((prev) => ({
traitNum: prev.traitNum.concat([
{
refname: null,
initialData: {},
uniq: new Date().valueOf(),
},
]),
}));
};
createApp = async () => {
const { traitNum } = this.state;
const { step1SubmitObj } = this.state;
if (step1SubmitObj.env_name !== this.props.currentEnv) {
step1SubmitObj.env_name = this.props.currentEnv;
}
const submitObj = _.cloneDeep(step1SubmitObj);
const { workload_name: workloadName } = step1SubmitObj;
submitObj.flags.push({
name: 'name',
value: workloadName.toString(),
});
// 处理数据为提交的格式
if (traitNum.length) {
const { env_name: envName } = step1SubmitObj;
const step2SubmitObj = [];
traitNum.forEach(({ initialData }) => {
if (initialData.name) {
const initialObj = {
name: initialData.name,
env_name: envName,
workload_name: workloadName,
flags: [],
};
Object.keys(initialData).forEach((key) => {
if (key !== 'name' && initialData[key]) {
initialObj.flags.push({
name: key,
value: initialData[key].toString(),
});
}
});
step2SubmitObj.push(initialObj);
}
});
submitObj.traits = step2SubmitObj;
}
const res = await this.props.dispatch({
type: 'workload/createWorkload',
payload: {
params: submitObj,
},
});
if (res) {
message.success(res);
this.props.history.push({
pathname: '/ApplicationList',
});
}
};
createWorkload = async () => {
await this.formRefStep1.current.validateFields();
const currentData = this.formRefStep1.current.getFieldsValue();
const submitObj = {
env_name: this.props.currentEnv,
workload_type: currentData.workload_type,
workload_name: currentData.workload_name,
flags: [],
};
Object.keys(currentData).forEach((key) => {
if (key !== 'workload_name' && key !== 'workload_type' && currentData[key]) {
submitObj.flags.push({
name: key,
value: currentData[key].toString(),
});
}
});
this.setState({
current: 1,
step1InitialValues: currentData,
step1Settings: submitObj.flags,
step1SubmitObj: submitObj,
});
this.getAcceptTrait(currentData.workload_type);
};
workloadTypeChange = (value) => {
const content = this.formRefStep1.current.getFieldsValue();
this.formRefStep1.current.resetFields();
const initialObj = {
workload_type: content.workload_type,
workload_name: content.workload_name,
};
this.formRefStep1.current.setFieldsValue(initialObj);
const currentWorkloadSetting = this.state.workloadList.filter((item) => {
return item.name === value;
});
if (currentWorkloadSetting.length) {
this.setState(
{
workloadSettings: currentWorkloadSetting[0].parameters,
},
() => {
this.state.workloadSettings.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep1.current.setFieldsValue(initialObj);
},
);
}
this.setState({
traitNum: [
{
refname: null,
initialData: {},
uniq: new Date().valueOf(),
},
],
});
};
getAcceptTrait = (workloadType) => {
const res = this.state.traitList.filter((item) => {
if (item.appliesTo.indexOf(workloadType) !== -1) {
return true;
}
return false;
});
this.setState(() => ({
availableTraitList: res,
}));
};
deleteTraitItem = (uniq) => {
// 删除的时候不要依据数组的index删除,要一个唯一性的值
this.state.traitNum = this.state.traitNum.filter((item) => {
return item.uniq !== uniq;
});
this.setState((prev) => ({
traitNum: prev.traitNum,
}));
};
render() {
const { current, step1InitialValues, traitNum, workloadSettings } = this.state;
let { workloadList } = this.state;
workloadList = Array.isArray(workloadList) ? workloadList : [];
let currentDetail;
if (current === 0) {
currentDetail = (
<div>
<div className="minBox">
<Form
initialValues={step1InitialValues}
labelAlign="left"
{...layout}
ref={this.formRefStep1}
name="control-ref"
onFinish={this.onFinishStep1}
style={{ width: '60%' }}
>
<div style={{ padding: '16px 48px 0px 16px' }}>
<Form.Item
name="workload_name"
label="Name"
rules={[
{
pattern: /^[a-z0-9-_]+$/,
message:
'Names can only use digits(0-9),lowercase letters(a-z),and dashes(-),Underline.',
},
{
required: true,
message: 'Please input name!',
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="workload_type"
label="Workload Type"
rules={[
{
required: true,
message: 'Please select Workload Type!',
},
]}
>
<Select
placeholder="Select a Workload Type"
allowClear
onChange={this.workloadTypeChange}
>
{workloadList.length ? (
workloadList.map((item) => {
return (
<Option value={item.name} key={item.name}>
{item.name}
</Option>
);
})
) : (
<></>
)}
</Select>
</Form.Item>
</div>
<Form.Item
label="Settings"
style={{ background: 'rgba(0, 0, 0, 0.04)', paddingLeft: '16px' }}
/>
<div className="relativeBox">
<p className="hasMore">?</p>
{Array.isArray(workloadSettings) && workloadSettings.length ? (
workloadSettings.map((item) => {
if (item.name === 'name') {
return <Fragment key={item.name} />;
}
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required,
message: `Please input ${item.name}!`,
},
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required,
message: `Please input ${item.name}!`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
</Form.Item>
);
})
) : (
<></>
)}
</div>
<div className="buttonBox">
<Button type="primary" className="floatRightGap" onClick={this.createWorkload}>
Next
</Button>
<Link to="/ApplicationList">
<Button className="floatRightGap">Cancle</Button>
</Link>
</div>
</Form>
</div>
</div>
);
} else if (current === 1) {
currentDetail = (
<div>
<div className="minBox" style={{ width: '60%' }}>
<div style={{ padding: '0px 48px 0px 16px', width: '60%' }}>
<p style={{ fontSize: '18px', lineHeight: '32px' }}>
Name:<span>{step1InitialValues.workload_name}</span>
</p>
</div>
<div style={{ border: '1px solid #eee', padding: '16px 48px 16px 16px' }}>
<p className="title">{step1InitialValues.workload_type}</p>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>apps/v1</span>
<span
style={{
color: '#1890ff',
cursor: 'pointer',
display: this.state.isShowMore ? 'none' : 'black',
}}
onClick={this.changeShowMore}
>
more...
</span>
</div>
{this.state.isShowMore ? (
<div>
<p className="title" style={{ marginTop: '16px' }}>
Settings:
</p>
<Row>
{this.state.step1Settings.map((item) => {
return (
<Fragment key={item.name}>
<Col span="8">
<p>{item.name}:</p>
</Col>
<Col span="16">
<p>{item.value}</p>
</Col>
</Fragment>
);
})}
</Row>
</div>
) : (
''
)}
</div>
<div ref={this.formRefStep2All}>
{traitNum.map((item) => {
return (
<CreateTraitItem
onRef={(ref) => {
// eslint-disable-next-line no-param-reassign
item.refname = ref;
}}
key={item.uniq.toString()}
availableTraitList={this.state.availableTraitList}
uniq={item.uniq}
initialValues={item.initialData}
deleteTraitItem={this.deleteTraitItem}
/>
);
})}
</div>
<button style={{ marginTop: '16px' }} onClick={this.addMore} type="button">
Add More...
</button>
<div className="buttonBox">
<Button type="primary" className="floatRight" onClick={this.onFinishStep2}>
Next
</Button>
<Button className="floatRightGap" onClick={this.gotoStep1}>
Back
</Button>
</div>
</div>
</div>
);
} else {
currentDetail = (
<div>
<div className="minBox">
<p>
Name:<span>{step1InitialValues.workload_name}</span>
</p>
<Row>
<Col span="11">
<div className="summaryBox1">
<Row>
<Col span="22">
<p className="title">{step1InitialValues.workload_type}</p>
<p>apps/v1</p>
</Col>
</Row>
<p className="title hasMargin">Settings:</p>
<Row>
{this.state.step1Settings.map((item) => {
return (
<Fragment key={item.name}>
<Col span="8">
<p>{item.name}:</p>
</Col>
<Col span="16">
<p>{item.value}</p>
</Col>
</Fragment>
);
})}
</Row>
</div>
</Col>
<Col span="1" />
<Col span="10">
{traitNum.map(({ initialData }, index) => {
if (initialData.name) {
return (
<div className="summaryBox" key={index.toString()}>
<Row>
<Col span="22">
<p className="title">{initialData.name}</p>
<p>core.oam.dev/v1alpha2</p>
</Col>
</Row>
<p className="title hasMargin">Properties:</p>
<Row>
{Object.keys(initialData).map((currentKey) => {
if (currentKey !== 'name') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}:</p>
</Col>
<Col span="16">
<p>{initialData[currentKey]}</p>
</Col>
</Fragment>
);
}
return <Fragment key={currentKey} />;
})}
</Row>
</div>
);
}
return <Fragment key={index.toString()} />;
})}
</Col>
</Row>
</div>
<div className="buttonBox">
<Button
type="primary"
className="floatRight"
onClick={() => {
this.createApp();
}}
>
Confirm
</Button>
<Button className="floatRightGap" onClick={this.gotoStep2}>
Back
</Button>
</div>
</div>
);
}
return (
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>CreateApplication</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<div className="create-container create-app">
<Steps current={current}>
<Step title="Step 1" description="Choose Workload" />
<Step title="Step 2" description="Attach Trait" />
<Step title="Step 3" description="Review and confirm" />
</Steps>
{currentDetail}
</div>
</PageContainer>
</div>
);
}
}
export default TableList;

View File

@@ -0,0 +1,66 @@
.create-container {
padding: 10px;
background: #fff;
}
.create-app {
.minBox {
margin: 20px 10px;
padding: 10px;
border: 1px solid #eee;
.title {
font-size: 16px;
line-height: 36px;
}
}
.buttonBox {
clear: both;
height: 42px;
margin: 0 10px;
padding: 10px;
}
.floatRight {
float: right;
}
.floatRightGap {
float: right;
margin-right: 16px;
}
.relativeBox {
position: relative;
padding: 0 48px 0 16px;
.hasMore {
position: absolute;
top: 0;
right: 16px;
padding: 4px;
color: rgb(24, 144, 255);
font-size: 16px;
cursor: pointer;
}
}
.summaryBox1,
.summaryBox2,
.summaryBox {
clear: both;
margin-bottom: 20px;
padding: 0 10px 10px;
p {
margin: 0;
font-size: 12px;
line-height: 20px;
}
}
.summaryBox1 {
color: #fff;
background: rgb(24, 144, 255);
}
.summaryBox2 {
background: yellow;
}
.summaryBox {
border: 1px solid black;
}
.hasMargin {
padding: 8px 0;
}
}

View File

@@ -46,12 +46,11 @@ class TableList extends React.Component {
workloadSettings: [],
step1SubmitObj: {},
step1InitialValues: {
workloadType: '',
workload_type: '',
},
step1Settings: [],
appName: '',
envName: '',
isCreate: '',
};
}
@@ -62,23 +61,18 @@ class TableList extends React.Component {
getInitalData = async () => {
let appName = '';
let envName = '';
let isCreate = '';
if (this.props.location.state) {
appName = _.get(this.props, 'location.state.appName', '');
envName = _.get(this.props, 'location.state.envName', '');
isCreate = _.get(this.props, 'location.state.isCreate', false);
sessionStorage.setItem('appName', appName);
sessionStorage.setItem('envName', envName);
sessionStorage.setItem('isCreate', isCreate);
} else {
appName = sessionStorage.getItem('appName');
envName = sessionStorage.getItem('envName');
isCreate = sessionStorage.getItem('isCreate');
}
this.setState({
appName,
envName,
isCreate,
});
const res = await this.props.dispatch({
type: 'workload/getWorkload',
@@ -104,9 +98,9 @@ class TableList extends React.Component {
WorkloadType = sessionStorage.getItem('WorkloadType');
}
this.formRefStep1.current.setFieldsValue({
workloadType: WorkloadType || this.state.workloadList[0].name,
workload_type: WorkloadType || this.state.workloadList[0].name,
});
this.workloadTypeChange(WorkloadType || this.state.workloadList[0].name);
this.workloadTypeChange(this.state.workloadList[0].name);
}
},
);
@@ -153,9 +147,8 @@ class TableList extends React.Component {
};
changeShowMore = () => {
const isMore = this.state.isShowMore;
this.setState({
isShowMore: !isMore,
isShowMore: true,
});
};
@@ -173,31 +166,27 @@ class TableList extends React.Component {
};
createApp = async () => {
const { traitNum, isCreate } = this.state;
const { traitNum } = this.state;
const { step1SubmitObj } = this.state;
if (isCreate === true || isCreate === 'true') {
step1SubmitObj.envName = this.props.currentEnv;
} else {
step1SubmitObj.envName = this.state.envName;
if (step1SubmitObj.env_name !== this.props.currentEnv) {
step1SubmitObj.env_name = this.props.currentEnv;
}
step1SubmitObj.appName = this.state.appName;
const submitObj = _.cloneDeep(step1SubmitObj);
const { workloadName, appName } = step1SubmitObj;
const { workload_name: workloadName } = step1SubmitObj;
submitObj.flags.push({
name: 'name',
value: workloadName.toString(),
});
// 处理数据为提交的格式
if (traitNum.length) {
const { envName } = step1SubmitObj;
const { env_name: envName } = step1SubmitObj;
const step2SubmitObj = [];
traitNum.forEach(({ initialData }) => {
if (initialData.name) {
const initialObj = {
name: initialData.name,
envName,
workloadName,
appName,
env_name: envName,
workload_name: workloadName,
flags: [],
};
Object.keys(initialData).forEach((key) => {
@@ -221,9 +210,10 @@ class TableList extends React.Component {
});
if (res) {
message.success(res);
const { appName, envName } = this.state;
this.props.history.push({
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName: step1SubmitObj.envName },
state: { appName, envName },
});
}
};
@@ -232,13 +222,13 @@ class TableList extends React.Component {
await this.formRefStep1.current.validateFields();
const currentData = this.formRefStep1.current.getFieldsValue();
const submitObj = {
envName: this.props.currentEnv,
workloadType: currentData.workloadType,
workloadName: currentData.workloadName,
env_name: this.props.currentEnv,
workload_type: currentData.workload_type,
workload_name: currentData.workload_name,
flags: [],
};
Object.keys(currentData).forEach((key) => {
if (key !== 'workloadName' && key !== 'workloadType' && currentData[key]) {
if (key !== 'workload_name' && key !== 'workload_type' && currentData[key]) {
submitObj.flags.push({
name: key,
value: currentData[key].toString(),
@@ -251,15 +241,15 @@ class TableList extends React.Component {
step1Settings: submitObj.flags,
step1SubmitObj: submitObj,
});
this.getAcceptTrait(currentData.workloadType);
this.getAcceptTrait(currentData.workload_type);
};
workloadTypeChange = (value) => {
const content = this.formRefStep1.current.getFieldsValue();
this.formRefStep1.current.resetFields();
const initialObj = {
workloadType: content.workloadType,
workloadName: content.workloadName,
workload_type: content.workload_type,
workload_name: content.workload_name,
};
this.formRefStep1.current.setFieldsValue(initialObj);
const currentWorkloadSetting = this.state.workloadList.filter((item) => {
@@ -293,14 +283,8 @@ class TableList extends React.Component {
getAcceptTrait = (workloadType) => {
const res = this.state.traitList.filter((item) => {
if (item.appliesTo) {
if (item.appliesTo === '*') {
return true;
}
if (item.appliesTo.indexOf(workloadType) !== -1) {
return true;
}
return false;
if (item.appliesTo.indexOf(workloadType) !== -1) {
return true;
}
return false;
});
@@ -321,7 +305,7 @@ class TableList extends React.Component {
render() {
const { appName, envName } = this.state;
const { current, step1InitialValues, traitNum, workloadSettings, isCreate } = this.state;
const { current, step1InitialValues, traitNum, workloadSettings } = this.state;
let { workloadList } = this.state;
workloadList = Array.isArray(workloadList) ? workloadList : [];
let currentDetail;
@@ -340,7 +324,7 @@ class TableList extends React.Component {
>
<div style={{ padding: '16px 48px 0px 16px' }}>
<Form.Item
name="workloadName"
name="workload_name"
label="Name"
rules={[
{
@@ -357,7 +341,7 @@ class TableList extends React.Component {
<Input />
</Form.Item>
<Form.Item
name="workloadType"
name="workload_type"
label="Workload Type"
rules={[
{
@@ -387,11 +371,7 @@ class TableList extends React.Component {
</div>
<Form.Item
label="Settings"
style={{
background: 'rgba(0, 0, 0, 0.04)',
paddingLeft: '16px',
marginLeft: '-10px',
}}
style={{ background: 'rgba(0, 0, 0, 0.04)', paddingLeft: '16px' }}
/>
<div className="relativeBox">
<p className="hasMore">?</p>
@@ -440,24 +420,14 @@ class TableList extends React.Component {
<Button type="primary" className="floatRightGap" onClick={this.createWorkload}>
Next
</Button>
{isCreate === true || isCreate === 'true' ? (
<Link
to={{
pathname: `/ApplicationList`,
}}
>
<Button className="floatRightGap">Cancle</Button>
</Link>
) : (
<Link
to={{
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
}}
>
<Button className="floatRightGap">Cancle</Button>
</Link>
)}
<Link
to={{
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
}}
>
<Button className="floatRightGap">Cancle</Button>
</Link>
</div>
</Form>
</div>
@@ -469,22 +439,22 @@ class TableList extends React.Component {
<div className="minBox" style={{ width: '60%' }}>
<div style={{ padding: '0px 48px 0px 16px', width: '60%' }}>
<p style={{ fontSize: '18px', lineHeight: '32px' }}>
Name:<span>{step1InitialValues.workloadName}</span>
Name:<span>{step1InitialValues.workload_name}</span>
</p>
</div>
<div style={{ border: '1px solid #eee', padding: '16px 48px 16px 16px' }}>
<p className="title">{step1InitialValues.workloadType}</p>
<p className="title">{step1InitialValues.workload_type}</p>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>apps/v1</span>
<span
style={{
color: '#1890ff',
cursor: 'pointer',
// display: this.state.isShowMore ? 'none' : 'black',
display: this.state.isShowMore ? 'none' : 'black',
}}
onClick={this.changeShowMore}
>
{this.state.isShowMore ? 'close...' : 'more...'}
more...
</span>
</div>
{this.state.isShowMore ? (
@@ -547,14 +517,14 @@ class TableList extends React.Component {
<div>
<div className="minBox">
<p>
Name:<span>{step1InitialValues.workloadName}</span>
Name:<span>{step1InitialValues.workload_name}</span>
</p>
<Row>
<Col span="11">
<div className="summaryBox1">
<Row>
<Col span="22">
<p className="title">{step1InitialValues.workloadType}</p>
<p className="title">{step1InitialValues.workload_type}</p>
<p>apps/v1</p>
</Col>
</Row>
@@ -641,18 +611,15 @@ class TableList extends React.Component {
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
{isCreate === true || isCreate === 'true' ? (
<span>{appName}</span>
) : (
<Link
to={{
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
}}
>
{appName}
</Link>
)}
<Link
to={{
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
}}
>
{' '}
{appName}
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>createComponent</Breadcrumb.Item>
</Breadcrumb>

View File

@@ -99,7 +99,7 @@ export default class CreateTraitItem extends React.PureComponent {
})}
</Select>
</Form.Item>
<Form.Item label="Properties" style={{ marginLeft: '-10px' }} />
<Form.Item label="Properties" />
</div>
<div className="relativeBox">
{this.state.parameters ? (

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Form, Spin, Breadcrumb, Modal, Input, Table, Space, message } from 'antd';
import { BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
import { Button, Card, Row, Col, Form, Spin, Empty, Breadcrumb, Modal, Input } from 'antd';
import { connect } from 'dva';
import moment from 'moment';
import './index.less';
@@ -23,55 +24,6 @@ const layout = {
class TableList extends React.Component {
formRef = React.createRef();
columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text, record) => {
return (
<Link
to={{
pathname: `/ApplicationList/${record.name}/Components`,
state: { appName: record.name, envName: this.props.currentEnv },
}}
>
{text}
</Link>
);
},
},
{
title: 'Status',
dataIndex: 'status',
key: 'status',
render: (text) => {
return text;
},
},
{
title: 'Created Time',
dataIndex: 'createdTime',
key: 'createdTime',
render: (text) => {
return this.getFormatDate(text);
},
},
{
title: 'Actions',
dataIndex: 'Actions',
key: 'Actions',
render: (text, record) => {
return (
<Space>
<Button onClick={() => this.goToDetail(record)}>Details</Button>
<Button onClick={() => this.deleteApp(record)}>Delete</Button>
</Space>
);
},
},
];
constructor(props) {
super(props);
this.state = {
@@ -80,7 +32,15 @@ class TableList extends React.Component {
}
componentDidMount() {
this.getInitData();
const { currentEnv } = this.props;
if (currentEnv) {
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${currentEnv}/apps/`,
},
});
}
}
shouldComponentUpdate(nextProps) {
@@ -96,43 +56,6 @@ class TableList extends React.Component {
return true;
}
getInitData = () => {
const { currentEnv } = this.props;
if (currentEnv) {
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${currentEnv}/apps/`,
},
});
}
};
goToDetail = (record) => {
this.props.history.push({
pathname: `/ApplicationList/${record.name}/Components`,
state: { appName: record.name, envName: this.props.currentEnv },
});
};
deleteApp = async (record) => {
const appName = record.name;
const envName = this.props.currentEnv;
if (appName && envName) {
const res = await this.props.dispatch({
type: 'applist/deleteApp',
payload: {
appName,
envName,
},
});
if (res) {
message.success(res);
this.getInitData();
}
}
};
showModal = () => {
this.setState({
visible: true,
@@ -140,11 +63,8 @@ class TableList extends React.Component {
};
handleOk = async () => {
const submitData = await this.formRef.current.validateFields();
this.props.history.push({
pathname: `/ApplicationList/${submitData.appName}/createComponent`,
state: { ...submitData, isCreate: true },
});
await this.formRef.current.validateFields();
// const submitData = await this.formRef.current.validateFields();
};
handleCancel = () => {
@@ -171,8 +91,14 @@ class TableList extends React.Component {
render() {
let { loadingAll, returnObj } = this.props;
returnObj = returnObj || [];
const { currentEnv } = this.props;
loadingAll = loadingAll || false;
returnObj = returnObj || [];
const colorObj = {
Deployed: 'first1',
Staging: 'first2',
UNKNOWN: 'first3',
};
return (
<div>
<div className="breadCrumb">
@@ -188,17 +114,74 @@ class TableList extends React.Component {
<div className="applist">
<Form name="horizontal_login" layout="inline" onFinish={this.onFinish}>
<Form.Item>
<Button onClick={this.showModal} type="primary" style={{ marginBottom: 16 }}>
create
</Button>
<Link to="/ApplicationList/CreateApplication">
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
create
</Button>
</Link>
</Form.Item>
</Form>
</div>
<Table
rowKey={(record) => record.name + Math.random(1, 100)}
columns={this.columns}
dataSource={returnObj}
/>
<Row gutter={16}>
{Array.isArray(returnObj) && returnObj.length ? (
returnObj.map((item, index) => {
const { traits = [] } = item;
return (
<Col span={6} onClick={this.gotoDetail} key={index.toString()}>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName: item.name, envName: currentEnv },
}}
>
<Card
title={item.name}
bordered={false}
extra={this.getFormatDate(item.created)}
>
<div className="cardContent">
<div
className="box2"
style={{ height: this.getHeight(traits.length) }}
/>
<div className="box1">
{traits.length ? (
<div className="box3" style={{ width: '30px' }} />
) : (
''
)}
<div
className={['hasPadding', colorObj[item.status] || 'first3'].join(
' ',
)}
>
<ApartmentOutlined style={{ marginRight: '4px' }} />
{item.workload}
</div>
</div>
{traits.map((item1, index1) => {
return (
<div className="box1" key={index1.toString()}>
<div className="box3" style={{ width: '50px' }} />
<div className="other hasPadding">
<BranchesOutlined style={{ marginRight: '4px' }} />
{item1}
</div>
</div>
);
})}
</div>
</Card>
</Link>
</Col>
);
})
) : (
<div style={{ width: '100%', height: '80%' }}>
<Empty />
</div>
)}
</Row>
</Spin>
<Modal
title="Create Application"
@@ -213,7 +196,7 @@ class TableList extends React.Component {
>
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
<Form.Item
name="appName"
name="name"
label="Name"
rules={[
{

View File

@@ -58,7 +58,6 @@ class TableList extends React.PureComponent {
btnValue: 'Create',
hrefAddress: '#',
btnIsShow: true,
history: this.props.history,
};
this.setState({
propsObj,

View File

@@ -1,22 +0,0 @@
import request from '@/utils/request';
/*
* GET /api/envs/:envName/apps/:appName/components/ (component list)
* Same as GET /api/envs/:envName/apps/:appName (app description).
*/
export async function getComponentList({ envName, appName }) {
return request(`/api/envs/${envName}/apps/${appName}/components/`);
}
/*
* GET /api/envs/:envName/apps/:appName/components/:compName (component details)
*/
export async function getComponentDetail({ envName, appName, compName }) {
return request(`/api/envs/${envName}/apps/${appName}/components/${compName}`);
}
/*
* DELETE /api/envs/:envName/apps/:appName/components/:compName (component delete)
*/
export async function deleteComponent({ envName, appName, compName }) {
return request(`/api/envs/${envName}/apps/${appName}/components/${compName}`, {
method: 'delete',
});
}

View File

@@ -12,10 +12,10 @@ export async function getTraits() {
return request('/api/traits/');
}
/*
* POST /api/envs/:envName/apps/:appName/components/:compName/traits/ (attach a trait)
* POST /api/envs/:envName/apps/:appName/traits/ (attach 单个 trait)
*/
export async function attachOneTraits({ envName, appName, compName, params }) {
return request(`/api/envs/${envName}/apps/${appName}/components/${compName}/traits/`, {
export async function attachOneTraits({ envName, appName, params }) {
return request(`/api/envs/${envName}/apps/${appName}/traits/`, {
method: 'post',
data: {
...params,
@@ -26,13 +26,10 @@ export async function attachOneTraits({ envName, appName, compName, params }) {
});
}
/*
* DELETE /api/envs/:envName/apps/:appName/components/:compName/traits/:traitName (detach a trait)
* DELETE /api/envs/:envName/apps/:appName/traits/:traitName (detach 单个 trait)
*/
export async function deleteOneTrait({ envName, appName, compName, traitName }) {
return request(
`/api/envs/${envName}/apps/${appName}/components/${compName}/traits/${traitName}`,
{
method: 'delete',
},
);
export async function deleteOneTrait({ envName, appName, traitName }) {
return request(`/api/envs/${envName}/apps/${appName}/traits/${traitName}`, {
method: 'delete',
});
}

View File

@@ -1,362 +0,0 @@
# Appfile: Extensible, User-friendly Application Config Format
- Owner: Hongchao Deng (@hongchaodeng)
- Date: 10/14/2020
- Status: Implemented
## Table of Contents
- [Intro](#intro)
- [Goals](#goals)
- [Proposal](#proposal)
- [Registration via Definition/Capability](#registration-via-definitioncapability)
- [Templating](#templating)
- [CLI/UI interoperability](#cliui-interoperability)
- [vela up](#vela-up)
- [Examples](#examples)
## Intro
Vela supports a user-friendly `docker-compose` style config format called `Appfile`. It allows you to define an application's workloads and traits with an opinionated, simplified API interface.
Here's an example to deploy a NodeJS express service:
```yaml
services:
express-server:
# this image will be used in both build and deploy config
image: oamdev/testapp:v1
build:
# Here more runtime specific build templates will be supported, like NodeJS, Go, Python, Ruby.
docker:
file: Dockerfile
context: .
cmd: ["node", "server.js"]
route:
domain: example.com
http: # match the longest prefix
"/": 8080
env:
- FOO=bar
- FOO2=sec:my-secret # map the key same as the env name (`FOO2`) from my-secret to env var
- FOO3=sec:my-secret:key # map specific key from my-secret to env var
- sec:my-secret # map all KV pairs from my-secret to env var
files: # Mount secret as a file
- /mnt/path=sec:my-secret
scale:
replica: 2
auto: # automatic scale up and down based on given metrics
range: "1-10"
cpu: 80 # if cpu utilization is above 80%, scale up
qps: 1000 # if qps is higher than 1k, scale up
canary: # Auto-create canary deployment. Only upgrade after verify successfully.
replica: 1 # canary deployment size
headers:
- "foo:bar.*"
```
Save this file to project root dir, and run:
```bash
vela up
```
It will build container image, render deployment manifests in yaml, and apply them to the server.
### Extensible Design
The Appfile could be extended with more configurations by adding more capabilities to the OAM system. The config fields in Appfile are strongly correlated to the [capabilities system of OAM](https://github.com/oam-dev/kubevela/blob/master/DESIGN.md#capability-register-and-discovery) Config fields are registered in the capabilities system and exposed via a [CUE template](https://cuelang.org/).
Here is an example of a capability definition that platform builders register:
```yaml
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: webservice
spec:
definitionRef:
name: deployments.apps
extension:
template: |
parameter: #webservice
#webservice: {
// +vela:cli:enabled=true
// +vela:cli:usage=specify commands to run in container
// +vela:cli:short=c
cmd: [...string]
env: [...string]
files: [...string]
image: string
}
output: {
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: context.name
spec: {
selector: {
matchLabels:
app: context.name
}
template: {
metadata:
labels:
app: context.name
spec: {
containers: [{
name: context.name
image: parameter.image
command: parameter.cmd
}]
}
}
}
}
```
Apply the file to APIServer, and the fields will be extended into Appfile. Note that there are some conventions that differs Workloads and Traits, and around CLI flags. We will cover that more detailedly below.
## Goals
The Appfile design has the following goals:
1. Provide a user friendly, `docker-compose` style config format to developers.
2. Configuration fields can be extended by registering more capabilities into OAM runtime.
In the following, we will discuss technical details of the proposed design.
## Proposal
### Registration via Definition/Capability
Vela allows platform builders to extend Appfile config fields by registering them via [capabilities system of OAM](https://github.com/oam-dev/kubevela/blob/master/DESIGN.md#capability-register-and-discovery).
The entire template should be put under `spec.extension.template` as raw string:
```yaml
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition | TraitDefinition
...
spec:
extension:
template: |
parameter: #webservice
...
```
By running `vela system update` or other similar commands, vela cli will read all definitions from APIServer and sink necessary information locally including templates. The templates will be further used to render final deploy manifests.
### Templating
Vela allows platform builders to write bespoke templates to extend Appfile configs.
#### Exposing Parameters
A template starts with `parameter` and its definition:
```yaml
parameter: #webservice
#webservice: {
// +vela:cli:enabled=true
// +vela:cli:usage=specify commands to run in container
// +vela:cli:short=c
cmd: [...string]
}
```
Here is the takeout:
* The `parameter` defines the user input fields and is used to render final output with user input values. These fields will be exposed to users in Appfile.
* The definition `#webservice` is used to tell the name of the template. This name is used to correlate workload and trait types to fields in Appfile.
Note that there is difference in how Workload and Trait expose parameters.
For Workload, each service will have a reserved field called `type` which is *webservice* by default.
Then all parameters are exposed as first level field under the service.
```yaml
services:
express-server:
# type: webservice (default) | task
cmd: ["node", "server.js"]
```
For Trait, its type will be used as the name to contain its parameters. There is a restriction that the trait type should not conflict any of the Workload parameters' first level name.
```yaml
services:
express-server:
route: # trait type
domain: example.com
http: # match the longest prefix
"/": 8080
# Workload parameters. The first level names do not conflict with trait type.
cmd: ...
env: ...
```
#### Rendering Outputs
A template should also have an `output` block:
```yaml
output: {
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: context.name
spec: {
...
containers: [{
name: context.name
image: parameter.image
command: parameter.cmd
}]
}
}
```
Here is the takeout:
* The object defined within `output` block will be the final manifest which is to `kubectl apply`.
* `parameter` is used here to render user config values in.
* A new object called `context` is used to render output. This is defined within vela-cli and vela-cli will fill its values based on each service dynamically. In above example, here is the value of the `context`:
```yaml
context:
name: express-server
```
You can check the definition of `context` block via `vela template context`.
Note that a TraitDefinition can have multiple outputs. In such case, just dismiss the `output` block and provide `outputs` block:
```yaml
outputs: service: {
...
}
outputs: ingress: {
...
}
```
Under the hood, vela-cli will iterate over all services and generate one AppConfig to contain them, and for each service generate one Component and multiple traits.
### CLI/UI Interoperability
For UI, The definition in a template will be used to generate v3 OpenAPI Schema and the UI will use that to render forms.
For CLI, a one level parameter can be exposed via CLI by adding the following "tags" in the comment:
```yaml
parameter: #webservice
#webservice: {
// +vela:cli:enabled=true
// +vela:cli:usage=specify commands to run in container
// +vela:cli:short=c
cmd: [...string]
...
}
```
Here is the takeout:
- The name of the parameter will be added as a flag, i.e. `--cmd`
- "enabled" indicates whether this parameter should be exposed
- "usage" is shown in help info
- "short" is the short flag, i.e. `-c`
### `vela up`
The vela-cli will have an `up` command to provide seamless workflow experience. Provide an `vela.yml` Appfile in the same directory that you will run `vela up` and it is good to go. There is an example under `examples/testapp/` .
## Examples
## Multiple Services
```yaml
services:
frontend:
build:
image: oamdev/frontend:v1
docker:
file: ./frontend/Dockerfile
context: ./frontend
cmd: ["node", "server.js"]
backend:
build:
image: oamdev/backend:v1
docker:
file: ./backend/Dockerfile
context: ./backend
cmd: ["node", "server.js"]
```
### Multiple Outputs in TraitDefinition
```yaml
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: route
spec:
definitionRef:
name: routes.standard.oam.dev
extension:
template: |
parameter: #route
#route: {
domain: string
http: [string]: int
}
// trait template can have multiple outputs and they are all traits
outputs: service: {
apiVersion: "v1"
kind: "Service"
metadata:
name: context.name
spec: {
selector:
app: context.name
ports: [
for k, v in parameter.http {
port: v
targetPort: v
}
]
}
}
outputs: ingress: {
apiVersion: "networking.k8s.io/v1beta1"
kind: "Ingress"
spec: {
rules: [{
host: parameter.domain
http: {
paths: [
for k, v in parameter.http {
path: k
backend: {
serviceName: context.name
servicePort: v
}
}
]
}
}]
}
}
```

View File

@@ -1,126 +0,0 @@
# Route Trait Design
The main idea of route trait is to let users have an entrypoint to visit their App.
In k8s world, if you want to do so, you have to understand K8s [Serivce](https://kubernetes.io/docs/concepts/services-networking/service/)
, [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/), [Ingress Controllers](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/).
It's not easy to get all of these things work well.
The route trait will help you setup service, ingress automatically according your workload, along with the mTLS enabled.
The schema is also clean and easy to understand.
```go
// RouteSpec defines the desired state of Route
type RouteSpec struct {
// WorkloadReference to the workload whose metrics needs to be exposed
WorkloadReference runtimev1alpha1.TypedReference `json:"workloadRef,omitempty"`
// Host is the host of the route
Host string `json:"host"`
// TLS indicate route trait will create SSL secret using cert-manager with specified issuer
// If this is nil, route trait will use a selfsigned issuer
TLS *TLS `json:"tls,omitempty"`
// Rules contain multiple rules of route
Rules []Rule `json:"rules"`
// Provider indicate which ingress controller implementation the route trait will use, by default it's nginx-ingress
Provider string `json:"provider,omitempty"`
}
// Rule defines to route rule
type Rule struct {
// Path is location Path, default for "/"
Path string `json:"path,omitempty"`
// RewriteTarget will rewrite request from Path to RewriteTarget path.
RewriteTarget string `json:"rewriteTarget,omitempty"`
// CustomHeaders pass a custom list of headers to the backend service.
CustomHeaders map[string]string `json:"customHeaders,omitempty"`
// DefaultBackend will become the ingress default backend if the backend is not available
DefaultBackend runtimev1alpha1.TypedReference `json:"defaultBackend,omitempty"`
// Backend indicate how to connect backend service
// If it's nil, will auto discovery
Backend *Backend `json:"backend,omitempty"`
}
type TLS struct {
IssuerName string `json:"issuerName,omitempty"`
// Type indicate the issuer is ClusterIssuer or NamespaceIssuer
Type IssuerType `json:"type,omitempty"`
}
type IssuerType string
const (
ClusterIssuer IssuerType = "ClusterIssuer"
NamespaceIssuer IssuerType = "Issuer"
)
// Route will automatically discover podSpec and label for BackendService.
// If BackendService is already set, discovery won't work.
// If BackendService is not set, the discovery mechanism will work.
type Backend struct {
// ReadTimeout used for setting read timeout duration for backend service, the unit is second.
ReadTimeout int `json:"readTimeout,omitempty"`
// SendTimeout used for setting send timeout duration for backend service, the unit is second.
SendTimeout int `json:"sendTimeout,omitempty"`
// BackendService specifies the backend K8s service and port
BackendService *BackendServiceRef `json:"backendService,omitempty"`
}
type BackendServiceRef struct {
// Port allow you direct specify backend service port.
Port intstr.IntOrString `json:"port,omitempty"`
// ServiceName allow you direct specify K8s service for backend service.
ServiceName string `json:"serviceName,omitempty"`
}
```
Route Trait specifies a target workload by using `workloadRef`, in OAM system, this field will be filled automatically
by OAM runtime.
Besides `workloadRef`, one Route will have only one `host` and many rules. `host` is actually your app's visiting URL.
It's required and will be used to generate mTLS secrets.
Route Trait designed to be compatible with different ingress controller implementations, the `provider` field will allow
you to give a specified ingress controller type. Currently, only nginx-ingress is supported.
The `tls` field allow you to specify a TLS for this route with an IssuerName, the IssuerName pointing to an Issuer Object
created by cert-manager. Cert-manager and ingress controller will handle certificate creation and binding.
If not specified, a selfsigned issuer will be created.
If no rule specified, route trait will create one rule automatically and match with the port.
For every rule, we will create an ingress. In the rule, you could specify `path`, `rewriteTarget`, `customHeaders`
and `defaultBackend`. All rules will using the same `tls`, `host` and `provider`.
`defaultBackend` will become the ingress default backend with K8s Object(apiVersion/kind/name).
`backend` of the rule is completely optional.
If backendService is specified, it will use it as backend of this rule. If not,
the route trait can automatically discovery backend settings from workload.
## Discovery mechanism
1. Check ChildResource of the workload, if there already has an existing K8s service match the Backend port, use it.
2. If there's no k8s service, this means we need to create one. In order to create K8s service, we need two information
`Container Port` and `Pod SelectorLabels`.
- 2.1 Use [`PodSpecable` mechanism](https://github.com/crossplane/oam-kubernetes-runtime/blob/master/design/one-pager-podspecable-workload.md),
route trait will check `WorkloadDefinition` for podSpec field, with the `podSpec` field, we can easily find the container port.
* If podSpecPath` is specified, we will use the workload labels as `Pod SelectorLabels`.
* If `workload.oam.dev/podspecable: true` but no `podSpecPath`, will use `spec.Template` as `PodTemplate`, which means
we can get `Pod SelectorLabels` from `spec.Template.Labels`.
- 2.2 Use ChildResource: If No `PodSpecable` mechanism found in workload, we will continue discovery child resources of workload. If there
is a valid `PodTemplate` structure in child resource, we will regard it as discovery target, use the same strategy like
`workload.oam.dev/podspecable: true` but no `podSpecPath`.

View File

@@ -32,17 +32,15 @@ the successful message.
sample request:
```json
{
"envName": "e2",
"namespace": "e2",
"email": "e@gmail.com",
"domain": "d.com"
}
{"envName":"ccc","namespace":"ccc"}
```
sample response:
```json
{"code":200,"data":"environment e2 created, Namespace: e2, Email: e@gmail.com."}
{
"code": 200,
"data": "Create env succeed"
}
```
### GET /api/envs/ (env list)
@@ -109,7 +107,7 @@ sample response
- example
sample response
```json
{"code":200,"data":"Set environment succeed"}
{"code":200,"data":"Set env succeed"}
```
```json
@@ -173,93 +171,44 @@ sample response
sample response
```json
{
"code": 200,
"data": {
"Status": "True",
"Components": [
{
"name": "web-comp",
"Status": "True",
"workload": {
"apiVersion": "standard.oam.dev/v1alpha1",
"kind": "PodSpecWorkload",
"metadata": {
"name": "web-comp"
},
"spec": {
"podSpec": {
"containers": [
{
"image": "nginx:1.9.4",
"name": "web-comp",
"ports": [
{
"containerPort": 80,
"name": "default",
"protocol": "TCP"
}
]
}
]
},
"replicas": 1
}
},
"Traits": [
{
"trait": {
"apiVersion": "standard.oam.dev/v1alpha1",
"kind": "Route",
"metadata": {
"annotations": {
"trait.oam.dev/name": "route"
}
},
"spec": {
"backend": {
"port": 0
},
"host": "web-comp.poc.oam.dev",
"path": "",
"tls": {
"issuerName": "oam-env-default"
}
}
}
}
]
},
{
"name": "comp1",
"Status": "True",
"workload": {
"apiVersion": "standard.oam.dev/v1alpha1",
"kind": "PodSpecWorkload",
"metadata": {
"name": "comp1"
},
"spec": {
"podSpec": {
"containers": [
{
"image": "nginx:1.9.4",
"name": "comp1",
"ports": [
{
"containerPort": 6379,
"name": "default",
"protocol": "TCP"
}
]
}
]
},
"replicas": 1
}
}
}
]
}
"code": 200,
"data": {
"Status": "UNKNOWN",
"Workload": {
"workload": {
"apiVersion": "core.oam.dev/v1alpha2",
"kind": "ContainerizedWorkload",
"metadata": {
"name": "poc5"
},
"spec": {
"containers": [{
"image": "nginx:1.9.4",
"name": "poc5",
"ports": [{
"containerPort": 80,
"name": "default",
"protocol": "TCP"
}]
}]
}
}
},
"Traits": [{
"trait": {
"apiVersion": "core.oam.dev/v1alpha2",
"kind": "ManualScalerTrait",
"metadata": {
"annotations": {
"vela.oam.dev/traitDef": "scale"
}
},
"spec": {
"replicaCount": 2
}
}
}]
}
}
```
### DELETE /api/envs/:envName/apps/:appName (app delete)
@@ -272,84 +221,8 @@ sample response
}
```
## Components
### GET /api/envs/:envName/apps/:appName/components/:compName (component details)
- example
sample response
```json
{
"code": 200,
"data": {
"name": "web-comp",
"Status": "True",
"workload": {
"apiVersion": "standard.oam.dev/v1alpha1",
"kind": "PodSpecWorkload",
"metadata": {
"name": "web-comp"
},
"spec": {
"podSpec": {
"containers": [
{
"image": "nginx:1.9.4",
"name": "web-comp",
"ports": [
{
"containerPort": 80,
"name": "default",
"protocol": "TCP"
}
]
}
]
},
"replicas": 1
}
},
"Traits": [
{
"trait": {
"apiVersion": "standard.oam.dev/v1alpha1",
"kind": "Route",
"metadata": {
"annotations": {
"trait.oam.dev/name": "route"
}
},
"spec": {
"backend": {
"port": 0
},
"host": "web-comp.poc.oam.dev",
"path": "",
"tls": {
"issuerName": "oam-env-default"
}
}
}
}
]
}
}
```
### GET /api/envs/:envName/apps/:appName/components/ (component list)
Same as `GET /api/envs/:envName/apps/:appName (app description)`.
### DELETE /api/envs/:envName/apps/:appName/components/:compName (component delete)
- example
sample response
```
{
"code": 200,
"data": "delete apps succeed a1 from default"
}
```
## Workloads
### POST /api/workloads/ (workload create, component create)
### POST /api/workloads/ (workload create)
- parameters
```go
type WorkloadRunBody struct {
@@ -379,7 +252,7 @@ sample request
```json
{
"env_name": "default",
"workload_type": "podspecworkload",
"workload_type": "containerized",
"workload_name": "poc2",
"flags": [
{
@@ -405,7 +278,7 @@ Please also specify `traits` values if need to attach a trait to several traits
```json
{
"env_name": "default",
"workload_type": "podspecworkload",
"workload_type": "containerized",
"workload_name": "poc5",
"app_group": "",
"flags": [
@@ -442,9 +315,9 @@ sample response
{
"code": 200,
"data": {
"name": "podspecworkload",
"name": "containerized",
"type": "workload",
"template": "#Template: {\n\tapiVersion: \"core.oam.dev/v1alpha2\"\n\tkind: \"PodSpecWorkload\"\n\tmetadata: name: podspecworkload.name\n\tspec: {\n\t\tcontainers: [{\n\t\t\timage: containerized.image\n\t\t\tname: containerized.name\n\t\t\tports: [{\n\t\t\t\tcontainerPort: containerized.port\n\t\t\t\tprotocol: \"TCP\"\n\t\t\t\tname: \"default\"\n\t\t\t}]\n\t\t}]\n\t}\n}\ncontainerized: {\n\tname: string\n\t// +usage=specify app image\n\t// +short=i\n\timage: string\n\t// +usage=specify port for container\n\t// +short=p\n\tport: *6379 | int\n}\n",
"template": "#Template: {\n\tapiVersion: \"core.oam.dev/v1alpha2\"\n\tkind: \"ContainerizedWorkload\"\n\tmetadata: name: containerized.name\n\tspec: {\n\t\tcontainers: [{\n\t\t\timage: containerized.image\n\t\t\tname: containerized.name\n\t\t\tports: [{\n\t\t\t\tcontainerPort: containerized.port\n\t\t\t\tprotocol: \"TCP\"\n\t\t\t\tname: \"default\"\n\t\t\t}]\n\t\t}]\n\t}\n}\ncontainerized: {\n\tname: string\n\t// +usage=specify app image\n\t// +short=i\n\timage: string\n\t// +usage=specify port for container\n\t// +short=p\n\tport: *6379 | int\n}\n",
"parameters": [{
"name": "name",
"required": true,
@@ -481,7 +354,7 @@ sample response
{
"code": 200,
"data": [{
"name": "podspecworkload",
"name": "containerized",
"parameters": [{
"name": "name",
"required": true,
@@ -526,7 +399,7 @@ sample response
```
## Trait
### POST /envs/:envName/apps/:appName/components/:compName/traits/ (attach a trait)
### POST /envs/:envName/apps/:appName/traits/ (attach a trait)
- example
sample request
```json
@@ -565,7 +438,7 @@ sample response
}],
"definition": "/Users/zhouzhengxi/.vela/capabilities/manualscalertraits.core.oam.dev.cue",
"crdName": "manualscalertraits.core.oam.dev",
"appliesTo": ["containerizedworkload"],
"appliesTo": ["containerized"],
"crdInfo": {
"apiVersion": "core.oam.dev/v1alpha2",
"kind": "ManualScalerTrait"
@@ -586,20 +459,20 @@ sample response
"data": [{
"name": "manualscaler",
"definition": "manualscalertraits.core.oam.dev",
"applies_to": ["podspecworkload"]
"applies_to": ["containerized"]
}, {
"name": "rollout",
"definition": "simplerollouttraits.extend.oam.dev",
"applies_to": ["podspecworkload", "deployment"]
"applies_to": ["containerized", "deployment"]
}, {
"name": "scale",
"definition": "manualscalertraits.core.oam.dev",
"applies_to": ["podspecworkload", "deployment"]
"applies_to": ["containerized", "deployment"]
}]
}
```
### DELETE /envs/:envName/apps/:appName/components/:compName/traits/:traitName (detach a trait)
### DELETE /envs/:envName/apps/:appName/traits/:traitName (detach a trait)
- example
sample response
```json
@@ -663,7 +536,7 @@ sample response
- example
sample response
```json
{"code":200,"data":"podspecworkload removed successfully"}
{"code":200,"data":"containerized removed successfully"}
```
### GET /api/capabilities/ (capability list)
@@ -673,9 +546,9 @@ sample response
{
"code": 200,
"data": [{
"name": "podspecworkload",
"name": "containerized",
"type": "workload",
"template": "#Template: {\n\tapiVersion: \"core.oam.dev/v1alpha2\"\n\tkind: \"ContainerizedWorkload\"\n\tmetadata: name: podspecworkload.name\n\tspec: {\n\t\tcontainers: [{\n\t\t\timage: containerized.image\n\t\t\tname: containerized.name\n\t\t\tports: [{\n\t\t\t\tcontainerPort: containerized.port\n\t\t\t\tprotocol: \"TCP\"\n\t\t\t\tname: \"default\"\n\t\t\t}]\n\t\t}]\n\t}\n}\ncontainerized: {\n\tname: string\n\t// +usage=specify app image\n\t// +short=i\n\timage: string\n\t// +usage=specify port for container\n\t// +short=p\n\tport: *6379 | int\n}\n",
"template": "#Template: {\n\tapiVersion: \"core.oam.dev/v1alpha2\"\n\tkind: \"ContainerizedWorkload\"\n\tmetadata: name: containerized.name\n\tspec: {\n\t\tcontainers: [{\n\t\t\timage: containerized.image\n\t\t\tname: containerized.name\n\t\t\tports: [{\n\t\t\t\tcontainerPort: containerized.port\n\t\t\t\tprotocol: \"TCP\"\n\t\t\t\tname: \"default\"\n\t\t\t}]\n\t\t}]\n\t}\n}\ncontainerized: {\n\tname: string\n\t// +usage=specify app image\n\t// +short=i\n\timage: string\n\t// +usage=specify port for container\n\t// +short=p\n\tport: *6379 | int\n}\n",
"parameters": [{
"name": "name",
"required": true,
@@ -719,7 +592,7 @@ sample response
"definition": "/Users/zhouzhengxi/.vela/centers/poc/.tmp/simplerollouttraits.extend.oam.dev.cue",
"crdName": "simplerollouttraits.extend.oam.dev",
"center": "poc",
"appliesTo": ["podspecworkload", "deployment"],
"appliesTo": ["containerized", "deployment"],
"status": "uninstalled"
}]
}

View File

@@ -1,10 +1,10 @@
## vela
✈️ A Micro App Platform for Kubernetes.
### Synopsis
✈️ A Micro App Platform for Kubernetes.
```
vela [flags]
@@ -13,7 +13,7 @@ vela [flags]
### Options
```
-e, --env string specify environment name for application
-e, --env string specify env name for application
-h, --help help for vela
```
@@ -25,17 +25,14 @@ vela [flags]
* [vela completion](vela_completion.md) - Output shell completion code for the specified shell (bash or zsh)
* [vela dashboard](vela_dashboard.md) - Setup API Server and launch Dashboard
* [vela env](vela_env.md) - Manage application environments
* [vela init](vela_init.md) - Init an OAM Application
* [vela install](vela_install.md) - Initialize vela on both client and server
* [vela logs](vela_logs.md) - Tail pods logs of an application
* [vela metrics](vela_metrics.md) - Attach metrics trait to an app
* [vela route](vela_route.md) - Attach route trait to an app
* [vela manualscaler](vela_manualscaler.md) - Attach manualscaler trait to an app
* [vela rollout](vela_rollout.md) - Attach rollout trait to an app
* [vela scale](vela_scale.md) - Attach scale trait to an app
* [vela system](vela_system.md) - system management utilities
* [vela template](vela_template.md) - Manage templates
* [vela traits](vela_traits.md) - List traits
* [vela up](vela_up.md) - Apply an appfile
* [vela version](vela_version.md) - Prints out build version information
* [vela workloads](vela_workloads.md) - List workloads
###### Auto generated by spf13/cobra on 20-Oct-2020
###### Auto generated by spf13/cobra on 11-Sep-2020

View File

@@ -21,7 +21,7 @@ vela app <command>
### Options inherited from parent commands
```
-e, --env string specify environment name for application
-e, --env string specify env name for application
```
### SEE ALSO
@@ -33,4 +33,4 @@ vela app <command>
* [vela app show](vela_app_show.md) - get details of your app
* [vela app status](vela_app_status.md) - get status of an application
###### Auto generated by spf13/cobra on 20-Oct-2020
###### Auto generated by spf13/cobra on 11-Sep-2020

View File

@@ -25,11 +25,11 @@ vela app delete frontend
### Options inherited from parent commands
```
-e, --env string specify environment name for application
-e, --env string specify env name for application
```
### SEE ALSO
* [vela app](vela_app.md) - Manage applications
###### Auto generated by spf13/cobra on 20-Oct-2020
###### Auto generated by spf13/cobra on 11-Sep-2020

View File

@@ -26,11 +26,11 @@ vela app ls
### Options inherited from parent commands
```
-e, --env string specify environment name for application
-e, --env string specify env name for application
```
### SEE ALSO
* [vela app](vela_app.md) - Manage applications
###### Auto generated by spf13/cobra on 20-Oct-2020
###### Auto generated by spf13/cobra on 11-Sep-2020

View File

@@ -26,11 +26,11 @@ vela app run myAppBundle
### Options inherited from parent commands
```
-e, --env string specify environment name for application
-e, --env string specify env name for application
```
### SEE ALSO
* [vela app](vela_app.md) - Manage applications
###### Auto generated by spf13/cobra on 20-Oct-2020
###### Auto generated by spf13/cobra on 11-Sep-2020

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