Compare commits

..

17 Commits

Author SHA1 Message Date
github-actions[bot]
066c448c1a Fix: show reconcile error log (#2628)
(cherry picked from commit 77db060f8e)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2021-11-04 19:02:41 +08:00
github-actions[bot]
8de80ebdb2 [Backport release-1.1] Fix: add owner reference in workflow context cm (#2621)
* Fix: add owner reference in workflow context cm

(cherry picked from commit f5d5fe4463)

* fix ci

(cherry picked from commit b4debba07b)

* delete useless test case

(cherry picked from commit 6fffd6c3d9)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2021-11-04 14:20:24 +08:00
github-actions[bot]
855cbfe3ec [Backport release-1.1] Feat: store workflow step def properties in cm (#2614)
* Fix: fix notification def

(cherry picked from commit f35a213048)

* Feat: store workflow step def properties in cm

(cherry picked from commit 7f3902536c)

* fix ci

(cherry picked from commit a252749f77)

* fix data race

(cherry picked from commit 0b55ce8386)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2021-11-03 19:10:18 +08:00
github-actions[bot]
162534b611 Fix: change Initializer to Application for addon Observability (#2617)
In this doc, updated the Observability implementation from initializer
to Application. I also store definitions as it's not well stored in
vela-templates/addons/observability

(cherry picked from commit 52e17dc466)

Co-authored-by: zzxwill <zzxwill@gmail.com>
2021-11-03 17:33:44 +08:00
github-actions[bot]
6bd5d8e6e2 Fix: abnormal aux name (#2613)
(cherry picked from commit 7b2503e85b)

Co-authored-by: Jian.Li <lj176172@alibaba-inc.com>
2021-11-03 15:27:37 +08:00
github-actions[bot]
22079aacd3 allow import package in custom status cue template (#2610)
(cherry picked from commit aa80658be8)

Co-authored-by: chwetion <chwetion@foxmail.com>
2021-11-03 12:22:41 +08:00
github-actions[bot]
b2329d548d [Backport release-1.1] Feat: vela logs support multicluster (#2603)
* Feat: add basic multiple cluster logs

(cherry picked from commit ed232ce4d8)

* fix context

(cherry picked from commit 27cdad1e4b)

* Fix select style

(cherry picked from commit 11ea2693a7)

* Fix select style

(cherry picked from commit 070dcf4e09)

* remove useless env

(cherry picked from commit dec63906ce)

* fix naming

(cherry picked from commit 395a655341)

Co-authored-by: qiaozp <chivalry.pp@gmail.com>
2021-11-02 15:51:58 +08:00
github-actions[bot]
9152c15a88 [Backport release-1.1] Fix(cli): client-side throttling in vela CLI (#2586)
* fix cli throttling

(cherry picked from commit ef7c640430)

* fix import

(cherry picked from commit 93a4b8fd0e)

* set to a lower value

(cherry picked from commit e14623c9ca)

Co-authored-by: qiaozp <chivalry.pp@gmail.com>
2021-10-29 15:08:30 +08:00
wyike
73b3d3106b Feat(rollout): fill rolloutBatches if empty when scale up/down (#2569) (#2582)
* Feat: fill rolloutBatches if empty

* Fix: fix unit-test

* Test: add more test

Fix: lint

Fix: fix lint
2021-10-29 15:07:45 +08:00
wyike
237c71d94e Backport 2527 to release 1.1 (#2555)
* Fix: resolve confict

* Fix: cherry pick 2472  to  1.1
2021-10-28 20:49:27 +08:00
github-actions[bot]
2200d199f3 fix incorrect addon status (#2579)
(cherry picked from commit 8b13335133)

Co-authored-by: qiaozp <chivalry.pp@gmail.com>
2021-10-28 20:43:35 +08:00
Zheng Xi Zhou
d083039a32 Fix: backport all Terraform related features to release-1.1 (#2553)
Copy all Terraform related code to release-1.1 to fix probelms and support
features.

Fix #2547
2021-10-28 11:37:41 +08:00
Tianxin Dong
5e6be649c1 [Backport release-1.1] Feat: Commit step-generate data without success (#2565)
* Feat: commit without success

* Feat: add test case

Co-authored-by: Jian.Li <lj176172@alibaba-inc.com>
2021-10-28 10:48:39 +08:00
github-actions[bot]
706a65beae [Backport release-1.1] Feat: add nocalhost dev config trait definition (#2564)
* Feat: add nocalhost dev config trait definition

(cherry picked from commit 965869b43b)

* Feat: add nocalhost dev config trait example

add nocalhost dev config trait example in `docs/examples/nocalhost`

Signed-off-by: yuyicai <yuyicai@hotmail.com>
(cherry picked from commit 70114877ce)

Co-authored-by: yuyicai <yuyicai@hotmail.com>
2021-10-27 16:03:39 +08:00
Jian.Li
d21a337dd7 Backport 2522 to release 1.1 (#2529)
* Feat: not apply if render hash not change

* Fix: generateRenderHash panic
2021-10-27 11:56:11 +08:00
github-actions[bot]
b53f4f4fdd Feat: add cluster in read or apply object (#2560)
(cherry picked from commit 5dbd8c8765)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2021-10-27 10:08:29 +08:00
github-actions[bot]
24970cd990 Fix: CVE-2021-42836 (#2552)
CVE-2021-42836
GJSON before 1.9.3 allows a ReDoS (regular expression denial of service) attack.

(cherry picked from commit 25e4b682a8)

Co-authored-by: zzxwill <zzxwill@gmail.com>
2021-10-26 12:03:59 +08:00
117 changed files with 2760 additions and 1040 deletions

View File

@@ -76,10 +76,16 @@ jobs:
- name: Load Image to kind cluster (Hub)
run: make kind-load
- name: Load Image to kind cluster (Worker)
run: |
make kind-load-runtime-cluster
- name: Cleanup for e2e tests
run: |
make e2e-cleanup
make e2e-setup-core
make
make setup-runtime-e2e-cluster
- name: Run e2e multicluster tests
run: make e2e-multicluster-test

5
.gitignore vendored
View File

@@ -45,4 +45,7 @@ charts/vela-core/crds/_.yaml
.vela/
# check docs
git-page/
git-page/
# e2e rollout runtime image build
runtime/rollout/e2e/tmp

View File

@@ -39,6 +39,9 @@ endif
VELA_CORE_IMAGE ?= vela-core:latest
VELA_CORE_TEST_IMAGE ?= vela-core-test:$(GIT_COMMIT)
VELA_RUNTIME_ROLLOUT_IMAGE ?= vela-runtime-rollout:latest
VELA_RUNTIME_ROLLOUT_TEST_IMAGE ?= vela-runtime-rollout-test:$(GIT_COMMIT)
RUNTIME_CLUSTER_CONFIG ?= /tmp/worker.kubeconfig
RUNTIME_CLUSTER_NAME ?= worker
all: build
@@ -143,6 +146,9 @@ e2e-setup-core:
helm upgrade --install --create-namespace --namespace vela-system --set image.pullPolicy=IfNotPresent --set image.repository=vela-core-test --set applicationRevisionLimit=5 --set dependCheckWait=10s --set image.tag=$(GIT_COMMIT) --set multicluster.enabled=true --wait kubevela ./charts/vela-core
kubectl wait --for=condition=Available deployment/kubevela-vela-core -n vela-system --timeout=180s
setup-runtime-e2e-cluster:
helm upgrade --install --create-namespace --namespace vela-system --kubeconfig=$(RUNTIME_CLUSTER_CONFIG) --set image.pullPolicy=IfNotPresent --set image.repository=vela-runtime-rollout-test --set image.tag=$(GIT_COMMIT) --wait vela-rollout ./runtime/rollout/charts
e2e-setup:
helm install kruise https://github.com/openkruise/kruise/releases/download/v0.9.0/kruise-chart.tgz --set featureGates="PreDownloadImageForInPlaceUpdate=true"
sh ./hack/e2e/modify_charts.sh
@@ -209,6 +215,12 @@ kind-load:
docker build -t $(VELA_CORE_TEST_IMAGE) -f Dockerfile.e2e .
kind load docker-image $(VELA_CORE_TEST_IMAGE) || { echo >&2 "kind not installed or error loading image: $(VELA_CORE_TEST_IMAGE)"; exit 1; }
kind-load-runtime-cluster:
/bin/sh hack/e2e/build_runtime_rollout.sh
docker build -t $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) -f runtime/rollout/e2e/Dockerfile.e2e runtime/rollout/e2e/
rm -rf runtime/rollout/e2e/tmp
kind load docker-image $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) --name=$(RUNTIME_CLUSTER_NAME) || { echo >&2 "kind not installed or error loading image: $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE)"; exit 1; }
# Run tests
core-test: fmt vet manifests
go test ./pkg/... -coverprofile cover.out

View File

@@ -114,6 +114,9 @@ type Terraform struct {
// +kubebuilder:validation:Enum:=hcl;json;remote
Type string `json:"type,omitempty"`
// Path is the sub-directory of remote git repository. It's valid when remote is set
Path string `json:"path,omitempty"`
// ProviderReference specifies the reference to Provider
ProviderReference *types.Reference `json:"providerRef,omitempty"`
}

View File

@@ -38,7 +38,8 @@ type WorkflowStepDefinitionSpec struct {
type WorkflowStepDefinitionStatus struct {
// ConditionedStatus reflects the observed status of a resource
condition.ConditionedStatus `json:",inline"`
// ConfigMapRef refer to a ConfigMap which contains OpenAPI V3 JSON schema of Component parameters.
ConfigMapRef string `json:"configMapRef,omitempty"`
// LatestRevision of the component definition
// +optional
LatestRevision *common.Revision `json:"latestRevision,omitempty"`

View File

@@ -188,6 +188,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -468,6 +472,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -193,6 +193,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -454,6 +458,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -1232,6 +1232,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1635,6 +1639,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1936,6 +1944,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -3360,6 +3372,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -3656,6 +3672,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -4041,6 +4061,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -4305,6 +4329,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -4371,6 +4399,10 @@ spec:
- type
type: object
type: array
configMapRef:
description: ConfigMapRef refer to a ConfigMap which contains
OpenAPI V3 JSON schema of Component parameters.
type: string
latestRevision:
description: LatestRevision of the component definition
properties:
@@ -4585,6 +4617,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider

View File

@@ -179,6 +179,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -463,6 +467,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -222,6 +222,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -506,6 +510,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -783,6 +791,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1041,6 +1053,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1107,6 +1123,10 @@ spec:
- type
type: object
type: array
configMapRef:
description: ConfigMapRef refer to a ConfigMap which contains
OpenAPI V3 JSON schema of Component parameters.
type: string
latestRevision:
description: LatestRevision of the component definition
properties:

View File

@@ -151,6 +151,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -188,6 +188,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -468,6 +472,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -148,6 +148,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -212,6 +216,10 @@ spec:
- type
type: object
type: array
configMapRef:
description: ConfigMapRef refer to a ConfigMap which contains OpenAPI
V3 JSON schema of Component parameters.
type: string
latestRevision:
description: LatestRevision of the component definition
properties:

View File

@@ -193,6 +193,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -454,6 +458,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -8,7 +8,7 @@ data:
addons.oam.dev/description: Kubernetes Terraform Controller for Alibaba Cloud
addons.oam.dev/name: terraform/provider-alibaba
name: terraform-provider-alibaba
namespace: default
namespace: vela-system
spec:
components:
- name: alibaba-account-creds
@@ -16,7 +16,7 @@ data:
apiVersion: v1
kind: Secret
metadata:
name: alibaba-provider-secret
name: alibaba-account-creds
namespace: vela-system
stringData:
credentials: |

View File

@@ -0,0 +1,61 @@
apiVersion: v1
data:
application: |
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
annotations:
addons.oam.dev/description: Kubernetes Terraform Controller for AWS
addons.oam.dev/name: terraform/provider-aws
name: terraform-provider-aws
namespace: vela-system
spec:
components:
- name: aws-account-creds
properties:
apiVersion: v1
kind: Secret
metadata:
name: aws-account-creds
namespace: vela-system
stringData:
credentials: |
awsAccessKeyID: [[ index .Args "AWS_ACCESS_KEY_ID" ]]
awsSecretAccessKey: [[ index .Args "AWS_SECRET_ACCESS_KEY" ]]
awsSessionToken: [[ index .Args "AWS_SESSION_TOKEN" ]]
type: Opaque
type: raw
- name: aws
properties:
apiVersion: terraform.core.oam.dev/v1beta1
kind: Provider
metadata:
name: aws
namespace: default
spec:
credentials:
secretRef:
key: credentials
name: aws-account-creds
namespace: vela-system
source: Secret
provider: aws
region: '[[ index .Args "AWS_DEFAULT_REGION" ]]'
type: raw
status:
rollout:
batchRollingState: ""
currentBatch: 0
lastTargetAppRevision: ""
rollingState: ""
upgradedReadyReplicas: 0
upgradedReplicas: 0
kind: ConfigMap
metadata:
annotations:
addons.oam.dev/description: Kubernetes Terraform Controller for AWS
addons.oam.dev/name: terraform/provider-aws
labels:
addons.oam.dev/type: terraform-provider-aws
name: terraform-provider-aws
namespace: {{.Values.systemDefinitionNamespace}}

View File

@@ -8,7 +8,7 @@ data:
addons.oam.dev/description: Kubernetes Terraform Controller for Azure
addons.oam.dev/name: terraform/provider-azure
name: terraform-provider-azure
namespace: default
namespace: vela-system
spec:
components:
- name: azure-account-creds

View File

@@ -23,7 +23,7 @@ data:
chart: terraform-controller
repoType: helm
url: https://charts.kubevela.net/addons
version: 0.1.19
version: 0.2.6
type: helm
- name: alibaba-ack
properties:
@@ -40,199 +40,9 @@ data:
spec:
schematic:
terraform:
configuration: |
module "kubernetes" {
source = "github.com/zzxwill/terraform-alicloud-kubernetes"
new_nat_gateway = true
vpc_name = var.vpc_name
vpc_cidr = var.vpc_cidr
vswitch_name_prefix = var.vswitch_name_prefix
vswitch_cidrs = var.vswitch_cidrs
master_instance_types = var.master_instance_types
worker_instance_types = var.worker_instance_types
k8s_pod_cidr = var.k8s_pod_cidr
k8s_service_cidr = var.k8s_service_cidr
k8s_worker_number = var.k8s_worker_number
cpu_core_count = var.cpu_core_count
memory_size = var.memory_size
zone_id = var.zone_id
k8s_version = var.k8s_version
k8s_name_prefix = var.k8s_name_prefix
}
######################
# Instance types variables
######################
variable "cpu_core_count" {
description = "CPU core count is used to fetch instance types."
type = number
default = 4
}
variable "memory_size" {
description = "Memory size used to fetch instance types."
type = number
default = 8
}
######################
# VPC variables
######################
variable "vpc_name" {
description = "The vpc name used to create a new vpc when 'vpc_id' is not specified. Default to variable `example_name`"
type = string
default = "tf-k8s-vpc"
}
variable "vpc_cidr" {
description = "The cidr block used to launch a new vpc when 'vpc_id' is not specified."
type = string
default = "10.0.0.0/8"
}
######################
# VSwitch variables
######################
variable "vswitch_name_prefix" {
type = string
description = "The vswitch name prefix used to create several new vswitches. Default to variable 'example_name'."
default = "tf-k8s-vsw"
}
variable "number_format" {
description = "The number format used to output."
type = string
default = "%02d"
}
variable "vswitch_ids" {
description = "List of existing vswitch id."
type = list
default = []
}
variable "vswitch_cidrs" {
description = "List of cidr blocks used to create several new vswitches when 'vswitch_ids' is not specified."
type = list
default = [
"10.1.0.0/16",
"10.2.0.0/16",
"10.3.0.0/16"]
}
variable "k8s_name_prefix" {
description = "The name prefix used to create several kubernetes clusters. Default to variable `example_name`"
type = string
default = "poc"
}
variable "new_nat_gateway" {
type = bool
description = "Whether to create a new nat gateway. In this template, a new nat gateway will create a nat gateway, eip and server snat entries."
default = true
}
variable "master_instance_types" {
description = "The ecs instance types used to launch master nodes."
type = list
default = [
# hongkong
"ecs.sn1ne.xlarge",
# hongkong
"ecs.c6.xlarge",
# hongkong
"ecs.c4.xlarge",
# hongkong
"ecs.c5.xlarge",
"ecs.n4.xlarge",
# "ecs.n1.large",
# "ecs.sn1.large",
# "ecs.s6-c1m2.xlarge",
# "ecs.c6e.xlarge"
]
}
variable "worker_instance_types" {
description = "The ecs instance types used to launch worker nodes."
type = list
default = [
# hongkong
"ecs.sn1ne.xlarge",
# hongkong
"ecs.c6.xlarge",
# hongkong
"ecs.c4.xlarge",
# hongkong
"ecs.c6e.xlarge",
"ecs.n4.xlarge",
// "ecs.n1.large",
// "ecs.sn1.large",
// "ecs.s6-c1m2.xlarge"
]
}
variable "node_cidr_mask" {
type = number
description = "The node cidr block to specific how many pods can run on single node. Valid values: [24-28]."
default = 24
}
variable "enable_ssh" {
description = "Enable login to the node through SSH."
type = bool
default = true
}
variable "install_cloud_monitor" {
description = "Install cloud monitor agent on ECS."
type = bool
default = true
}
variable "cpu_policy" {
type = string
description = "kubelet cpu policy. Valid values: 'none','static'. Default to 'none'."
default = "none"
}
variable "proxy_mode" {
description = "Proxy mode is option of kube-proxy. Valid values: 'ipvs','iptables'. Default to 'iptables'."
type = string
default = "iptables"
}
variable "password" {
description = "The password of ECS instance."
type = string
default = "Just4Test"
}
variable "k8s_worker_number" {
description = "The number of worker nodes in kubernetes cluster."
type = number
default = 2
}
# k8s_pod_cidr is only for flannel network
variable "k8s_pod_cidr" {
description = "The kubernetes pod cidr block. It cannot be equals to vpc's or vswitch's and cannot be in them."
type = string
default = "172.20.0.0/16"
}
variable "k8s_service_cidr" {
description = "The kubernetes service cidr block. It cannot be equals to vpc's or vswitch's or pod's and cannot be in them."
type = string
default = "192.168.0.0/16"
}
variable "k8s_version" {
description = "The version of the kubernetes version. Valid values: '1.16.6-aliyun.1','1.14.8-aliyun.1'. Default to '1.16.6-aliyun.1'."
type = string
default = "1.20.4-aliyun.1"
}
variable "zone_id" {
description = "Availability Zone ID"
type = string
default = "cn-hongkong-b"
# "cn-beijing-a"
}
output "name" {
value = module.kubernetes.name
}
output "kubeconfig" {
value = module.kubernetes.kubeconfig
}
output "cluster_ca_cert" {
value = module.kubernetes.cluster_ca_cert
}
output "client_cert" {
value = module.kubernetes.client_cert
}
output "client_key" {
value = module.kubernetes.client_key
}
output "api_server_internet" {
value = module.kubernetes.api_server_internet
}
configuration: https://github.com/kubevela-contrib/terraform-modules.git
path: alibaba/cs/dedicated-kubernetes
type: remote
workload:
definition:
apiVersion: terraform.core.oam.dev/v1beta1
@@ -304,7 +114,7 @@ data:
terraform:
configuration: |
module "rds" {
source = "terraform-alicloud-modules/rds/alicloud"
source = "github.com/kubevela-contrib/terraform-alicloud-rds"
engine = "MySQL"
engine_version = "8.0"
instance_type = "rds.mysql.c1.large"
@@ -312,6 +122,8 @@ data:
instance_name = var.instance_name
account_name = var.account_name
password = var.password
allocate_public_connection = var.allocate_public_connection
security_ips = ["0.0.0.0/0",]
}
output "DB_NAME" {
@@ -329,6 +141,9 @@ data:
output "DB_PASSWORD" {
value = var.password
}
output "DB_PUBLIC_HOST" {
value = module.rds.db_public_connection_string
}
variable "instance_name" {
description = "RDS instance name"
@@ -347,6 +162,52 @@ data:
type = string
default = "Xyfff83jfewGGfaked"
}
variable "allocate_public_connection" {
description = "Whether to allocate public connection for a RDS instance."
type = bool
default = true
}
workload:
definition:
apiVersion: terraform.core.oam.dev/v1beta1
kind: Configuration
type: raw
- name: aws-s3
properties:
apiVersion: core.oam.dev/v1alpha2
kind: ComponentDefinition
metadata:
annotations:
definition.oam.dev/description: Terraform configuration for AWS S3
labels:
type: terraform
name: aws-s3
namespace: vela-system
spec:
schematic:
terraform:
configuration: |
resource "aws_s3_bucket" "bucket-acl" {
bucket = var.bucket
acl = var.acl
}
output "BUCKET_NAME" {
value = aws_s3_bucket.bucket-acl.bucket_domain_name
}
variable "bucket" {
description = "S3 bucket name"
default = "vela-website"
type = string
}
variable "acl" {
description = "S3 bucket ACL"
default = "private"
type = string
}
workload:
definition:
apiVersion: terraform.core.oam.dev/v1beta1

View File

@@ -16,7 +16,13 @@ spec:
)
apply: op.#Apply & {
value: parameter
value: parameter.value
cluster: parameter.cluster
}
parameter: {
// +usage=Specify the value of the object
value: {...}
// +usage=Specify the cluster of the object
cluster: *"" | string
}
parameter: {}

View File

@@ -0,0 +1,113 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/nocalhost.cue
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: nocalhost develop configuration.
name: nocalhost
namespace: {{.Values.systemDefinitionNamespace}}
spec:
appliesToWorkloads:
- '*'
podDisruptive: true
schematic:
cue:
template: |
import (
"encoding/json"
)
patch: metadata: annotations: {
"dev.nocalhost/application-name": context.appName
"dev.nocalhost/application-namespace": context.namespace
"dev.nocalhost": json.Marshal({
containers: [
{
name: context.name
dev: {
if parameter.gitUrl != _|_ {
gitUrl: parameter.gitUrl
}
image: parameter.image
shell: parameter.shell
workDir: parameter.workDir
if parameter.storageClass != _|_ {
storageClass: parameter.storageClass
}
resources: {
limits: parameter.resources.limits
requests: parameter.resources.requests
}
if parameter.persistentVolumeDirs != _|_ {
persistentVolumeDirs: [
for v in parameter.persistentVolumeDirs {
path: v.path
capacity: v.capacity
},
]
}
if parameter.command != _|_ {
command: parameter.command
}
if parameter.debug != _|_ {
debug: parameter.debug
}
hotReload: parameter.hotReload
if parameter.sync != _|_ {
sync: parameter.sync
}
if parameter.env != _|_ {
env: [
for v in parameter.env {
name: v.name
value: v.value
},
]
}
if parameter.portForward != _|_ {
portForward: parameter.portForward
}
}
},
]
})
}
parameter: {
gitUrl?: string
image: string
shell: *"bash" | string
workDir: *"/home/nocalhost-dev" | string
storageClass?: string
command?: {
run?: [...string]
debug?: [...string]
}
debug?: remoteDebugPort?: int
hotReload: *true | bool
sync: {
type: *"send" | string
filePattern?: [...string]
ignoreFilePattern?: [...string]
}
env?: [...{
name: string
value: string
}]
portForward?: [...string]
persistentVolumeDirs?: [...{
path: string
capacity: string
}]
resources: {
limits: {
memory: *"2Gi" | string
cpu: *"2" | string
}
requests: {
memory: *"512Mi" | string
cpu: *"0.5" | string
}
}
}

View File

@@ -28,6 +28,7 @@ spec:
}
}
}
cluster: parameter.cluster
}
}
if parameter.apiVersion != _|_ || parameter.kind != _|_ {
@@ -42,6 +43,7 @@ spec:
}
}
}
cluster: parameter.cluster
}
}
}
@@ -54,5 +56,7 @@ spec:
name: string
// +usage=Specify the namespace of the object
namespace?: string
// +usage=Specify the cluster of the object
cluster: *"" | string
}

View File

@@ -24,8 +24,10 @@ spec:
componentName: context.name
rolloutPlan: {
rolloutStrategy: "IncreaseFirst"
rolloutBatches: parameter.rolloutBatches
targetSize: parameter.targetSize
if parameter.rolloutBatches != _|_ {
rolloutBatches: parameter.rolloutBatches
}
targetSize: parameter.targetSize
if parameter["batchPartition"] != _|_ {
batchPartition: parameter.batchPartition
}
@@ -35,7 +37,7 @@ spec:
parameter: {
targetRevision: *context.revision | string
targetSize: int
rolloutBatches: [...rolloutBatch]
rolloutBatches?: [...rolloutBatch]
batchPartition?: int
}
rolloutBatch: replicas: int

View File

@@ -97,17 +97,17 @@ spec:
url?: string
value?: string
style?: string
text?: text
text?: textType
confirm?: {
title: text
text: text
confirm: text
deny: text
title: textType
text: textType
confirm: textType
deny: textType
style?: string
}
options?: [...option]
initial_options?: [...option]
placeholder?: text
placeholder?: textType
initial_date?: string
image_url?: string
alt_text?: string
@@ -121,16 +121,16 @@ spec:
initial_time?: string
}]
}
text: {
textType: {
type: string
text: string
emoji?: bool
verbatim?: bool
}
option: {
text: text
text: textType
value: string
description?: text
description?: textType
url?: string
}
// send webhook notification

View File

@@ -1232,6 +1232,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1635,6 +1639,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1936,6 +1944,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -3360,6 +3372,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -3656,6 +3672,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -4041,6 +4061,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -4305,6 +4329,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -4371,6 +4399,10 @@ spec:
- type
type: object
type: array
configMapRef:
description: ConfigMapRef refer to a ConfigMap which contains
OpenAPI V3 JSON schema of Component parameters.
type: string
latestRevision:
description: LatestRevision of the component definition
properties:
@@ -4585,6 +4617,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider

View File

@@ -222,6 +222,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -506,6 +510,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -783,6 +791,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1041,6 +1053,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1107,6 +1123,10 @@ spec:
- type
type: object
type: array
configMapRef:
description: ConfigMapRef refer to a ConfigMap which contains
OpenAPI V3 JSON schema of Component parameters.
type: string
latestRevision:
description: LatestRevision of the component definition
properties:

View File

@@ -151,6 +151,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -188,6 +188,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -468,6 +472,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -148,6 +148,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -212,6 +216,10 @@ spec:
- type
type: object
type: array
configMapRef:
description: ConfigMapRef refer to a ConfigMap which contains OpenAPI
V3 JSON schema of Component parameters.
type: string
latestRevision:
description: LatestRevision of the component definition
properties:

View File

@@ -193,6 +193,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -454,6 +458,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -16,7 +16,13 @@ spec:
)
apply: op.#Apply & {
value: parameter
value: parameter.value
cluster: parameter.cluster
}
parameter: {
// +usage=Specify the value of the object
value: {...}
// +usage=Specify the cluster of the object
cluster: *"" | string
}
parameter: {}

View File

@@ -0,0 +1,113 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/nocalhost.cue
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: nocalhost develop configuration.
name: nocalhost
namespace: {{.Values.systemDefinitionNamespace}}
spec:
appliesToWorkloads:
- '*'
podDisruptive: true
schematic:
cue:
template: |
import (
"encoding/json"
)
patch: metadata: annotations: {
"dev.nocalhost/application-name": context.appName
"dev.nocalhost/application-namespace": context.namespace
"dev.nocalhost": json.Marshal({
containers: [
{
name: context.name
dev: {
if parameter.gitUrl != _|_ {
gitUrl: parameter.gitUrl
}
image: parameter.image
shell: parameter.shell
workDir: parameter.workDir
if parameter.storageClass != _|_ {
storageClass: parameter.storageClass
}
resources: {
limits: parameter.resources.limits
requests: parameter.resources.requests
}
if parameter.persistentVolumeDirs != _|_ {
persistentVolumeDirs: [
for v in parameter.persistentVolumeDirs {
path: v.path
capacity: v.capacity
},
]
}
if parameter.command != _|_ {
command: parameter.command
}
if parameter.debug != _|_ {
debug: parameter.debug
}
hotReload: parameter.hotReload
if parameter.sync != _|_ {
sync: parameter.sync
}
if parameter.env != _|_ {
env: [
for v in parameter.env {
name: v.name
value: v.value
},
]
}
if parameter.portForward != _|_ {
portForward: parameter.portForward
}
}
},
]
})
}
parameter: {
gitUrl?: string
image: string
shell: *"bash" | string
workDir: *"/home/nocalhost-dev" | string
storageClass?: string
command?: {
run?: [...string]
debug?: [...string]
}
debug?: remoteDebugPort?: int
hotReload: *true | bool
sync: {
type: *"send" | string
filePattern?: [...string]
ignoreFilePattern?: [...string]
}
env?: [...{
name: string
value: string
}]
portForward?: [...string]
persistentVolumeDirs?: [...{
path: string
capacity: string
}]
resources: {
limits: {
memory: *"2Gi" | string
cpu: *"2" | string
}
requests: {
memory: *"512Mi" | string
cpu: *"0.5" | string
}
}
}

View File

@@ -28,6 +28,7 @@ spec:
}
}
}
cluster: parameter.cluster
}
}
if parameter.apiVersion != _|_ || parameter.kind != _|_ {
@@ -42,6 +43,7 @@ spec:
}
}
}
cluster: parameter.cluster
}
}
}
@@ -54,5 +56,7 @@ spec:
name: string
// +usage=Specify the namespace of the object
namespace?: string
// +usage=Specify the cluster of the object
cluster: *"" | string
}

View File

@@ -24,8 +24,10 @@ spec:
componentName: context.name
rolloutPlan: {
rolloutStrategy: "IncreaseFirst"
rolloutBatches: parameter.rolloutBatches
targetSize: parameter.targetSize
if parameter.rolloutBatches != _|_ {
rolloutBatches: parameter.rolloutBatches
}
targetSize: parameter.targetSize
if parameter["batchPartition"] != _|_ {
batchPartition: parameter.batchPartition
}
@@ -35,7 +37,7 @@ spec:
parameter: {
targetRevision: *context.revision | string
targetSize: int
rolloutBatches: [...rolloutBatch]
rolloutBatches?: [...rolloutBatch]
batchPartition?: int
}
rolloutBatch: replicas: int

View File

@@ -97,17 +97,17 @@ spec:
url?: string
value?: string
style?: string
text?: text
text?: textType
confirm?: {
title: text
text: text
confirm: text
deny: text
title: textType
text: textType
confirm: textType
deny: textType
style?: string
}
options?: [...option]
initial_options?: [...option]
placeholder?: text
placeholder?: textType
initial_date?: string
image_url?: string
alt_text?: string
@@ -121,16 +121,16 @@ spec:
initial_time?: string
}]
}
text: {
textType: {
type: string
text: string
emoji?: bool
verbatim?: bool
}
option: {
text: text
text: textType
value: string
description?: text
description?: textType
url?: string
}
// send webhook notification

View File

@@ -32,6 +32,7 @@ import (
appsv1 "k8s.io/api/apps/v1"
"k8s.io/klog/v2"
"k8s.io/klog/v2/klogr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/healthz"
@@ -192,7 +193,7 @@ func main() {
os.Exit(1)
}
}
ctrl.SetLogger(klogr.New())
mgr, err := ctrl.NewManager(restConfig, ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,

View File

@@ -0,0 +1,157 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: bookinfo
spec:
components:
- name: productpage
type: webservice
properties:
image: nocalhost-docker.pkg.coding.net/nocalhost/bookinfo/productpage:latest
port: 9080
traits:
- type: expose
properties:
port:
- 9080
- type: nocalhost
properties:
gitUrl: https://github.com/nocalhost/bookinfo-productpage.git
image: nocalhost-docker.pkg.coding.net/nocalhost/dev-images/python:3.7.7-slim-productpage-with-pydevd
shell: "bash"
workDir: "/opt/work"
resources:
limits:
memory: 1Gi
cpu: "1"
requests:
memory: 512Mi
cpu: "0.5"
debug:
remoteDebugPort: 9009
hotReload: true
sync:
type: send
filePattern:
- ./
ignoreFilePattern:
- .git
- .idea
command:
run:
- sh
- run.sh
debug:
- sh
- debug.sh
env:
- name: "foo"
value: "bar"
portForward:
- 39080:9080
- name: authors
type: webservice
properties:
image: nocalhost-docker.pkg.coding.net/nocalhost/bookinfo/authors:latest
port: 9080
traits:
- type: expose
properties:
port:
- 9080
- type: nocalhost
properties:
gitUrl: https://github.com/nocalhost/bookinfo-authors.git
image: nocalhost-docker.pkg.coding.net/nocalhost/dev-images/golang:latest
command:
run:
- sh
- run.sh
debug:
- sh
- debug.sh
debug:
remoteDebugPort: 9009
- name: details
type: webservice
properties:
image: nocalhost-docker.pkg.coding.net/nocalhost/bookinfo/details:latest
port: 9080
traits:
- type: expose
properties:
port:
- 9080
- type: nocalhost
properties:
gitUrl: https://github.com/nocalhost/bookinfo-details.git
image: nocalhost-docker.pkg.coding.net/nocalhost/dev-images/ruby:2.7.1-slim
command:
run:
- sh
- run.sh
debug:
- sh
- debug.sh
sync:
filePattern:
- ./
ignoreFilePattern:
- .git
- .idea
- name: ratings
type: webservice
properties:
image: nocalhost-docker.pkg.coding.net/nocalhost/bookinfo/ratings:latest
port: 9080
traits:
- type: expose
properties:
port:
- 9080
- type: nocalhost
properties:
gitUrl: https://github.com/nocalhost/bookinfo-ratings.git
image: nocalhost-docker.pkg.coding.net/nocalhost/dev-images/node:12.18.1-slim
command:
run:
- sh
- run.sh
debug:
- sh
- debug.sh
- name: reviews
type: webservice
properties:
image: nocalhost-docker.pkg.coding.net/nocalhost/bookinfo/reviews:latest
port: 9080
traits:
- type: expose
properties:
port:
- 9080
- type: nocalhost
properties:
gitUrl: https://github.com/nocalhost/bookinfo-reviews.git
image: nocalhost-docker.pkg.coding.net/nocalhost/dev-images/java:latest
command:
run:
- sh
- run.sh
debug:
- sh
- debug.sh
debug:
remoteDebugPort: 5005
sync:
filePattern:
- ./
ignoreFilePattern:
- .git
- .idea
- .gradle
- build

View File

@@ -0,0 +1,130 @@
apiVersion: v1
kind: Namespace
metadata:
name: observability
spec: { }
---
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
annotations:
addons.oam.dev/description: "An out of the box solution for KubeVela observability"
name: grafana
namespace: observability
spec:
components:
# install grafana datasource registration chart
- name: grafana-registration-release
type: helm
properties:
repoType: git
url: https://github.com/oam-dev/grafana-registration
git:
branch: master
chart: ./chart
targetNamespace: observability
values:
replicaCount: 1
# install Grafana
- name: grafana
properties:
chart: grafana
version: 6.14.1
repoType: helm
# original url: https://grafana.github.io/helm-charts
url: https://charts.kubevela.net/addons
targetNamespace: observability
releaseName: grafana
type: helm
traits:
- type: pure-ingress
properties:
domain: grafana.c58136db32cbc44cca364bf1cf7f90519.cn-hongkong.alicontainer.com
http:
"/": 80
- type: import-grafana-dashboard
properties:
grafanaServiceName: grafana
grafanaServiceNamespace: observability
credentialSecret: grafana
credentialSecretNamespace: observability
urls:
- "https://charts.kubevela.net/addons/dashboards/kubevela_core_logging.json"
- "https://charts.kubevela.net/addons/dashboards/kubevela_core_monitoring.json"
- "https://charts.kubevela.net/addons/dashboards/flux2/cluster.json"
- "https://charts.kubevela.net/addons/dashboards/kubevela_application_logging.json"
# install loki
- name: loki
type: helm
properties:
chart: loki-stack
version: 2.4.1
repoType: helm
# original url: https://grafana.github.io/helm-charts
url: https://charts.kubevela.net/addons
targetNamespace: observability
releaseName: loki
traits:
- type: register-grafana-datasource # register loki datasource to Grafana
properties:
grafanaServiceName: grafana
grafanaServiceNamespace: observability
credentialSecret: grafana
credentialSecretNamespace: observability
name: loki
service: loki
namespace: observability
type: loki
access: proxy
# install Prometheus
- name: prometheus-server
type: helm
properties:
chart: prometheus
version: 14.4.1
repoType: helm
# original url: https://prometheus-community.github.io/helm-charts
url: https://charts.kubevela.net/addons
targetNamespace: observability
releaseName: prometheus
values:
alertmanager:
persistentVolume:
storageClass: "alicloud-disk-available"
size: "20Gi"
server:
persistentVolume:
storageClass: "alicloud-disk-available"
size: "20Gi"
traits:
- type: register-grafana-datasource # register Prometheus datasource to Grafana
properties:
grafanaServiceName: grafana
grafanaServiceNamespace: observability
credentialSecret: grafana
credentialSecretNamespace: observability
name: prometheus
service: prometheus-server
namespace: observability
type: prometheus
access: proxy
# install kube-state-metrics
- name: kube-state-metrics
type: helm
properties:
chart: kube-state-metrics
version: 3.4.1
repoType: helm
# original url: https://prometheus-community.github.io/helm-charts
url: https://charts.kubevela.net/addons
targetNamespace: observability
values:
image:
repository: oamdev/kube-state-metrics
tag: v2.1.0

View File

@@ -0,0 +1,31 @@
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "Import dashboards to Grafana"
name: import-grafana-dashboard
namespace: vela-system
spec:
schematic:
cue:
template: |
outputs: registerdatasource: {
apiVersion: "grafana.extension.oam.dev/v1alpha1"
kind: "ImportDashboard"
spec: {
grafana: {
service: parameter.grafanaServiceName
namespace: parameter.grafanaServiceNamespace
credentialSecret: parameter.credentialSecret
credentialSecretNamespace: parameter.credentialSecretNamespace
}
urls: parameter.urls
}
}
parameter: {
grafanaServiceName: string
grafanaServiceNamespace: *"default" | string
credentialSecret: string
credentialSecretNamespace: *"default" | string
urls: [...string]
}

View File

@@ -0,0 +1,58 @@
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "Enable public web traffic for the component without creating a Service."
name: pure-ingress
namespace: vela-system
spec:
status:
customStatus: |-
let igs = context.outputs.ingress.status.loadBalancer.ingress
if igs == _|_ {
message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + " --route'\n"
}
if len(igs) > 0 {
if igs[0].ip != _|_ {
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
}
if igs[0].ip == _|_ {
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
}
}
healthPolicy: |
isHealth: len(context.outputs.ingress.status.loadBalancer.ingress) > 0
schematic:
cue:
template: |
outputs: ingress: {
apiVersion: "networking.k8s.io/v1beta1"
kind: "Ingress"
metadata:
name: context.name
spec: {
rules: [{
host: parameter.domain
http: {
paths: [
for k, v in parameter.http {
path: k
backend: {
serviceName: context.name
servicePort: v
}
},
]
}
}]
}
}
parameter: {
// +usage=Specify the domain you want to expose
domain: string
// +usage=Specify the mapping relationship between the http path and the workload port
http: [string]: int
}

View File

@@ -0,0 +1,42 @@
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "Add a datasource to Grafana"
name: register-grafana-datasource
namespace: vela-system
spec:
schematic:
cue:
template: |
outputs: registerdatasource: {
apiVersion: "grafana.extension.oam.dev/v1alpha1"
kind: "DatasourceRegistration"
spec: {
grafana: {
service: parameter.grafanaServiceName
namespace: parameter.grafanaServiceNamespace
credentialSecret: parameter.credentialSecret
credentialSecretNamespace: parameter.credentialSecretNamespace
}
datasource: {
name: parameter.name
type: parameter.type
access: parameter.access
service: parameter.service
namespace: parameter.namespace
}
}
}
parameter: {
grafanaServiceName: string
grafanaServiceNamespace: *"default" | string
credentialSecret: string
credentialSecretNamespace: string
name: string
type: string
access: *"proxy" | string
service: string
namespace: *"default" | string
}

View File

@@ -1,159 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: observability
spec: { }
---
apiVersion: core.oam.dev/v1beta1
kind: Initializer
metadata:
name: grafana
namespace: observability
spec:
appTemplate:
spec:
components:
# install grafana datasource registration chart
- name: grafana-registration-release
properties:
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: grafana-registration-release
namespace: observability
spec:
chart:
spec:
chart: ./chart
interval: 1m
sourceRef:
kind: GitRepository
name: grafana-registration-repo
namespace: observability
interval: 5m
values:
replicaCount: 1
type: raw
- name: grafana-registration-repo
properties:
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
name: grafana-registration-repo
namespace: observability
spec:
interval: 5m
ref:
branch: master
url: https://github.com/oam-dev/grafana-registration
type: raw
# install Grafana
- name: grafana
properties:
chart: grafana
version: 6.14.1
repoType: helm
# original url: https://grafana.github.io/helm-charts
url: https://charts.kubevela.net/addons
targetNamespace: observability
releaseName: grafana
type: helm
traits:
- type: pure-ingress
properties:
domain: grafana.cf7223b8abedc4691b7eccfe3c675850a.cn-hongkong.alicontainer.com
http:
"/": 80
- type: import-grafana-dashboard
properties:
grafanaServiceName: grafana
grafanaServiceNamespace: observability
credentialSecret: grafana
credentialSecretNamespace: observability
urls:
- "https://charts.kubevela.net/addons/dashboards/kubevela_core_logging.json"
- "https://charts.kubevela.net/addons/dashboards/kubevela_core_monitoring.json"
- "https://charts.kubevela.net/addons/dashboards/flux2/cluster.json"
- "https://charts.kubevela.net/addons/dashboards/kubevela_application_logging.json"
# install loki
- name: loki
type: helm
properties:
chart: loki-stack
version: 2.4.1
repoType: helm
# original url: https://grafana.github.io/helm-charts
url: https://charts.kubevela.net/addons
targetNamespace: observability
releaseName: loki
traits:
- type: register-grafana-datasource # register loki datasource to Grafana
properties:
grafanaServiceName: grafana
grafanaServiceNamespace: observability
credentialSecret: grafana
credentialSecretNamespace: observability
name: loki
service: loki
namespace: observability
type: loki
access: proxy
# install Prometheus
- name: prometheus-server
type: helm
properties:
chart: prometheus
version: 14.4.1
repoType: helm
# original url: https://prometheus-community.github.io/helm-charts
url: https://charts.kubevela.net/addons
targetNamespace: observability
releaseName: prometheus
values:
alertmanager:
persistentVolume:
storageClass: "alicloud-disk-available"
size: "20Gi"
server:
persistentVolume:
storageClass: "alicloud-disk-available"
size: "20Gi"
traits:
- type: register-grafana-datasource # register Prometheus datasource to Grafana
properties:
grafanaServiceName: grafana
grafanaServiceNamespace: observability
credentialSecret: grafana
credentialSecretNamespace: observability
name: prometheus
service: prometheus-server
namespace: observability
type: prometheus
access: proxy
# install kube-state-metrics
- name: kube-state-metrics
type: helm
properties:
chart: kube-state-metrics
version: 3.4.1
repoType: helm
# original url: https://prometheus-community.github.io/helm-charts
url: https://charts.kubevela.net/addons
targetNamespace: observability
values:
image:
repository: oamdev/kube-state-metrics
tag: v2.1.0
dependsOn:
- ref:
apiVersion: core.oam.dev/v1beta1
kind: Initializer
name: fluxcd
namespace: vela-system

View File

@@ -21,18 +21,21 @@ spec:
- name: apply-pvc
type: apply-object
properties:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
storageClassName: standard
value:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
storageClassName: standard
# for multi-cluster
# cluster: my-cluster
- name: apply-server
type: apply-component
properties:

View File

@@ -23,6 +23,8 @@ spec:
apiVersion: v1
kind: ConfigMap
name: my-cm-name
# for multi cluster
# cluster: my-cluster
- name: apply
type: apply-component
inputs:

4
go.mod
View File

@@ -34,7 +34,7 @@ require (
github.com/mitchellh/hashstructure/v2 v2.0.1
github.com/oam-dev/cluster-gateway v1.1.2
github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28
github.com/oam-dev/terraform-controller v0.2.4
github.com/oam-dev/terraform-controller v0.2.6
github.com/olekukonko/tablewriter v0.0.5
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.16.0
@@ -45,7 +45,7 @@ require (
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
github.com/tidwall/gjson v1.6.8
github.com/tidwall/gjson v1.9.3
github.com/wercker/stern v0.0.0-20190705090245-4fa46dd6987f
github.com/wonderflow/cert-manager-api v1.0.3
go.mongodb.org/mongo-driver v1.5.1

16
go.sum
View File

@@ -1206,8 +1206,8 @@ github.com/oam-dev/stern v1.13.0-alpha h1:EVjM8Qvh6LssB6t4RZrjf9DtCq1cz+/cy6OF7f
github.com/oam-dev/stern v1.13.0-alpha/go.mod h1:AOkvfFUv0Arz7GBi0jz7S0Jsu4K/kdvSjNsnRt1+BIg=
github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28 h1:tD8HiFKnt0jnwdTWjeqUnfnUYLD/+Nsmj8ZGIxqDWiU=
github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28/go.mod h1:Mu8i0/DdplvnjwRbAYPsc8+LRR27n/mp8VWdkN10GzE=
github.com/oam-dev/terraform-controller v0.2.4 h1:yGgIzm2EWNghuRutnChrRfhMjdlU/jE/cLfBizCgE24=
github.com/oam-dev/terraform-controller v0.2.4/go.mod h1:wd4rnqnJzz274Sg1/qoeIhBx1rvTZ/ECzXoMff0ucR0=
github.com/oam-dev/terraform-controller v0.2.6 h1:aoEj4sfxDMBdTkM5uKYmjVFOgjhYeYBm0xzdRb4+Xu0=
github.com/oam-dev/terraform-controller v0.2.6/go.mod h1:xFSCd3Nwuh6P1HEpx5jDnLzN35T9hoH53zdojDYE6LI=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
@@ -1511,13 +1511,13 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG
github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
github.com/tetafro/godot v1.4.7/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8=
github.com/thanos-io/thanos v0.11.0/go.mod h1:N/Yes7J68KqvmY+xM6J5CJqEvWIvKSR5sqGtmuD6wDc=
github.com/tidwall/gjson v1.6.8 h1:CTmXMClGYPAmln7652e69B7OLXfTi5ABcPPwjIWUv7w=
github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/gjson v1.9.3 h1:hqzS9wAHMO+KVBBkLxYdkEeeFHuqr95GfClRLKlgK0E=
github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek=

View File

@@ -0,0 +1,12 @@
#!/bin/sh
TEMP_DIR="./runtime/rollout/e2e/tmp/"
mkdir -p $TEMP_DIR
cp -r go.mod $TEMP_DIR
cp -r go.sum $TEMP_DIR
cp -r entrypoint.sh $TEMP_DIR
cp -r runtime/rollout/cmd/main.go $TEMP_DIR
cp -r ./apis $TEMP_DIR
cp -r ./pkg $TEMP_DIR
cp -r ./version $TEMP_DIR

View File

@@ -1232,6 +1232,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1635,6 +1639,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1936,6 +1944,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -3360,6 +3372,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -3656,6 +3672,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -4041,6 +4061,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -4305,6 +4329,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -4371,6 +4399,10 @@ spec:
- type
type: object
type: array
configMapRef:
description: ConfigMapRef refer to a ConfigMap which contains
OpenAPI V3 JSON schema of Component parameters.
type: string
latestRevision:
description: LatestRevision of the component definition
properties:
@@ -4585,6 +4617,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote
git repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider

View File

@@ -179,6 +179,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -463,6 +467,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -222,6 +222,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -506,6 +510,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -783,6 +791,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1041,6 +1053,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git
repository. It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference
to Provider
@@ -1107,6 +1123,10 @@ spec:
- type
type: object
type: array
configMapRef:
description: ConfigMapRef refer to a ConfigMap which contains
OpenAPI V3 JSON schema of Component parameters.
type: string
latestRevision:
description: LatestRevision of the component definition
properties:

View File

@@ -151,6 +151,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -188,6 +188,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -468,6 +472,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -148,6 +148,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -212,6 +216,10 @@ spec:
- type
type: object
type: array
configMapRef:
description: ConfigMapRef refer to a ConfigMap which contains OpenAPI
V3 JSON schema of Component parameters.
type: string
latestRevision:
description: LatestRevision of the component definition
properties:

View File

@@ -193,6 +193,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider
@@ -454,6 +458,10 @@ spec:
configuration:
description: Configuration is Terraform Configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
It's valid when remote is set
type: string
providerRef:
description: ProviderReference specifies the reference to
Provider

View File

@@ -669,6 +669,7 @@ func generateTerraformConfigurationWorkload(wl *Workload, ns string) (*unstructu
configuration.Spec.JSON = wl.FullTemplate.Terraform.Configuration
case "remote":
configuration.Spec.Remote = wl.FullTemplate.Terraform.Configuration
configuration.Spec.Path = wl.FullTemplate.Terraform.Path
}
if wl.FullTemplate.Terraform.ProviderReference != nil {

View File

@@ -43,6 +43,7 @@ import (
common2 "github.com/oam-dev/kubevela/pkg/controller/common"
core "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha1/envbinding"
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application/assemble"
"github.com/oam-dev/kubevela/pkg/cue/packages"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
@@ -223,7 +224,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return r.endWithNegativeCondition(ctx, app, condition.ErrorCondition("Render", err), common.ApplicationRendering)
}
handler.handleCheckManageWorkloadTrait(handler.currentAppRev.Spec.TraitDefinitions, comps)
assemble.HandleCheckManageWorkloadTrait(*handler.currentAppRev, comps)
if err := handler.HandleComponentsRevision(ctx, comps); err != nil {
klog.ErrorS(err, "Failed to handle compoents revision", "application", klog.KObj(app))

View File

@@ -26,8 +26,6 @@ import (
"strconv"
"time"
"github.com/oam-dev/kubevela/pkg/oam/testutil"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -40,6 +38,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/yaml"
@@ -52,6 +51,7 @@ import (
velatypes "github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/testutil"
"github.com/oam-dev/kubevela/pkg/oam/util"
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
)
@@ -2069,6 +2069,51 @@ var _ = Describe("Test Application Controller", func() {
}))
})
It("app record execution state with controllerRevision", func() {
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "vela-test-app-trace",
},
}
app := appwithNoTrait.DeepCopy()
app.Name = "vela-test-app-trace"
app.SetNamespace(ns.Name)
Expect(k8sClient.Create(ctx, ns)).Should(BeNil())
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
appKey := client.ObjectKey{
Name: app.Name,
Namespace: app.Namespace,
}
testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey})
checkApp := &v1beta1.Application{}
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning))
web := &v1.Deployment{}
Expect(k8sClient.Get(ctx, client.ObjectKey{
Name: "myweb2",
Namespace: app.Namespace,
}, web)).Should(BeNil())
web.Spec.Replicas = pointer.Int32(0)
Expect(k8sClient.Update(ctx, web)).Should(BeNil())
checkApp.Status.Workflow = nil
Expect(k8sClient.Update(ctx, checkApp)).Should(BeNil())
testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey})
checkApp = &v1beta1.Application{}
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning))
checkWeb := &v1.Deployment{}
Expect(k8sClient.Get(ctx, client.ObjectKey{
Name: "myweb2",
Namespace: app.Namespace,
}, checkWeb)).Should(BeNil())
Expect(*(checkWeb.Spec.Replicas)).Should(BeEquivalentTo(int32(0)))
})
})
const (

View File

@@ -37,7 +37,6 @@ import (
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/applicationrollout"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/cue/process"
"github.com/oam-dev/kubevela/pkg/oam"
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
)
@@ -304,26 +303,6 @@ func (h *AppHandler) aggregateHealthStatus(appFile *appfile.Appfile) ([]common.A
return appStatus, healthy, nil
}
func (h *AppHandler) handleCheckManageWorkloadTrait(traitDefs map[string]v1beta1.TraitDefinition, comps []*types.ComponentManifest) {
manageWorkloadTrait := map[string]bool{}
for traitName, definition := range traitDefs {
if definition.Spec.ManageWorkload {
manageWorkloadTrait[traitName] = true
}
}
if len(manageWorkloadTrait) == 0 {
return
}
for _, comp := range comps {
for _, trait := range comp.Traits {
traitType := trait.GetLabels()[oam.TraitTypeLabel]
if manageWorkloadTrait[traitType] {
trait.SetLabels(oamutil.MergeMapOverrideWithDst(trait.GetLabels(), map[string]string{oam.LabelManageWorkloadTrait: "true"}))
}
}
}
}
func generateScopeReference(scopes []appfile.Scope) []corev1.ObjectReference {
var references []corev1.ObjectReference
for _, scope := range scopes {

View File

@@ -30,7 +30,6 @@ import (
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -41,7 +40,6 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
velatypes "github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/appfile"
"github.com/oam-dev/kubevela/pkg/oam"
)
const workloadDefinition = `
@@ -217,37 +215,3 @@ var _ = Describe("Test statusAggregate", func() {
Expect(err).Should(BeNil())
})
})
var _ = Describe("Test handleCheckManageWorkloadTrait func", func() {
It("Test every situation", func() {
traitDefs := map[string]v1beta1.TraitDefinition{
"rollout": v1beta1.TraitDefinition{
Spec: v1beta1.TraitDefinitionSpec{
ManageWorkload: true,
},
},
"normal": v1beta1.TraitDefinition{
Spec: v1beta1.TraitDefinitionSpec{},
},
}
rolloutTrait := &unstructured.Unstructured{}
rolloutTrait.SetLabels(map[string]string{oam.TraitTypeLabel: "rollout"})
normalTrait := &unstructured.Unstructured{}
normalTrait.SetLabels(map[string]string{oam.TraitTypeLabel: "normal"})
comps := []*velatypes.ComponentManifest{
{
Traits: []*unstructured.Unstructured{
rolloutTrait,
normalTrait,
},
},
}
h := AppHandler{}
h.handleCheckManageWorkloadTrait(traitDefs, comps)
Expect(len(rolloutTrait.GetLabels())).Should(BeEquivalentTo(2))
Expect(rolloutTrait.GetLabels()[oam.LabelManageWorkloadTrait]).Should(BeEquivalentTo("true"))
Expect(len(normalTrait.GetLabels())).Should(BeEquivalentTo(1))
Expect(normalTrait.GetLabels()[oam.LabelManageWorkloadTrait]).Should(BeEquivalentTo(""))
})
})

View File

@@ -232,6 +232,9 @@ func PrepareBeforeApply(comp *types.ComponentManifest, appRev *v1beta1.Applicati
}
assembledTraits := make([]*unstructured.Unstructured, len(comp.Traits))
HandleCheckManageWorkloadTrait(*appRev, []*types.ComponentManifest{comp})
for i, trait := range comp.Traits {
setTraitLabels(trait, additionalLabel)
assembledTraits[i] = trait
@@ -329,3 +332,25 @@ func setTraitLabels(trait *unstructured.Unstructured, additionalLabels map[strin
// add more trait-specific labels here
util.AddLabels(trait, additionalLabels)
}
// HandleCheckManageWorkloadTrait will checkout every trait whether a manage-workload trait, if yes set label and annotation in trait
func HandleCheckManageWorkloadTrait(appRev v1beta1.ApplicationRevision, comps []*types.ComponentManifest) {
traitDefs := appRev.Spec.TraitDefinitions
manageWorkloadTrait := map[string]bool{}
for traitName, definition := range traitDefs {
if definition.Spec.ManageWorkload {
manageWorkloadTrait[traitName] = true
}
}
if len(manageWorkloadTrait) == 0 {
return
}
for _, comp := range comps {
for _, trait := range comp.Traits {
traitType := trait.GetLabels()[oam.TraitTypeLabel]
if manageWorkloadTrait[traitType] {
trait.SetLabels(util.MergeMapOverrideWithDst(trait.GetLabels(), map[string]string{oam.LabelManageWorkloadTrait: "true"}))
}
}
}
}

View File

@@ -26,6 +26,7 @@ import (
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
velatypes "github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/oam"
)
@@ -203,3 +204,49 @@ var _ = Describe("Test Assemble Options", func() {
Expect(wl.GetName()).Should(Equal(workloadName))
})
})
var _ = Describe("Test handleCheckManageWorkloadTrait func", func() {
It("Test every situation", func() {
traitDefs := map[string]v1beta1.TraitDefinition{
"rollout": v1beta1.TraitDefinition{
Spec: v1beta1.TraitDefinitionSpec{
ManageWorkload: true,
},
},
"normal": v1beta1.TraitDefinition{
Spec: v1beta1.TraitDefinitionSpec{},
},
}
appRev := v1beta1.ApplicationRevision{
Spec: v1beta1.ApplicationRevisionSpec{
TraitDefinitions: traitDefs,
},
}
rolloutTrait := &unstructured.Unstructured{}
rolloutTrait.SetLabels(map[string]string{oam.TraitTypeLabel: "rollout"})
normalTrait := &unstructured.Unstructured{}
normalTrait.SetLabels(map[string]string{oam.TraitTypeLabel: "normal"})
workload := unstructured.Unstructured{}
workload.SetLabels(map[string]string{
oam.WorkloadTypeLabel: "webservice",
})
comps := []*velatypes.ComponentManifest{
{
Traits: []*unstructured.Unstructured{
rolloutTrait,
normalTrait,
},
StandardWorkload: &workload,
},
}
HandleCheckManageWorkloadTrait(appRev, comps)
Expect(len(rolloutTrait.GetLabels())).Should(BeEquivalentTo(2))
Expect(rolloutTrait.GetLabels()[oam.LabelManageWorkloadTrait]).Should(BeEquivalentTo("true"))
Expect(len(normalTrait.GetLabels())).Should(BeEquivalentTo(1))
Expect(normalTrait.GetLabels()[oam.LabelManageWorkloadTrait]).Should(BeEquivalentTo(""))
})
})

View File

@@ -241,7 +241,7 @@ func (a *AppManifestsDispatcher) applyAndRecordManifests(ctx context.Context, ma
ctrlUIDs = append(ctrlUIDs, rt.UID)
}
applyOpts := []apply.ApplyOption{apply.MustBeControllableByAny(ctrlUIDs)}
applyOpts := []apply.ApplyOption{apply.MustBeControllableByAny(ctrlUIDs), apply.NotUpdateRenderHashEqual()}
ownerRef := metav1.OwnerReference{
APIVersion: v1beta1.SchemeGroupVersion.String(),
Kind: reflect.TypeOf(v1beta1.ResourceTracker{}).Name(),

View File

@@ -693,7 +693,7 @@ func (e *GenerationUnchanged) Error() string {
// applyOnceOnly is an ApplyOption that controls the applying mechanism for workload and trait.
// More detail refers to the ApplyOnceOnlyMode type annotation
func applyOnceOnly(ac *v1alpha2.ApplicationConfiguration, mode core.ApplyOnceOnlyMode) apply.ApplyOption {
return func(_ context.Context, existing, desired runtime.Object) error {
return apply.MakeCustomApplyOption(func(existing, desired client.Object) error {
if mode == core.ApplyOnceOnlyOff {
return nil
}
@@ -791,5 +791,5 @@ func applyOnceOnly(ac *v1alpha2.ApplicationConfiguration, mode core.ApplyOnceOnl
}
// its spec is not changed, return an error to abort applying it
return &GenerationUnchanged{}
}
})
}

View File

@@ -89,13 +89,10 @@ func HandleReplicas(ctx context.Context, rolloutComp string, c client.Client) as
pv := fieldpath.Pave(u.UnstructuredContent())
// we hard code here, but we can easily support more types of workload by add more cases logic in switch
var replicasFieldPath string
switch u.GetKind() {
case reflect.TypeOf(v1alpha1.CloneSet{}).Name(), reflect.TypeOf(appsv1.Deployment{}).Name(), reflect.TypeOf(appsv1.StatefulSet{}).Name():
replicasFieldPath = "spec.replicas"
default:
replicasFieldPath, err := GetWorkloadReplicasPath(*u)
if err != nil {
klog.Errorf("rollout meet a workload we cannot support yet", "Kind", u.GetKind(), "name", u.GetName())
return fmt.Errorf("rollout meet a workload we cannot support yet Kind %s name %s", u.GetKind(), u.GetName())
return err
}
workload := u.DeepCopy()
@@ -127,6 +124,16 @@ func HandleReplicas(ctx context.Context, rolloutComp string, c client.Client) as
})
}
// GetWorkloadReplicasPath get replicas path of workload
func GetWorkloadReplicasPath(u unstructured.Unstructured) (string, error) {
switch u.GetKind() {
case reflect.TypeOf(v1alpha1.CloneSet{}).Name(), reflect.TypeOf(appsv1.Deployment{}).Name(), reflect.TypeOf(appsv1.StatefulSet{}).Name():
return "spec.replicas", nil
default:
return "", fmt.Errorf("rollout meet a workload we cannot support yet Kind %s name %s", u.GetKind(), u.GetName())
}
}
// appRollout should take over updating workload, so disable previous controller owner(resourceTracker)
func disableControllerOwner(workload *unstructured.Unstructured) {
if workload == nil {

View File

@@ -20,12 +20,13 @@ import (
"testing"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
oamstandard "github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/pkg/oam/util"
"gotest.tools/assert"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/utils/pointer"
@@ -192,3 +193,37 @@ func TestHandleTerminated(t *testing.T) {
}
}
}
func TestGetWorkloadReplicasPath(t *testing.T) {
deploy := appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "appsv1",
Kind: "Deployment",
},
}
u, err := util.Object2Unstructured(deploy)
if err != nil {
t.Errorf("deployment shounld't meet an error %w", err)
}
pathStr, err := GetWorkloadReplicasPath(*u)
if err != nil {
t.Errorf("deployment should handle deployment")
}
if pathStr != "spec.replicas" {
t.Errorf("deployPath error got %s", pathStr)
}
ds := appsv1.DaemonSet{
TypeMeta: metav1.TypeMeta{
APIVersion: "appsv1",
Kind: "DaemonSet",
},
}
u, err = util.Object2Unstructured(ds)
if err != nil {
t.Errorf("ds shounld't meet an error %w", err)
}
_, err = GetWorkloadReplicasPath(*u)
if err == nil {
t.Errorf("daemonset shouldn't support")
}
}

View File

@@ -29,6 +29,7 @@ import (
"github.com/pkg/errors"
apps "k8s.io/api/apps/v1"
core "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
@@ -370,7 +371,7 @@ func getAppConfigNameFromLabel(o metav1.Object) string {
func getVersioningPeerWorkloadRefs(ctx context.Context, c client.Reader, wlRef core.ObjectReference, ns string) ([]core.ObjectReference, error) {
o := &unstructured.Unstructured{}
o.SetGroupVersionKind(wlRef.GroupVersionKind())
if err := c.Get(ctx, client.ObjectKey{Namespace: ns, Name: wlRef.Name}, o); err != nil {
if err := c.Get(ctx, client.ObjectKey{Namespace: ns, Name: wlRef.Name}, o); err != nil && !apierrors.IsNotFound(err) {
return nil, err
}

View File

@@ -18,6 +18,7 @@ package healthscope
import (
"context"
"encoding/json"
"sort"
"strings"
"sync"
@@ -615,12 +616,34 @@ func (r *Reconciler) createWorkloadRefs(ctx context.Context, appRef v1alpha2.App
}, o); err != nil {
continue
}
if labels := o.GetLabels(); labels != nil && labels[oam.WorkloadTypeLabel] != "" {
wlRefs = append(wlRefs, WorkloadReference{
ObjectReference: rs.ObjectReference,
clusterName: rs.Cluster,
envName: decisionsMap[rs.Cluster],
})
if labels := o.GetLabels(); labels != nil {
if labels[oam.WorkloadTypeLabel] != "" {
wlRefs = append(wlRefs, WorkloadReference{
ObjectReference: rs.ObjectReference,
clusterName: rs.Cluster,
envName: decisionsMap[rs.Cluster],
})
} else if labels[oam.TraitTypeLabel] != "" && labels[oam.LabelManageWorkloadTrait] == "true" {
// this means this trait is a manage-Workload trait, get workload GVK and name for trait's annotation
objectRef := corev1.ObjectReference{}
err := json.Unmarshal([]byte(o.GetAnnotations()[oam.AnnotationWorkloadGVK]), &objectRef)
if err != nil {
// don't break whole check process due to this error
continue
}
if o.GetAnnotations() != nil && len(o.GetAnnotations()[oam.AnnotationWorkloadName]) != 0 {
objectRef.Name = o.GetAnnotations()[oam.AnnotationWorkloadName]
} else {
// use component name as default
objectRef.Name = labels[oam.LabelAppComponent]
}
wlRefs = append(wlRefs, WorkloadReference{
ObjectReference: objectRef,
clusterName: rs.Cluster,
envName: decisionsMap[rs.Cluster],
})
}
}
}
}

View File

@@ -122,6 +122,28 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
r.record.Event(&wfstepdefinition, event.Warning("failed to garbage collect DefinitionRevision of type WorkflowStepDefinition", err))
}
def := utils.NewCapabilityStepDef(&wfstepdefinition)
def.Name = req.NamespacedName.Name
// Store the parameter of stepDefinition to configMap
cmName, err := def.StoreOpenAPISchema(ctx, r.Client, r.pd, req.Namespace, req.Name, defRev.Name)
if err != nil {
klog.InfoS("Could not store capability in ConfigMap", "err", err)
r.record.Event(&(wfstepdefinition), event.Warning("Could not store capability in ConfigMap", err))
return ctrl.Result{}, util.PatchCondition(ctx, r, &wfstepdefinition,
condition.ReconcileError(fmt.Errorf(util.ErrStoreCapabilityInConfigMap, wfstepdefinition.Name, err)))
}
if wfstepdefinition.Status.ConfigMapRef != cmName {
wfstepdefinition.Status.ConfigMapRef = cmName
if err := r.UpdateStatus(ctx, &wfstepdefinition); err != nil {
klog.ErrorS(err, "Could not update WorkflowStepDefinition Status", "workflowStepDefinition", klog.KRef(req.Namespace, req.Name))
r.record.Event(&wfstepdefinition, event.Warning("Could not update WorkflowStepDefinition Status", err))
return ctrl.Result{}, util.PatchCondition(ctx, r, &wfstepdefinition,
condition.ReconcileError(fmt.Errorf(util.ErrUpdateWorkflowStepDefinition, wfstepdefinition.Name, err)))
}
klog.InfoS("Successfully updated the status.configMapRef of the WorkflowStepDefinition", "workflowStepDefinition",
klog.KRef(req.Namespace, req.Name), "status.configMapRef", cmName)
}
return ctrl.Result{}, nil
}

View File

@@ -34,6 +34,7 @@ import (
"k8s.io/utils/pointer"
"github.com/crossplane/crossplane-runtime/pkg/event"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
@@ -240,6 +241,19 @@ func (h *handler) checkWorkloadNotExist(ctx context.Context) (bool, error) {
return false, nil
}
func getWorkloadReplicasNum(u unstructured.Unstructured) (int32, error) {
replicaPath, err := applicationrollout.GetWorkloadReplicasPath(u)
if err != nil {
return 0, fmt.Errorf("get workload replicas path err %w", err)
}
wlpv := fieldpath.Pave(u.UnstructuredContent())
replicas, err := wlpv.GetInteger(replicaPath)
if err != nil {
return 0, fmt.Errorf("get workload replicas err %w", err)
}
return int32(replicas), nil
}
// checkRollingTerminated check the rollout if have finished
func checkRollingTerminated(rollout v1alpha1.Rollout) bool {
// handle rollout completed

View File

@@ -40,6 +40,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
)
var _ = Describe("Test rollout related handler func", func() {
@@ -513,6 +514,50 @@ var _ = Describe("Test rollout related handler func", func() {
Expect(checkRt.Status.TrackedResources[0].Name).Should(BeEquivalentTo(u.GetName()))
Expect(checkRt.Status.TrackedResources[0].UID).Should(BeEquivalentTo(u.GetUID()))
})
It("TestGetWorkloadReplicasNum", func() {
deployName := "test-workload-get"
deploy := appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: deployName,
},
Spec: appsv1.DeploymentSpec{
Replicas: pointer.Int32Ptr(3),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "test",
},
},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "test",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "test-container",
Image: "test-image",
},
},
},
},
},
}
Expect(k8sClient.Create(ctx, &deploy)).Should(BeNil())
u := unstructured.Unstructured{}
u.SetAPIVersion("apps/v1")
u.SetKind("Deployment")
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: deployName, Namespace: namespace}, &u)).Should(BeNil())
rep, err := getWorkloadReplicasNum(u)
Expect(err).Should(BeNil())
Expect(rep).Should(BeEquivalentTo(3))
})
})
})

View File

@@ -18,6 +18,10 @@ package rollout
import (
"context"
"encoding/json"
"math"
"k8s.io/apimachinery/pkg/util/intstr"
"github.com/pkg/errors"
@@ -34,6 +38,8 @@ import (
common2 "github.com/oam-dev/kubevela/pkg/controller/common"
rolloutplan "github.com/oam-dev/kubevela/pkg/controller/common/rollout"
oamctrl "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
"github.com/oam-dev/kubevela/pkg/oam"
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/utils/apply"
)
@@ -110,6 +116,33 @@ func (r *reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{}, err
}
if rollout.Status.RollingState == v1alpha1.LocatingTargetAppState {
if rollout.GetAnnotations() == nil || rollout.GetAnnotations()[oam.AnnotationWorkloadName] != h.targetWorkload.GetName() {
// this is a update operation, the target workload will change so modify annotation
gvk := map[string]string{"apiVersion": h.targetWorkload.GetAPIVersion(), "kind": h.targetWorkload.GetKind()}
gvkValue, _ := json.Marshal(gvk)
rollout.SetAnnotations(oamutil.MergeMapOverrideWithDst(rollout.GetAnnotations(),
map[string]string{oam.AnnotationWorkloadName: h.targetWorkload.GetName(), oam.AnnotationWorkloadGVK: string(gvkValue)}))
klog.InfoS("rollout controller set targetWorkload ", h.targetWorkload.GetName(),
"in annotation in rollout namespace: ", rollout.Namespace, " name", rollout.Name, "gvk", gvkValue)
// exit current reconcile before create target workload, this reconcile don't update status just modify annotation
// next round reconcile will create workload and pass `LocatingTargetAppState` phase
return ctrl.Result{}, h.Update(ctx, rollout)
}
// this is a scale operation, if user don't fill rolloutBatches, fill it with default value
if len(h.sourceRevName) == 0 && len(rollout.Spec.RolloutPlan.RolloutBatches) == 0 {
// logic reach here means cannot get an error, so ignore it
replicas, _ := getWorkloadReplicasNum(*h.targetWorkload)
rollout.Spec.RolloutPlan.RolloutBatches = []v1alpha1.RolloutBatch{{
Replicas: intstr.FromInt(int(math.Abs(float64(*rollout.Spec.RolloutPlan.TargetSize - replicas))))},
}
klog.InfoS("rollout controller set default rollout batches ", h.rollout.GetName(),
" namespace: ", rollout.Namespace, "targetSize", rollout.Spec.RolloutPlan.TargetSize)
return ctrl.Result{}, h.Update(ctx, rollout)
}
}
switch rollout.Status.RollingState {
case v1alpha1.RolloutDeletingState:
removed, err := h.checkWorkloadNotExist(ctx)

View File

@@ -351,6 +351,75 @@ func (def *CapabilityTraitDefinition) StoreOpenAPISchema(ctx context.Context, k8
return cmName, nil
}
// CapabilityStepDefinition is the Capability struct for WorkflowStepDefinition
type CapabilityStepDefinition struct {
Name string `json:"name"`
StepDefinition v1beta1.WorkflowStepDefinition `json:"stepDefinition"`
CapabilityBaseDefinition
}
// NewCapabilityStepDef will create a CapabilityStepDefinition
func NewCapabilityStepDef(stepdefinition *v1beta1.WorkflowStepDefinition) CapabilityStepDefinition {
var def CapabilityStepDefinition
def.Name = stepdefinition.Name
def.StepDefinition = *stepdefinition.DeepCopy()
return def
}
// GetOpenAPISchema gets OpenAPI v3 schema by StepDefinition name
func (def *CapabilityStepDefinition) GetOpenAPISchema(pd *packages.PackageDiscover, name string) ([]byte, error) {
capability, err := appfile.ConvertTemplateJSON2Object(name, nil, def.StepDefinition.Spec.Schematic)
if err != nil {
return nil, fmt.Errorf("failed to convert WorkflowStepDefinition to Capability Object")
}
return getOpenAPISchema(capability, pd)
}
// StoreOpenAPISchema stores OpenAPI v3 schema from StepDefinition in ConfigMap
func (def *CapabilityStepDefinition) StoreOpenAPISchema(ctx context.Context, k8sClient client.Client, pd *packages.PackageDiscover, namespace, name string, revName string) (string, error) {
var jsonSchema []byte
var err error
jsonSchema, err = def.GetOpenAPISchema(pd, name)
if err != nil {
return "", fmt.Errorf("failed to generate OpenAPI v3 JSON schema for capability %s: %w", def.Name, err)
}
stepDefinition := def.StepDefinition
ownerReference := []metav1.OwnerReference{{
APIVersion: stepDefinition.APIVersion,
Kind: stepDefinition.Kind,
Name: stepDefinition.Name,
UID: stepDefinition.GetUID(),
Controller: pointer.BoolPtr(true),
BlockOwnerDeletion: pointer.BoolPtr(true),
}}
cmName, err := def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, stepDefinition.Name, jsonSchema, ownerReference)
if err != nil {
return cmName, err
}
// Create a configmap to store parameter for each definitionRevision
defRev := new(v1beta1.DefinitionRevision)
if err = k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revName}, defRev); err != nil {
return "", err
}
ownerReference = []metav1.OwnerReference{{
APIVersion: defRev.APIVersion,
Kind: defRev.Kind,
Name: defRev.Name,
UID: defRev.GetUID(),
Controller: pointer.BoolPtr(true),
BlockOwnerDeletion: pointer.BoolPtr(true),
}}
_, err = def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, revName, jsonSchema, ownerReference)
if err != nil {
return cmName, err
}
return cmName, nil
}
// CapabilityBaseDefinition is the base struct for CapabilityWorkloadDefinition and CapabilityTraitDefinition
type CapabilityBaseDefinition struct {
}
@@ -399,7 +468,7 @@ func (def *CapabilityBaseDefinition) CreateOrUpdateConfigMap(ctx context.Context
return cmName, nil
}
// getDefinition is the main function for GetDefinition API
// getOpenAPISchema is the main function for GetDefinition API
func getOpenAPISchema(capability types.Capability, pd *packages.PackageDiscover) ([]byte, error) {
openAPISchema, err := generateOpenAPISchemaFromCapabilityParameter(capability, pd)
if err != nil {

View File

@@ -231,10 +231,11 @@ func (wd *workloadDef) Status(ctx process.Context, cli client.Client, ns string,
if err != nil {
return "", errors.WithMessage(err, "get template context")
}
return getStatusMessage(templateContext, customStatusTemplate, parameter)
return getStatusMessage(wd.pd, templateContext, customStatusTemplate, parameter)
}
func getStatusMessage(templateContext map[string]interface{}, customStatusTemplate string, parameter interface{}) (string, error) {
func getStatusMessage(pd *packages.PackageDiscover, templateContext map[string]interface{}, customStatusTemplate string, parameter interface{}) (string, error) {
bi := build.NewContext().NewInstance("", nil)
var ctxBuff string
var paramBuff = "parameter: {}\n"
@@ -251,10 +252,12 @@ func getStatusMessage(templateContext map[string]interface{}, customStatusTempla
if string(bt) != "null" {
paramBuff = "parameter: " + string(bt) + "\n"
}
var buff = ctxBuff + paramBuff + customStatusTemplate
var buff = customStatusTemplate + "\n" + ctxBuff + paramBuff
if err := bi.AddFile("-", buff); err != nil {
return "", errors.WithMessagef(err, "invalid cue template of customStatus")
}
var r cue.Runtime
inst, err := r.Compile("-", buff)
inst, err := pd.ImportPackagesAndBuildInstance(bi)
if err != nil {
return "", errors.WithMessage(err, "compile customStatus template")
}
@@ -426,7 +429,7 @@ func (td *traitDef) Status(ctx process.Context, cli client.Client, ns string, cu
if err != nil {
return "", errors.WithMessage(err, "get template context")
}
return getStatusMessage(templateContext, customStatusTemplate, parameter)
return getStatusMessage(td.pd, templateContext, customStatusTemplate, parameter)
}
// HealthCheck address health check for trait

View File

@@ -1245,9 +1245,36 @@ if len(context.outputs.ingress.status.loadBalancer.ingress) == 0 {
statusTemp: `message: parameter.configInfo.name + ".type: " + context.outputs["\(parameter.configInfo.name)"].spec.type`,
expMessage: "test-name.type: NodePort",
},
"import package in template": {
tpContext: map[string]interface{}{
"outputs": map[string]interface{}{
"service": map[string]interface{}{
"spec": map[string]interface{}{
"type": "NodePort",
"clusterIP": "10.0.0.1",
"ports": []interface{}{
map[string]interface{}{
"port": 80,
},
},
},
},
"ingress": map[string]interface{}{
"rules": []interface{}{
map[string]interface{}{
"host": "example.com",
},
},
},
},
},
statusTemp: `import "strconv"
message: "ports: " + strconv.FormatInt(context.outputs.service.spec.ports[0].port,10)`,
expMessage: "ports: 80",
},
}
for message, ca := range cases {
gotMessage, err := getStatusMessage(ca.tpContext, ca.statusTemp, ca.parameter)
gotMessage, err := getStatusMessage(&packages.PackageDiscover{}, ca.tpContext, ca.statusTemp, ca.parameter)
assert.NoError(t, err, message)
assert.Equal(t, ca.expMessage, gotMessage, message)
}

View File

@@ -311,8 +311,11 @@ func openBaiscLit(root ast.Node) {
field, ok := node.(*ast.Field)
if ok {
v := field.Value
if lit, ok := v.(*ast.BasicLit); ok {
switch lit := v.(type) {
case *ast.BasicLit:
field.Value = ast.NewBinExpr(token.OR, &ast.UnaryExpr{X: lit, Op: token.MUL}, ast.NewIdent("_"))
case *ast.ListLit:
field.Value = ast.NewBinExpr(token.OR, &ast.UnaryExpr{X: lit, Op: token.MUL}, ast.NewList(&ast.Ellipsis{}))
}
}
return true

View File

@@ -248,6 +248,7 @@ b: "foo"
b1: string
c: true
c1: bool
arr: [1,2]
top: _
bottom: _|_
`)
@@ -258,6 +259,7 @@ b: *"foo" | _
b1: string
c: *true | _
c1: bool
arr: *[1, 2] | [...]
top: _
bottom: _|_
`)

View File

@@ -35,6 +35,8 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"github.com/oam-dev/kubevela/pkg/stdlib"
)
const (
@@ -107,6 +109,9 @@ func (pd *PackageDiscover) ImportBuiltinPackagesFor(bi *build.Instance) {
// ImportPackagesAndBuildInstance Combine import built-in packages and build cue template together to avoid data race
func (pd *PackageDiscover) ImportPackagesAndBuildInstance(bi *build.Instance) (inst *cue.Instance, err error) {
pd.ImportBuiltinPackagesFor(bi)
if err := stdlib.AddImportsFor(bi, ""); err != nil {
return nil, err
}
var r cue.Runtime
pd.mutex.Lock()
defer pd.mutex.Unlock()

View File

@@ -197,7 +197,7 @@ func (ctx *templateContext) BaseContextFile() string {
if len(ctx.auxiliaries) > 0 {
var auxLines []string
for _, auxiliary := range ctx.auxiliaries {
auxLines = append(auxLines, fmt.Sprintf("%s: %s", auxiliary.Name, structMarshal(auxiliary.Ins.String())))
auxLines = append(auxLines, fmt.Sprintf("\"%s\": %s", auxiliary.Name, structMarshal(auxiliary.Ins.String())))
}
if len(auxLines) > 0 {
buff += fmt.Sprintf(model.OutputsFieldName+": {%s}\n", strings.Join(auxLines, "\n"))

View File

@@ -64,6 +64,11 @@ image: "myserver"
Name: "service",
}
svcAuxWithAbnormalName := Auxiliary{
Ins: svcIns,
Name: "service-1",
}
targetParams := map[string]interface{}{
"parameter1": "string",
"parameter2": map[string]string{
@@ -98,6 +103,7 @@ image: "myserver"
ctx := NewContext("myns", "mycomp", "myapp", "myapp-v1")
ctx.SetBase(base)
ctx.AppendAuxiliaries(svcAux)
ctx.AppendAuxiliaries(svcAuxWithAbnormalName)
ctx.SetParameters(targetParams)
ctx.PushData(model.ContextDataArtifacts, targetData)
ctx.PushData("arbitraryData", targetArbitraryData)
@@ -132,6 +138,10 @@ image: "myserver"
assert.Equal(t, nil, err)
assert.Equal(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\"}", string(outputsJs))
outputsJs, err = ctxInst.Lookup("context", model.OutputsFieldName, "service-1").MarshalJSON()
assert.Equal(t, nil, err)
assert.Equal(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\"}", string(outputsJs))
ns, err := ctxInst.Lookup("context", model.ContextNamespace).String()
assert.Equal(t, nil, err)
assert.Equal(t, "myns", ns)

View File

@@ -125,4 +125,10 @@ const (
// AnnotationLastAppliedConfiguration is kubectl annotations for 3-way merge
AnnotationLastAppliedConfiguration = "kubectl.kubernetes.io/last-applied-configuration"
// AnnotationWorkloadGVK indicates the managed workload's GVK by trait
AnnotationWorkloadGVK = "trait.oam.dev/workload-gvk"
// AnnotationWorkloadName indicates the managed workload's name by trait
AnnotationWorkloadName = "trait.oam.dev/workload-name"
)

View File

@@ -100,6 +100,8 @@ const (
ErrUpdateComponentDefinition = "cannot update ComponentDefinition %s: %v"
// ErrUpdateTraitDefinition is the error while update TraitDefinition
ErrUpdateTraitDefinition = "cannot update TraitDefinition %s: %v"
// ErrUpdateStepDefinition is the error while update WorkflowStepDefinition
ErrUpdateStepDefinition = "cannot update WorkflowStepDefinition %s: %v"
// ErrUpdatePolicyDefinition is the error while update PolicyDefinition
ErrUpdatePolicyDefinition = "cannot update PolicyDefinition %s: %v"
// ErrUpdateWorkflowStepDefinition is the error while update WorkflowStepDefinition

View File

@@ -27,8 +27,7 @@ import (
var (
//go:embed pkgs op.cue
fs embed.FS
pkgContent string
fs embed.FS
)
// GetPackages Get Stdlib packages
@@ -44,7 +43,7 @@ func GetPackages(tagTempl string) (map[string]string, error) {
return nil, err
}
pkgContent = string(opBytes) + "\n"
pkgContent := string(opBytes) + "\n"
for _, file := range files {
body, err := fs.ReadFile("pkgs/" + file.Name())
if err != nil {

View File

@@ -18,17 +18,17 @@
url?: string
value?: string
style?: string
text?: #text
text?: #textType
confirm?: {
title: #text
text: #text
confirm: #text
deny: #text
title: #textType
text: #textType
confirm: #textType
deny: #textType
style?: string
}
options?: [...#option]
initial_options?: [...#option]
placeholder?: #text
placeholder?: #textType
initial_date?: string
image_url?: string
alt_text?: string
@@ -45,7 +45,7 @@
}]
}
#text: {
#textType: {
type: string
text: string
emoji?: bool
@@ -53,8 +53,8 @@
}
#option: {
text: text
text: #textType
value: string
description?: text
description?: #textType
url?: string
}

View File

@@ -19,18 +19,25 @@ package apply
import (
"context"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/pkg/errors"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
)
const (
// LabelRenderHash is the label that record the hash value of the rendering resource.
LabelRenderHash = "oam.dev/render-hash"
)
// Applicator applies new state to an object or create it if not exist.
// It uses the same mechanism as `kubectl apply`, that is, for each resource being applied,
// computing a three-way diff merge in client side based on its current state, modified stated,
@@ -40,11 +47,15 @@ type Applicator interface {
Apply(context.Context, client.Object, ...ApplyOption) error
}
type applyAction struct {
skipUpdate bool
}
// ApplyOption is called before applying state to the object.
// ApplyOption is still called even if the object does NOT exist.
// If the object does not exist, `existing` will be assigned as `nil`.
// nolint: golint
type ApplyOption func(ctx context.Context, existing, desired runtime.Object) error
type ApplyOption func(act *applyAction, existing, desired client.Object) error
// NewAPIApplicator creates an Applicator that applies state to an
// object or creates the object if not exist.
@@ -57,13 +68,13 @@ func NewAPIApplicator(c client.Client) *APIApplicator {
}
type creator interface {
createOrGetExisting(context.Context, client.Client, client.Object, ...ApplyOption) (client.Object, error)
createOrGetExisting(context.Context, *applyAction, client.Client, client.Object, ...ApplyOption) (client.Object, error)
}
type creatorFn func(context.Context, client.Client, client.Object, ...ApplyOption) (client.Object, error)
type creatorFn func(context.Context, *applyAction, client.Client, client.Object, ...ApplyOption) (client.Object, error)
func (fn creatorFn) createOrGetExisting(ctx context.Context, c client.Client, o client.Object, ao ...ApplyOption) (client.Object, error) {
return fn(ctx, c, o, ao...)
func (fn creatorFn) createOrGetExisting(ctx context.Context, act *applyAction, c client.Client, o client.Object, ao ...ApplyOption) (client.Object, error) {
return fn(ctx, act, c, o, ao...)
}
type patcher interface {
@@ -95,7 +106,12 @@ func loggingApply(msg string, desired client.Object) {
// Apply applies new state to an object or create it if not exist
func (a *APIApplicator) Apply(ctx context.Context, desired client.Object, ao ...ApplyOption) error {
existing, err := a.createOrGetExisting(ctx, a.c, desired, ao...)
_, err := generateRenderHash(desired)
if err != nil {
return err
}
applyAct := new(applyAction)
existing, err := a.createOrGetExisting(ctx, applyAct, a.c, desired, ao...)
if err != nil {
return err
}
@@ -104,9 +120,15 @@ func (a *APIApplicator) Apply(ctx context.Context, desired client.Object, ao ...
}
// the object already exists, apply new state
if err := executeApplyOptions(ctx, existing, desired, ao); err != nil {
if err := executeApplyOptions(applyAct, existing, desired, ao); err != nil {
return err
}
if applyAct.skipUpdate {
loggingApply("skip update", desired)
return nil
}
loggingApply("patching object", desired)
patch, err := a.patcher.patch(existing, desired)
if err != nil {
@@ -115,12 +137,34 @@ func (a *APIApplicator) Apply(ctx context.Context, desired client.Object, ao ...
return errors.Wrapf(a.c.Patch(ctx, desired, patch), "cannot patch object")
}
func generateRenderHash(desired client.Object) (string, error) {
if desired == nil {
return "", nil
}
desiredHash, err := utils.ComputeSpecHash(desired)
if err != nil {
return "", errors.Wrap(err, "compute desired hash")
}
util.AddLabels(desired, map[string]string{
LabelRenderHash: desiredHash,
})
return desiredHash, nil
}
func getRenderHash(existing client.Object) string {
labels := existing.GetLabels()
if labels == nil {
return ""
}
return labels[LabelRenderHash]
}
// createOrGetExisting will create the object if it does not exist
// or get and return the existing object
func createOrGetExisting(ctx context.Context, c client.Client, desired client.Object, ao ...ApplyOption) (client.Object, error) {
func createOrGetExisting(ctx context.Context, act *applyAction, c client.Client, desired client.Object, ao ...ApplyOption) (client.Object, error) {
var create = func() (client.Object, error) {
// execute ApplyOptions even the object doesn't exist
if err := executeApplyOptions(ctx, nil, desired, ao); err != nil {
if err := executeApplyOptions(act, nil, desired, ao); err != nil {
return nil, err
}
if err := addLastAppliedConfigAnnotation(desired); err != nil {
@@ -147,22 +191,44 @@ func createOrGetExisting(ctx context.Context, c client.Client, desired client.Ob
return existing, nil
}
func executeApplyOptions(ctx context.Context, existing, desired runtime.Object, aos []ApplyOption) error {
func executeApplyOptions(act *applyAction, existing, desired client.Object, aos []ApplyOption) error {
// if existing is nil, it means the object is going to be created.
// ApplyOption function should handle this situation carefully by itself.
for _, fn := range aos {
if err := fn(ctx, existing, desired); err != nil {
if err := fn(act, existing, desired); err != nil {
return errors.Wrap(err, "cannot apply ApplyOption")
}
}
return nil
}
// NotUpdateRenderHashEqual if the render hash of new object equal to the old hash, should not apply.
func NotUpdateRenderHashEqual() ApplyOption {
return func(act *applyAction, existing, desired client.Object) error {
if existing == nil || desired == nil {
return nil
}
newSt, ok := desired.(*unstructured.Unstructured)
if !ok {
return nil
}
oldSt := existing.(*unstructured.Unstructured)
if !ok {
return nil
}
if getRenderHash(existing) == getRenderHash(desired) {
*newSt = *oldSt
act.skipUpdate = true
}
return nil
}
}
// MustBeControllableBy requires that the new object is controllable by an
// object with the supplied UID. An object is controllable if its controller
// reference includes the supplied UID.
func MustBeControllableBy(u types.UID) ApplyOption {
return func(_ context.Context, existing, _ runtime.Object) error {
return func(_ *applyAction, existing, _ client.Object) error {
if existing == nil {
return nil
}
@@ -180,7 +246,7 @@ func MustBeControllableBy(u types.UID) ApplyOption {
// MustBeControllableByAny requires that the new object is controllable by any of the object with
// the supplied UID.
func MustBeControllableByAny(ctrlUIDs []types.UID) ApplyOption {
return func(_ context.Context, existing, _ runtime.Object) error {
return func(_ *applyAction, existing, _ client.Object) error {
if existing == nil || len(ctrlUIDs) == 0 {
return nil
}
@@ -206,3 +272,10 @@ func MustBeControllableByAny(ctrlUIDs []types.UID) ApplyOption {
return errors.Errorf("existing object is not controlled by any of UID %q", ctrlUIDs)
}
}
// MakeCustomApplyOption let user can generate applyOption that restrict change apply action.
func MakeCustomApplyOption(f func(existing, desired client.Object) error) ApplyOption {
return func(act *applyAction, existing, desired client.Object) error {
return f(existing, desired)
}
}

View File

@@ -98,7 +98,7 @@ func TestAPIApplicator(t *testing.T) {
args: args{
existing: existing,
ao: []ApplyOption{
func(ctx context.Context, existing, desired runtime.Object) error {
func(_ *applyAction, existing, desired client.Object) error {
return errFake
},
},
@@ -137,7 +137,7 @@ func TestAPIApplicator(t *testing.T) {
for caseName, tc := range cases {
t.Run(caseName, func(t *testing.T) {
a := &APIApplicator{
creator: creatorFn(func(_ context.Context, _ client.Client, _ client.Object, _ ...ApplyOption) (client.Object, error) {
creator: creatorFn(func(_ context.Context, _ *applyAction, _ client.Client, _ client.Object, _ ...ApplyOption) (client.Object, error) {
return tc.args.existing, tc.args.creatorErr
}),
patcher: patcherFn(func(c, m client.Object) (client.Patch, error) {
@@ -220,7 +220,7 @@ func TestCreator(t *testing.T) {
},
},
ao: []ApplyOption{
func(ctx context.Context, existing, desired runtime.Object) error {
func(_ *applyAction, existing, desired client.Object) error {
return errFake
},
},
@@ -236,7 +236,7 @@ func TestCreator(t *testing.T) {
args: args{
desired: desired,
ao: []ApplyOption{
func(ctx context.Context, existing, desired runtime.Object) error {
func(_ *applyAction, existing, desired client.Object) error {
return errFake
},
},
@@ -294,7 +294,8 @@ func TestCreator(t *testing.T) {
for caseName, tc := range cases {
t.Run(caseName, func(t *testing.T) {
result, err := createOrGetExisting(ctx, tc.c, tc.args.desired, tc.args.ao...)
act := new(applyAction)
result, err := createOrGetExisting(ctx, act, tc.c, tc.args.desired, tc.args.ao...)
if diff := cmp.Diff(tc.want.existing, result); diff != "" {
t.Errorf("\n%s\ncreateOrGetExisting(...): -want , +got \n%s\n", tc.reason, diff)
}
@@ -309,11 +310,10 @@ func TestCreator(t *testing.T) {
func TestMustBeControllableBy(t *testing.T) {
uid := types.UID("very-unique-string")
controller := true
ctx := context.TODO()
cases := map[string]struct {
reason string
current runtime.Object
current client.Object
u types.UID
want error
}{
@@ -356,7 +356,8 @@ func TestMustBeControllableBy(t *testing.T) {
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
ao := MustBeControllableBy(tc.u)
err := ao(ctx, tc.current, nil)
act := new(applyAction)
err := ao(act, tc.current, nil)
if diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nMustBeControllableBy(...)(...): -want error, +got error\n%s\n", tc.reason, diff)
}
@@ -368,7 +369,7 @@ func TestMustBeControllableByAny(t *testing.T) {
ctrlByAny := []types.UID{"owner1", "owner2"}
cases := map[string]struct {
reason string
current runtime.Object
current client.Object
want error
}{
"NoExistingObject": {
@@ -418,7 +419,8 @@ func TestMustBeControllableByAny(t *testing.T) {
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
ao := MustBeControllableByAny(ctrlByAny)
err := ao(context.TODO(), tc.current, nil)
act := new(applyAction)
err := ao(act, tc.current, nil)
if diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nMustBeControllableByAny(...)(...): -want error, +got error\n%s\n", tc.reason, diff)
}

View File

@@ -21,6 +21,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/client-go/util/flowcontrol"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
@@ -43,6 +44,7 @@ func (a *Args) SetConfig() error {
if err != nil {
return err
}
restConf.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(100, 200)
a.Config = restConf
return nil
}

View File

@@ -44,6 +44,7 @@ import (
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/util/flowcontrol"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
ocmclusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
ocmworkv1 "open-cluster-management.io/api/work/v1"
@@ -52,6 +53,7 @@ import (
"sigs.k8s.io/yaml"
oamcore "github.com/oam-dev/kubevela/apis/core.oam.dev"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
oamstandard "github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
velacue "github.com/oam-dev/kubevela/pkg/cue"
"github.com/oam-dev/kubevela/pkg/cue/model"
@@ -83,8 +85,10 @@ func InitBaseRestConfig() (Args, error) {
if err != nil && os.Getenv("IGNORE_KUBE_CONFIG") != "true" {
fmt.Println("get kubeConfig err", err)
os.Exit(1)
} else if err != nil {
return Args{}, err
}
restConf.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(100, 200)
return Args{
Config: restConf,
Schema: Scheme,
@@ -236,6 +240,51 @@ func RealtimePrintCommandOutput(cmd *exec.Cmd, logFile string) error {
return nil
}
// ClusterObject2Map convert ClusterObjectReference to a readable map
func ClusterObject2Map(refs []common.ClusterObjectReference) map[string]string {
clusterResourceRefTmpl := "Cluster: %s | Namespace: %s | GVK: %s/%s | Name: %s"
objs := make(map[string]string, len(refs))
for _, r := range refs {
if r.Cluster == "" {
r.Cluster = "local"
}
objs[r.Name] = fmt.Sprintf(clusterResourceRefTmpl, r.Cluster, r.Namespace, r.APIVersion, r.ResourceVersion, r.Name)
}
return objs
}
// AskToChooseOneAppliedResource will ask users to select one applied resource of the application if more than one
// resources is a map for component to applied resources
// return the selected ClusterObjectReference
func AskToChooseOneAppliedResource(resources []common.ClusterObjectReference) (*common.ClusterObjectReference, error) {
if len(resources) == 0 {
return nil, fmt.Errorf("no applied resources exist in the application")
}
if len(resources) == 1 {
return &resources[0], nil
}
opMap := ClusterObject2Map(resources)
var ops []string
for _, r := range opMap {
ops = append(ops, r)
}
prompt := &survey.Select{
Message: "You have multiple applied resources in your app. Please choose one:",
Options: ops,
}
var selectedRsc string
err := survey.AskOne(prompt, &selectedRsc)
if err != nil {
return nil, fmt.Errorf("choosing resource err %w", err)
}
for k, resource := range ops {
if selectedRsc == resource {
return &resources[k], nil
}
}
return nil, fmt.Errorf("choosing resource err %w", err)
}
// AskToChooseOneService will ask users to select one service of the application if more than one exidi
func AskToChooseOneService(svcNames []string) (string, error) {
if len(svcNames) == 0 {

View File

@@ -26,8 +26,12 @@ import (
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/cue/model"
"github.com/oam-dev/kubevela/pkg/cue/model/value"
"github.com/oam-dev/kubevela/pkg/oam/util"
@@ -48,6 +52,7 @@ type WorkflowContext struct {
store corev1.ConfigMap
components map[string]*ComponentManifest
vars *value.Value
modified bool
}
// GetComponent Get ComponentManifest from workflow context.
@@ -70,7 +75,11 @@ func (wf *WorkflowContext) PatchComponent(name string, patchValue *value.Value)
if err != nil {
return err
}
return component.Patch(patchValue)
if err := component.Patch(patchValue); err != nil {
return err
}
wf.modified = true
return nil
}
// GetVar get variable from workflow context.
@@ -87,7 +96,11 @@ func (wf *WorkflowContext) SetVar(v *value.Value, paths ...string) error {
if err := wf.vars.FillRaw(str, paths...); err != nil {
return err
}
return wf.vars.Error()
if err := wf.vars.Error(); err != nil {
return err
}
wf.modified = true
return nil
}
// MakeParameter make 'value' with interface{}
@@ -106,6 +119,9 @@ func (wf *WorkflowContext) MakeParameter(parameter interface{}) (*value.Value, e
// Commit the workflow context and persist it's content.
func (wf *WorkflowContext) Commit() error {
if !wf.modified {
return nil
}
if err := wf.writeToStore(); err != nil {
return err
}
@@ -244,35 +260,9 @@ func (comp *ComponentManifest) unmarshal(v string) error {
return nil
}
// NewContext new workflow context.
func NewContext(cli client.Client, ns, rev string) (Context, error) {
var (
ctx = context.Background()
manifestCm corev1.ConfigMap
)
if err := cli.Get(ctx, client.ObjectKey{
Namespace: ns,
Name: rev,
}, &manifestCm); err != nil {
return nil, errors.WithMessagef(err, "Get manifest ConfigMap %s/%s ", ns, rev)
}
wfCtx, err := newContext(cli, ns, rev)
if err != nil {
return nil, err
}
if err := wfCtx.LoadFromConfigMap(manifestCm); err != nil {
return nil, errors.WithMessagef(err, "load from ConfigMap %s/%s", ns, rev)
}
return wfCtx, wfCtx.Commit()
}
// NewEmptyContext new workflow context without initialize data.
func NewEmptyContext(cli client.Client, ns, app string) (Context, error) {
wfCtx, err := newContext(cli, ns, app)
// NewContext new workflow context without initialize data.
func NewContext(cli client.Client, ns, app string, appUID types.UID) (Context, error) {
wfCtx, err := newContext(cli, ns, app, appUID)
if err != nil {
return nil, err
}
@@ -280,13 +270,22 @@ func NewEmptyContext(cli client.Client, ns, app string) (Context, error) {
return wfCtx, wfCtx.Commit()
}
func newContext(cli client.Client, ns, app string) (*WorkflowContext, error) {
func newContext(cli client.Client, ns, app string, appUID types.UID) (*WorkflowContext, error) {
var (
ctx = context.Background()
store corev1.ConfigMap
)
store.Name = generateStoreName(app)
store.Namespace = ns
store.SetOwnerReferences([]metav1.OwnerReference{
{
APIVersion: v1beta1.SchemeGroupVersion.String(),
Kind: v1beta1.ApplicationKind,
Name: app,
UID: appUID,
Controller: pointer.BoolPtr(true),
},
})
if err := cli.Get(ctx, client.ObjectKey{Name: store.Name, Namespace: store.Namespace}, &store); err != nil {
if kerrors.IsNotFound(err) {
if err := cli.Create(ctx, &store); err != nil {
@@ -303,6 +302,7 @@ func newContext(cli client.Client, ns, app string) (*WorkflowContext, error) {
cli: cli,
store: store,
components: map[string]*ComponentManifest{},
modified: true,
}
var err error
wfCtx.vars, err = value.NewValue("", nil, "")

View File

@@ -259,14 +259,11 @@ func TestContext(t *testing.T) {
},
}
wfCtx, err := NewContext(cli, "default", "app-v1")
wfCtx, err := NewContext(cli, "default", "app-v1", "testuid")
assert.NilError(t, err)
err = wfCtx.Commit()
assert.NilError(t, err)
_, err = NewContext(cli, "default", "app-not-found")
assert.Equal(t, err != nil, true)
wfCtx, err = LoadContext(cli, "default", "app-v1")
assert.NilError(t, err)
err = wfCtx.Commit()
@@ -276,7 +273,7 @@ func TestContext(t *testing.T) {
_, err = LoadContext(cli, "default", "app-v1")
assert.Equal(t, err != nil, true)
wfCtx, err = NewEmptyContext(cli, "default", "app-v1")
wfCtx, err = NewContext(cli, "default", "app-v1", "testuid")
assert.NilError(t, err)
assert.Equal(t, len(wfCtx.GetComponents()), 0)
_, err = wfCtx.GetComponent("server")

View File

@@ -106,7 +106,7 @@ func mockContext(t *testing.T) wfContext.Context {
return nil
},
}
wfCtx, err := wfContext.NewEmptyContext(cli, "default", "v1")
wfCtx, err := wfContext.NewContext(cli, "default", "v1", "testuid")
require.NoError(t, err)
return wfCtx
}

View File

@@ -144,7 +144,7 @@ func (w *workflow) makeContext(appName string) (wfCtx wfContext.Context, err err
return
}
wfCtx, err = wfContext.NewEmptyContext(w.cli, w.app.Namespace, appName)
wfCtx, err = wfContext.NewContext(w.cli, w.app.Namespace, appName, w.app.GetUID())
if err != nil {
err = errors.WithMessage(err, "new context")
@@ -249,6 +249,10 @@ func (e *engine) steps(wfCtx wfContext.Context, taskRunners []wfTypes.TaskRunner
e.updateStepStatus(status)
if err := wfCtx.Commit(); err != nil {
return errors.WithMessage(err, "commit workflow context")
}
if status.Phase != common.WorkflowStepPhaseSucceeded {
if e.isDag() {
continue
@@ -256,10 +260,6 @@ func (e *engine) steps(wfCtx wfContext.Context, taskRunners []wfTypes.TaskRunner
return nil
}
if err := wfCtx.Commit(); err != nil {
return errors.WithMessage(err, "commit workflow context")
}
e.finishStep(operation)
if e.needStop() {
return nil

View File

@@ -20,6 +20,8 @@ import (
"context"
"encoding/json"
"github.com/oam-dev/kubevela/pkg/cue/model/value"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -332,10 +334,36 @@ var _ = Describe("Test Workflow", func() {
}},
})).Should(BeEquivalentTo(""))
})
It("step commit data without success", func() {
app, runners := makeTestCase([]oamcore.WorkflowStep{
{
Name: "s1",
Type: "wait-with-set-var",
},
{
Name: "s2",
Type: "success",
},
})
wf := NewWorkflow(app, k8sClient, common.WorkflowModeStep)
state, err := wf.ExecuteSteps(context.Background(), revision, runners)
Expect(err).ToNot(HaveOccurred())
Expect(state).Should(BeEquivalentTo(common.WorkflowStateExecuting))
Expect(app.Status.Workflow.Steps[0].Phase).Should(BeEquivalentTo(common.WorkflowStepPhaseRunning))
wfCtx, err := wfContext.LoadContext(k8sClient, app.Namespace, app.Name)
Expect(err).ToNot(HaveOccurred())
v, err := wfCtx.GetVar("saved")
Expect(err).ToNot(HaveOccurred())
saved, err := v.CueValue().Bool()
Expect(err).ToNot(HaveOccurred())
Expect(saved).Should(BeEquivalentTo(true))
})
})
func makeTestCase(steps []oamcore.WorkflowStep) (*oamcore.Application, []wfTypes.TaskRunner) {
app := &oamcore.Application{
ObjectMeta: metav1.ObjectMeta{UID: "test-uid"},
Spec: oamcore.ApplicationSpec{
Workflow: &oamcore.Workflow{
Steps: steps,
@@ -401,6 +429,16 @@ func makeRunner(name string, tpy string) wfTypes.TaskRunner {
Phase: common.WorkflowStepPhaseRunning,
}, &wfTypes.Operation{}, errors.New("error for test")
}
case "wait-with-set-var":
run = func(ctx wfContext.Context, options *wfTypes.TaskRunOptions) (common.WorkflowStepStatus, *wfTypes.Operation, error) {
v, _ := value.NewValue(`saved: true`, nil, "")
err := ctx.SetVar(v)
return common.WorkflowStepStatus{
Name: name,
Type: "wait-with-set-var",
Phase: common.WorkflowStepPhaseRunning,
}, &wfTypes.Operation{}, err
}
default:
run = func(ctx wfContext.Context, options *wfTypes.TaskRunOptions) (common.WorkflowStepStatus, *wfTypes.Operation, error) {

View File

@@ -84,7 +84,7 @@ func LoadApplication(namespace, appName string, c common.Args) (*v1beta1.Applica
return app, nil
}
// GetComponents will get oam components from Appfile.
// GetComponents will get oam components from v1beta1.Application.
func GetComponents(app *v1beta1.Application) []string {
var components []string
for _, cmp := range app.Spec.Components {

View File

@@ -24,6 +24,8 @@ import (
"text/template"
"time"
"github.com/oam-dev/kubevela/pkg/multicluster"
"github.com/fatih/color"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -40,73 +42,72 @@ import (
// NewLogsCommand creates `logs` command to tail logs of application
func NewLogsCommand(c common.Args, ioStreams util.IOStreams) *cobra.Command {
largs := &Args{C: c}
cmd := &cobra.Command{}
cmd.Use = "logs"
cmd.Short = "Tail logs for application"
cmd.Long = "Tail logs for application"
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if err := c.SetConfig(); err != nil {
return err
}
largs.C = c
return nil
}
cmd.RunE = func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
ioStreams.Errorf("please specify app name")
largs := &Args{Args: c}
cmd := &cobra.Command{
Use: "logs <appName>",
Short: "Tail logs for application in multicluster",
Long: "Tail logs for application in multicluster",
Args: cobra.ExactArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := c.SetConfig(); err != nil {
return err
}
largs.Args = c
largs.Args.Config.Wrap(multicluster.NewSecretModeMultiClusterRoundTripper)
return nil
}
env, err := GetFlagEnvOrCurrent(cmd, c)
if err != nil {
return err
}
app, err := appfile.LoadApplication(env.Namespace, args[0], c)
if err != nil {
return err
}
largs.App = app
largs.Env = env
ctx := context.Background()
if err := largs.Run(ctx, ioStreams); err != nil {
return err
}
return nil
}
cmd.Annotations = map[string]string{
types.TagCommandType: types.TypeApp,
},
RunE: func(cmd *cobra.Command, args []string) error {
app, err := appfile.LoadApplication(largs.Namespace, args[0], c)
if err != nil {
return err
}
largs.App = app
ctx := context.Background()
if err := largs.Run(ctx, ioStreams); err != nil {
return err
}
return nil
},
Annotations: map[string]string{
types.TagCommandType: types.TypeApp,
},
}
cmd.Flags().StringVarP(&largs.Output, "output", "o", "default", "output format for logs, support: [default, raw, json]")
cmd.Flags().StringVarP(&largs.Namespace, "namespace", "n", "default", "application namespace")
return cmd
}
// Args creates arguments for `logs` command
type Args struct {
Output string
Env *types.EnvMeta
C common.Args
App *v1beta1.Application
Output string
Args common.Args
Namespace string
App *v1beta1.Application
}
// Run refer to the implementation at https://github.com/oam-dev/stern/blob/master/stern/main.go
func (l *Args) Run(ctx context.Context, ioStreams util.IOStreams) error {
clientSet, err := kubernetes.NewForConfig(l.C.Config)
clientSet, err := kubernetes.NewForConfig(l.Args.Config)
if err != nil {
return err
}
compName, err := common.AskToChooseOneService(appfile.GetComponents(l.App))
appliedResources := l.App.Status.AppliedResources
selectedRes, err := common.AskToChooseOneAppliedResource(appliedResources)
if err != nil {
return err
}
ctx = multicluster.ContextWithClusterName(ctx, selectedRes.Cluster)
// TODO(wonderflow): we could get labels from service to narrow the pods scope selected
labelSelector := labels.Everything()
pod, err := regexp.Compile(compName + "-.*")
pod, err := regexp.Compile(selectedRes.Name + "-.*")
if err != nil {
return fmt.Errorf("fail to compile '%s' for logs query", compName+".*")
return fmt.Errorf("fail to compile '%s' for logs query", selectedRes.Name+".*")
}
container := regexp.MustCompile(".*")
namespace := l.Env.Namespace
namespace := selectedRes.Namespace
added, removed, err := stern.Watch(ctx, clientSet.CoreV1().Pods(namespace), pod, container, nil, stern.RUNNING, labelSelector)
if err != nil {
return err

View File

@@ -66,7 +66,7 @@ func main() {
"Determines the namespace in which the leader election configmap will be created.")
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
flag.StringVar(&healthAddr, "health-addr", ":9440", "The address the health endpoint binds to.")
flag.StringVar(&healthAddr, "health-addr", ":19440", "The address the health endpoint binds to.")
flag.Parse()
// setup logging

View File

@@ -0,0 +1,44 @@
# Build the manager binary
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.16-alpine as builder
WORKDIR /workspace
# Copy the Go Modules manifests
COPY ./tmp/go.mod go.mod
COPY ./tmp/go.sum go.sum
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download
# Copy the go source
COPY ./tmp/main.go main.go
COPY ./tmp/apis apis/
COPY ./tmp/pkg pkg/
COPY ./tmp/version version/
# Build
ARG TARGETARCH
ARG VERSION
ARG GITVERSION
RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
go build -a -ldflags "-s -w -X github.com/oam-dev/kubevela/version.VelaVersion=${VERSION:-undefined} -X github.com/oam-dev/kubevela/version.GitRevision=${GITVERSION:-undefined}" \
-o manager-${TARGETARCH} main.go
# Use alpine as base image due to the discussion in issue #1448
# You can replace distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
# Overwrite `BASE_IMAGE` by passing `--build-arg=BASE_IMAGE=gcr.io/distroless/static:nonroot`
ARG BASE_IMAGE
FROM ${BASE_IMAGE:-alpine:latest}
# This is required by daemon connnecting with cri
RUN apk add --no-cache ca-certificates bash
WORKDIR /
ARG TARGETARCH
COPY --from=builder /workspace/manager-${TARGETARCH} /usr/local/bin/manager
COPY ./tmp/entrypoint.sh /usr/local/bin/
ENTRYPOINT ["entrypoint.sh"]
CMD ["manager"]

View File

@@ -0,0 +1,213 @@
/*
Copyright 2021 The KubeVela 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.
*/
package e2e_multicluster_test
import (
"context"
"fmt"
"io/ioutil"
"strings"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/pkg/oam/util"
"sigs.k8s.io/yaml"
)
var _ = Describe("Test MultiClustet Rollout", func() {
Context("Test Runtime Cluster Rollout", func() {
var namespace string
var hubCtx context.Context
var workerCtx context.Context
var rollout v1alpha1.Rollout
var componentName string
var targetDeploy appsv1.Deployment
var sourceDeploy appsv1.Deployment
BeforeEach(func() {
hubCtx, workerCtx, namespace = initializeContextAndNamespace()
componentName = "hello-world-server"
})
AfterEach(func() {
cleanUpNamespace(hubCtx, workerCtx, namespace)
ns := v1.Namespace{}
Eventually(func() error { return k8sClient.Get(hubCtx, types.NamespacedName{Name: namespace}, &ns) }, 300*time.Second, 300*time.Millisecond).Should(util.NotFoundMatcher{})
})
verifySucceed := func(componentRevision string) {
By("check rollout status have succeed")
Eventually(func() error {
rolloutKey := types.NamespacedName{Namespace: namespace, Name: componentName}
if err := k8sClient.Get(workerCtx, rolloutKey, &rollout); err != nil {
return err
}
if rollout.Spec.TargetRevisionName != componentRevision {
return fmt.Errorf("rollout have not point to right targetRevision")
}
if rollout.Status.RollingState != v1alpha1.RolloutSucceedState {
return fmt.Errorf("error rollout status state %s", rollout.Status.RollingState)
}
compRevName := rollout.Spec.TargetRevisionName
deployKey := types.NamespacedName{Namespace: namespace, Name: compRevName}
if err := k8sClient.Get(workerCtx, deployKey, &targetDeploy); err != nil {
return err
}
if *targetDeploy.Spec.Replicas != *rollout.Spec.RolloutPlan.TargetSize {
return fmt.Errorf("targetDeploy replicas missMatch %d, %d", targetDeploy.Spec.Replicas, rollout.Spec.RolloutPlan.TargetSize)
}
if targetDeploy.Status.UpdatedReplicas != *targetDeploy.Spec.Replicas {
return fmt.Errorf("update not finish")
}
if len(targetDeploy.OwnerReferences) != 1 {
return fmt.Errorf("workload ownerReference missMatch")
}
// guarantee rollout's owners and workload's owners are same
if targetDeploy.OwnerReferences[0].Kind != rollout.OwnerReferences[0].Kind ||
targetDeploy.OwnerReferences[0].Name != rollout.OwnerReferences[0].Name {
return fmt.Errorf("workload ownerReference missMatch")
}
if rollout.Status.LastSourceRevision == "" {
return nil
}
deployKey = types.NamespacedName{Namespace: namespace, Name: rollout.Status.LastSourceRevision}
if err := k8sClient.Get(workerCtx, deployKey, &sourceDeploy); err == nil || !apierrors.IsNotFound(err) {
return fmt.Errorf("source deploy still exist")
}
return nil
}, time.Second*360, 300*time.Millisecond).Should(BeNil())
}
It("Test Rollout whole feature in runtime cluster ", func() {
app := &v1beta1.Application{}
appYaml, err := ioutil.ReadFile("./testdata/app/app-rollout-envbinding.yaml")
Expect(err).Should(Succeed())
Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
app.SetNamespace(namespace)
err = k8sClient.Create(hubCtx, app)
Expect(err).Should(Succeed())
verifySucceed(componentName + "-v1")
By("update application to v2")
checkApp := &v1beta1.Application{}
Eventually(func() error {
if err := k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: app.Name}, checkApp); err != nil {
return err
}
checkApp.Spec.Components[0].Properties.Raw = []byte(`{"image": "stefanprodan/podinfo:5.0.2"}`)
if err := k8sClient.Update(hubCtx, checkApp); err != nil {
return err
}
return nil
}, 500*time.Millisecond, 30*time.Second).Should(BeNil())
verifySucceed(componentName + "-v2")
By("revert to v1, should guarantee compRev v1 still exist")
appYaml, err = ioutil.ReadFile("./testdata/app/revert-app-envbinding.yaml")
Expect(err).Should(Succeed())
Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: app.Name}, checkApp)).Should(BeNil())
revertApp := &v1beta1.Application{}
Expect(yaml.Unmarshal([]byte(appYaml), revertApp)).Should(Succeed())
revertApp.SetNamespace(namespace)
revertApp.SetResourceVersion(checkApp.ResourceVersion)
Eventually(func() error {
if err := k8sClient.Update(hubCtx, revertApp); err != nil {
return err
}
return nil
}, 500*time.Millisecond, 30*time.Second).Should(BeNil())
verifySucceed(componentName + "-v1")
})
It("Test Rollout with health check policy, guarantee health scope controller work ", func() {
app := &v1beta1.Application{}
appYaml, err := ioutil.ReadFile("./testdata/app/multi-cluster-health-policy.yaml")
Expect(err).Should(Succeed())
Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
app.SetNamespace(namespace)
err = k8sClient.Create(hubCtx, app)
Expect(err).Should(Succeed())
verifySucceed(componentName + "-v1")
Eventually(func() error {
checkApp := v1beta1.Application{}
if err := k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: app.Name}, &checkApp); err != nil {
return err
}
if len(checkApp.Status.Services) == 0 {
return fmt.Errorf("app status service haven't write back")
}
compStatus := checkApp.Status.Services[0]
if compStatus.Env != "staging" {
return fmt.Errorf("comp status env miss-match")
}
if !compStatus.Healthy {
return fmt.Errorf("comp status not healthy")
}
if !strings.Contains(compStatus.Message, "Ready:2/2") {
return fmt.Errorf("comp status workload check don't work")
}
return nil
}, 300*time.Millisecond, 30*time.Second).Should(BeNil())
By("update application to v2")
checkApp := &v1beta1.Application{}
Eventually(func() error {
if err := k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: app.Name}, checkApp); err != nil {
return err
}
checkApp.Spec.Components[0].Properties.Raw = []byte(`{"image": "stefanprodan/podinfo:5.0.2"}`)
if err := k8sClient.Update(hubCtx, checkApp); err != nil {
return err
}
return nil
}, 500*time.Millisecond, 30*time.Second).Should(BeNil())
verifySucceed(componentName + "-v2")
Eventually(func() error {
// Note: KubeVela will only check the workload of the target revision
checkApp := v1beta1.Application{}
if err := k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: app.Name}, &checkApp); err != nil {
return err
}
if len(checkApp.Status.Services) == 0 {
return fmt.Errorf("app status service haven't write back")
}
compStatus := checkApp.Status.Services[0]
if compStatus.Env != "staging" {
return fmt.Errorf("comp status env miss-match")
}
if !compStatus.Healthy {
return fmt.Errorf("comp status not healthy")
}
if !strings.Contains(compStatus.Message, "Ready:2/2") {
return fmt.Errorf("comp status workload check don't work")
}
return nil
}, 300*time.Millisecond, 30*time.Second).Should(BeNil())
})
})
})

View File

@@ -0,0 +1,37 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: example-app
namespace: default
spec:
components:
- name: hello-world-server
type: webservice
properties:
image: stefanprodan/podinfo:4.0.3
traits:
- type: rollout
properties:
targetSize: 2
rolloutBatches:
- replicas: 1
- replicas: 1
policies:
- name: example-multi-env-policy
type: env-binding
properties:
envs:
- name: staging
placement: # selecting the cluster to deploy to
clusterSelector:
name: cluster-worker
workflow:
steps:
# deploy to staging env
- name: deploy-staging
type: deploy2env
properties:
policy: example-multi-env-policy
env: staging

View File

@@ -0,0 +1,48 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: example-app-rollout
namespace: default
spec:
components:
- name: hello-world-server
type: webservice
properties:
image: crccheck/hello-world
port: 8000
type: webservice
traits:
- type: rollout
properties:
targetSize: 2
rolloutBatches:
- replicas: 1
- replicas: 1
policies:
- name: example-multi-env-policy
type: env-binding
properties:
envs:
- name: staging
placement: # 选择要部署的集群,并执行默认的发布策略
clusterSelector:
name: cluster-worker
- name: health-policy-demo
type: health
properties:
probeInterval: 5
probeTimeout: 10
workflow:
steps:
# 部署到预发环境中
- name: deploy-staging
type: deploy2env
properties:
policy: example-multi-env-policy
env: staging

View File

@@ -0,0 +1,38 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: example-app
namespace: default
spec:
components:
- name: hello-world-server
type: webservice
properties:
image: stefanprodan/podinfo:5.0.2
traits:
- type: rollout
properties:
targetRevision: hello-world-server-v1
targetSize: 2
rolloutBatches:
- replicas: 1
- replicas: 1
policies:
- name: example-multi-env-policy
type: env-binding
properties:
envs:
- name: staging
placement: # selecting the cluster to deploy to
clusterSelector:
name: cluster-worker
workflow:
steps:
# deploy to staging env
- name: deploy-staging
type: deploy2env
properties:
policy: example-multi-env-policy
env: staging

View File

@@ -18,18 +18,20 @@ package controllers_test
import (
"context"
"encoding/json"
"fmt"
"time"
"sigs.k8s.io/yaml"
v1 "k8s.io/api/apps/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/utils/common"
@@ -113,10 +115,22 @@ var _ = Describe("rollout related e2e-test,rollout trait test", func() {
return fmt.Errorf("error rollout status state %s", rollout.Status.RollingState)
}
compRevName = rollout.Spec.TargetRevisionName
if rollout.GetAnnotations() == nil || rollout.GetAnnotations()[oam.AnnotationWorkloadName] != componentRevision {
return fmt.Errorf("target workload name annotation missmatch want %s acctually %s",
rollout.GetAnnotations()[oam.AnnotationWorkloadName], componentRevision)
}
deployKey := types.NamespacedName{Namespace: namespaceName, Name: compRevName}
if err := k8sClient.Get(ctx, deployKey, &targerDeploy); err != nil {
return err
}
gvkStr := rollout.GetAnnotations()[oam.AnnotationWorkloadGVK]
gvk := map[string]string{}
if err := json.Unmarshal([]byte(gvkStr), &gvk); err != nil {
return err
}
if gvk["apiVersion"] != "apps/v1" || gvk["kind"] != "Deployment" {
return fmt.Errorf("error targetWorkload gvk")
}
if *targerDeploy.Spec.Replicas != *rollout.Spec.RolloutPlan.TargetSize {
return fmt.Errorf("targetDeploy replicas missMatch %d, %d", targerDeploy.Spec.Replicas, rollout.Spec.RolloutPlan.TargetSize)
}
@@ -139,12 +153,13 @@ var _ = Describe("rollout related e2e-test,rollout trait test", func() {
return fmt.Errorf("source deploy still exist")
}
return nil
}, time.Second*360, 300*time.Millisecond).Should(BeNil())
}, time.Second*60, 300*time.Millisecond).Should(BeNil())
}
BeforeEach(func() {
By("Start to run a test, init whole env")
namespaceName = randomNamespaceName("rollout-trait-e2e-test")
app = v1beta1.Application{}
createNamespace()
createAllDef()
componentName = "express-server"
@@ -202,7 +217,7 @@ var _ = Describe("rollout related e2e-test,rollout trait test", func() {
if err = k8sClient.Get(ctx, appKey, checkApp); err != nil {
return err
}
checkApp.Spec.Components[0].Traits[0].Properties.Raw = []byte(`{"targetRevision":"express-server-v2"}`)
checkApp.Spec.Components[0].Traits[0].Properties.Raw = []byte(`{"targetRevision":"express-server-v2","firstBatchReplicas":1,"secondBatchReplicas":1}`)
if err = k8sClient.Update(ctx, checkApp); err != nil {
return err
}
@@ -305,6 +320,95 @@ var _ = Describe("rollout related e2e-test,rollout trait test", func() {
return nil
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
})
It("rollout scale up adnd down without rollout batches", func() {
By("first scale operation")
Expect(common.ReadYamlToObject("testdata/rollout/deployment/application.yaml", &app)).Should(BeNil())
app.Namespace = namespaceName
Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
verifySuccess("express-server-v1")
By("scale again to targetSize 4")
appKey := types.NamespacedName{Namespace: namespaceName, Name: app.Name}
checkApp := &v1beta1.Application{}
Eventually(func() error {
if err = k8sClient.Get(ctx, appKey, checkApp); err != nil {
return err
}
// scale up without rollout batches, test rollout controller will fill default batches
checkApp.Spec.Components[0].Traits[0].Properties.Raw =
[]byte(`{"targetSize":4}`)
if err = k8sClient.Update(ctx, checkApp); err != nil {
return err
}
return nil
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
Eventually(func() error {
checkRollout := v1alpha1.Rollout{}
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespaceName, Name: componentName}, &checkRollout); err != nil {
return err
}
if *checkRollout.Spec.RolloutPlan.TargetSize != 4 {
return fmt.Errorf("rollout targetSize haven't update")
}
if len(checkRollout.Spec.RolloutPlan.RolloutBatches) != 1 {
return fmt.Errorf("fail to fill rollout batches")
}
if checkRollout.Spec.RolloutPlan.RolloutBatches[0].Replicas != intstr.FromInt(2) {
return fmt.Errorf("fill rollout batches missmatch")
}
return nil
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
verifySuccess("express-server-v1")
checkApp = &v1beta1.Application{}
By("update application upgrade to v2")
Eventually(func() error {
if err = k8sClient.Get(ctx, appKey, checkApp); err != nil {
return err
}
checkApp.Spec.Components[0].Properties.Raw = []byte(`{"image":"stefanprodan/podinfo:4.0.3","cpu":"0.1"}`)
checkApp.Spec.Components[0].Traits[0].Properties.Raw =
[]byte(`{"firstBatchReplicas":2,"secondBatchReplicas":2,"targetSize":4}`)
if err = k8sClient.Update(ctx, checkApp); err != nil {
return err
}
return nil
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
verifySuccess("express-server-v2")
By("scale down to targetSize 2")
appKey = types.NamespacedName{Namespace: namespaceName, Name: app.Name}
checkApp = &v1beta1.Application{}
Eventually(func() error {
if err = k8sClient.Get(ctx, appKey, checkApp); err != nil {
return err
}
// scale down without rollout batches, test rollout controller will fill default batches
checkApp.Spec.Components[0].Traits[0].Properties.Raw =
[]byte(`{"targetSize":2}`)
if err = k8sClient.Update(ctx, checkApp); err != nil {
return err
}
return nil
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
Eventually(func() error {
checkRollout := v1alpha1.Rollout{}
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespaceName, Name: componentName}, &checkRollout); err != nil {
return err
}
if *checkRollout.Spec.RolloutPlan.TargetSize != 2 {
return fmt.Errorf("rollout targetSize haven't update")
}
if len(checkRollout.Spec.RolloutPlan.RolloutBatches) != 1 {
return fmt.Errorf("fail to fill rollout batches")
}
if checkRollout.Spec.RolloutPlan.RolloutBatches[0].Replicas != intstr.FromInt(2) {
return fmt.Errorf("fill rollout batches missmatch")
}
return nil
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
verifySuccess("express-server-v2")
})
})
const (
@@ -381,9 +485,11 @@ spec:
componentName: context.name
rolloutPlan: {
rolloutStrategy: "DecreaseFirst"
rolloutBatches:[
if parameter.firstBatchReplicas != _|_ && parameter.secondBatchReplicas != _|_ {
rolloutBatches:[
{ replicas: parameter.firstBatchReplicas},
{ replicas: parameter.secondBatchReplicas}]
}
targetSize: parameter.targetSize
if parameter["batchPartition"] != _|_ {
batchPartition: parameter.batchPartition
@@ -395,8 +501,8 @@ spec:
parameter: {
targetRevision: *context.revision|string
targetSize: *2|int
firstBatchReplicas: *1|int
secondBatchReplicas: *1|int
firstBatchReplicas?: int
secondBatchReplicas?: int
batchPartition?: int
}`
)

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