mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-28 16:50:29 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
066c448c1a | ||
|
|
8de80ebdb2 | ||
|
|
855cbfe3ec | ||
|
|
162534b611 | ||
|
|
6bd5d8e6e2 | ||
|
|
22079aacd3 | ||
|
|
b2329d548d | ||
|
|
9152c15a88 | ||
|
|
73b3d3106b | ||
|
|
237c71d94e | ||
|
|
2200d199f3 | ||
|
|
d083039a32 | ||
|
|
5e6be649c1 | ||
|
|
706a65beae | ||
|
|
d21a337dd7 | ||
|
|
b53f4f4fdd | ||
|
|
24970cd990 |
6
.github/workflows/e2e-multicluster-test.yml
vendored
6
.github/workflows/e2e-multicluster-test.yml
vendored
@@ -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
5
.gitignore
vendored
@@ -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
|
||||
12
Makefile
12
Makefile
@@ -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
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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}}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: {}
|
||||
|
||||
|
||||
113
charts/vela-core/templates/defwithtemplate/nocalhost.yaml
Normal file
113
charts/vela-core/templates/defwithtemplate/nocalhost.yaml
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: {}
|
||||
|
||||
|
||||
113
charts/vela-minimal/templates/defwithtemplate/nocalhost.yaml
Normal file
113
charts/vela-minimal/templates/defwithtemplate/nocalhost.yaml
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
157
docs/examples/nocalhost/nocalhost.yaml
Normal file
157
docs/examples/nocalhost/nocalhost.yaml
Normal 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
|
||||
130
docs/examples/obervability/application-observability.yaml
Normal file
130
docs/examples/obervability/application-observability.yaml
Normal 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
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
4
go.mod
@@ -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
16
go.sum
@@ -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=
|
||||
|
||||
12
hack/e2e/build_runtime_rollout.sh
Executable file
12
hack/e2e/build_runtime_rollout.sh
Executable 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(""))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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"}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(""))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: _|_
|
||||
`)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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, "")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
44
runtime/rollout/e2e/Dockerfile.e2e
Normal file
44
runtime/rollout/e2e/Dockerfile.e2e
Normal 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"]
|
||||
213
test/e2e-multicluster-test/multicluster_rollout_test.go
Normal file
213
test/e2e-multicluster-test/multicluster_rollout_test.go
Normal 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())
|
||||
})
|
||||
})
|
||||
})
|
||||
37
test/e2e-multicluster-test/testdata/app/app-rollout-envbinding.yaml
vendored
Normal file
37
test/e2e-multicluster-test/testdata/app/app-rollout-envbinding.yaml
vendored
Normal 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
|
||||
48
test/e2e-multicluster-test/testdata/app/multi-cluster-health-policy.yaml
vendored
Normal file
48
test/e2e-multicluster-test/testdata/app/multi-cluster-health-policy.yaml
vendored
Normal 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
|
||||
38
test/e2e-multicluster-test/testdata/app/revert-app-envbinding.yaml
vendored
Normal file
38
test/e2e-multicluster-test/testdata/app/revert-app-envbinding.yaml
vendored
Normal 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
|
||||
@@ -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
Reference in New Issue
Block a user