mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-02 17:50:58 +00:00
Compare commits
51 Commits
v1.5.0-alp
...
v1.5.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ab0b503c5 | ||
|
|
85e168fea7 | ||
|
|
189d74e87b | ||
|
|
8ec0209026 | ||
|
|
c3a7209fa7 | ||
|
|
564cba9aac | ||
|
|
f7c21df915 | ||
|
|
0b9c7f66c0 | ||
|
|
54867c50d8 | ||
|
|
16d7a4b4f4 | ||
|
|
35ae4e5ef5 | ||
|
|
853f44cf61 | ||
|
|
ca2a90a097 | ||
|
|
d110e97d68 | ||
|
|
741559c8e0 | ||
|
|
25b0cb8ee1 | ||
|
|
96ece000dc | ||
|
|
67f3f2747a | ||
|
|
5890b58aea | ||
|
|
68967f7af8 | ||
|
|
68a9565a1f | ||
|
|
8aaf526877 | ||
|
|
957302cb9d | ||
|
|
285a5cce18 | ||
|
|
d386b64ea2 | ||
|
|
181bc926f6 | ||
|
|
ab6c1a57eb | ||
|
|
e37b0276c8 | ||
|
|
fd784e291c | ||
|
|
16dfc1bf8a | ||
|
|
f876a0b8f8 | ||
|
|
60129e0f94 | ||
|
|
e747eae779 | ||
|
|
b24e7523d8 | ||
|
|
a519a6c89d | ||
|
|
ace23f1c6f | ||
|
|
557f7197b5 | ||
|
|
01737d62b0 | ||
|
|
945852284f | ||
|
|
eed081fd6f | ||
|
|
203a7dfbda | ||
|
|
cba8cb4c94 | ||
|
|
1fc65f56bf | ||
|
|
c29e980e8a | ||
|
|
e52ae78bd7 | ||
|
|
930d866e09 | ||
|
|
3159da0bb3 | ||
|
|
8571aa76ef | ||
|
|
072b80f6c6 | ||
|
|
cde76989a0 | ||
|
|
3b3898bf71 |
63
.github/workflows/apiserver-test.yaml
vendored
63
.github/workflows/apiserver-test.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: APIServer Unit Test & E2E Test
|
||||
name: VelaUX APIServer Test
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
steps:
|
||||
- name: Detect No-op Changes
|
||||
id: noop
|
||||
uses: fkirc/skip-duplicate-actions@v3.3.0
|
||||
uses: fkirc/skip-duplicate-actions@v4.0.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]'
|
||||
@@ -53,8 +53,58 @@ jobs:
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSION }}"
|
||||
fi
|
||||
|
||||
|
||||
apiserver-unit-tests:
|
||||
runs-on: ubuntu-20.04
|
||||
needs: detect-noop
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Cache Go Dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .work/pkg
|
||||
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-pkg-
|
||||
|
||||
- name: Install ginkgo
|
||||
run: |
|
||||
sudo apt-get install -y golang-ginkgo-dev
|
||||
|
||||
- name: Start MongoDB
|
||||
uses: supercharge/mongodb-github-action@1.7.0
|
||||
with:
|
||||
mongodb-version: '5.0'
|
||||
|
||||
- name: install Kubebuilder
|
||||
uses: RyanSiu1995/kubebuilder-action@v1.2
|
||||
with:
|
||||
version: 3.1.0
|
||||
kubebuilderOnly: false
|
||||
kubernetesVersion: v1.21.2
|
||||
|
||||
- name: Run api server unit test
|
||||
run: make unit-test-apiserver
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ./coverage.txt
|
||||
flags: apiserver-unittests
|
||||
name: codecov-umbrella
|
||||
|
||||
apiserver-e2e-tests:
|
||||
runs-on: aliyun
|
||||
needs: [ detect-noop,set-k8s-matrix ]
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
@@ -99,9 +149,6 @@ jobs:
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }}
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
- name: Run api server unit test
|
||||
run: make unit-test-apiserver
|
||||
|
||||
- name: Load Image to kind cluster
|
||||
run: make kind-load
|
||||
@@ -131,8 +178,8 @@ jobs:
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./coverage.txt,/tmp/e2e_apiserver_test.out
|
||||
flags: apiserver-unittests
|
||||
files: /tmp/e2e_apiserver_test.out
|
||||
flags: apiserver-e2etests
|
||||
name: codecov-umbrella
|
||||
|
||||
- name: Clean e2e profile
|
||||
|
||||
2
.github/workflows/e2e-multicluster-test.yml
vendored
2
.github/workflows/e2e-multicluster-test.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
steps:
|
||||
- name: Detect No-op Changes
|
||||
id: noop
|
||||
uses: fkirc/skip-duplicate-actions@v3.3.0
|
||||
uses: fkirc/skip-duplicate-actions@v4.0.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]'
|
||||
|
||||
2
.github/workflows/e2e-rollout-test.yml
vendored
2
.github/workflows/e2e-rollout-test.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
steps:
|
||||
- name: Detect No-op Changes
|
||||
id: noop
|
||||
uses: fkirc/skip-duplicate-actions@v3.3.0
|
||||
uses: fkirc/skip-duplicate-actions@v4.0.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]'
|
||||
|
||||
2
.github/workflows/e2e-test.yml
vendored
2
.github/workflows/e2e-test.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
steps:
|
||||
- name: Detect No-op Changes
|
||||
id: noop
|
||||
uses: fkirc/skip-duplicate-actions@v3.3.0
|
||||
uses: fkirc/skip-duplicate-actions@v4.0.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]'
|
||||
|
||||
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
steps:
|
||||
- name: Detect No-op Changes
|
||||
id: noop
|
||||
uses: fkirc/skip-duplicate-actions@v3.3.0
|
||||
uses: fkirc/skip-duplicate-actions@v4.0.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]'
|
||||
|
||||
2
.github/workflows/unit-test.yml
vendored
2
.github/workflows/unit-test.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
steps:
|
||||
- name: Detect No-op Changes
|
||||
id: noop
|
||||
uses: fkirc/skip-duplicate-actions@v3.3.0
|
||||
uses: fkirc/skip-duplicate-actions@v4.0.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]'
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
# Binaries for programs and plugins
|
||||
# Binaries for programs and docgen
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
@@ -51,3 +51,4 @@ git-page/
|
||||
|
||||
# e2e rollout runtime image build
|
||||
runtime/rollout/e2e/tmp
|
||||
vela.json
|
||||
|
||||
@@ -39,5 +39,5 @@ RUN apk add --no-cache ca-certificates bash expat
|
||||
WORKDIR /
|
||||
|
||||
ARG TARGETARCH
|
||||
COPY --from=builder /workspace/vela-${TARGETARCH} /vela
|
||||
ENTRYPOINT ["/vela"]
|
||||
COPY --from=builder /workspace/vela-${TARGETARCH} /bin/vela
|
||||
ENTRYPOINT ["/bin/vela"]
|
||||
|
||||
2
Makefile
2
Makefile
@@ -14,7 +14,7 @@ test: vet lint staticcheck unit-test-core test-cli-gen
|
||||
|
||||
test-cli-gen:
|
||||
mkdir -p ./bin/doc
|
||||
go run ./hack/docgen/gen.go ./bin/doc
|
||||
go run ./hack/docgen/cli/gen.go ./bin/doc
|
||||
unit-test-core:
|
||||
go test -coverprofile=coverage.txt $(shell go list ./pkg/... ./cmd/... ./apis/... | grep -v apiserver | grep -v applicationconfiguration)
|
||||
go test $(shell go list ./references/... | grep -v apiserver)
|
||||
|
||||
@@ -420,6 +420,8 @@ const (
|
||||
WorkflowStepPhaseStopped WorkflowStepPhase = "stopped"
|
||||
// WorkflowStepPhaseRunning will make the controller continue the workflow.
|
||||
WorkflowStepPhaseRunning WorkflowStepPhase = "running"
|
||||
// WorkflowStepPhasePending will make the controller wait for the step to run.
|
||||
WorkflowStepPhasePending WorkflowStepPhase = "pending"
|
||||
)
|
||||
|
||||
// DefinitionType describes the type of DefinitionRevision.
|
||||
|
||||
@@ -165,6 +165,7 @@ type Capability struct {
|
||||
Center string `json:"center,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Example string `json:"example,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Category CapabilityCategory `json:"category,omitempty"`
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ var DefaultKubeVelaNS = "vela-system"
|
||||
const (
|
||||
// AnnoDefinitionDescription is the annotation which describe what is the capability used for in a WorkloadDefinition/TraitDefinition Object
|
||||
AnnoDefinitionDescription = "definition.oam.dev/description"
|
||||
// AnnoDefinitionExampleURL is the annotation which describe url of usage examples of the capability, it will be loaded in documentation generate.
|
||||
AnnoDefinitionExampleURL = "definition.oam.dev/example-url"
|
||||
// AnnoDefinitionAlias is the annotation for definition alias
|
||||
AnnoDefinitionAlias = "definition.oam.dev/alias"
|
||||
// AnnoDefinitionIcon is the annotation which describe the icon url
|
||||
@@ -70,6 +72,8 @@ const (
|
||||
AnnoIngressControllerHTTPSPort = "ingress.controller/https-port"
|
||||
// AnnoIngressControllerHTTPPort define ingress controller listen port for http
|
||||
AnnoIngressControllerHTTPPort = "ingress.controller/http-port"
|
||||
// AnnoIngressControllerHost define ingress controller externally host
|
||||
AnnoIngressControllerHost = "ingress.controller/host"
|
||||
// LabelConfigType is the label for config type
|
||||
LabelConfigType = "config.oam.dev/type"
|
||||
// LabelConfigCatalog is the label for config catalog
|
||||
|
||||
@@ -6,6 +6,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply components of an application in parallel for your workflow steps
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-application-in-parallel
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -4,8 +4,9 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply application for your workflow steps
|
||||
definition.oam.dev/description: Apply application for your workflow steps, it has no arguments, should be used for custom steps before or after application applied.
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-application
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -22,9 +22,9 @@ spec:
|
||||
cluster: parameter.cluster
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the value of the object
|
||||
// +usage=Specify Kubernetes native resource object to be applied
|
||||
value: {...}
|
||||
// +usage=Specify the cluster of the object
|
||||
// +usage=The cluster you want to apply the resource to, default is the current control plane cluster
|
||||
cluster: *"" | string
|
||||
}
|
||||
|
||||
|
||||
44
charts/vela-core/templates/defwithtemplate/apply-once.yaml
Normal file
44
charts/vela-core/templates/defwithtemplate/apply-once.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/apply-once.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Allow configuration drift for applied resources, delivery the resource without continuously reconciliation.
|
||||
name: apply-once
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
#ApplyOnceStrategy: {
|
||||
// +usage=Specify the path of the resource that allow configuration drift
|
||||
path: [...string]
|
||||
}
|
||||
#ApplyOncePolicyRule: {
|
||||
// +usage=Specify how to select the targets of the rule
|
||||
selector?: #ResourcePolicyRuleSelector
|
||||
// +usage=Specify the strategy for configuring the resource level configuration drift behaviour
|
||||
strategy: #ApplyOnceStrategy
|
||||
}
|
||||
#ResourcePolicyRuleSelector: {
|
||||
// +usage=Select resources by component names
|
||||
componentNames?: [...string]
|
||||
// +usage=Select resources by component types
|
||||
componentTypes?: [...string]
|
||||
// +usage=Select resources by oamTypes (COMPONENT or TRAIT)
|
||||
oamTypes?: [...string]
|
||||
// +usage=Select resources by trait types
|
||||
traitTypes?: [...string]
|
||||
// +usage=Select resources by resource types (like Deployment)
|
||||
resourceTypes?: [...string]
|
||||
// +usage=Select resources by their names
|
||||
resourceNames?: [...string]
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Whether to enable apply-once for the whole application
|
||||
enable: *false | bool
|
||||
// +usage=Specify the rules for configuring apply-once policy in resource level
|
||||
rules?: [...#ApplyOncePolicyRule]
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply remaining components and traits
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-remaining
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -4,13 +4,13 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
custom.definition.oam.dev/alias.config.oam.dev: Image Registry
|
||||
alias.config.oam.dev: Image Registry
|
||||
definition.oam.dev/description: Config information to authenticate image registry
|
||||
labels:
|
||||
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
|
||||
custom.definition.oam.dev/multi-cluster.config.oam.dev: "true"
|
||||
custom.definition.oam.dev/type.config.oam.dev: image-registry
|
||||
catalog.config.oam.dev: velacore-config
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
multi-cluster.config.oam.dev: "true"
|
||||
type.config.oam.dev: image-registry
|
||||
name: config-image-registry
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: check or install depends-on Application
|
||||
definition.oam.dev/description: Wait for the specified Application to complete.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: depends-on-app
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Deploy cloud resource and bind secret to clusters
|
||||
definition.oam.dev/description: Deploy cloud resource and deliver secret to multi clusters.
|
||||
name: deploy-cloud-resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Deploy components with policies.
|
||||
definition.oam.dev/description: A powerful and unified deploy step for components multi-cluster delivery with policies.
|
||||
name: deploy
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
@@ -21,9 +21,9 @@ spec:
|
||||
ignoreTerraformComponent: parameter.ignoreTerraformComponent
|
||||
}
|
||||
parameter: {
|
||||
//+usage=If set false, the workflow will be suspend before this step.
|
||||
//+usage=If set to false, the workflow will suspend automatically before this step, default to be true.
|
||||
auto: *true | bool
|
||||
//+usage=Declare the policies used for this step.
|
||||
//+usage=Declare the policies that used for this deployment. If not specified, the components will be deployed to the hub cluster.
|
||||
policies?: [...string]
|
||||
//+usage=Maximum number of concurrent delivered components.
|
||||
parallelism: *5 | int
|
||||
|
||||
@@ -6,6 +6,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Deploy env binding component to target env
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: deploy2env
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -6,6 +6,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Deploy application to runtime clusters
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: deploy2runtime
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -22,7 +22,7 @@ spec:
|
||||
traits?: [...{
|
||||
type: string
|
||||
properties?: {...}
|
||||
// +usage=Specify if the trait shoued be remove, default false
|
||||
// +usage=Specify if the trait should be remove, default false
|
||||
disable: *false | bool
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Export data to config map for your workflow steps
|
||||
definition.oam.dev/description: Export data to specified Kubernetes ConfigMap in your workflow.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: export2config
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Export data to secret for your workflow steps
|
||||
definition.oam.dev/description: Export data to Kubernetes Secret in your workflow.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: export2secret
|
||||
@@ -46,7 +46,7 @@ spec:
|
||||
type?: string
|
||||
// +usage=Specify the data of secret
|
||||
data: {}
|
||||
// +usage=Specify the cluster of the config map
|
||||
// +usage=Specify the cluster of the secret
|
||||
cluster: *"" | string
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,20 @@ spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
outputs: service: {
|
||||
apiVersion: "v1"
|
||||
kind: "Service"
|
||||
metadata: name: context.name
|
||||
metadata: name: context.name
|
||||
metadata: annotations: parameter.annotations
|
||||
spec: {
|
||||
selector: "app.oam.dev/component": context.name
|
||||
ports: [
|
||||
for p in parameter.port {
|
||||
name: "port-" + strconv.FormatInt(p, 10)
|
||||
port: p
|
||||
targetPort: p
|
||||
},
|
||||
@@ -32,7 +38,33 @@ spec:
|
||||
parameter: {
|
||||
// +usage=Specify the exposion ports
|
||||
port: [...int]
|
||||
// +usage=Specify the annotaions of the exposed service
|
||||
annotations: [string]: string
|
||||
// +usage=Specify what kind of Service you want. options: "ClusterIP","NodePort","LoadBalancer","ExternalName"
|
||||
type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
message: *"" | string
|
||||
service: context.outputs.service
|
||||
if service.spec.type == "ClusterIP" {
|
||||
message: "ClusterIP: \(service.spec.clusterIP)"
|
||||
}
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
if !isHealth {
|
||||
message: "ExternalIP: Pending"
|
||||
}
|
||||
if isHealth {
|
||||
message: "ExternalIP: \(status.loadBalancer.ingress[0].ip)"
|
||||
}
|
||||
}
|
||||
healthPolicy: |-
|
||||
isHealth: *true | bool
|
||||
service: context.outputs.service
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/garbage-collect.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Configure the garbage collect behaviour for the application.
|
||||
name: garbage-collect
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
#GarbageCollectPolicyRule: {
|
||||
// +usage=Specify how to select the targets of the rule
|
||||
selector: [...#ResourcePolicyRuleSelector]
|
||||
// +usage=Specify the strategy for target resource to recycle
|
||||
strategy: *"onAppUpdate" | "onAppDelete" | "never"
|
||||
}
|
||||
#ResourcePolicyRuleSelector: {
|
||||
// +usage=Select resources by component names
|
||||
componentNames?: [...string]
|
||||
// +usage=Select resources by component types
|
||||
componentTypes?: [...string]
|
||||
// +usage=Select resources by oamTypes (COMPONENT or TRAIT)
|
||||
oamTypes?: [...string]
|
||||
// +usage=Select resources by trait types
|
||||
traitTypes?: [...string]
|
||||
// +usage=Select resources by resource types (like Deployment)
|
||||
resourceTypes?: [...string]
|
||||
// +usage=Select resources by their names
|
||||
resourceNames?: [...string]
|
||||
}
|
||||
parameter: {
|
||||
// +usage=If is set, outdated versioned resourcetracker will not be recycled automatically, outdated resources will be kept until resourcetracker be deleted manually
|
||||
keepLegacyResource: *false | bool
|
||||
// +usage=Specify the list of rules to control gc strategy at resource level, if one resource is controlled by multiple rules, first rule will be used
|
||||
rules?: [...#GarbageCollectPolicyRule]
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@ spec:
|
||||
if !parameter.classInSpec {
|
||||
"kubernetes.io/ingress.class": parameter.class
|
||||
}
|
||||
if parameter.gatewayHost != _|_ {
|
||||
"ingress.controller/host": parameter.gatewayHost
|
||||
}
|
||||
}
|
||||
}
|
||||
spec: {
|
||||
@@ -84,6 +87,9 @@ spec:
|
||||
|
||||
// +usage=Specify the secret name you want to quote.
|
||||
secretName?: string
|
||||
|
||||
// +usage=Specify the host of the ingress gateway, which is used to generate the endpoints when the host is empty.
|
||||
gatewayHost?: string
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Send message to webhook
|
||||
definition.oam.dev/description: Send notifications to Email, DingTalk, Slack, Lark or webhook in your workflow.
|
||||
name: notification
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
@@ -17,9 +17,11 @@ spec:
|
||||
)
|
||||
|
||||
parameter: {
|
||||
// +usage=Please fulfill its url and message if you want to send Lark messages
|
||||
lark?: {
|
||||
// +usage=Specify the the lark url, you can either sepcify it in value or use secretRef
|
||||
url: {
|
||||
// +usage=the url address content in string
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
@@ -29,7 +31,7 @@ spec:
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
// +usage=Specify the message that you want to sent, refer to [Lark messaging](https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN#8b0f2a1b).
|
||||
message: {
|
||||
// +usage=msg_type can be text, post, image, interactive, share_chat, share_user, audio, media, file, sticker
|
||||
msg_type: string
|
||||
@@ -37,10 +39,11 @@ spec:
|
||||
content: string
|
||||
}
|
||||
}
|
||||
|
||||
// +usage=Please fulfill its url and message if you want to send DingTalk messages
|
||||
dingding?: {
|
||||
// +usage=Specify the the dingding url, you can either sepcify it in value or use secretRef
|
||||
url: {
|
||||
// +usage=the url address content in string
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
@@ -50,8 +53,9 @@ spec:
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
// +usage=Specify the message that you want to sent, refer to [dingtalk messaging](https://developers.dingtalk.com/document/robots/custom-robot-access/title-72m-8ag-pqw)
|
||||
message: {
|
||||
// +usage=Specify the message content of dingtalk notification
|
||||
text?: *null | {
|
||||
content: string
|
||||
}
|
||||
@@ -93,10 +97,11 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// +usage=Please fulfill its url and message if you want to send Slack messages
|
||||
slack?: {
|
||||
// +usage=Specify the the slack url, you can either sepcify it in value or use secretRef
|
||||
url: {
|
||||
// +usage=the url address content in string
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
@@ -106,8 +111,9 @@ spec:
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
// +usage=Specify the message that you want to sent, refer to [slack messaging](https://api.slack.com/reference/messaging/payload)
|
||||
message: {
|
||||
// +usage=Specify the message text for slack notification
|
||||
text: string
|
||||
blocks?: *null | [...block]
|
||||
attachments?: *null | {
|
||||
@@ -115,10 +121,11 @@ spec:
|
||||
color?: string
|
||||
}
|
||||
thread_ts?: string
|
||||
mrkdwn?: *true | bool
|
||||
// +usage=Specify the message text format in markdown for slack notification
|
||||
mrkdwn?: *true | bool
|
||||
}
|
||||
}
|
||||
|
||||
// +usage=Please fulfill its from, to and content if you want to send email
|
||||
email?: {
|
||||
// +usage=Specify the email info that you want to send from
|
||||
from: {
|
||||
@@ -128,6 +135,7 @@ spec:
|
||||
alias?: string
|
||||
// +usage=Specify the password of the email, you can either sepcify it in value or use secretRef
|
||||
password: {
|
||||
// +usage=the password content in string
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Override configuration when deploying resources
|
||||
definition.oam.dev/description: Describe the configuration to override when deploying resources, it only works with specified `deploy` step in workflow.
|
||||
name: override
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
@@ -16,11 +16,15 @@ spec:
|
||||
name?: string
|
||||
// +usage=Specify the type of the patch component.
|
||||
type?: string
|
||||
// +usage=Specify the properties to override.
|
||||
properties?: {...}
|
||||
// +usage=Specify the traits to override.
|
||||
traits?: [...{
|
||||
// +usage=Specify the type of the trait to be patched.
|
||||
type: string
|
||||
// +usage=Specify the properties to override.
|
||||
properties?: {...}
|
||||
// +usage=Specify if the trait shoued be remove, default false
|
||||
// +usage=Specify if the trait should be remove, default false
|
||||
disable: *false | bool
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Read objects for your workflow steps
|
||||
definition.oam.dev/description: Read Kubernetes objects from cluster for your workflow steps
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: read-object
|
||||
@@ -50,15 +50,15 @@ spec:
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the apiVersion of the object, defaults to core.oam.dev/v1beta1
|
||||
// +usage=Specify the apiVersion of the object, defaults to 'core.oam.dev/v1beta1'
|
||||
apiVersion?: string
|
||||
// +usage=Specify the kind of the object, defaults to Application
|
||||
kind?: string
|
||||
// +usage=Specify the name of the object
|
||||
name: string
|
||||
// +usage=Specify the namespace of the object
|
||||
namespace?: string
|
||||
// +usage=Specify the cluster of the object
|
||||
// +usage=The namespace of the resource you want to read
|
||||
namespace?: *"default" | string
|
||||
// +usage=The cluster you want to apply the resource to, default is the current control plane cluster
|
||||
cluster: *"" | string
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,17 @@ spec:
|
||||
cue:
|
||||
template: |
|
||||
#K8sObject: {
|
||||
resource?: string
|
||||
group?: string
|
||||
name?: string
|
||||
// +usage=The resource type for the Kubernetes objects
|
||||
resource?: string
|
||||
// +usage=The group name for the Kubernetes objects
|
||||
group?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects with the name, exclusive to labelSelector
|
||||
name?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects from the namespace. Otherwise, fetch from the application's namespace.
|
||||
namespace?: string
|
||||
cluster?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects from the cluster. Otherwise, fetch from the local cluster.
|
||||
cluster?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects according to the label selector, exclusive to name
|
||||
labelSelector?: [string]: string
|
||||
...
|
||||
}
|
||||
@@ -35,7 +41,12 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: objects: [...#K8sObject]
|
||||
parameter: {
|
||||
// +usage=If specified, application will fetch native Kubernetes objects according to the object description
|
||||
objects?: [...#K8sObject]
|
||||
// +usage=If specified, the objects in the urls will be loaded.
|
||||
urls?: [...string]
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
if context.output.apiVersion == "apps/v1" && context.output.kind == "Deployment" {
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: step group
|
||||
definition.oam.dev/description: A special step that you can declare 'subSteps' in it, 'subSteps' is an array containing any step type whose valid parameters do not include the `step-group` step type itself. The sub steps were executed in parallel.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: step-group
|
||||
@@ -13,6 +13,6 @@ spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
// no parameters
|
||||
parameter: {}
|
||||
// no parameters, the nop only to make the template not empty or it's invalid
|
||||
nop: {}
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ spec:
|
||||
{
|
||||
name: "pvc-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -73,6 +76,9 @@ spec:
|
||||
{
|
||||
name: "configmap-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -103,6 +109,9 @@ spec:
|
||||
{
|
||||
name: "secret-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -133,6 +142,9 @@ spec:
|
||||
{
|
||||
name: "emptydir-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -141,12 +153,28 @@ spec:
|
||||
{
|
||||
name: "pvc-" + v.name
|
||||
devicePath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
volumesList: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
|
||||
deDupVolumesArray: [
|
||||
for val in [
|
||||
for i, vi in volumesList {
|
||||
for j, vj in volumesList if j < i && vi.name == vj.name {
|
||||
_ignore: true
|
||||
}
|
||||
vi
|
||||
},
|
||||
] if val._ignore == _|_ {
|
||||
val
|
||||
},
|
||||
]
|
||||
patch: spec: template: spec: {
|
||||
// +patchKey=name
|
||||
volumes: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
|
||||
volumes: deDupVolumesArray
|
||||
|
||||
containers: [{
|
||||
// +patchKey=name
|
||||
@@ -234,6 +262,7 @@ spec:
|
||||
name: string
|
||||
mountOnly: *false | bool
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
volumeMode: *"Filesystem" | string
|
||||
volumeName?: string
|
||||
accessModes: *["ReadWriteOnce"] | [...string]
|
||||
@@ -275,6 +304,7 @@ spec:
|
||||
configMapKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
data?: {...}
|
||||
@@ -298,6 +328,7 @@ spec:
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
stringData?: {...}
|
||||
@@ -313,6 +344,7 @@ spec:
|
||||
emptyDir?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Suspend your workflow
|
||||
definition.oam.dev/description: Suspend the current workflow, it can be resumed by 'vela workflow resume' command.
|
||||
name: suspend
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Determining the destination where components should be deployed to.
|
||||
definition.oam.dev/description: Describe the destination where components should be deployed to.
|
||||
name: topology
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Send webhook request to the url
|
||||
definition.oam.dev/description: Send a request to the specified Webhook URL. If no request body is specified, the current Application body will be sent by default.
|
||||
name: webhook
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -20,7 +20,10 @@ spec:
|
||||
for v in parameter.volumeMounts.pvc {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -29,7 +32,10 @@ spec:
|
||||
for v in parameter.volumeMounts.configMap {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -38,7 +44,10 @@ spec:
|
||||
for v in parameter.volumeMounts.secret {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -47,7 +56,10 @@ spec:
|
||||
for v in parameter.volumeMounts.emptyDir {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -56,7 +68,10 @@ spec:
|
||||
for v in parameter.volumeMounts.hostPath {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -119,6 +134,19 @@ spec:
|
||||
},
|
||||
] | []
|
||||
}
|
||||
volumesList: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
|
||||
deDupVolumesArray: [
|
||||
for val in [
|
||||
for i, vi in volumesList {
|
||||
for j, vj in volumesList if j < i && vi.name == vj.name {
|
||||
_ignore: true
|
||||
}
|
||||
vi
|
||||
},
|
||||
] if val._ignore == _|_ {
|
||||
val
|
||||
},
|
||||
]
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
@@ -262,7 +290,7 @@ spec:
|
||||
}
|
||||
|
||||
if parameter["volumeMounts"] != _|_ {
|
||||
volumes: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
|
||||
volumes: deDupVolumesArray
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,8 +357,8 @@ spec:
|
||||
}]
|
||||
|
||||
// +ignore
|
||||
// +usage=Specify what kind of Service you want. options: "ClusterIP", "NodePort", "LoadBalancer", "ExternalName"
|
||||
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
|
||||
// +usage=Specify what kind of Service you want. options: "ClusterIP", "NodePort", "LoadBalancer"
|
||||
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer"
|
||||
|
||||
// +ignore
|
||||
// +usage=If addRevisionLabel is true, the revision label will be added to the underlying pods
|
||||
@@ -375,6 +403,7 @@ spec:
|
||||
pvc?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
// +usage=The name of the PVC
|
||||
claimName: string
|
||||
}]
|
||||
@@ -382,6 +411,7 @@ spec:
|
||||
configMap?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
cmName: string
|
||||
items?: [...{
|
||||
@@ -394,6 +424,7 @@ spec:
|
||||
secret?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
secretName: string
|
||||
items?: [...{
|
||||
@@ -406,12 +437,14 @@ spec:
|
||||
emptyDir?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
// +usage=Mount HostPath type volume
|
||||
hostPath?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
path: string
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply components of an application in parallel for your workflow steps
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-application-in-parallel
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -4,8 +4,9 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply application for your workflow steps
|
||||
definition.oam.dev/description: Apply application for your workflow steps, it has no arguments, should be used for custom steps before or after application applied.
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-application
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -22,9 +22,9 @@ spec:
|
||||
cluster: parameter.cluster
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the value of the object
|
||||
// +usage=Specify Kubernetes native resource object to be applied
|
||||
value: {...}
|
||||
// +usage=Specify the cluster of the object
|
||||
// +usage=The cluster you want to apply the resource to, default is the current control plane cluster
|
||||
cluster: *"" | string
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/apply-once.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Allow configuration drift for applied resources, delivery the resource without continuously reconciliation.
|
||||
name: apply-once
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
#ApplyOnceStrategy: {
|
||||
// +usage=Specify the path of the resource that allow configuration drift
|
||||
path: [...string]
|
||||
}
|
||||
#ApplyOncePolicyRule: {
|
||||
// +usage=Specify how to select the targets of the rule
|
||||
selector?: #ResourcePolicyRuleSelector
|
||||
// +usage=Specify the strategy for configuring the resource level configuration drift behaviour
|
||||
strategy: #ApplyOnceStrategy
|
||||
}
|
||||
#ResourcePolicyRuleSelector: {
|
||||
// +usage=Select resources by component names
|
||||
componentNames?: [...string]
|
||||
// +usage=Select resources by component types
|
||||
componentTypes?: [...string]
|
||||
// +usage=Select resources by oamTypes (COMPONENT or TRAIT)
|
||||
oamTypes?: [...string]
|
||||
// +usage=Select resources by trait types
|
||||
traitTypes?: [...string]
|
||||
// +usage=Select resources by resource types (like Deployment)
|
||||
resourceTypes?: [...string]
|
||||
// +usage=Select resources by their names
|
||||
resourceNames?: [...string]
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Whether to enable apply-once for the whole application
|
||||
enable: *false | bool
|
||||
// +usage=Specify the rules for configuring apply-once policy in resource level
|
||||
rules?: [...#ApplyOncePolicyRule]
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply remaining components and traits
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-remaining
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -4,13 +4,13 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
custom.definition.oam.dev/alias.config.oam.dev: Image Registry
|
||||
alias.config.oam.dev: Image Registry
|
||||
definition.oam.dev/description: Config information to authenticate image registry
|
||||
labels:
|
||||
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
|
||||
custom.definition.oam.dev/multi-cluster.config.oam.dev: "true"
|
||||
custom.definition.oam.dev/type.config.oam.dev: image-registry
|
||||
catalog.config.oam.dev: velacore-config
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
multi-cluster.config.oam.dev: "true"
|
||||
type.config.oam.dev: image-registry
|
||||
name: config-image-registry
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: check or install depends-on Application
|
||||
definition.oam.dev/description: Wait for the specified Application to complete.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: depends-on-app
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Deploy cloud resource and bind secret to clusters
|
||||
definition.oam.dev/description: Deploy cloud resource and deliver secret to multi clusters.
|
||||
name: deploy-cloud-resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Deploy components with policies.
|
||||
definition.oam.dev/description: A powerful and unified deploy step for components multi-cluster delivery with policies.
|
||||
name: deploy
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
@@ -21,9 +21,9 @@ spec:
|
||||
ignoreTerraformComponent: parameter.ignoreTerraformComponent
|
||||
}
|
||||
parameter: {
|
||||
//+usage=If set false, the workflow will be suspend before this step.
|
||||
//+usage=If set to false, the workflow will suspend automatically before this step, default to be true.
|
||||
auto: *true | bool
|
||||
//+usage=Declare the policies used for this step.
|
||||
//+usage=Declare the policies that used for this deployment. If not specified, the components will be deployed to the hub cluster.
|
||||
policies?: [...string]
|
||||
//+usage=Maximum number of concurrent delivered components.
|
||||
parallelism: *5 | int
|
||||
|
||||
@@ -6,6 +6,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Deploy application to runtime clusters
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: deploy2runtime
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Export data to config map for your workflow steps
|
||||
definition.oam.dev/description: Export data to specified Kubernetes ConfigMap in your workflow.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: export2config
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Export data to secret for your workflow steps
|
||||
definition.oam.dev/description: Export data to Kubernetes Secret in your workflow.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: export2secret
|
||||
@@ -46,7 +46,7 @@ spec:
|
||||
type?: string
|
||||
// +usage=Specify the data of secret
|
||||
data: {}
|
||||
// +usage=Specify the cluster of the config map
|
||||
// +usage=Specify the cluster of the secret
|
||||
cluster: *"" | string
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,20 @@ spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
outputs: service: {
|
||||
apiVersion: "v1"
|
||||
kind: "Service"
|
||||
metadata: name: context.name
|
||||
metadata: name: context.name
|
||||
metadata: annotations: parameter.annotations
|
||||
spec: {
|
||||
selector: "app.oam.dev/component": context.name
|
||||
ports: [
|
||||
for p in parameter.port {
|
||||
name: "port-" + strconv.FormatInt(p, 10)
|
||||
port: p
|
||||
targetPort: p
|
||||
},
|
||||
@@ -32,7 +38,33 @@ spec:
|
||||
parameter: {
|
||||
// +usage=Specify the exposion ports
|
||||
port: [...int]
|
||||
// +usage=Specify the annotaions of the exposed service
|
||||
annotations: [string]: string
|
||||
// +usage=Specify what kind of Service you want. options: "ClusterIP","NodePort","LoadBalancer","ExternalName"
|
||||
type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
message: *"" | string
|
||||
service: context.outputs.service
|
||||
if service.spec.type == "ClusterIP" {
|
||||
message: "ClusterIP: \(service.spec.clusterIP)"
|
||||
}
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
if !isHealth {
|
||||
message: "ExternalIP: Pending"
|
||||
}
|
||||
if isHealth {
|
||||
message: "ExternalIP: \(status.loadBalancer.ingress[0].ip)"
|
||||
}
|
||||
}
|
||||
healthPolicy: |-
|
||||
isHealth: *true | bool
|
||||
service: context.outputs.service
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/garbage-collect.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Configure the garbage collect behaviour for the application.
|
||||
name: garbage-collect
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
#GarbageCollectPolicyRule: {
|
||||
// +usage=Specify how to select the targets of the rule
|
||||
selector: [...#ResourcePolicyRuleSelector]
|
||||
// +usage=Specify the strategy for target resource to recycle
|
||||
strategy: *"onAppUpdate" | "onAppDelete" | "never"
|
||||
}
|
||||
#ResourcePolicyRuleSelector: {
|
||||
// +usage=Select resources by component names
|
||||
componentNames?: [...string]
|
||||
// +usage=Select resources by component types
|
||||
componentTypes?: [...string]
|
||||
// +usage=Select resources by oamTypes (COMPONENT or TRAIT)
|
||||
oamTypes?: [...string]
|
||||
// +usage=Select resources by trait types
|
||||
traitTypes?: [...string]
|
||||
// +usage=Select resources by resource types (like Deployment)
|
||||
resourceTypes?: [...string]
|
||||
// +usage=Select resources by their names
|
||||
resourceNames?: [...string]
|
||||
}
|
||||
parameter: {
|
||||
// +usage=If is set, outdated versioned resourcetracker will not be recycled automatically, outdated resources will be kept until resourcetracker be deleted manually
|
||||
keepLegacyResource: *false | bool
|
||||
// +usage=Specify the list of rules to control gc strategy at resource level, if one resource is controlled by multiple rules, first rule will be used
|
||||
rules?: [...#GarbageCollectPolicyRule]
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@ spec:
|
||||
if !parameter.classInSpec {
|
||||
"kubernetes.io/ingress.class": parameter.class
|
||||
}
|
||||
if parameter.gatewayHost != _|_ {
|
||||
"ingress.controller/host": parameter.gatewayHost
|
||||
}
|
||||
}
|
||||
}
|
||||
spec: {
|
||||
@@ -84,6 +87,9 @@ spec:
|
||||
|
||||
// +usage=Specify the secret name you want to quote.
|
||||
secretName?: string
|
||||
|
||||
// +usage=Specify the host of the ingress gateway, which is used to generate the endpoints when the host is empty.
|
||||
gatewayHost?: string
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Send message to webhook
|
||||
definition.oam.dev/description: Send notifications to Email, DingTalk, Slack, Lark or webhook in your workflow.
|
||||
name: notification
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
@@ -17,9 +17,11 @@ spec:
|
||||
)
|
||||
|
||||
parameter: {
|
||||
// +usage=Please fulfill its url and message if you want to send Lark messages
|
||||
lark?: {
|
||||
// +usage=Specify the the lark url, you can either sepcify it in value or use secretRef
|
||||
url: {
|
||||
// +usage=the url address content in string
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
@@ -29,7 +31,7 @@ spec:
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
// +usage=Specify the message that you want to sent, refer to [Lark messaging](https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN#8b0f2a1b).
|
||||
message: {
|
||||
// +usage=msg_type can be text, post, image, interactive, share_chat, share_user, audio, media, file, sticker
|
||||
msg_type: string
|
||||
@@ -37,10 +39,11 @@ spec:
|
||||
content: string
|
||||
}
|
||||
}
|
||||
|
||||
// +usage=Please fulfill its url and message if you want to send DingTalk messages
|
||||
dingding?: {
|
||||
// +usage=Specify the the dingding url, you can either sepcify it in value or use secretRef
|
||||
url: {
|
||||
// +usage=the url address content in string
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
@@ -50,8 +53,9 @@ spec:
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
// +usage=Specify the message that you want to sent, refer to [dingtalk messaging](https://developers.dingtalk.com/document/robots/custom-robot-access/title-72m-8ag-pqw)
|
||||
message: {
|
||||
// +usage=Specify the message content of dingtalk notification
|
||||
text?: *null | {
|
||||
content: string
|
||||
}
|
||||
@@ -93,10 +97,11 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// +usage=Please fulfill its url and message if you want to send Slack messages
|
||||
slack?: {
|
||||
// +usage=Specify the the slack url, you can either sepcify it in value or use secretRef
|
||||
url: {
|
||||
// +usage=the url address content in string
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
@@ -106,8 +111,9 @@ spec:
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
// +usage=Specify the message that you want to sent, refer to [slack messaging](https://api.slack.com/reference/messaging/payload)
|
||||
message: {
|
||||
// +usage=Specify the message text for slack notification
|
||||
text: string
|
||||
blocks?: *null | [...block]
|
||||
attachments?: *null | {
|
||||
@@ -115,10 +121,11 @@ spec:
|
||||
color?: string
|
||||
}
|
||||
thread_ts?: string
|
||||
mrkdwn?: *true | bool
|
||||
// +usage=Specify the message text format in markdown for slack notification
|
||||
mrkdwn?: *true | bool
|
||||
}
|
||||
}
|
||||
|
||||
// +usage=Please fulfill its from, to and content if you want to send email
|
||||
email?: {
|
||||
// +usage=Specify the email info that you want to send from
|
||||
from: {
|
||||
@@ -128,6 +135,7 @@ spec:
|
||||
alias?: string
|
||||
// +usage=Specify the password of the email, you can either sepcify it in value or use secretRef
|
||||
password: {
|
||||
// +usage=the password content in string
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Override configuration when deploying resources
|
||||
definition.oam.dev/description: Describe the configuration to override when deploying resources, it only works with specified `deploy` step in workflow.
|
||||
name: override
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
@@ -16,11 +16,15 @@ spec:
|
||||
name?: string
|
||||
// +usage=Specify the type of the patch component.
|
||||
type?: string
|
||||
// +usage=Specify the properties to override.
|
||||
properties?: {...}
|
||||
// +usage=Specify the traits to override.
|
||||
traits?: [...{
|
||||
// +usage=Specify the type of the trait to be patched.
|
||||
type: string
|
||||
// +usage=Specify the properties to override.
|
||||
properties?: {...}
|
||||
// +usage=Specify if the trait shoued be remove, default false
|
||||
// +usage=Specify if the trait should be remove, default false
|
||||
disable: *false | bool
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Read objects for your workflow steps
|
||||
definition.oam.dev/description: Read Kubernetes objects from cluster for your workflow steps
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: read-object
|
||||
@@ -50,15 +50,15 @@ spec:
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the apiVersion of the object, defaults to core.oam.dev/v1beta1
|
||||
// +usage=Specify the apiVersion of the object, defaults to 'core.oam.dev/v1beta1'
|
||||
apiVersion?: string
|
||||
// +usage=Specify the kind of the object, defaults to Application
|
||||
kind?: string
|
||||
// +usage=Specify the name of the object
|
||||
name: string
|
||||
// +usage=Specify the namespace of the object
|
||||
namespace?: string
|
||||
// +usage=Specify the cluster of the object
|
||||
// +usage=The namespace of the resource you want to read
|
||||
namespace?: *"default" | string
|
||||
// +usage=The cluster you want to apply the resource to, default is the current control plane cluster
|
||||
cluster: *"" | string
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,17 @@ spec:
|
||||
cue:
|
||||
template: |
|
||||
#K8sObject: {
|
||||
resource?: string
|
||||
group?: string
|
||||
name?: string
|
||||
// +usage=The resource type for the Kubernetes objects
|
||||
resource?: string
|
||||
// +usage=The group name for the Kubernetes objects
|
||||
group?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects with the name, exclusive to labelSelector
|
||||
name?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects from the namespace. Otherwise, fetch from the application's namespace.
|
||||
namespace?: string
|
||||
cluster?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects from the cluster. Otherwise, fetch from the local cluster.
|
||||
cluster?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects according to the label selector, exclusive to name
|
||||
labelSelector?: [string]: string
|
||||
...
|
||||
}
|
||||
@@ -35,7 +41,12 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: objects: [...#K8sObject]
|
||||
parameter: {
|
||||
// +usage=If specified, application will fetch native Kubernetes objects according to the object description
|
||||
objects?: [...#K8sObject]
|
||||
// +usage=If specified, the objects in the urls will be loaded.
|
||||
urls?: [...string]
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
if context.output.apiVersion == "apps/v1" && context.output.kind == "Deployment" {
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: step group
|
||||
definition.oam.dev/description: A special step that you can declare 'subSteps' in it, 'subSteps' is an array containing any step type whose valid parameters do not include the `step-group` step type itself. The sub steps were executed in parallel.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: step-group
|
||||
@@ -13,6 +13,6 @@ spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
// no parameters
|
||||
parameter: {}
|
||||
// no parameters, the nop only to make the template not empty or it's invalid
|
||||
nop: {}
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ spec:
|
||||
{
|
||||
name: "pvc-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -73,6 +76,9 @@ spec:
|
||||
{
|
||||
name: "configmap-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -103,6 +109,9 @@ spec:
|
||||
{
|
||||
name: "secret-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -133,6 +142,9 @@ spec:
|
||||
{
|
||||
name: "emptydir-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -141,12 +153,28 @@ spec:
|
||||
{
|
||||
name: "pvc-" + v.name
|
||||
devicePath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
volumesList: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
|
||||
deDupVolumesArray: [
|
||||
for val in [
|
||||
for i, vi in volumesList {
|
||||
for j, vj in volumesList if j < i && vi.name == vj.name {
|
||||
_ignore: true
|
||||
}
|
||||
vi
|
||||
},
|
||||
] if val._ignore == _|_ {
|
||||
val
|
||||
},
|
||||
]
|
||||
patch: spec: template: spec: {
|
||||
// +patchKey=name
|
||||
volumes: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
|
||||
volumes: deDupVolumesArray
|
||||
|
||||
containers: [{
|
||||
// +patchKey=name
|
||||
@@ -234,6 +262,7 @@ spec:
|
||||
name: string
|
||||
mountOnly: *false | bool
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
volumeMode: *"Filesystem" | string
|
||||
volumeName?: string
|
||||
accessModes: *["ReadWriteOnce"] | [...string]
|
||||
@@ -275,6 +304,7 @@ spec:
|
||||
configMapKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
data?: {...}
|
||||
@@ -298,6 +328,7 @@ spec:
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
stringData?: {...}
|
||||
@@ -313,6 +344,7 @@ spec:
|
||||
emptyDir?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Suspend your workflow
|
||||
definition.oam.dev/description: Suspend the current workflow, it can be resumed by 'vela workflow resume' command.
|
||||
name: suspend
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Determining the destination where components should be deployed to.
|
||||
definition.oam.dev/description: Describe the destination where components should be deployed to.
|
||||
name: topology
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Send webhook request to the url
|
||||
definition.oam.dev/description: Send a request to the specified Webhook URL. If no request body is specified, the current Application body will be sent by default.
|
||||
name: webhook
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -20,7 +20,10 @@ spec:
|
||||
for v in parameter.volumeMounts.pvc {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -29,7 +32,10 @@ spec:
|
||||
for v in parameter.volumeMounts.configMap {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -38,7 +44,10 @@ spec:
|
||||
for v in parameter.volumeMounts.secret {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -47,7 +56,10 @@ spec:
|
||||
for v in parameter.volumeMounts.emptyDir {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -56,7 +68,10 @@ spec:
|
||||
for v in parameter.volumeMounts.hostPath {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -119,6 +134,19 @@ spec:
|
||||
},
|
||||
] | []
|
||||
}
|
||||
volumesList: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
|
||||
deDupVolumesArray: [
|
||||
for val in [
|
||||
for i, vi in volumesList {
|
||||
for j, vj in volumesList if j < i && vi.name == vj.name {
|
||||
_ignore: true
|
||||
}
|
||||
vi
|
||||
},
|
||||
] if val._ignore == _|_ {
|
||||
val
|
||||
},
|
||||
]
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
@@ -262,7 +290,7 @@ spec:
|
||||
}
|
||||
|
||||
if parameter["volumeMounts"] != _|_ {
|
||||
volumes: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
|
||||
volumes: deDupVolumesArray
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,8 +357,8 @@ spec:
|
||||
}]
|
||||
|
||||
// +ignore
|
||||
// +usage=Specify what kind of Service you want. options: "ClusterIP", "NodePort", "LoadBalancer", "ExternalName"
|
||||
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
|
||||
// +usage=Specify what kind of Service you want. options: "ClusterIP", "NodePort", "LoadBalancer"
|
||||
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer"
|
||||
|
||||
// +ignore
|
||||
// +usage=If addRevisionLabel is true, the revision label will be added to the underlying pods
|
||||
@@ -375,6 +403,7 @@ spec:
|
||||
pvc?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
// +usage=The name of the PVC
|
||||
claimName: string
|
||||
}]
|
||||
@@ -382,6 +411,7 @@ spec:
|
||||
configMap?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
cmName: string
|
||||
items?: [...{
|
||||
@@ -394,6 +424,7 @@ spec:
|
||||
secret?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
secretName: string
|
||||
items?: [...{
|
||||
@@ -406,12 +437,14 @@ spec:
|
||||
emptyDir?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
// +usage=Mount HostPath type volume
|
||||
hostPath?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
path: string
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -690,8 +690,8 @@
|
||||
"tags": [
|
||||
"application"
|
||||
],
|
||||
"summary": "compare application with env latest revision",
|
||||
"operationId": "compareAppWithLatestRevision",
|
||||
"summary": "compare application",
|
||||
"operationId": "compareApp",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
@@ -1726,6 +1726,12 @@
|
||||
"name": "policyName",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Force delete the policy and all references",
|
||||
"name": "force",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -2796,6 +2802,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/cloudshell": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/xml",
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/xml"
|
||||
],
|
||||
"tags": [
|
||||
"cloudshell"
|
||||
],
|
||||
"summary": "prepare the user's cloud shell environment",
|
||||
"operationId": "prepareCloudShell",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.SimpleResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/bcode.Bcode"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Bummer, something went wrong"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/clusters": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
@@ -5957,6 +5997,74 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/view/cloudshell": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/xml",
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/xml"
|
||||
],
|
||||
"tags": [
|
||||
"cloudshell"
|
||||
],
|
||||
"summary": "prepare the user's cloud shell environment",
|
||||
"operationId": "proxy",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.SimpleResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/bcode.Bcode"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Bummer, something went wrong"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/view/cloudshell/{subpath}": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/xml",
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/xml"
|
||||
],
|
||||
"tags": [
|
||||
"cloudshell"
|
||||
],
|
||||
"summary": "prepare the user's cloud shell environment",
|
||||
"operationId": "proxy",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.SimpleResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/bcode.Bcode"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Bummer, something went wrong"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -5972,7 +6080,6 @@
|
||||
},
|
||||
"addon.DeployTo": {
|
||||
"required": [
|
||||
"runtime_cluster",
|
||||
"disableControlPlane",
|
||||
"runtimeCluster"
|
||||
],
|
||||
@@ -6032,6 +6139,9 @@
|
||||
},
|
||||
"addon.HelmSource": {
|
||||
"properties": {
|
||||
"insecureSkipTLS": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -7090,8 +7200,8 @@
|
||||
},
|
||||
"model.ApplicationRevision": {
|
||||
"required": [
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"createTime",
|
||||
"appPrimaryKey",
|
||||
"version",
|
||||
"status",
|
||||
@@ -7794,12 +7904,15 @@
|
||||
"type": "object"
|
||||
},
|
||||
"v1.AppCompareReq": {
|
||||
"required": [
|
||||
"env"
|
||||
],
|
||||
"properties": {
|
||||
"env": {
|
||||
"type": "string"
|
||||
"compareLatestWithRunning": {
|
||||
"$ref": "#/definitions/v1.CompareLatestWithRunningOption"
|
||||
},
|
||||
"compareRevisionWithLatest": {
|
||||
"$ref": "#/definitions/v1.CompareRevisionWithLatestOption"
|
||||
},
|
||||
"compareRevisionWithRunning": {
|
||||
"$ref": "#/definitions/v1.CompareRevisionWithRunningOption"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -7807,35 +7920,32 @@
|
||||
"required": [
|
||||
"isDiff",
|
||||
"diffReport",
|
||||
"newAppYAML",
|
||||
"oldAppYAML"
|
||||
"baseAppYAML",
|
||||
"targetAppYAML"
|
||||
],
|
||||
"properties": {
|
||||
"baseAppYAML": {
|
||||
"type": "string"
|
||||
},
|
||||
"diffReport": {
|
||||
"type": "string"
|
||||
},
|
||||
"isDiff": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"newAppYAML": {
|
||||
"type": "string"
|
||||
},
|
||||
"oldAppYAML": {
|
||||
"targetAppYAML": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.AppDryRunReq": {
|
||||
"required": [
|
||||
"appName",
|
||||
"dryRunType",
|
||||
"env",
|
||||
"workflow",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"appName": {
|
||||
"type": "string"
|
||||
},
|
||||
"dryRunType": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -7844,14 +7954,24 @@
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"workflow": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.AppDryRunResponse": {
|
||||
"required": [
|
||||
"yaml"
|
||||
"yaml",
|
||||
"success"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"yaml": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -7944,10 +8064,10 @@
|
||||
"required": [
|
||||
"version",
|
||||
"note",
|
||||
"triggerType",
|
||||
"envName",
|
||||
"createTime",
|
||||
"status",
|
||||
"envName"
|
||||
"triggerType"
|
||||
],
|
||||
"properties": {
|
||||
"codeInfo": {
|
||||
@@ -8263,6 +8383,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.CloudShellPrepareResponse": {
|
||||
"required": [
|
||||
"status",
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ClusterBase": {
|
||||
"required": [
|
||||
"name",
|
||||
@@ -8381,6 +8515,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.CompareLatestWithRunningOption": {
|
||||
"required": [
|
||||
"env"
|
||||
],
|
||||
"properties": {
|
||||
"env": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.CompareRevisionWithLatestOption": {
|
||||
"properties": {
|
||||
"revision": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.CompareRevisionWithRunningOption": {
|
||||
"properties": {
|
||||
"revision": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ComponentBase": {
|
||||
"required": [
|
||||
"name",
|
||||
@@ -9044,6 +9202,9 @@
|
||||
"alias": {
|
||||
"type": "string"
|
||||
},
|
||||
"allowTargetConflict": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -9099,14 +9260,23 @@
|
||||
"v1.CreatePolicyRequest": {
|
||||
"required": [
|
||||
"name",
|
||||
"alias",
|
||||
"envName",
|
||||
"description",
|
||||
"type",
|
||||
"properties"
|
||||
"properties",
|
||||
"workflowPolicyBind"
|
||||
],
|
||||
"properties": {
|
||||
"alias": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"envName": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -9115,6 +9285,12 @@
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"workflowPolicyBind": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1.WorkflowPolicyBinding"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -9249,7 +9425,8 @@
|
||||
"description",
|
||||
"icon",
|
||||
"status",
|
||||
"labels"
|
||||
"labels",
|
||||
"ownerAddon"
|
||||
],
|
||||
"properties": {
|
||||
"alias": {
|
||||
@@ -9273,6 +9450,9 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"ownerAddon": {
|
||||
"type": "string"
|
||||
},
|
||||
"policy": {
|
||||
"$ref": "#/definitions/v1beta1.PolicyDefinitionSpec"
|
||||
},
|
||||
@@ -9329,11 +9509,11 @@
|
||||
},
|
||||
"v1.DetailAddonResponse": {
|
||||
"required": [
|
||||
"description",
|
||||
"invisible",
|
||||
"version",
|
||||
"icon",
|
||||
"name",
|
||||
"description",
|
||||
"icon",
|
||||
"version",
|
||||
"invisible",
|
||||
"schema",
|
||||
"uiSchema",
|
||||
"definitions",
|
||||
@@ -9413,13 +9593,13 @@
|
||||
},
|
||||
"v1.DetailApplicationResponse": {
|
||||
"required": [
|
||||
"description",
|
||||
"icon",
|
||||
"name",
|
||||
"alias",
|
||||
"project",
|
||||
"description",
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"name",
|
||||
"policies",
|
||||
"envBindings",
|
||||
"resourceInfo"
|
||||
@@ -9477,19 +9657,19 @@
|
||||
"v1.DetailClusterResponse": {
|
||||
"required": [
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"reason",
|
||||
"dashboardURL",
|
||||
"labels",
|
||||
"apiServerURL",
|
||||
"kubeConfig",
|
||||
"kubeConfigSecret",
|
||||
"name",
|
||||
"alias",
|
||||
"icon",
|
||||
"status",
|
||||
"kubeConfig",
|
||||
"updateTime",
|
||||
"dashboardURL",
|
||||
"name",
|
||||
"description",
|
||||
"icon",
|
||||
"labels",
|
||||
"reason",
|
||||
"alias",
|
||||
"provider",
|
||||
"apiServerURL",
|
||||
"kubeConfigSecret",
|
||||
"resourceInfo"
|
||||
],
|
||||
"properties": {
|
||||
@@ -9547,14 +9727,14 @@
|
||||
},
|
||||
"v1.DetailComponentResponse": {
|
||||
"required": [
|
||||
"type",
|
||||
"createTime",
|
||||
"name",
|
||||
"updateTime",
|
||||
"alias",
|
||||
"creator",
|
||||
"main",
|
||||
"appPrimaryKey",
|
||||
"alias",
|
||||
"createTime",
|
||||
"main",
|
||||
"name",
|
||||
"creator",
|
||||
"type",
|
||||
"updateTime",
|
||||
"definition"
|
||||
],
|
||||
"properties": {
|
||||
@@ -9643,11 +9823,12 @@
|
||||
"v1.DetailDefinitionResponse": {
|
||||
"required": [
|
||||
"name",
|
||||
"status",
|
||||
"labels",
|
||||
"alias",
|
||||
"description",
|
||||
"icon",
|
||||
"alias",
|
||||
"status",
|
||||
"labels",
|
||||
"ownerAddon",
|
||||
"schema",
|
||||
"uiSchema"
|
||||
],
|
||||
@@ -9673,6 +9854,9 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"ownerAddon": {
|
||||
"type": "string"
|
||||
},
|
||||
"policy": {
|
||||
"$ref": "#/definitions/v1beta1.PolicyDefinitionSpec"
|
||||
},
|
||||
@@ -9701,16 +9885,20 @@
|
||||
},
|
||||
"v1.DetailPolicyResponse": {
|
||||
"required": [
|
||||
"creator",
|
||||
"updateTime",
|
||||
"envName",
|
||||
"name",
|
||||
"alias",
|
||||
"type",
|
||||
"description",
|
||||
"creator",
|
||||
"properties",
|
||||
"createTime"
|
||||
],
|
||||
"properties": {
|
||||
"alias": {
|
||||
"type": "string"
|
||||
},
|
||||
"createTime": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
@@ -9736,22 +9924,28 @@
|
||||
"updateTime": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"workflowPolicyBind": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1.WorkflowPolicyBinding"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.DetailRevisionResponse": {
|
||||
"required": [
|
||||
"version",
|
||||
"envName",
|
||||
"createTime",
|
||||
"appPrimaryKey",
|
||||
"reason",
|
||||
"deployUser",
|
||||
"workflowName",
|
||||
"updateTime",
|
||||
"triggerType",
|
||||
"createTime",
|
||||
"version",
|
||||
"reason",
|
||||
"envName",
|
||||
"appPrimaryKey",
|
||||
"deployUser",
|
||||
"note",
|
||||
"status",
|
||||
"note"
|
||||
"workflowName",
|
||||
"triggerType"
|
||||
],
|
||||
"properties": {
|
||||
"appPrimaryKey": {
|
||||
@@ -9805,10 +9999,10 @@
|
||||
},
|
||||
"v1.DetailTargetResponse": {
|
||||
"required": [
|
||||
"project",
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"name"
|
||||
"name",
|
||||
"createTime",
|
||||
"project"
|
||||
],
|
||||
"properties": {
|
||||
"alias": {
|
||||
@@ -9893,12 +10087,12 @@
|
||||
},
|
||||
"v1.DetailWorkflowRecordResponse": {
|
||||
"required": [
|
||||
"status",
|
||||
"name",
|
||||
"namespace",
|
||||
"workflowName",
|
||||
"workflowAlias",
|
||||
"applicationRevision",
|
||||
"status",
|
||||
"name",
|
||||
"deployTime",
|
||||
"deployUser",
|
||||
"note",
|
||||
@@ -9950,14 +10144,14 @@
|
||||
},
|
||||
"v1.DetailWorkflowResponse": {
|
||||
"required": [
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"name",
|
||||
"description",
|
||||
"envName",
|
||||
"name",
|
||||
"alias",
|
||||
"default",
|
||||
"updateTime",
|
||||
"description",
|
||||
"enable",
|
||||
"default"
|
||||
"createTime"
|
||||
],
|
||||
"properties": {
|
||||
"alias": {
|
||||
@@ -10662,11 +10856,11 @@
|
||||
},
|
||||
"v1.LoginUserInfoResponse": {
|
||||
"required": [
|
||||
"name",
|
||||
"email",
|
||||
"disabled",
|
||||
"createTime",
|
||||
"lastLoginTime",
|
||||
"name",
|
||||
"projects",
|
||||
"platformPermissions",
|
||||
"projectPermissions"
|
||||
@@ -10870,6 +11064,7 @@
|
||||
"v1.PolicyBase": {
|
||||
"required": [
|
||||
"name",
|
||||
"alias",
|
||||
"type",
|
||||
"description",
|
||||
"creator",
|
||||
@@ -10879,6 +11074,9 @@
|
||||
"envName"
|
||||
],
|
||||
"properties": {
|
||||
"alias": {
|
||||
"type": "string"
|
||||
},
|
||||
"createTime": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
@@ -11307,19 +11505,34 @@
|
||||
},
|
||||
"v1.UpdatePolicyRequest": {
|
||||
"required": [
|
||||
"alias",
|
||||
"envName",
|
||||
"description",
|
||||
"type",
|
||||
"properties"
|
||||
"properties",
|
||||
"workflowPolicyBind"
|
||||
],
|
||||
"properties": {
|
||||
"alias": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"envName": {
|
||||
"type": "string"
|
||||
},
|
||||
"properties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"workflowPolicyBind": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1.WorkflowPolicyBinding"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11498,6 +11711,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.WorkflowPolicyBinding": {
|
||||
"required": [
|
||||
"name",
|
||||
"steps"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"steps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.WorkflowRecord": {
|
||||
"required": [
|
||||
"name",
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
## General
|
||||
|
||||
- list all configuration types
|
||||
- list all configuration types. Note before vela v1.5, the key is "custom.definition.oam.dev/catalog.config.oam.dev"
|
||||
```shell
|
||||
$ vela components --label custom.definition.oam.dev/catalog.config.oam.dev=velacore-config
|
||||
$ vela components --label catalog.config.oam.dev=velacore-config
|
||||
NAME DEFINITION
|
||||
config-dex-connector autodetects.core.oam.dev
|
||||
config-helm-repository autodetects.core.oam.dev
|
||||
|
||||
@@ -25,3 +25,7 @@ spec:
|
||||
- name: my-mount
|
||||
mountPath: /test
|
||||
claimName: myclaim
|
||||
- name: my-mount
|
||||
mountPath: /test2
|
||||
subPath: /sub
|
||||
claimName: myclaim
|
||||
|
||||
@@ -16,8 +16,9 @@ spec:
|
||||
pvc:
|
||||
- name: test1
|
||||
mountPath: /test/mount/pvc
|
||||
- name: test2
|
||||
- name: test1
|
||||
mountPath: /test/mount2/pvc
|
||||
subPath: /sub
|
||||
configMap:
|
||||
- name: test1
|
||||
mountPath: /test/mount/cm
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: a-static-website
|
||||
spec:
|
||||
components:
|
||||
- name: create-bucket
|
||||
type: alibaba-oss-website
|
||||
properties:
|
||||
bucket: oss-website-20220302-2135
|
||||
acl: public-read
|
||||
index_document: index.html
|
||||
error_document: error/index.html
|
||||
writeConnectionSecretToRef:
|
||||
name: oss-website-conn
|
||||
outputs:
|
||||
- name: bucket
|
||||
valueFrom: output.status.apply.outputs.BUCKET_NAME.value
|
||||
- name: endpoint
|
||||
valueFrom: output.status.apply.outputs.EXTERNAL_ENDPOINT.value
|
||||
- name: index_document
|
||||
valueFrom: properties.index_document
|
||||
# valueFrom: output.spec.variable.index_document
|
||||
|
||||
- name: deploy-website
|
||||
type: deploy-website
|
||||
inputs:
|
||||
- from: bucket
|
||||
parameterKey: properties.bucket
|
||||
- from: endpoint
|
||||
parameterKey: properties.endpoint
|
||||
- from: index_document
|
||||
parameterKey: properties.index_document
|
||||
properties:
|
||||
static_web_url: "https://github.com/cloudacademy/static-website-example.git"
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: webapp
|
||||
spec:
|
||||
components:
|
||||
- name: express-server
|
||||
type: webservice
|
||||
properties:
|
||||
image: zzxwill/flask-web-application:v0.3.1-crossplane
|
||||
ports: 80
|
||||
traits:
|
||||
- type: service-binding
|
||||
properties:
|
||||
envMappings:
|
||||
# environments refer to db-conn secret
|
||||
DB_PASSWORD:
|
||||
secret: db-conn # 1) If the env name is the same as the secret key, secret key can be omitted.
|
||||
endpoint:
|
||||
secret: db-conn
|
||||
key: DB_HOST # 2) If the env name is different from secret key, secret key has to be set.
|
||||
username:
|
||||
secret: db-conn
|
||||
key: DB_USER
|
||||
# environments refer to oss-conn secret
|
||||
BUCKET_NAME:
|
||||
secret: oss-conn
|
||||
|
||||
- name: sample-db
|
||||
type: alibaba-rds
|
||||
properties:
|
||||
instance_name: sample-db
|
||||
account_name: oamtest
|
||||
password: U34rfwefwefffaked
|
||||
writeConnectionSecretToRef:
|
||||
name: db-conn
|
||||
|
||||
- name: sample-oss
|
||||
type: alibaba-oss
|
||||
properties:
|
||||
bucket: vela-website
|
||||
acl: private
|
||||
writeConnectionSecretToRef:
|
||||
name: oss-conn
|
||||
@@ -41,26 +41,28 @@ var _ = Describe("Test Kubectl Plugin", func() {
|
||||
Eventually(func() error {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentDefName}, &cd)
|
||||
return err
|
||||
}, 5*time.Second).Should(BeNil())
|
||||
}, 5*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
var td v1beta1.TraitDefinition
|
||||
Eventually(func() error {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: traitDefName}, &td)
|
||||
return err
|
||||
}, 5*time.Second).Should(BeNil())
|
||||
}, 5*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
By("dry-run application")
|
||||
err := os.WriteFile("dry-run-app.yaml", []byte(application), 0644)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
output, err := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -n vela-system")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).Should(ContainSubstring(dryRunResult))
|
||||
Eventually(func() string {
|
||||
output, _ := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -n vela-system")
|
||||
return output
|
||||
}, 10*time.Second, time.Second).Should(ContainSubstring(dryRunResult))
|
||||
})
|
||||
|
||||
It("Test dry-run application use definitions in local", func() {
|
||||
output, err := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -d definitions")
|
||||
Expect(output).Should(ContainSubstring(dryRunResult))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Eventually(func() string {
|
||||
output, _ := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -d definitions")
|
||||
return output
|
||||
}, 10*time.Second, time.Second).Should(ContainSubstring(dryRunResult))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -93,7 +95,7 @@ var _ = Describe("Test Kubectl Plugin", func() {
|
||||
var tempApp v1beta1.Application
|
||||
_ = k8sClient.Get(ctx, client.ObjectKey{Namespace: "default", Name: app.Name}, &tempApp)
|
||||
return tempApp.Status.LatestRevision != nil
|
||||
}, 20*time.Second).Should(BeTrue())
|
||||
}, 20*time.Second, time.Second).Should(BeTrue())
|
||||
|
||||
By("live-diff application")
|
||||
err := os.WriteFile("live-diff-app.yaml", []byte(newApplication), 0644)
|
||||
@@ -108,7 +110,7 @@ var _ = Describe("Test Kubectl Plugin", func() {
|
||||
var tempApp v1beta1.Application
|
||||
_ = k8sClient.Get(ctx, client.ObjectKey{Namespace: "default", Name: app.Name}, &tempApp)
|
||||
return tempApp.Status.LatestRevision != nil
|
||||
}, 20*time.Second).Should(BeTrue())
|
||||
}, 20*time.Second, time.Second).Should(BeTrue())
|
||||
|
||||
output, err := e2e.Exec("kubectl-vela live-diff -f live-diff-app.yaml -d definitions")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -134,7 +136,7 @@ var _ = Describe("Test Kubectl Plugin", func() {
|
||||
cdName := "test-webapp-chart"
|
||||
output, _ := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", cdName))
|
||||
return output
|
||||
}, 20*time.Second).Should(ContainSubstring("Properties"))
|
||||
}, 20*time.Second, time.Second).Should(ContainSubstring("Specification"))
|
||||
})
|
||||
It("Test show componentDefinition def with raw Kube mode", func() {
|
||||
cdName := "kube-worker"
|
||||
@@ -156,7 +158,7 @@ var _ = Describe("Test Kubectl Plugin", func() {
|
||||
tdName := "annotations"
|
||||
output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", tdName))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).Should(ContainSubstring("map[string](null|string)"))
|
||||
Expect(output).Should(ContainSubstring("map[string]:(null|string)"))
|
||||
})
|
||||
It("Test show webservice def with cue ignore annotation ", func() {
|
||||
tdName := "webservice"
|
||||
@@ -679,6 +681,7 @@ spec:
|
||||
- containerPort: 80
|
||||
|
||||
---
|
||||
## From the trait test-ingress
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
@@ -701,6 +704,7 @@ spec:
|
||||
app.oam.dev/component: express-server
|
||||
|
||||
---
|
||||
## From the trait test-ingress
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
@@ -976,20 +980,20 @@ spec:
|
||||
}
|
||||
`
|
||||
|
||||
var showCdResult = `# Properties
|
||||
var showCdResult = `# Specification
|
||||
+---------+--------------------------------------------------------------------------------------------------+----------+----------+---------+
|
||||
| NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |
|
||||
+---------+--------------------------------------------------------------------------------------------------+----------+----------+---------+
|
||||
| cmd | Commands to run in the container | []string | false | |
|
||||
| count | specify number of tasks to run in parallel | int | true | 1 |
|
||||
| restart | Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never. | string | true | Never |
|
||||
| image | Which image would you like to use for your service | string | true | |
|
||||
| cmd | Commands to run in the container. | []string | false | |
|
||||
| count | specify number of tasks to run in parallel. | int | false | 1 |
|
||||
| restart | Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never. | string | false | Never |
|
||||
| image | Which image would you like to use for your service. | string | true | |
|
||||
+---------+--------------------------------------------------------------------------------------------------+----------+----------+---------+
|
||||
|
||||
|
||||
`
|
||||
|
||||
var showTdResult = `# Properties
|
||||
var showTdResult = `# Specification
|
||||
+---------+-------------+----------+----------+---------+
|
||||
| NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |
|
||||
+---------+-------------+----------+----------+---------+
|
||||
|
||||
1
go.mod
1
go.mod
@@ -121,6 +121,7 @@ require (
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
sigs.k8s.io/gateway-api v0.4.3
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
9
go.sum
9
go.sum
@@ -220,6 +220,7 @@ github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki
|
||||
github.com/agiledragon/gomonkey/v2 v2.4.0 h1:YDQJYiSQ8o78dCMXehU1E4F/Kh4jPX+MV+/iK/yfL7s=
|
||||
github.com/agiledragon/gomonkey/v2 v2.4.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
@@ -3256,6 +3257,7 @@ k8s.io/code-generator v0.20.10/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibgh
|
||||
k8s.io/code-generator v0.20.10/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU=
|
||||
k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U=
|
||||
k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo=
|
||||
k8s.io/code-generator v0.22.0/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
|
||||
k8s.io/code-generator v0.22.1/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
|
||||
k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
|
||||
k8s.io/code-generator v0.22.4/go.mod h1:qjYl54pQ/emhkT0UxbufbREYJMWsHNNV/jSVwhYZQGw=
|
||||
@@ -3300,11 +3302,13 @@ k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
||||
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao=
|
||||
k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
@@ -3315,6 +3319,7 @@ k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc=
|
||||
k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
@@ -3363,6 +3368,7 @@ k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
|
||||
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20211208161948-7d6a63dca704/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
@@ -3406,6 +3412,7 @@ sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2
|
||||
sigs.k8s.io/controller-runtime v0.6.2/go.mod h1:vhcq/rlnENJ09SIRp3EveTaZ0yqH526hjf9iJdbUJ/E=
|
||||
sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk=
|
||||
sigs.k8s.io/controller-runtime v0.9.5/go.mod h1:q6PpkM5vqQubEKUKOM6qr06oXGzOBcCby1DA9FbyZeA=
|
||||
sigs.k8s.io/controller-runtime v0.9.6/go.mod h1:q6PpkM5vqQubEKUKOM6qr06oXGzOBcCby1DA9FbyZeA=
|
||||
sigs.k8s.io/controller-runtime v0.10.3/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY=
|
||||
sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=
|
||||
sigs.k8s.io/controller-runtime v0.11.1/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=
|
||||
@@ -3415,6 +3422,8 @@ sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3L
|
||||
sigs.k8s.io/controller-tools v0.2.8/go.mod h1:9VKHPszmf2DHz/QmHkcfZoewO6BL7pPs9uAiBVsaJSE=
|
||||
sigs.k8s.io/controller-tools v0.6.2 h1:+Y8L0UsAugDipGRw8lrkPoAi6XqlQVZuf1DQHME3PgU=
|
||||
sigs.k8s.io/controller-tools v0.6.2/go.mod h1:oaeGpjXn6+ZSEIQkUe/+3I40PNiDYp9aeawbt3xTgJ8=
|
||||
sigs.k8s.io/gateway-api v0.4.3 h1:9kdHAcfkyP7jVMSFshc8EYEKNLlFM7hbZL8vCKcMwps=
|
||||
sigs.k8s.io/gateway-api v0.4.3/go.mod h1:r3eiNP+0el+NTLwaTfOrCNXy8TukC+dIM3ggc+fbNWk=
|
||||
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
Copyright 2022 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
|
||||
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
|
||||
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.
|
||||
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 main
|
||||
150
hack/docgen/def/collect-translation/collect.go
Normal file
150
hack/docgen/def/collect-translation/collect.go
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright 2022 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 main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var i18nDoc = map[string]map[string]string{}
|
||||
|
||||
const cnComp = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/components/references.md"
|
||||
const enComp = "../kubevela.io/docs/end-user/components/references.md"
|
||||
|
||||
/*
|
||||
const enTrait = "../kubevela.io/docs/end-user/traits/references.md"
|
||||
const cnTrait = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/traits/references.md"
|
||||
const enPolicy = "../kubevela.io/docs/end-user/policies/references.md"
|
||||
const cnPolicy = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/policies/references.md"
|
||||
const cnWorkflow = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/workflow/built-in-workflow-defs.md"
|
||||
const enWorkflow = "../kubevela.io/docs/end-user/workflow/built-in-workflow-defs.md"
|
||||
*/
|
||||
|
||||
func main() {
|
||||
|
||||
pathCN := flag.String("path-cn", cnComp, "specify the path of chinese reference doc.")
|
||||
pathEN := flag.String("path-en", enComp, "specify the path of english reference doc.")
|
||||
path := flag.String("path", "", "path of existing i18n json data, if specified, it will read the file and keep the old data with append only.")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *path != "" {
|
||||
data, err := ioutil.ReadFile(*path)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(data, &i18nDoc)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paths := strings.Split(*pathEN, ";")
|
||||
var enbuff string
|
||||
for _, v := range paths {
|
||||
if strings.TrimSpace(v) == "" {
|
||||
continue
|
||||
}
|
||||
data, err := ioutil.ReadFile(v)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
enbuff += string(data) + "\n"
|
||||
}
|
||||
|
||||
cnpaths := strings.Split(*pathCN, ";")
|
||||
var cnbuff string
|
||||
for _, v := range cnpaths {
|
||||
if strings.TrimSpace(v) == "" {
|
||||
continue
|
||||
}
|
||||
data, err := ioutil.ReadFile(v)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
cnbuff += string(data) + "\n"
|
||||
}
|
||||
|
||||
var entable, cntable = map[string]string{}, map[string]string{}
|
||||
ens := strings.Split(enbuff, "\n")
|
||||
for _, v := range ens {
|
||||
values := strings.Split(v, "|")
|
||||
if len(values) < 4 {
|
||||
continue
|
||||
}
|
||||
var a, b = 0, 1
|
||||
if values[0] == "" {
|
||||
a, b = 1, 2
|
||||
}
|
||||
key := strings.TrimSpace(values[a])
|
||||
desc := strings.Trim(strings.TrimSpace(values[b]), ".")
|
||||
if strings.Contains(key, "----") {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(desc) == "" {
|
||||
continue
|
||||
}
|
||||
if len(entable[key]) > len(desc) {
|
||||
continue
|
||||
}
|
||||
entable[key] = desc
|
||||
}
|
||||
|
||||
cns := strings.Split(cnbuff, "\n")
|
||||
for _, v := range cns {
|
||||
values := strings.Split(v, "|")
|
||||
if len(values) < 5 {
|
||||
continue
|
||||
}
|
||||
var a, b = 0, 1
|
||||
if values[0] == "" {
|
||||
a, b = 1, 2
|
||||
}
|
||||
key := strings.TrimSpace(values[a])
|
||||
desc := strings.Trim(strings.TrimSpace(values[b]), ".")
|
||||
if strings.Contains(key, "----") {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(desc) == "" {
|
||||
continue
|
||||
}
|
||||
if len(cntable[key]) > len(desc) {
|
||||
continue
|
||||
}
|
||||
cntable[key] = desc
|
||||
}
|
||||
|
||||
for k, v := range entable {
|
||||
|
||||
trans := i18nDoc[v]
|
||||
if trans == nil {
|
||||
trans = map[string]string{}
|
||||
}
|
||||
trans["Chinese"] = cntable[k]
|
||||
// fmt.Println("Key=", k, " | ", v, " | ", cntable[k])
|
||||
i18nDoc[v] = trans
|
||||
}
|
||||
output, err := json.MarshalIndent(i18nDoc, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
}
|
||||
75
hack/docgen/def/gen.go
Normal file
75
hack/docgen/def/gen.go
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright 2022 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/hack/docgen/def/mods"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/references/docgen"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
ctx := context.Background()
|
||||
c, err := common.InitBaseRestConfig()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
path := flag.String("path", "", "specify the path of output")
|
||||
location := flag.String("location", "", "path of output")
|
||||
defdir := flag.String("def-dir", "", "path of definition dir")
|
||||
tp := flag.String("type", "", "choose one of the definition to print")
|
||||
i18nfile := flag.String("i18n", "../kubevela.io/static/reference-i18n.json", "file path of i18n data")
|
||||
flag.Parse()
|
||||
|
||||
if *i18nfile != "" {
|
||||
docgen.LoadI18nData(*i18nfile)
|
||||
}
|
||||
|
||||
if *tp == "" && (*defdir != "" || *path != "") {
|
||||
fmt.Println("you must specify a type with definition ref path specified ")
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("creating docs with args path=%s, location=%s, defdir=%s, type=%s.\n", *path, *location, *defdir, *tp)
|
||||
switch types.CapType(*tp) {
|
||||
case types.TypeComponentDefinition, "component", "comp":
|
||||
mods.ComponentDef(ctx, c, path, location, *defdir)
|
||||
case types.TypeTrait:
|
||||
mods.TraitDef(ctx, c, path, location, *defdir)
|
||||
case types.TypePolicy:
|
||||
mods.PolicyDef(ctx, c, path, location, *defdir)
|
||||
case types.TypeWorkflowStep, "workflow", "wf":
|
||||
mods.WorkflowDef(ctx, c, path, location, *defdir)
|
||||
case "":
|
||||
mods.ComponentDef(ctx, c, path, location, *defdir)
|
||||
mods.TraitDef(ctx, c, path, location, *defdir)
|
||||
mods.PolicyDef(ctx, c, path, location, *defdir)
|
||||
mods.WorkflowDef(ctx, c, path, location, *defdir)
|
||||
default:
|
||||
fmt.Printf("type %s not supported\n", *tp)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
||||
120
hack/docgen/def/mods/component.go
Normal file
120
hack/docgen/def/mods/component.go
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2022 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 mods
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/references/docgen"
|
||||
)
|
||||
|
||||
const (
|
||||
// ComponentDefRefPath is the target path for kubevela.io component ref docs
|
||||
ComponentDefRefPath = "../kubevela.io/docs/end-user/components/references.md"
|
||||
// ComponentDefRefPathZh is the target path for kubevela.io component ref docs in Chinese
|
||||
ComponentDefRefPathZh = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/components/references.md"
|
||||
|
||||
// ComponentDefDir store inner CUE definition
|
||||
ComponentDefDir = "./vela-templates/definitions/internal/component/"
|
||||
)
|
||||
|
||||
// CustomComponentHeaderEN .
|
||||
var CustomComponentHeaderEN = `---
|
||||
title: Built-in Component Type
|
||||
---
|
||||
|
||||
This documentation will walk through all the built-in component types sorted alphabetically.
|
||||
|
||||
` + fmt.Sprintf("> It was generated automatically by [scripts](../../contributor/cli-ref-doc), please don't update manually, last updated at %s.\n\n", time.Now().Format(time.RFC3339))
|
||||
|
||||
// CustomComponentHeaderZH .
|
||||
var CustomComponentHeaderZH = `---
|
||||
title: 内置组件列表
|
||||
---
|
||||
|
||||
本文档将**按字典序**展示所有内置组件的参数列表。
|
||||
|
||||
` + fmt.Sprintf("> 本文档由[脚本](../../contributor/cli-ref-doc)自动生成,请勿手动修改,上次更新于 %s。\n\n", time.Now().Format(time.RFC3339))
|
||||
|
||||
// ComponentDef generate component def reference doc
|
||||
func ComponentDef(ctx context.Context, c common.Args, path, location *string, defdir string) {
|
||||
if defdir == "" {
|
||||
defdir = ComponentDefDir
|
||||
}
|
||||
ref := &docgen.MarkdownReference{
|
||||
AllInOne: true,
|
||||
Filter: func(capability types.Capability) bool {
|
||||
if capability.Type != types.TypeComponentDefinition || capability.Category != types.CUECategory {
|
||||
return false
|
||||
}
|
||||
if capability.Labels != nil && (capability.Labels[types.LabelDefinitionHidden] == "true" || capability.Labels[types.LabelDefinitionDeprecated] == "true") {
|
||||
return false
|
||||
}
|
||||
// only print capability which contained in cue def
|
||||
files, err := ioutil.ReadDir(defdir)
|
||||
if err != nil {
|
||||
fmt.Println("read dir err", defdir, err)
|
||||
return false
|
||||
}
|
||||
for _, f := range files {
|
||||
if strings.Contains(f.Name(), capability.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
CustomDocHeader: CustomComponentHeaderEN,
|
||||
}
|
||||
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
|
||||
|
||||
if *path != "" {
|
||||
ref.I18N = &docgen.En
|
||||
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomComponentHeaderZH
|
||||
}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
|
||||
}
|
||||
if *location == "" || *location == "en" {
|
||||
ref.I18N = &docgen.En
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, ComponentDefRefPath); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), ComponentDefRefPath)
|
||||
}
|
||||
if *location == "" || *location == "zh" {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomComponentHeaderZH
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, ComponentDefRefPathZh); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), ComponentDefRefPathZh)
|
||||
}
|
||||
}
|
||||
119
hack/docgen/def/mods/policy.go
Normal file
119
hack/docgen/def/mods/policy.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2022 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 mods
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/references/docgen"
|
||||
)
|
||||
|
||||
const (
|
||||
// PolicyDefRefPath is the target path for kubevela.io policy ref docs
|
||||
PolicyDefRefPath = "../kubevela.io/docs/end-user/policies/references.md"
|
||||
// PolicyDefRefPathZh is the target path for kubevela.io policy ref docs in Chinese
|
||||
PolicyDefRefPathZh = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/policies/references.md"
|
||||
|
||||
// PolicyDefDir store inner CUE definition
|
||||
PolicyDefDir = "./vela-templates/definitions/internal/policy/"
|
||||
)
|
||||
|
||||
// CustomPolicyHeaderEN .
|
||||
var CustomPolicyHeaderEN = `---
|
||||
title: Built-in Policy Type
|
||||
---
|
||||
|
||||
This documentation will walk through all the built-in policy types sorted alphabetically.
|
||||
|
||||
` + fmt.Sprintf("> It was generated automatically by [scripts](../../contributor/cli-ref-doc), please don't update manually, last updated at %s.\n\n", time.Now().Format(time.RFC3339))
|
||||
|
||||
// CustomPolicyHeaderZH .
|
||||
var CustomPolicyHeaderZH = `---
|
||||
title: 内置策略列表
|
||||
---
|
||||
|
||||
本文档将**按字典序**展示所有内置策略的参数列表。
|
||||
|
||||
` + fmt.Sprintf("> 本文档由[脚本](../../contributor/cli-ref-doc)自动生成,请勿手动修改,上次更新于 %s。\n\n", time.Now().Format(time.RFC3339))
|
||||
|
||||
// PolicyDef generate policy def reference doc
|
||||
func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdir string) {
|
||||
if defdir == "" {
|
||||
defdir = PolicyDefDir
|
||||
}
|
||||
ref := &docgen.MarkdownReference{
|
||||
AllInOne: true,
|
||||
Filter: func(capability types.Capability) bool {
|
||||
if capability.Type != types.TypePolicy || capability.Category != types.CUECategory {
|
||||
return false
|
||||
}
|
||||
if capability.Labels != nil && (capability.Labels[types.LabelDefinitionHidden] == "true" || capability.Labels[types.LabelDefinitionDeprecated] == "true") {
|
||||
return false
|
||||
}
|
||||
// only print capability which contained in cue def
|
||||
files, err := ioutil.ReadDir(defdir)
|
||||
if err != nil {
|
||||
fmt.Println("read dir err", defdir, err)
|
||||
return false
|
||||
}
|
||||
for _, f := range files {
|
||||
if strings.Contains(f.Name(), capability.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
CustomDocHeader: CustomPolicyHeaderEN,
|
||||
}
|
||||
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
|
||||
if *path != "" {
|
||||
ref.I18N = &docgen.En
|
||||
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomPolicyHeaderZH
|
||||
}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
|
||||
}
|
||||
if *location == "" || *location == "en" {
|
||||
ref.I18N = &docgen.En
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, PolicyDefRefPath); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), PolicyDefRefPath)
|
||||
}
|
||||
if *location == "" || *location == "zh" {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomPolicyHeaderZH
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, PolicyDefRefPathZh); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), PolicyDefRefPathZh)
|
||||
}
|
||||
}
|
||||
120
hack/docgen/def/mods/trait.go
Normal file
120
hack/docgen/def/mods/trait.go
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2022 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 mods
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/references/docgen"
|
||||
)
|
||||
|
||||
const (
|
||||
// TraitDefRefPath is the target path for kubevela.io trait ref docs
|
||||
TraitDefRefPath = "../kubevela.io/docs/end-user/traits/references.md"
|
||||
// TraitDefRefPathZh is the target path for kubevela.io trait ref docs in Chinese
|
||||
TraitDefRefPathZh = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/traits/references.md"
|
||||
|
||||
// TraitDefDir store inner CUE definition
|
||||
TraitDefDir = "./vela-templates/definitions/internal/trait/"
|
||||
)
|
||||
|
||||
// CustomTraitHeaderEN .
|
||||
var CustomTraitHeaderEN = `---
|
||||
title: Built-in Trait Type
|
||||
---
|
||||
|
||||
This documentation will walk through all the built-in trait types sorted alphabetically.
|
||||
|
||||
` + fmt.Sprintf("> It was generated automatically by [scripts](../../contributor/cli-ref-doc), please don't update manually, last updated at %s.\n\n", time.Now().Format(time.RFC3339))
|
||||
|
||||
// CustomTraitHeaderZH .
|
||||
var CustomTraitHeaderZH = `---
|
||||
title: 内置运维特征列表
|
||||
---
|
||||
|
||||
本文档将**按字典序**展示所有内置运维特征的参数列表。
|
||||
|
||||
` + fmt.Sprintf("> 本文档由[脚本](../../contributor/cli-ref-doc)自动生成,请勿手动修改,上次更新于 %s。\n\n", time.Now().Format(time.RFC3339))
|
||||
|
||||
// TraitDef generate trait def reference doc
|
||||
func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir string) {
|
||||
if defdir == "" {
|
||||
defdir = TraitDefDir
|
||||
}
|
||||
ref := &docgen.MarkdownReference{
|
||||
AllInOne: true,
|
||||
Filter: func(capability types.Capability) bool {
|
||||
if capability.Type != types.TypeTrait || capability.Category != types.CUECategory {
|
||||
return false
|
||||
}
|
||||
if capability.Labels != nil && (capability.Labels[types.LabelDefinitionDeprecated] == "true") {
|
||||
return false
|
||||
}
|
||||
// only print capability which contained in cue def
|
||||
files, err := ioutil.ReadDir(defdir)
|
||||
if err != nil {
|
||||
fmt.Println("read dir err", defdir, err)
|
||||
return false
|
||||
}
|
||||
for _, f := range files {
|
||||
if strings.Contains(f.Name(), capability.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
CustomDocHeader: CustomTraitHeaderEN,
|
||||
}
|
||||
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
|
||||
|
||||
if *path != "" {
|
||||
ref.I18N = &docgen.En
|
||||
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomTraitHeaderZH
|
||||
}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
|
||||
}
|
||||
if *location == "" || *location == "en" {
|
||||
ref.I18N = &docgen.En
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, TraitDefRefPath); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), TraitDefRefPath)
|
||||
}
|
||||
if *location == "" || *location == "zh" {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomTraitHeaderZH
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, TraitDefRefPathZh); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), TraitDefRefPathZh)
|
||||
}
|
||||
}
|
||||
122
hack/docgen/def/mods/workflow.go
Normal file
122
hack/docgen/def/mods/workflow.go
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
Copyright 2022 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 mods
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/references/docgen"
|
||||
)
|
||||
|
||||
const (
|
||||
// WorkflowDefRefPath is the target path for kubevela.io workflow ref docs
|
||||
WorkflowDefRefPath = "../kubevela.io/docs/end-user/workflow/built-in-workflow-defs.md"
|
||||
// WorkflowDefRefPathZh is the target path for kubevela.io workflow ref docs in Chinese
|
||||
WorkflowDefRefPathZh = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/workflow/built-in-workflow-defs.md"
|
||||
|
||||
// WorkflowDefDir store inner CUE definition
|
||||
WorkflowDefDir = "./vela-templates/definitions/internal/workflowstep/"
|
||||
)
|
||||
|
||||
// CustomWorkflowHeaderEN .
|
||||
var CustomWorkflowHeaderEN = `---
|
||||
title: Built-in WorkflowStep Type
|
||||
---
|
||||
|
||||
This documentation will walk through all the built-in workflow step types sorted alphabetically.
|
||||
|
||||
` + fmt.Sprintf("> It was generated automatically by [scripts](../../contributor/cli-ref-doc), please don't update manually, last updated at %s.\n\n", time.Now().Format(time.RFC3339))
|
||||
|
||||
// CustomWorkflowHeaderZH .
|
||||
var CustomWorkflowHeaderZH = `---
|
||||
title: 内置工作流步骤列表
|
||||
---
|
||||
|
||||
本文档将**按字典序**展示所有内置工作流步骤的参数列表。
|
||||
|
||||
` + fmt.Sprintf("> 本文档由[脚本](../../contributor/cli-ref-doc)自动生成,请勿手动修改,上次更新于 %s。\n\n", time.Now().Format(time.RFC3339))
|
||||
|
||||
// WorkflowDef generate workflow def reference doc
|
||||
func WorkflowDef(ctx context.Context, c common.Args, path, location *string, defdir string) {
|
||||
if defdir == "" {
|
||||
defdir = WorkflowDefDir
|
||||
}
|
||||
ref := &docgen.MarkdownReference{
|
||||
AllInOne: true,
|
||||
Filter: func(capability types.Capability) bool {
|
||||
|
||||
if capability.Type != types.TypeWorkflowStep || capability.Category != types.CUECategory {
|
||||
return false
|
||||
}
|
||||
|
||||
if capability.Labels != nil && capability.Labels[types.LabelDefinitionDeprecated] == "true" {
|
||||
return false
|
||||
}
|
||||
// only print capability which contained in cue def
|
||||
files, err := ioutil.ReadDir(defdir)
|
||||
if err != nil {
|
||||
fmt.Println("read dir err", defdir, err)
|
||||
return false
|
||||
}
|
||||
for _, f := range files {
|
||||
if strings.Contains(f.Name(), capability.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
CustomDocHeader: CustomWorkflowHeaderEN,
|
||||
}
|
||||
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
|
||||
|
||||
if *path != "" {
|
||||
ref.I18N = &docgen.En
|
||||
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomWorkflowHeaderZH
|
||||
}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
|
||||
}
|
||||
if *location == "" || *location == "en" {
|
||||
ref.I18N = &docgen.En
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, WorkflowDefRefPath); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), WorkflowDefRefPath)
|
||||
}
|
||||
if *location == "" || *location == "zh" {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomWorkflowHeaderZH
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, WorkflowDefRefPathZh); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), WorkflowDefRefPathZh)
|
||||
}
|
||||
}
|
||||
87
hack/docgen/terraform/generate.go
Normal file
87
hack/docgen/terraform/generate.go
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/references/docgen"
|
||||
)
|
||||
|
||||
const (
|
||||
// KubeVelaIOTerraformPath is the target path for kubevela.io terraform docs
|
||||
KubeVelaIOTerraformPath = "../kubevela.io/docs/end-user/components/cloud-services/terraform"
|
||||
// KubeVelaIOTerraformPathZh is the target path for kubevela.io terraform docs in Chinese
|
||||
KubeVelaIOTerraformPathZh = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/components/cloud-services/terraform"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ref := &docgen.MarkdownReference{}
|
||||
ctx := context.Background()
|
||||
|
||||
c, err := common.InitBaseRestConfig()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
|
||||
ref.Filter = func(capability types.Capability) bool {
|
||||
if capability.Labels != nil && capability.Labels[types.LabelDefinitionHidden] == "true" {
|
||||
return false
|
||||
}
|
||||
return capability.Type == types.TypeComponentDefinition && capability.Category == types.TerraformCategory
|
||||
}
|
||||
|
||||
path := flag.String("path", "", "path of output")
|
||||
location := flag.String("location", "", "path of output")
|
||||
i18nfile := flag.String("i18n", "../kubevela.io/static/reference-i18n.json", "file path of i18n data")
|
||||
flag.Parse()
|
||||
|
||||
if *i18nfile != "" {
|
||||
docgen.LoadI18nData(*i18nfile)
|
||||
}
|
||||
|
||||
if *path != "" {
|
||||
ref.I18N = &docgen.En
|
||||
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
|
||||
ref.I18N = &docgen.Zh
|
||||
}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("terraform reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
|
||||
}
|
||||
ref.I18N = &docgen.En
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, KubeVelaIOTerraformPath); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("terraform reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), KubeVelaIOTerraformPath)
|
||||
ref.I18N = &docgen.Zh
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, KubeVelaIOTerraformPathZh); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("terraform reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), KubeVelaIOTerraformPathZh)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
## Conflicts With
|
||||
|
||||
### `Autoscale`
|
||||
|
||||
When `Rollout` and `Autoscle` traits are attached to the same service, they two will fight over the number of instances during rollout. Thus, it's by design that `Rollout` will take over replicas control (specified by `.replicas` field) during rollout.
|
||||
|
||||
> Note: in up coming releases, KubeVela will introduce a separate section in Appfile to define release phase configurations such as `Rollout`.
|
||||
|
||||
## How `Rollout` works?
|
||||
|
||||
`Rollout` trait implements progressive release process to rollout your app following [Canary strategy](https://martinfowler.com/bliki/CanaryRelease.html).
|
||||
|
||||
In detail, `Rollout` controller will create a canary of your app , and then gradually shift traffic to the canary while measuring key performance indicators like HTTP requests success rate at the same time.
|
||||
|
||||
|
||||

|
||||
|
||||
In this sample, for every `10s`, `5%` traffic will be shifted to canary from the primary, until the traffic on canary reached `50%`. At the mean time, the instance number of canary will automatically scale to `replicas: 2` per configured in Appfile.
|
||||
|
||||
|
||||
Based on analysis result of the KPIs during this traffic shifting, a canary will be promoted or aborted if analysis is failed. If promoting, the primary will be upgraded from v1 to v2, and traffic will be fully shifted back to the primary instances. So as result, canary instances will be deleted after the promotion finished.
|
||||
|
||||

|
||||
|
||||
> Note: KubeVela's `Rollout` trait is implemented with [Weaveworks Flagger](https://flagger.app/) operator.
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/references/plugins"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ref := &plugins.MarkdownReference{}
|
||||
ctx := context.Background()
|
||||
path := plugins.BaseRefPath
|
||||
|
||||
if len(os.Args) == 2 {
|
||||
ref.DefinitionName = os.Args[1]
|
||||
path = plugins.KubeVelaIOTerraformPath
|
||||
}
|
||||
|
||||
c, err := common.InitBaseRestConfig()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ref.Remote = &plugins.Remote{Namespace: types.DefaultKubeVelaNS}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -33,9 +33,9 @@ import (
|
||||
"time"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
cueyaml "cuelang.org/go/encoding/yaml"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/google/go-github/v32/github"
|
||||
"github.com/imdario/mergo"
|
||||
prismclusterv1alpha1 "github.com/kubevela/prism/pkg/apis/cluster/v1alpha1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
@@ -59,13 +59,10 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
|
||||
utils2 "github.com/oam-dev/kubevela/pkg/controller/utils"
|
||||
cuemodel "github.com/oam-dev/kubevela/pkg/cue/model"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
||||
"github.com/oam-dev/kubevela/pkg/definition"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
@@ -80,7 +77,7 @@ import (
|
||||
|
||||
const (
|
||||
// ReadmeFileName is the addon readme file name
|
||||
ReadmeFileName string = "readme.md"
|
||||
ReadmeFileName string = "README.md"
|
||||
|
||||
// MetadataFileName is the addon meatadata.yaml file name
|
||||
MetadataFileName string = "metadata.yaml"
|
||||
@@ -88,6 +85,12 @@ const (
|
||||
// TemplateFileName is the addon template.yaml file name
|
||||
TemplateFileName string = "template.yaml"
|
||||
|
||||
// AppTemplateCueFileName is the addon application template.cue file name
|
||||
AppTemplateCueFileName string = "template.cue"
|
||||
|
||||
// GlobalParameterFileName is the addon global parameter.cue file name
|
||||
GlobalParameterFileName string = "parameter.cue"
|
||||
|
||||
// ResourcesDirName is the addon resources/ dir name
|
||||
ResourcesDirName string = "resources"
|
||||
|
||||
@@ -140,6 +143,8 @@ const (
|
||||
ObservabilityAddonDomainArg = "domain"
|
||||
// LocalAddonRegistryName is the addon-registry name for those installed by local dir
|
||||
LocalAddonRegistryName = "local"
|
||||
// ClusterLabelSelector define the key of topology cluster label selector
|
||||
ClusterLabelSelector = "clusterLabelSelector"
|
||||
)
|
||||
|
||||
// ObservabilityEnvironment contains the Observability addon's domain for each cluster
|
||||
@@ -197,7 +202,10 @@ type Pattern struct {
|
||||
}
|
||||
|
||||
// Patterns is the file pattern that the addon should be in
|
||||
var Patterns = []Pattern{{Value: ReadmeFileName}, {Value: MetadataFileName}, {Value: TemplateFileName}, {Value: ParameterFileName}, {IsDir: true, Value: ResourcesDirName}, {IsDir: true, Value: DefinitionsDirName}, {IsDir: true, Value: DefSchemaName}, {IsDir: true, Value: ViewDirName}}
|
||||
var Patterns = []Pattern{
|
||||
{Value: ReadmeFileName}, {Value: MetadataFileName}, {Value: TemplateFileName},
|
||||
{Value: ParameterFileName}, {IsDir: true, Value: ResourcesDirName}, {IsDir: true, Value: DefinitionsDirName},
|
||||
{IsDir: true, Value: DefSchemaName}, {IsDir: true, Value: ViewDirName}, {Value: AppTemplateCueFileName}, {Value: GlobalParameterFileName}}
|
||||
|
||||
// GetPatternFromItem will check if the file path has a valid pattern, return empty string if it's invalid.
|
||||
// AsyncReader is needed to calculate relative path
|
||||
@@ -276,10 +284,11 @@ func GetUIDataFromReader(r AsyncReader, meta *SourceMeta, opt ListOptions) (*UID
|
||||
skip bool
|
||||
read func(a *UIData, reader AsyncReader, readPath string) error
|
||||
}{
|
||||
ReadmeFileName: {!opt.GetDetail, readReadme},
|
||||
MetadataFileName: {false, readMetadata},
|
||||
DefinitionsDirName: {!opt.GetDefinition, readDefFile},
|
||||
ParameterFileName: {!opt.GetParameter, readParamFile},
|
||||
ReadmeFileName: {!opt.GetDetail, readReadme},
|
||||
MetadataFileName: {false, readMetadata},
|
||||
DefinitionsDirName: {!opt.GetDefinition, readDefFile},
|
||||
ParameterFileName: {!opt.GetParameter, readParamFile},
|
||||
GlobalParameterFileName: {!opt.GetParameter, readGlobalParamFile},
|
||||
}
|
||||
ptItems := ClassifyItemByPattern(meta, r)
|
||||
var addon = &UIData{}
|
||||
@@ -296,7 +305,13 @@ func GetUIDataFromReader(r AsyncReader, meta *SourceMeta, opt ListOptions) (*UID
|
||||
}
|
||||
}
|
||||
|
||||
if opt.GetParameter && addon.Parameters != "" {
|
||||
if opt.GetParameter && (len(addon.Parameters) != 0 || len(addon.GlobalParameters) != 0) {
|
||||
if addon.GlobalParameters != "" {
|
||||
if addon.Parameters != "" {
|
||||
klog.Warning("both legacy parameter and global parameter are provided, but only global parameter will be used. Consider removing the legacy parameters.")
|
||||
}
|
||||
addon.Parameters = addon.GlobalParameters
|
||||
}
|
||||
err := genAddonAPISchema(addon)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to generate openAPIschema for addon %s : %w", meta.Name, err)
|
||||
@@ -309,10 +324,11 @@ func GetUIDataFromReader(r AsyncReader, meta *SourceMeta, opt ListOptions) (*UID
|
||||
// GetInstallPackageFromReader get install package of addon from Reader, this is used to enable an addon
|
||||
func GetInstallPackageFromReader(r AsyncReader, meta *SourceMeta, uiData *UIData) (*InstallPackage, error) {
|
||||
addonContentsReader := map[string]func(a *InstallPackage, reader AsyncReader, readPath string) error{
|
||||
TemplateFileName: readTemplate,
|
||||
ResourcesDirName: readResFile,
|
||||
DefSchemaName: readDefSchemaFile,
|
||||
ViewDirName: readViewFile,
|
||||
TemplateFileName: readTemplate,
|
||||
ResourcesDirName: readResFile,
|
||||
DefSchemaName: readDefSchemaFile,
|
||||
ViewDirName: readViewFile,
|
||||
AppTemplateCueFileName: readAppCueTemplate,
|
||||
}
|
||||
ptItems := ClassifyItemByPattern(meta, r)
|
||||
|
||||
@@ -353,6 +369,15 @@ func readTemplate(a *InstallPackage, reader AsyncReader, readPath string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func readAppCueTemplate(a *InstallPackage, reader AsyncReader, readPath string) error {
|
||||
data, err := reader.ReadFile(readPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.AppCueTemplate = ElementFile{Data: data, Name: filepath.Base(readPath)}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readParamFile read single resource/parameter.cue file
|
||||
func readParamFile(a *UIData, reader AsyncReader, readPath string) error {
|
||||
b, err := reader.ReadFile(readPath)
|
||||
@@ -363,6 +388,16 @@ func readParamFile(a *UIData, reader AsyncReader, readPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// readGlobalParamFile read global parameter file.
|
||||
func readGlobalParamFile(a *UIData, reader AsyncReader, readPath string) error {
|
||||
b, err := reader.ReadFile(readPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.GlobalParameters = b
|
||||
return nil
|
||||
}
|
||||
|
||||
// readResFile read single resource file
|
||||
func readResFile(a *InstallPackage, reader AsyncReader, readPath string) error {
|
||||
filename := path.Base(readPath)
|
||||
@@ -600,56 +635,6 @@ func renderNeededNamespaceAsComps(addon *InstallPackage) []common2.ApplicationCo
|
||||
return nscomps
|
||||
}
|
||||
|
||||
func renderResources(addon *InstallPackage, args map[string]interface{}) ([]common2.ApplicationComponent, error) {
|
||||
var resources []common2.ApplicationComponent
|
||||
if len(addon.YAMLTemplates) != 0 {
|
||||
comp, err := renderK8sObjectsComponent(addon.YAMLTemplates, addon.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resources = append(resources, *comp)
|
||||
}
|
||||
|
||||
for _, tmpl := range addon.CUETemplates {
|
||||
comp, err := renderCUETemplate(tmpl, addon.Parameters, args, addon.Meta)
|
||||
if err != nil && strings.Contains(err.Error(), "var(path=output) not exist") {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, NewAddonError(fmt.Sprintf("fail to render cue template %s", err.Error()))
|
||||
}
|
||||
if addon.Name == ObservabilityAddon && strings.HasSuffix(comp.Name, ".cue") {
|
||||
comp.Name = strings.Split(comp.Name, ".cue")[0]
|
||||
}
|
||||
resources = append(resources, *comp)
|
||||
}
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func formatAppFramework(addon *InstallPackage) *v1beta1.Application {
|
||||
app := addon.AppTemplate
|
||||
if app == nil {
|
||||
app = &v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "core.oam.dev/v1beta1", Kind: "Application"},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common2.ApplicationComponent{},
|
||||
},
|
||||
}
|
||||
}
|
||||
if app.Spec.Components == nil {
|
||||
app.Spec.Components = []common2.ApplicationComponent{}
|
||||
}
|
||||
app.Name = addonutil.Addon2AppName(addon.Name)
|
||||
// force override the namespace defined vela with DefaultVelaNS,this value can be modified by Env
|
||||
app.SetNamespace(types.DefaultKubeVelaNS)
|
||||
if app.Labels == nil {
|
||||
app.Labels = make(map[string]string)
|
||||
}
|
||||
app.Labels[oam.LabelAddonName] = addon.Name
|
||||
app.Labels[oam.LabelAddonVersion] = addon.Version
|
||||
return app
|
||||
}
|
||||
|
||||
func checkDeployClusters(ctx context.Context, k8sClient client.Client, args map[string]interface{}) ([]string, error) {
|
||||
deployClusters := getClusters(args)
|
||||
if len(deployClusters) == 0 || k8sClient == nil {
|
||||
@@ -682,119 +667,6 @@ func checkDeployClusters(ctx context.Context, k8sClient client.Client, args map[
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// RenderApp render a K8s application
|
||||
func RenderApp(ctx context.Context, addon *InstallPackage, k8sClient client.Client, args map[string]interface{}) (*v1beta1.Application, error) {
|
||||
|
||||
if args == nil {
|
||||
args = map[string]interface{}{}
|
||||
}
|
||||
|
||||
app := formatAppFramework(addon)
|
||||
app.Spec.Components = append(app.Spec.Components, renderNeededNamespaceAsComps(addon)...)
|
||||
|
||||
resources, err := renderResources(addon, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.Spec.Components = append(app.Spec.Components, resources...)
|
||||
|
||||
deployClusters, err := checkDeployClusters(ctx, k8sClient, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case isDeployToRuntimeOnly(addon):
|
||||
if len(deployClusters) == 0 {
|
||||
// deploy to all clusters
|
||||
app.Spec.Workflow = &v1beta1.Workflow{Steps: []v1beta1.WorkflowStep{
|
||||
{
|
||||
Name: "deploy-control-plane",
|
||||
Type: "apply-application",
|
||||
},
|
||||
{
|
||||
Name: "deploy-runtime",
|
||||
Type: "deploy2runtime",
|
||||
},
|
||||
}}
|
||||
// TODO(wonderflow): this can be merged into len(deployClusters) > 0 case
|
||||
/*
|
||||
allclusters, err := multicluster.ListVirtualClusters(ctx, k8sClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range allclusters {
|
||||
deployClusters = append(deployClusters, c.Name)
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
var found bool
|
||||
for _, c := range deployClusters {
|
||||
if c == multicluster.ClusterLocalName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
deployClusters = append(deployClusters, multicluster.ClusterLocalName)
|
||||
}
|
||||
// deploy to specified clusters
|
||||
if app.Spec.Policies == nil {
|
||||
app.Spec.Policies = []v1beta1.AppPolicy{}
|
||||
}
|
||||
body, err := json.Marshal(map[string][]string{types.ClustersArg: deployClusters})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.Spec.Policies = append(app.Spec.Policies, v1beta1.AppPolicy{
|
||||
Name: "specified-addon-clusters",
|
||||
Type: v1alpha1.TopologyPolicyType,
|
||||
Properties: &runtime.RawExtension{Raw: body},
|
||||
})
|
||||
// addon should not contain workflow, this also update legacy addon with deploy2runtime steps
|
||||
app.Spec.Workflow = nil
|
||||
}
|
||||
case addon.Name == ObservabilityAddon:
|
||||
clusters, err := allocateDomainForAddon(ctx, k8sClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
policies, err := preparePolicies4Observability(clusters)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fail to render the policies for Add-on Observability")
|
||||
}
|
||||
app.Spec.Policies = policies
|
||||
|
||||
if len(clusters) > 0 {
|
||||
app.Spec.Workflow = &v1beta1.Workflow{
|
||||
Steps: []v1beta1.WorkflowStep{{
|
||||
Name: "deploy-control-plane",
|
||||
Type: "apply-application-in-parallel",
|
||||
}},
|
||||
}
|
||||
} else {
|
||||
app.Spec.Workflow = &v1beta1.Workflow{
|
||||
Steps: []v1beta1.WorkflowStep{{
|
||||
Name: "deploy-control-plane",
|
||||
Type: "apply-application",
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
workflowSteps, err := prepareWorkflow4Observability(clusters)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fail to prepare the workflow for Add-on Observability")
|
||||
}
|
||||
app.Spec.Workflow.Steps = append(app.Spec.Workflow.Steps, workflowSteps...)
|
||||
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
return app, nil
|
||||
}
|
||||
|
||||
// RenderDefinitions render definition objects if needed
|
||||
func RenderDefinitions(addon *InstallPackage, config *rest.Config) ([]*unstructured.Unstructured, error) {
|
||||
defObjs := make([]*unstructured.Unstructured, 0)
|
||||
@@ -878,55 +750,6 @@ func allocateDomainForAddon(ctx context.Context, k8sClient client.Client) ([]Obs
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
func preparePolicies4Observability(clusters []ObservabilityEnvironment) ([]v1beta1.AppPolicy, error) {
|
||||
if clusters == nil {
|
||||
return nil, nil
|
||||
}
|
||||
envProperties, err := render(clusters, ObservabilityEnvBindingEnvTmpl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var properties runtime.RawExtension
|
||||
envs := fmt.Sprintf("%s\n%s", ObservabilityEnvBindingEnvTag, envProperties)
|
||||
envJSON, err := yaml.YAMLToJSON([]byte(envs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(envJSON, &properties)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
policies := []v1beta1.AppPolicy{{
|
||||
Name: "domain",
|
||||
Type: "env-binding",
|
||||
Properties: &properties,
|
||||
}}
|
||||
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
func prepareWorkflow4Observability(clusters []ObservabilityEnvironment) ([]v1beta1.WorkflowStep, error) {
|
||||
envBindingWorkflow, err := render(clusters, ObservabilityWorkflow4EnvBindingTmpl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var workflow v1beta1.Workflow
|
||||
envs := fmt.Sprintf("%s\n%s", ObservabilityWorkflowStepsTag, envBindingWorkflow)
|
||||
envJSON, err := yaml.YAMLToJSON([]byte(envs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(envJSON, &workflow)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return workflow.Steps, nil
|
||||
}
|
||||
|
||||
func render(envs []ObservabilityEnvironment, tmpl string) (string, error) {
|
||||
todos := ObservabilityEnvBindingValues{
|
||||
Envs: envs,
|
||||
@@ -942,13 +765,6 @@ func render(envs []ObservabilityEnvironment, tmpl string) (string, error) {
|
||||
return rendered.String(), nil
|
||||
}
|
||||
|
||||
func isDeployToRuntimeOnly(addon *InstallPackage) bool {
|
||||
if addon.DeployTo == nil {
|
||||
return false
|
||||
}
|
||||
return addon.DeployTo.RuntimeCluster || addon.DeployTo.LegacyRuntimeCluster
|
||||
}
|
||||
|
||||
func renderObject(elem ElementFile) (*unstructured.Unstructured, error) {
|
||||
obj := &unstructured.Unstructured{}
|
||||
dec := k8syaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
|
||||
@@ -1017,56 +833,6 @@ func renderCUEView(elem ElementFile) (*unstructured.Unstructured, error) {
|
||||
return util.Object2Unstructured(*cm)
|
||||
}
|
||||
|
||||
// renderCUETemplate will return a component from cue template
|
||||
func renderCUETemplate(elem ElementFile, parameters string, args map[string]interface{}, metadata Meta) (*common2.ApplicationComponent, error) {
|
||||
bt, err := json.Marshal(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var contextFile = strings.Builder{}
|
||||
var paramFile = cuemodel.ParameterFieldName + ": {}"
|
||||
if string(bt) != "null" {
|
||||
paramFile = fmt.Sprintf("%s: %s", cuemodel.ParameterFieldName, string(bt))
|
||||
}
|
||||
// addon metadata context
|
||||
metadataJSON, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contextFile.WriteString(fmt.Sprintf("context: metadata: %s\n", string(metadataJSON)))
|
||||
// parameter definition
|
||||
contextFile.WriteString(paramFile + "\n")
|
||||
// user custom parameter
|
||||
contextFile.WriteString(parameters + "\n")
|
||||
|
||||
v, err := value.NewValue(contextFile.String(), nil, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := v.LookupByScript(elem.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
compContent, err := out.LookupValue("output")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := cueyaml.Encode(compContent.CueValue())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileName := strings.ReplaceAll(elem.Name, path.Ext(elem.Name), "")
|
||||
comp := common2.ApplicationComponent{
|
||||
Name: strings.ReplaceAll(fileName, ".", "-"),
|
||||
}
|
||||
err = yaml.Unmarshal(b, &comp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &comp, err
|
||||
}
|
||||
|
||||
// RenderArgsSecret render addon enable argument to secret
|
||||
func RenderArgsSecret(addon *InstallPackage, args map[string]interface{}) *unstructured.Unstructured {
|
||||
argsByte, err := json.Marshal(args)
|
||||
@@ -1194,8 +960,9 @@ func (h *Installer) loadInstallPackage(name, version string) (*InstallPackage, e
|
||||
}
|
||||
} else {
|
||||
versionedRegistry := BuildVersionedRegistry(h.r.Name, h.r.Helm.URL, &common.HTTPOption{
|
||||
Username: h.r.Helm.Username,
|
||||
Password: h.r.Helm.Password,
|
||||
Username: h.r.Helm.Username,
|
||||
Password: h.r.Helm.Password,
|
||||
InsecureSkipTLS: h.r.Helm.InsecureSkipTLS,
|
||||
})
|
||||
installPackage, err = versionedRegistry.GetAddonInstallPackage(context.Background(), name, version)
|
||||
if err != nil {
|
||||
@@ -1289,7 +1056,6 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "render addon application fail")
|
||||
}
|
||||
|
||||
appName, err := determineAddonAppName(h.ctx, h.cli, h.addon.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1317,6 +1083,12 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
|
||||
return errors.Wrapf(err, "cannot pass definition to addon app's annotation")
|
||||
}
|
||||
|
||||
var auxiliaryOutputs []*unstructured.Unstructured
|
||||
auxiliaryOutputs, err = renderOutputs(addon, h.args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = h.createOrUpdate(app); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1345,6 +1117,14 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
|
||||
}
|
||||
}
|
||||
|
||||
for _, o := range auxiliaryOutputs {
|
||||
addOwner(o, app)
|
||||
err = h.apply.Apply(h.ctx, o, apply.DisableUpdateAnnotation())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if h.args != nil && len(h.args) > 0 {
|
||||
sec := RenderArgsSecret(addon, h.args)
|
||||
addOwner(sec, app)
|
||||
@@ -1596,3 +1376,42 @@ func PackageAddon(addonDictPath string) (string, error) {
|
||||
}
|
||||
return archive, nil
|
||||
}
|
||||
|
||||
// GetAddonLegacyParameters get addon's legacy parameters, that is stored in Secret
|
||||
func GetAddonLegacyParameters(ctx context.Context, k8sClient client.Client, addonName string) (map[string]interface{}, error) {
|
||||
var sec v1.Secret
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: addonutil.Addon2SecName(addonName)}, &sec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args, err := FetchArgsFromSecret(&sec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// MergeAddonInstallArgs merge addon's legacy parameter and new input args
|
||||
func MergeAddonInstallArgs(ctx context.Context, k8sClient client.Client, addonName string, args map[string]interface{}) (map[string]interface{}, error) {
|
||||
legacyParams, err := GetAddonLegacyParameters(ctx, k8sClient, addonName)
|
||||
if err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
if args == nil && legacyParams == nil {
|
||||
return args, nil
|
||||
}
|
||||
|
||||
r := make(map[string]interface{})
|
||||
if err := mergo.Merge(&r, legacyParams, mergo.WithOverride); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := mergo.Merge(&r, args, mergo.WithOverride); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ var _ = Describe("Test render addon with specified clusters", func() {
|
||||
}
|
||||
ap, err := RenderApp(ctx, i, k8sClient, args)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(ap.Spec.Policies).Should(BeEquivalentTo([]v1beta1.AppPolicy{{Name: "specified-addon-clusters",
|
||||
Expect(ap.Spec.Policies).Should(BeEquivalentTo([]v1beta1.AppPolicy{{Name: specifyAddonClustersTopologyPolicy,
|
||||
Type: v1alpha12.TopologyPolicyType,
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"clusters":["add-c1","add-c2","local"]}`)}}}))
|
||||
})
|
||||
|
||||
@@ -47,6 +47,7 @@ import (
|
||||
clustercommon "github.com/oam-dev/cluster-gateway/pkg/common"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
@@ -57,12 +58,20 @@ import (
|
||||
var paths = []string{
|
||||
"example/metadata.yaml",
|
||||
"example/readme.md",
|
||||
"example/template.yaml",
|
||||
"example/template.cue",
|
||||
"example/definitions/helm.yaml",
|
||||
"example/resources/configmap.cue",
|
||||
"example/resources/parameter.cue",
|
||||
"example/parameter.cue",
|
||||
"example/resources/service/source-controller.yaml",
|
||||
|
||||
"example-legacy/metadata.yaml",
|
||||
"example-legacy/readme.md",
|
||||
"example-legacy/template.yaml",
|
||||
"example-legacy/definitions/helm.yaml",
|
||||
"example-legacy/resources/configmap.cue",
|
||||
"example-legacy/resources/parameter.cue",
|
||||
"example-legacy/resources/service/source-controller.yaml",
|
||||
|
||||
"terraform/metadata.yaml",
|
||||
"terraform-alibaba/metadata.yaml",
|
||||
|
||||
@@ -158,11 +167,25 @@ func testReaderFunc(t *testing.T, reader AsyncReader) {
|
||||
assert.True(t, uiData.Parameters != "")
|
||||
assert.True(t, len(uiData.Definitions) > 0)
|
||||
|
||||
testAddonName = "example-legacy"
|
||||
for _, m := range registryMeta {
|
||||
if m.Name == testAddonName {
|
||||
testAddonMeta = m
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
uiData, err = GetUIDataFromReader(reader, &testAddonMeta, UIMetaOptions)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, uiData.Name, testAddonName)
|
||||
assert.True(t, uiData.Parameters != "")
|
||||
assert.True(t, len(uiData.Definitions) > 0)
|
||||
|
||||
// test get ui data
|
||||
rName := "KubeVela"
|
||||
uiDataList, err := ListAddonUIDataFromReader(reader, registryMeta, rName, UIMetaOptions)
|
||||
assert.True(t, strings.Contains(err.Error(), "#parameter.example: preference mark not allowed at this position"))
|
||||
assert.Equal(t, 4, len(uiDataList))
|
||||
assert.Equal(t, 5, len(uiDataList))
|
||||
assert.Equal(t, uiDataList[0].RegistryName, rName)
|
||||
|
||||
// test get install package
|
||||
@@ -290,10 +313,9 @@ func TestRenderDeploy2RuntimeAddon(t *testing.T) {
|
||||
|
||||
app, err := RenderApp(ctx, &addonDeployToRuntime, nil, map[string]interface{}{})
|
||||
assert.NoError(t, err)
|
||||
steps := app.Spec.Workflow.Steps
|
||||
assert.True(t, len(steps) >= 2)
|
||||
assert.Equal(t, steps[len(steps)-2].Type, "apply-application")
|
||||
assert.Equal(t, steps[len(steps)-1].Type, "deploy2runtime")
|
||||
policies := app.Spec.Policies
|
||||
assert.True(t, len(policies) == 1)
|
||||
assert.Equal(t, policies[0].Type, v1alpha1.TopologyPolicyType)
|
||||
}
|
||||
|
||||
func TestRenderDefinitions(t *testing.T) {
|
||||
@@ -517,6 +539,7 @@ var baseAddon = InstallPackage{
|
||||
Meta: Meta{
|
||||
Name: "test-render-cue-definition-addon",
|
||||
NeedNamespace: []string{"test-ns"},
|
||||
DeployTo: &DeployTo{RuntimeCluster: true},
|
||||
},
|
||||
CUEDefinitions: []ElementFile{
|
||||
{
|
||||
@@ -757,84 +780,6 @@ status: {
|
||||
|
||||
`
|
||||
|
||||
func TestRenderApp4Observability(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().Build()
|
||||
testcases := []struct {
|
||||
addon InstallPackage
|
||||
args map[string]interface{}
|
||||
application string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
addon: InstallPackage{
|
||||
Meta: Meta{
|
||||
Name: "observability",
|
||||
},
|
||||
},
|
||||
args: map[string]interface{}{},
|
||||
application: `{"kind":"Application","apiVersion":"core.oam.dev/v1beta1","metadata":{"name":"addon-observability","namespace":"vela-system","creationTimestamp":null,"labels":{"addons.oam.dev/name":"observability","addons.oam.dev/version":""}},"spec":{"components":[],"policies":[{"name":"domain","type":"env-binding","properties":{"envs":null}}],"workflow":{"steps":[{"name":"deploy-control-plane","type":"apply-application"}]}},"status":{}}`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
app, err := RenderApp(ctx, &tc.addon, k8sClient, tc.args)
|
||||
assert.Equal(t, tc.err, err)
|
||||
if app != nil {
|
||||
data, err := json.Marshal(app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.application, string(data))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestRenderApp4ObservabilityWithEnvBinding tests the case of RenderApp for Addon Observability with some Kubernetes data
|
||||
func TestRenderApp4ObservabilityWithK8sData(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().Build()
|
||||
ctx := context.Background()
|
||||
secret1 := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-secret",
|
||||
Labels: map[string]string{
|
||||
clustercommon.LabelKeyClusterCredentialType: string(v1alpha12.CredentialTypeX509Certificate),
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"test-key": []byte("test-value"),
|
||||
},
|
||||
}
|
||||
err := k8sClient.Create(ctx, secret1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
testcases := []struct {
|
||||
addon InstallPackage
|
||||
args map[string]interface{}
|
||||
application string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
addon: InstallPackage{
|
||||
Meta: Meta{
|
||||
Name: "observability",
|
||||
},
|
||||
},
|
||||
args: map[string]interface{}{},
|
||||
application: `{"kind":"Application","apiVersion":"core.oam.dev/v1beta1","metadata":{"name":"addon-observability","namespace":"vela-system","creationTimestamp":null,"labels":{"addons.oam.dev/name":"observability","addons.oam.dev/version":""}},"spec":{"components":[],"policies":[{"name":"domain","type":"env-binding","properties":{"envs":[{"name":"test-secret","placement":{"clusterSelector":{"name":"test-secret"}}}]}}],"workflow":{"steps":[{"name":"deploy-control-plane","type":"apply-application-in-parallel"},{"name":"test-secret","type":"deploy2env","properties":{"env":"test-secret","parallel":true,"policy":"domain"}}]}},"status":{}}`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
app, err := RenderApp(ctx, &tc.addon, k8sClient, tc.args)
|
||||
assert.Equal(t, tc.err, err)
|
||||
if app != nil {
|
||||
data, err := json.Marshal(app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.application, string(data))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPatternFromItem(t *testing.T) {
|
||||
ossR, err := NewAsyncReader("http://ep.beijing", "some-bucket", "", "some-sub-path", "", ossType)
|
||||
assert.NoError(t, err)
|
||||
@@ -1199,10 +1144,14 @@ func TestReadViewFile(t *testing.T) {
|
||||
func TestRenderCUETemplate(t *testing.T) {
|
||||
fileDate, err := os.ReadFile("./testdata/example/resources/configmap.cue")
|
||||
assert.NoError(t, err)
|
||||
component, err := renderCUETemplate(ElementFile{Data: string(fileDate), Name: "configmap.cue"}, "{\"example\": \"\"}", map[string]interface{}{
|
||||
addon := &InstallPackage{
|
||||
Meta: Meta{
|
||||
Version: "1.0.1",
|
||||
},
|
||||
Parameters: "{\"example\": \"\"}",
|
||||
}
|
||||
component, err := renderCompAccordingCUETemplate(ElementFile{Data: string(fileDate), Name: "configmap.cue"}, addon, map[string]interface{}{
|
||||
"example": "render",
|
||||
}, Meta{
|
||||
Version: "1.0.1",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, component.Type == "raw")
|
||||
@@ -1224,13 +1173,13 @@ func TestCheckEnableAddonErrorWhenMissMatch(t *testing.T) {
|
||||
func TestPackageAddon(t *testing.T) {
|
||||
pwd, _ := os.Getwd()
|
||||
|
||||
validAddonDict := "./testdata/example"
|
||||
validAddonDict := "./testdata/example-legacy"
|
||||
archiver, err := PackageAddon(validAddonDict)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, filepath.Join(pwd, "example-1.0.1.tgz"), archiver)
|
||||
assert.Equal(t, filepath.Join(pwd, "example-legacy-1.0.1.tgz"), archiver)
|
||||
// Remove generated package after tests
|
||||
defer func() {
|
||||
_ = os.RemoveAll(filepath.Join(pwd, "example-1.0.1.tgz"))
|
||||
_ = os.RemoveAll(filepath.Join(pwd, "example-legacy-1.0.1.tgz"))
|
||||
}()
|
||||
|
||||
invalidAddonDict := "./testdata"
|
||||
@@ -1260,3 +1209,100 @@ func TestGenerateAnnotation(t *testing.T) {
|
||||
assert.Equal(t, res[velaSystemRequirement], "")
|
||||
assert.Equal(t, res[kubernetesSystemRequirement], ">=1.20.1")
|
||||
}
|
||||
|
||||
func TestMergeAddonInstallArgs(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().Build()
|
||||
ctx := context.Background()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
legacyArgs string
|
||||
args map[string]interface{}
|
||||
mergedArgs string
|
||||
application string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "addon1",
|
||||
legacyArgs: "{\"clusters\":[\"\"],\"imagePullSecrets\":[\"test-hub\"],\"repo\":\"hub.vela.com\",\"serviceType\":\"NodePort\"}",
|
||||
args: map[string]interface{}{
|
||||
"serviceType": "NodePort",
|
||||
},
|
||||
mergedArgs: "{\"clusters\":[\"\"],\"imagePullSecrets\":[\"test-hub\"],\"repo\":\"hub.vela.com\",\"serviceType\":\"NodePort\"}",
|
||||
},
|
||||
{
|
||||
name: "addon2",
|
||||
legacyArgs: "{\"clusters\":[\"\"]}",
|
||||
args: map[string]interface{}{
|
||||
"repo": "hub.vela.com",
|
||||
"serviceType": "NodePort",
|
||||
"imagePullSecrets": []string{"test-hub"},
|
||||
},
|
||||
mergedArgs: "{\"clusters\":[\"\"],\"imagePullSecrets\":[\"test-hub\"],\"repo\":\"hub.vela.com\",\"serviceType\":\"NodePort\"}",
|
||||
},
|
||||
{
|
||||
name: "addon3",
|
||||
legacyArgs: "{\"clusters\":[\"\"],\"imagePullSecrets\":[\"test-hub\"],\"repo\":\"hub.vela.com\",\"serviceType\":\"NodePort\"}",
|
||||
args: map[string]interface{}{
|
||||
"imagePullSecrets": []string{"test-hub-2"},
|
||||
},
|
||||
mergedArgs: "{\"clusters\":[\"\"],\"imagePullSecrets\":[\"test-hub-2\"],\"repo\":\"hub.vela.com\",\"serviceType\":\"NodePort\"}",
|
||||
},
|
||||
{
|
||||
// merge nested parameters
|
||||
name: "addon4",
|
||||
legacyArgs: "{\"clusters\":[\"\"],\"p1\":{\"p11\":\"p11-v1\",\"p12\":\"p12-v1\"}}",
|
||||
args: map[string]interface{}{
|
||||
"p1": map[string]interface{}{
|
||||
"p12": "p12-v2",
|
||||
"p13": "p13-v1",
|
||||
},
|
||||
},
|
||||
mergedArgs: "{\"clusters\":[\"\"],\"p1\":{\"p11\":\"p11-v1\",\"p12\":\"p12-v2\",\"p13\":\"p13-v1\"}}",
|
||||
},
|
||||
{
|
||||
// there is not legacyArgs
|
||||
name: "addon5",
|
||||
legacyArgs: "",
|
||||
args: map[string]interface{}{
|
||||
"p1": map[string]interface{}{
|
||||
"p12": "p12-v2",
|
||||
"p13": "p13-v1",
|
||||
},
|
||||
},
|
||||
mergedArgs: "{\"p1\":{\"p12\":\"p12-v2\",\"p13\":\"p13-v1\"}}",
|
||||
},
|
||||
{
|
||||
// there is not new args
|
||||
name: "addon6",
|
||||
legacyArgs: "{\"clusters\":[\"\"],\"p1\":{\"p11\":\"p11-v1\",\"p12\":\"p12-v1\"}}",
|
||||
args: nil,
|
||||
mergedArgs: "{\"clusters\":[\"\"],\"p1\":{\"p11\":\"p11-v1\",\"p12\":\"p12-v1\"}}",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
if len(tc.legacyArgs) != 0 {
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: addonutil.Addon2SecName(tc.name),
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
AddonParameterDataKey: []byte(tc.legacyArgs),
|
||||
},
|
||||
}
|
||||
err := k8sClient.Create(ctx, secret)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
addonArgs, err := MergeAddonInstallArgs(ctx, k8sClient, tc.name, tc.args)
|
||||
assert.NoError(t, err)
|
||||
args, err := json.Marshal(addonArgs)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.mergedArgs, string(args), tc.name)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -107,8 +107,9 @@ func (u *Cache) GetUIData(r Registry, addonName, version string) (*UIData, error
|
||||
}
|
||||
} else {
|
||||
versionedRegistry := BuildVersionedRegistry(r.Name, r.Helm.URL, &common.HTTPOption{
|
||||
Username: r.Helm.Username,
|
||||
Password: r.Helm.Password,
|
||||
Username: r.Helm.Username,
|
||||
Password: r.Helm.Password,
|
||||
InsecureSkipTLS: r.Helm.InsecureSkipTLS,
|
||||
})
|
||||
addon, err = versionedRegistry.GetAddonUIData(context.Background(), addonName, version)
|
||||
if err != nil {
|
||||
@@ -315,8 +316,9 @@ func (u *Cache) listUIDataAndCache(r Registry) ([]*UIData, error) {
|
||||
|
||||
func (u *Cache) listVersionRegistryUIDataAndCache(r Registry) ([]*UIData, error) {
|
||||
versionedRegistry := BuildVersionedRegistry(r.Name, r.Helm.URL, &common.HTTPOption{
|
||||
Username: r.Helm.Username,
|
||||
Password: r.Helm.Password,
|
||||
Username: r.Helm.Username,
|
||||
Password: r.Helm.Password,
|
||||
InsecureSkipTLS: r.Helm.InsecureSkipTLS,
|
||||
})
|
||||
uiDatas, err := versionedRegistry.ListAddon()
|
||||
if err != nil {
|
||||
|
||||
@@ -19,6 +19,8 @@ package addon
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -52,23 +54,26 @@ func TestListCachedUIData(t *testing.T) {
|
||||
assert.Equal(t, u.listCachedUIData(name), addons)
|
||||
}
|
||||
|
||||
func testListUIData(t *testing.T) {
|
||||
var _ = Describe("Test addon cache", func() {
|
||||
vr := Registry{Name: "helm-repo", Helm: &HelmSource{URL: "http://127.0.0.1:18083/authReg", Username: "hello", Password: "hello"}}
|
||||
uiData := UIData{Meta: Meta{
|
||||
Name: "fluxcd",
|
||||
Description: "Extended workload to do continuous and progressive delivery",
|
||||
Icon: "https://raw.githubusercontent.com/fluxcd/flux/master/docs/_files/weave-flux.png",
|
||||
Version: "1.0.0",
|
||||
Tags: []string{"extended_workload", "gitops"},
|
||||
},
|
||||
AvailableVersions: []string{"1.0.0"},
|
||||
RegistryName: "helm-repo"}
|
||||
addons := []*UIData{&uiData}
|
||||
u := NewCache(nil)
|
||||
uiDatas, err := u.ListUIData(vr)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, uiDatas, addons)
|
||||
}
|
||||
|
||||
It("Test list addon helm repo UI data", func() {
|
||||
uiData := UIData{Meta: Meta{
|
||||
Name: "fluxcd",
|
||||
Description: "Extended workload to do continuous and progressive delivery",
|
||||
Icon: "https://raw.githubusercontent.com/fluxcd/flux/master/docs/_files/weave-flux.png",
|
||||
Version: "1.0.0",
|
||||
Tags: []string{"extended_workload", "gitops"},
|
||||
},
|
||||
AvailableVersions: []string{"1.0.0"},
|
||||
RegistryName: "helm-repo"}
|
||||
addons := []*UIData{&uiData}
|
||||
u := NewCache(nil)
|
||||
uiDatas, err := u.ListUIData(vr)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(uiDatas).To(Equal(addons))
|
||||
})
|
||||
})
|
||||
|
||||
func TestListVersionRegistryCachedUIData(t *testing.T) {
|
||||
name := "fluxcd"
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 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 addon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/format"
|
||||
"cuelang.org/go/encoding/gocode/gocodec"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// CreateAddonFromHelmChart creates an addon scaffold from a Helm Chart, with a Helm component inside
|
||||
func CreateAddonFromHelmChart(addonName, addonPath, helmRepoURL, chartName, chartVersion string) error {
|
||||
if len(addonName) == 0 || len(helmRepoURL) == 0 || len(chartName) == 0 || len(chartVersion) == 0 {
|
||||
return fmt.Errorf("addon addonPath, helm URL, chart name, and chart verion should not be empty")
|
||||
}
|
||||
|
||||
// Currently, we do not check whether the Helm Chart actually exists, because it is just a scaffold.
|
||||
// The user can still edit it after creation.
|
||||
// Also, if the user is offline, we cannot check whether the Helm Chart exists.
|
||||
// TODO(charlie0129): check whether the Helm Chart exists (if the user wants)
|
||||
|
||||
// Make sure url is valid
|
||||
isValidURL := utils.IsValidURL(helmRepoURL)
|
||||
if !isValidURL {
|
||||
return fmt.Errorf("invalid helm repo url %s", helmRepoURL)
|
||||
}
|
||||
|
||||
err := preAddonCreation(addonName, addonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create files like template.yaml, README.md, and etc.
|
||||
err = createFilesFromHelmChart(addonName, addonPath, helmRepoURL, chartName, chartVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create addon files: %w", err)
|
||||
}
|
||||
|
||||
postAddonCreation(addonPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateAddonSample creates an empty addon scaffold, with some required files
|
||||
func CreateAddonSample(addonName, addonPath string) error {
|
||||
if len(addonName) == 0 || len(addonPath) == 0 {
|
||||
return fmt.Errorf("addon name and addon path should not be empty")
|
||||
}
|
||||
|
||||
err := preAddonCreation(addonName, addonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = createSampleFiles(addonName, addonPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
postAddonCreation(addonPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// preAddonCreation is executed before creating an addon scaffold
|
||||
// It makes sure that user-provided info is valid.
|
||||
func preAddonCreation(addonName, addonPath string) error {
|
||||
if len(addonName) == 0 || len(addonPath) == 0 {
|
||||
return fmt.Errorf("addon name and addonPath should not be empty")
|
||||
}
|
||||
|
||||
// Make sure addon name is valid
|
||||
err := CheckAddonName(addonName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create dirs
|
||||
err = createAddonDirs(addonPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create addon structure: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// postAddonCreation is after before creating an addon scaffold
|
||||
// It prints some instructions to get started.
|
||||
func postAddonCreation(addonPath string) {
|
||||
fmt.Println("Scaffold created in directory " +
|
||||
color.New(color.Bold).Sprint(addonPath) + ". What to do next:\n" +
|
||||
"- Check out our guide on how to build your own addon: " +
|
||||
color.BlueString("https://kubevela.io/docs/platform-engineers/addon/intro") + "\n" +
|
||||
"- Review and edit what we have generated in " + color.New(color.Bold).Sprint(addonPath) + "\n" +
|
||||
"- To enable the addon, run: " +
|
||||
color.New(color.FgGreen).Sprint("vela") + color.GreenString(" addon enable ") + color.New(color.Bold, color.FgGreen).Sprint(addonPath))
|
||||
}
|
||||
|
||||
// CheckAddonName checks if an addon name is valid
|
||||
func CheckAddonName(addonName string) error {
|
||||
if len(addonName) == 0 {
|
||||
return fmt.Errorf("addon name should not be empty")
|
||||
}
|
||||
|
||||
// Make sure addonName only contains lowercase letters, dashes, and numbers, e.g. some-addon
|
||||
re := regexp.MustCompile(`^[a-z\d]+(-[a-z\d]+)*$`)
|
||||
if !re.MatchString(addonName) {
|
||||
return fmt.Errorf("addon name should only cocntain lowercase letters, dashes, and numbers, e.g. some-addon")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createFilesFromHelmChart creates the file structure for a Helm Chart addon,
|
||||
// including template.yaml, readme.md, metadata.yaml, and <addon-nam>.cue.
|
||||
func createFilesFromHelmChart(addonName, addonPath, helmRepoURL, chartName, chartVersion string) error {
|
||||
// Generate template.yaml with an empty Application
|
||||
applicationTemplate := v1beta1.Application{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
APIVersion: v1beta1.SchemeGroupVersion.String(),
|
||||
Kind: "Application",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: addonName,
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
}
|
||||
|
||||
applicationTemplateBytes, err := yaml.Marshal(applicationTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate metadata.yaml with `fluxcd` as a dependency because we are using helm.
|
||||
// However, this may change in the future, possibly with `argocd`.
|
||||
metadataTemplate := Meta{
|
||||
Name: addonName,
|
||||
Version: chartVersion,
|
||||
Description: "An addon for KubeVela.",
|
||||
Tags: []string{chartVersion},
|
||||
Dependencies: []*Dependency{{Name: "fluxcd"}},
|
||||
}
|
||||
metadataTemplateBytes, err := yaml.Marshal(metadataTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write template.yaml, readme.md, and metadata.yaml
|
||||
err = writeRequiredFiles(addonPath,
|
||||
applicationTemplateBytes,
|
||||
[]byte(strings.ReplaceAll(readmeTemplate, "ADDON_NAME", addonName)),
|
||||
metadataTemplateBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write addonName.cue, containing the helm chart
|
||||
addonResourcePath := path.Join(addonPath, ResourcesDirName, addonName+".cue")
|
||||
resourceTmpl := HelmCUETemplate{}
|
||||
resourceTmpl.Output.Type = "helm"
|
||||
resourceTmpl.Output.Properties.RepoType = "helm"
|
||||
resourceTmpl.Output.Properties.URL = helmRepoURL
|
||||
resourceTmpl.Output.Properties.Chart = chartName
|
||||
resourceTmpl.Output.Properties.Version = chartVersion
|
||||
err = writeHelmCUETemplate(resourceTmpl, addonResourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createSampleFiles creates the file structure for an empty addon
|
||||
func createSampleFiles(addonName, addonPath string) error {
|
||||
// Generate metadata.yaml
|
||||
metadataTemplate := Meta{
|
||||
Name: addonName,
|
||||
Version: "1.0.0",
|
||||
Description: "An addon for KubeVela.",
|
||||
Tags: []string{},
|
||||
Dependencies: []*Dependency{},
|
||||
}
|
||||
metadataTemplateBytes, err := yaml.Marshal(metadataTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate template.yaml
|
||||
applicationTemplate := v1beta1.Application{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
APIVersion: v1beta1.SchemeGroupVersion.String(),
|
||||
Kind: "Application",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: addonName,
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
}
|
||||
applicationTemplateBytes, err := yaml.Marshal(applicationTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeRequiredFiles(addonPath,
|
||||
applicationTemplateBytes,
|
||||
[]byte(strings.ReplaceAll(readmeTemplate, "ADDON_NAME", addonName)),
|
||||
metadataTemplateBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeRequiredFiles creates required files for an addon,
|
||||
// including template.yaml, readme.md, and metadata.yaml
|
||||
func writeRequiredFiles(addonPath string, tmplContent, readmeContent, metadataContent []byte) error {
|
||||
// Write template.yaml
|
||||
templateFilePath := path.Join(addonPath, TemplateFileName)
|
||||
err := os.WriteFile(templateFilePath,
|
||||
tmplContent,
|
||||
0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot write %s: %w", templateFilePath, err)
|
||||
}
|
||||
|
||||
// Write README.md
|
||||
readmeFilePath := path.Join(addonPath, ReadmeFileName)
|
||||
err = os.WriteFile(readmeFilePath,
|
||||
readmeContent,
|
||||
0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot write %s: %w", readmeFilePath, err)
|
||||
}
|
||||
|
||||
// Write metadata.yaml
|
||||
metadataFilePath := path.Join(addonPath, MetadataFileName)
|
||||
err = os.WriteFile(metadataFilePath,
|
||||
metadataContent,
|
||||
0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot write %s: %w", metadataFilePath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createAddonDirs creates the directory structure for an addon
|
||||
func createAddonDirs(addonDir string) error {
|
||||
// Make sure addonDir is pointing to an empty directory, or does not exist at all
|
||||
// so that we can create it later
|
||||
_, err := os.Stat(addonDir)
|
||||
if !os.IsNotExist(err) {
|
||||
emptyDir, err := utils.IsEmptyDir(addonDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("we can't create directory %s. Make sure the name has not already been taken and you have the proper rights to write to it", addonDir)
|
||||
}
|
||||
|
||||
if !emptyDir {
|
||||
return fmt.Errorf("directory %s is not empty. To avoid any data loss, please manually delete it first, then try again", addonDir)
|
||||
}
|
||||
|
||||
// Now we are sure addonPath is en empty dir, delete it
|
||||
err = os.Remove(addonDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:gosec
|
||||
err = os.MkdirAll(addonDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dirs := []string{
|
||||
path.Join(addonDir, ResourcesDirName),
|
||||
path.Join(addonDir, DefinitionsDirName),
|
||||
path.Join(addonDir, DefSchemaName),
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
// nolint:gosec
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeHelmCUETemplate writes a cue, with a helm component inside, intended as addon resource
|
||||
func writeHelmCUETemplate(tmpl HelmCUETemplate, filePath string) error {
|
||||
r := cue.Runtime{}
|
||||
v, err := gocodec.New(&r, nil).Decode(tmpl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use `output` value
|
||||
v = v.Lookup("output")
|
||||
// Format output
|
||||
bs, err := format.Node(v.Syntax())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Append "output: " to the beginning of the string, like "output: {}"
|
||||
bs = append([]byte("output: "), bs...)
|
||||
err = os.WriteFile(filePath, bs, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot write %s: %w", filePath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HelmCUETemplate is a template for a helm component .cue in an addon
|
||||
type HelmCUETemplate struct {
|
||||
Output struct {
|
||||
Type string `json:"type"`
|
||||
Properties struct {
|
||||
RepoType string `json:"repoType"`
|
||||
URL string `json:"url"`
|
||||
Chart string `json:"chart"`
|
||||
Version string `json:"version"`
|
||||
} `json:"properties"`
|
||||
} `json:"output"`
|
||||
}
|
||||
|
||||
const (
|
||||
readmeTemplate = "# ADDON_NAME\n" +
|
||||
"\n" +
|
||||
"This is an addon template. Check how to build your own addon: https://kubevela.net/docs/platform-engineers/addon/intro\n" +
|
||||
"\n" +
|
||||
"## Directory Structure\n" +
|
||||
"\n" +
|
||||
"- `template.yaml`: contains the basic app, you can add some component and workflow to meet your requirements. Other files in `resources/` and `definitions/` will be rendered as Components and appended in `spec.components`\n" +
|
||||
"- `metadata.yaml`: contains addon metadata information.\n" +
|
||||
"- `definitions/`: contains the X-Definition yaml/cue files. These file will be rendered as KubeVela Component in `template.yaml`\n" +
|
||||
"- `resources/`:\n" +
|
||||
" - `parameter.cue` to expose parameters. It will be converted to JSON schema and rendered in UI forms.\n" +
|
||||
" - All other files will be rendered as KubeVela Components. It can be one of the two types:\n" +
|
||||
" - YAML file that contains only one resource. This will be rendered as a `raw` component\n" +
|
||||
" - CUE template file that can read user input as `parameter.XXX` as defined `parameter.cue`.\n" +
|
||||
" Basically the CUE template file will be combined with `parameter.cue` to render a resource.\n" +
|
||||
" **You can specify the type and trait in this format**\n" +
|
||||
""
|
||||
)
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 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 addon
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestCheckAddonName(t *testing.T) {
|
||||
var err error
|
||||
|
||||
err = CheckAddonName("")
|
||||
assert.ErrorContains(t, err, "should not be empty")
|
||||
|
||||
invalidNames := []string{
|
||||
"-addon",
|
||||
"addon-",
|
||||
"Caps",
|
||||
"=",
|
||||
".",
|
||||
}
|
||||
for _, name := range invalidNames {
|
||||
err = CheckAddonName(name)
|
||||
assert.ErrorContains(t, err, "should only")
|
||||
}
|
||||
|
||||
validNames := []string{
|
||||
"addon-name",
|
||||
"3-addon-name",
|
||||
"addon-name-3",
|
||||
"addon",
|
||||
}
|
||||
for _, name := range validNames {
|
||||
err = CheckAddonName(name)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteHelmCUETemplate(t *testing.T) {
|
||||
resourceTmpl := HelmCUETemplate{}
|
||||
resourceTmpl.Output.Type = "helm"
|
||||
resourceTmpl.Output.Properties.RepoType = "helm"
|
||||
resourceTmpl.Output.Properties.URL = "https://charts.bitnami.com/bitnami"
|
||||
resourceTmpl.Output.Properties.Chart = "bitnami/nginx"
|
||||
resourceTmpl.Output.Properties.Version = "12.0.4"
|
||||
err := writeHelmCUETemplate(resourceTmpl, "test.cue")
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
_ = os.Remove("test.cue")
|
||||
}()
|
||||
data, err := os.ReadFile("test.cue")
|
||||
assert.NilError(t, err)
|
||||
expected := `output: {
|
||||
type: "helm"
|
||||
properties: {
|
||||
url: "https://charts.bitnami.com/bitnami"
|
||||
repoType: "helm"
|
||||
chart: "bitnami/nginx"
|
||||
version: "12.0.4"
|
||||
}
|
||||
}`
|
||||
assert.Equal(t, string(data), expected)
|
||||
}
|
||||
|
||||
func TestCreateAddonFromHelmChart(t *testing.T) {
|
||||
err := CreateAddonFromHelmChart("", "", "", "bitnami/nginx", "12.0.4")
|
||||
assert.ErrorContains(t, err, "should not be empty")
|
||||
|
||||
checkFiles := func(base string) {
|
||||
fileList := []string{
|
||||
"definitions",
|
||||
path.Join("resources", base+".cue"),
|
||||
"schemas",
|
||||
MetadataFileName,
|
||||
ReadmeFileName,
|
||||
TemplateFileName,
|
||||
}
|
||||
for _, file := range fileList {
|
||||
_, err = os.Stat(path.Join(base, file))
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Empty dir already exists
|
||||
_ = os.MkdirAll("test-addon", 0755)
|
||||
err = CreateAddonFromHelmChart("test-addon", "./test-addon", "https://charts.bitnami.com/bitnami", "bitnami/nginx", "12.0.4")
|
||||
checkFiles("test-addon")
|
||||
defer func() {
|
||||
_ = os.RemoveAll("test-addon")
|
||||
}()
|
||||
|
||||
// Non-empty dir already exists
|
||||
err = CreateAddonFromHelmChart("test-addon", "test-addon", "https://charts.bitnami.com/bitnami", "bitnami/nginx", "12.0.4")
|
||||
assert.ErrorContains(t, err, "not empty")
|
||||
|
||||
// Name already taken
|
||||
err = os.WriteFile("already-taken", []byte{}, 0644)
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
_ = os.Remove("already-taken")
|
||||
}()
|
||||
err = CreateAddonFromHelmChart("already-taken", "already-taken", "https://charts.bitnami.com/bitnami", "bitnami/nginx", "12.0.4")
|
||||
assert.ErrorContains(t, err, "can't create")
|
||||
|
||||
// Invalid addon name
|
||||
err = CreateAddonFromHelmChart("/", "./a", "https://charts.bitnami.com/bitnami", "bitnami/nginx", "12.0.4")
|
||||
assert.ErrorContains(t, err, "should only")
|
||||
|
||||
// Invalid URL
|
||||
err = CreateAddonFromHelmChart("invalid-url", "invalid-url", "invalid-url", "bitnami/nginx", "12.0.4")
|
||||
assert.ErrorContains(t, err, "invalid helm repo url")
|
||||
}
|
||||
|
||||
func TestCreateAddonSample(t *testing.T) {
|
||||
checkFiles := func(base string) {
|
||||
fileList := []string{
|
||||
"definitions",
|
||||
"resources",
|
||||
"schemas",
|
||||
MetadataFileName,
|
||||
ReadmeFileName,
|
||||
TemplateFileName,
|
||||
}
|
||||
for _, file := range fileList {
|
||||
_, err := os.Stat(path.Join(base, file))
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Normal creation
|
||||
err := CreateAddonSample("test-addon", "test-addon")
|
||||
assert.NilError(t, err)
|
||||
checkFiles("test-addon")
|
||||
|
||||
// Non-empty dir already exists
|
||||
err = CreateAddonSample("test-addon", "test-addon")
|
||||
assert.ErrorContains(t, err, "directory")
|
||||
|
||||
defer func() {
|
||||
_ = os.RemoveAll("test-addon")
|
||||
}()
|
||||
|
||||
err = CreateAddonSample("", "")
|
||||
assert.ErrorContains(t, err, "empty")
|
||||
}
|
||||
|
||||
func TestPreAddonCreation(t *testing.T) {
|
||||
err := preAddonCreation("", "")
|
||||
assert.ErrorContains(t, err, "empty")
|
||||
|
||||
err = preAddonCreation("=", "a")
|
||||
assert.ErrorContains(t, err, "name")
|
||||
}
|
||||
@@ -40,6 +40,9 @@ var (
|
||||
|
||||
// ErrRegistryNotExist means registry not exists
|
||||
ErrRegistryNotExist = NewAddonError("registry does not exist")
|
||||
|
||||
// ErrBothCueAndYamlTmpl means yaml and cue app template are exist in addon
|
||||
ErrBothCueAndYamlTmpl = NewAddonError("yaml and cue app template are exist in addon, should only keep one of them")
|
||||
)
|
||||
|
||||
// WrapErrRateLimit return ErrRateLimit if is the situation, or return error directly
|
||||
|
||||
@@ -326,7 +326,11 @@ func FindWholeAddonPackagesFromRegistry(ctx context.Context, k8sClient client.Cl
|
||||
// Find matched addons in registries
|
||||
for _, r := range registries {
|
||||
if IsVersionRegistry(r) {
|
||||
vr := BuildVersionedRegistry(r.Name, r.Helm.URL, &common.HTTPOption{Username: r.Helm.Username, Password: r.Helm.Password})
|
||||
vr := BuildVersionedRegistry(r.Name, r.Helm.URL, &common.HTTPOption{
|
||||
Username: r.Helm.Username,
|
||||
Password: r.Helm.Password,
|
||||
InsecureSkipTLS: r.Helm.InsecureSkipTLS,
|
||||
})
|
||||
for _, addonName := range addonNames {
|
||||
wholePackage, err := vr.GetDetailedAddon(ctx, addonName, "")
|
||||
if err != nil {
|
||||
|
||||
@@ -141,6 +141,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
cmYaml := strings.ReplaceAll(registryCmYaml, "TEST_SERVER_URL", server.URL)
|
||||
cmYaml = strings.ReplaceAll(cmYaml, "KubeVela", "testreg")
|
||||
Expect(yaml.Unmarshal([]byte(cmYaml), &cm)).Should(BeNil())
|
||||
_ = k8sClient.Create(ctx, &cm)
|
||||
Expect(k8sClient.Update(ctx, &cm)).Should(BeNil())
|
||||
})
|
||||
|
||||
|
||||
515
pkg/addon/init.go
Normal file
515
pkg/addon/init.go
Normal file
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
Copyright 2022 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 addon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/format"
|
||||
"cuelang.org/go/encoding/gocode/gocodec"
|
||||
"github.com/fatih/color"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// AddonNameRegex is the regex to validate addon names
|
||||
AddonNameRegex = `^[a-z\d]+(-[a-z\d]+)*$`
|
||||
// helmComponentDependency is the dependent addon of Helm Component
|
||||
helmComponentDependency = "fluxcd"
|
||||
)
|
||||
|
||||
// InitCmd contains the options to initialize an addon scaffold
|
||||
type InitCmd struct {
|
||||
AddonName string
|
||||
NoSamples bool
|
||||
HelmRepoURL string
|
||||
HelmChartName string
|
||||
HelmChartVersion string
|
||||
Path string
|
||||
Overwrite bool
|
||||
RefObjURLs []string
|
||||
// We use string instead of v1beta1.Application is because
|
||||
// the cue formatter is having some problems: it will keep
|
||||
// TypeMeta (instead of inlined).
|
||||
AppTmpl string
|
||||
Metadata Meta
|
||||
Readme string
|
||||
Resources []ElementFile
|
||||
Schemas []ElementFile
|
||||
Views []ElementFile
|
||||
Definitions []ElementFile
|
||||
}
|
||||
|
||||
// CreateScaffold creates an addon scaffold
|
||||
func (cmd *InitCmd) CreateScaffold() error {
|
||||
var err error
|
||||
|
||||
if len(cmd.AddonName) == 0 || len(cmd.Path) == 0 {
|
||||
return fmt.Errorf("addon name and path should not be empty")
|
||||
}
|
||||
|
||||
err = CheckAddonName(cmd.AddonName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmd.createDirs()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create addon structure: %w", err)
|
||||
}
|
||||
// Delete created files if an error occurred afterwards.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(cmd.Path)
|
||||
}
|
||||
}()
|
||||
|
||||
cmd.createRequiredFiles()
|
||||
|
||||
if cmd.HelmChartName != "" && cmd.HelmChartVersion != "" && cmd.HelmRepoURL != "" {
|
||||
klog.Info("Creating Helm component...")
|
||||
err = cmd.createHelmComponent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(cmd.RefObjURLs) > 0 {
|
||||
klog.Info("Creating ref-objects URL component...")
|
||||
err = cmd.createURLComponent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !cmd.NoSamples {
|
||||
cmd.createSamples()
|
||||
}
|
||||
|
||||
err = cmd.writeFiles()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Print some instructions to get started.
|
||||
fmt.Println("\nScaffold created in directory " +
|
||||
color.New(color.Bold).Sprint(cmd.Path) + ". What to do next:\n" +
|
||||
"- Check out our guide on how to build your own addon: " +
|
||||
color.New(color.Bold, color.FgBlue).Sprint("https://kubevela.io/docs/platform-engineers/addon/intro") + "\n" +
|
||||
"- Review and edit what we have generated in " + color.New(color.Bold).Sprint(cmd.Path) + "\n" +
|
||||
"- To enable this addon, run: " +
|
||||
color.New(color.FgGreen).Sprint("vela") + color.GreenString(" addon enable ") + color.New(color.Bold, color.FgGreen).Sprint(cmd.Path))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckAddonName checks if an addon name is valid
|
||||
func CheckAddonName(addonName string) error {
|
||||
if len(addonName) == 0 {
|
||||
return fmt.Errorf("addon name should not be empty")
|
||||
}
|
||||
|
||||
// Make sure addonName only contains lowercase letters, dashes, and numbers, e.g. some-addon
|
||||
re := regexp.MustCompile(AddonNameRegex)
|
||||
if !re.MatchString(addonName) {
|
||||
return fmt.Errorf("addon name should only cocntain lowercase letters, dashes, and numbers, e.g. some-addon")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createSamples creates sample files
|
||||
func (cmd *InitCmd) createSamples() {
|
||||
// Sample Definition mytrait.cue
|
||||
cmd.Definitions = append(cmd.Definitions, ElementFile{
|
||||
Data: traitTemplate,
|
||||
Name: "mytrait.cue",
|
||||
})
|
||||
// Sample Resource
|
||||
cmd.Resources = append(cmd.Resources, ElementFile{
|
||||
Data: resourceTemplate,
|
||||
Name: "myresource.cue",
|
||||
})
|
||||
// Sample schema
|
||||
cmd.Schemas = append(cmd.Schemas, ElementFile{
|
||||
Data: schemaTemplate,
|
||||
Name: "myschema.yaml",
|
||||
})
|
||||
// Sample View
|
||||
cmd.Views = append(cmd.Views, ElementFile{
|
||||
Data: strings.ReplaceAll(viewTemplate, "ADDON_NAME", cmd.AddonName),
|
||||
Name: "my-view.cue",
|
||||
})
|
||||
}
|
||||
|
||||
// createRequiredFiles creates README.md, template.yaml and metadata.yaml
|
||||
func (cmd *InitCmd) createRequiredFiles() {
|
||||
// README.md
|
||||
cmd.Readme = strings.ReplaceAll(readmeTemplate, "ADDON_NAME", cmd.AddonName)
|
||||
|
||||
// template.cue
|
||||
cmd.AppTmpl = appTemplate
|
||||
|
||||
// metadata.yaml
|
||||
cmd.Metadata = Meta{
|
||||
Name: cmd.AddonName,
|
||||
Version: "1.0.0",
|
||||
Description: "An addon for KubeVela.",
|
||||
Tags: []string{"my-tag"},
|
||||
Dependencies: []*Dependency{},
|
||||
DeployTo: &DeployTo{
|
||||
RuntimeCluster: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// createHelmComponent creates a <addon-name-helm>.cue in /resources
|
||||
func (cmd *InitCmd) createHelmComponent() error {
|
||||
// Make fluxcd a dependency, since it uses a helm component
|
||||
cmd.Metadata.addDependency(helmComponentDependency)
|
||||
// Make addon version same as chart version
|
||||
cmd.Metadata.Version = cmd.HelmChartVersion
|
||||
|
||||
// Create a <addon-name-helm>.cue in resources
|
||||
tmpl := helmComponentTmpl{}
|
||||
tmpl.Type = "helm"
|
||||
tmpl.Properties.RepoType = "helm"
|
||||
tmpl.Properties.URL = cmd.HelmRepoURL
|
||||
tmpl.Properties.Chart = cmd.HelmChartName
|
||||
tmpl.Properties.Version = cmd.HelmChartVersion
|
||||
|
||||
str, err := toCUEResourceString(tmpl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Resources = append(cmd.Resources, ElementFile{
|
||||
Name: "helm.cue",
|
||||
Data: str,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createURLComponent creates a ref-object component containing URLs
|
||||
func (cmd *InitCmd) createURLComponent() error {
|
||||
tmpl := refObjURLTmpl{Type: "ref-objects"}
|
||||
|
||||
for _, url := range cmd.RefObjURLs {
|
||||
if !utils.IsValidURL(url) {
|
||||
return fmt.Errorf("%s is not a valid url", url)
|
||||
}
|
||||
|
||||
tmpl.Properties.URLs = append(tmpl.Properties.URLs, url)
|
||||
}
|
||||
|
||||
str, err := toCUEResourceString(tmpl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Resources = append(cmd.Resources, ElementFile{
|
||||
Data: str,
|
||||
Name: "from-url.cue",
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// toCUEResourceString formats object to CUE string used in addons
|
||||
func toCUEResourceString(obj interface{}) (string, error) {
|
||||
r := cue.Runtime{}
|
||||
v, err := gocodec.New(&r, nil).Decode(obj)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bs, err := format.Node(v.Syntax())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Append "output: " to the beginning of the string, like "output: {}"
|
||||
bs = append([]byte("output: "), bs...)
|
||||
|
||||
return string(bs), nil
|
||||
}
|
||||
|
||||
// addDependency adds a dependency into metadata.yaml
|
||||
func (m *Meta) addDependency(dep string) {
|
||||
for _, d := range m.Dependencies {
|
||||
if d.Name == dep {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.Dependencies = append(m.Dependencies, &Dependency{Name: dep})
|
||||
}
|
||||
|
||||
// createDirs creates the directory structure for an addon
|
||||
func (cmd *InitCmd) createDirs() error {
|
||||
// Make sure addonDir is pointing to an empty directory, or does not exist at all
|
||||
// so that we can create it later
|
||||
_, err := os.Stat(cmd.Path)
|
||||
if !os.IsNotExist(err) {
|
||||
emptyDir, err := utils.IsEmptyDir(cmd.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("we can't create directory %s. Make sure the name has not already been taken and you have the proper rights to write to it", cmd.Path)
|
||||
}
|
||||
|
||||
if !emptyDir {
|
||||
if !cmd.Overwrite {
|
||||
return fmt.Errorf("directory %s is not empty. To avoid any data loss, please manually delete it first or use -f, then try again", cmd.Path)
|
||||
}
|
||||
klog.Warningf("Overwriting non-empty directory %s", cmd.Path)
|
||||
}
|
||||
|
||||
// Now we are sure addonPath is en empty dir, (or the user want to overwrite), delete it
|
||||
err = os.RemoveAll(cmd.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:gosec
|
||||
err = os.MkdirAll(cmd.Path, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dirs := []string{
|
||||
path.Join(cmd.Path, ResourcesDirName),
|
||||
path.Join(cmd.Path, DefinitionsDirName),
|
||||
path.Join(cmd.Path, DefSchemaName),
|
||||
path.Join(cmd.Path, ViewDirName),
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
// nolint:gosec
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeFiles writes addon to disk
|
||||
func (cmd *InitCmd) writeFiles() error {
|
||||
var files []ElementFile
|
||||
|
||||
files = append(files, ElementFile{
|
||||
Name: ReadmeFileName,
|
||||
Data: cmd.Readme,
|
||||
}, ElementFile{
|
||||
Data: parameterTemplate,
|
||||
Name: GlobalParameterFileName,
|
||||
})
|
||||
|
||||
for _, v := range cmd.Resources {
|
||||
files = append(files, ElementFile{
|
||||
Data: v.Data,
|
||||
Name: filepath.Join(ResourcesDirName, v.Name),
|
||||
})
|
||||
}
|
||||
for _, v := range cmd.Views {
|
||||
files = append(files, ElementFile{
|
||||
Data: v.Data,
|
||||
Name: filepath.Join(ViewDirName, v.Name),
|
||||
})
|
||||
}
|
||||
for _, v := range cmd.Definitions {
|
||||
files = append(files, ElementFile{
|
||||
Data: v.Data,
|
||||
Name: filepath.Join(DefinitionsDirName, v.Name),
|
||||
})
|
||||
}
|
||||
for _, v := range cmd.Schemas {
|
||||
files = append(files, ElementFile{
|
||||
Data: v.Data,
|
||||
Name: filepath.Join(DefSchemaName, v.Name),
|
||||
})
|
||||
}
|
||||
|
||||
// Prepare template.cue
|
||||
files = append(files, ElementFile{
|
||||
Data: cmd.AppTmpl,
|
||||
Name: AppTemplateCueFileName,
|
||||
})
|
||||
|
||||
// Prepare metadata.yaml
|
||||
metaBytes, err := yaml.Marshal(cmd.Metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files = append(files, ElementFile{
|
||||
Data: string(metaBytes),
|
||||
Name: MetadataFileName,
|
||||
})
|
||||
|
||||
// Write files
|
||||
for _, f := range files {
|
||||
err := os.WriteFile(filepath.Join(cmd.Path, f.Name), []byte(f.Data), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// helmComponentTmpl is a template for a helm component .cue in an addon
|
||||
type helmComponentTmpl struct {
|
||||
Type string `json:"type"`
|
||||
Properties struct {
|
||||
RepoType string `json:"repoType"`
|
||||
URL string `json:"url"`
|
||||
Chart string `json:"chart"`
|
||||
Version string `json:"version"`
|
||||
} `json:"properties"`
|
||||
}
|
||||
|
||||
// refObjURLTmpl is a template for ref-objects containing URLs in an addon
|
||||
type refObjURLTmpl struct {
|
||||
Type string `json:"type"`
|
||||
Properties struct {
|
||||
URLs []string `json:"urls"`
|
||||
} `json:"properties"`
|
||||
}
|
||||
|
||||
const (
|
||||
readmeTemplate = "# ADDON_NAME\n" +
|
||||
"\n" +
|
||||
"This is an addon template. Check how to build your own addon: https://kubevela.net/docs/platform-engineers/addon/intro\n" +
|
||||
""
|
||||
viewTemplate = `// We put VelaQL views in views directory.
|
||||
//
|
||||
// VelaQL(Vela Query Language) is a resource query language for KubeVela,
|
||||
// used to query status of any extended resources in application-level.
|
||||
// Reference: https://kubevela.net/docs/platform-engineers/system-operation/velaql
|
||||
//
|
||||
// This VelaQL View querys the status of this addon.
|
||||
// Use this view to query by:
|
||||
// vela ql --query 'my-view{addonName:ADDON_NAME}.status'
|
||||
// You should see 'running'.
|
||||
|
||||
import (
|
||||
"vela/ql"
|
||||
)
|
||||
|
||||
app: ql.#Read & {
|
||||
value: {
|
||||
kind: "Application"
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
metadata: {
|
||||
name: "addon-" + parameter.addonName
|
||||
namespace: "vela-system"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
addonName: *"ADDON_NAME" | string
|
||||
}
|
||||
|
||||
status: app.value.status.status
|
||||
`
|
||||
traitTemplate = `// We put Definitions in definitions directory.
|
||||
// References:
|
||||
// - https://kubevela.net/docs/platform-engineers/cue/definition-edit
|
||||
// - https://kubevela.net/docs/platform-engineers/addon/intro#definitions-directoryoptional
|
||||
"mytrait": {
|
||||
alias: "mt"
|
||||
annotations: {}
|
||||
attributes: {
|
||||
appliesToWorkloads: [
|
||||
"deployments.apps",
|
||||
"replicasets.apps",
|
||||
"statefulsets.apps",
|
||||
]
|
||||
conflictsWith: []
|
||||
podDisruptive: false
|
||||
workloadRefPath: ""
|
||||
}
|
||||
description: "My trait description."
|
||||
labels: {}
|
||||
type: "trait"
|
||||
}
|
||||
template: {
|
||||
parameter: {param: ""}
|
||||
outputs: {sample: {}}
|
||||
}
|
||||
`
|
||||
resourceTemplate = `// We put Components in resources directory.
|
||||
// References:
|
||||
// - https://kubevela.net/docs/end-user/components/references
|
||||
// - https://kubevela.net/docs/platform-engineers/addon/intro#resources-directoryoptional
|
||||
output: {
|
||||
type: "k8s-objects"
|
||||
properties: {
|
||||
objects: [
|
||||
{
|
||||
// This creates a plain old Kubernetes namespace
|
||||
apiVersion: "v1"
|
||||
kind: "Namespace"
|
||||
// We can use the parameter defined in parameter.cue like this.
|
||||
metadata: name: parameter.myparam
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
`
|
||||
parameterTemplate = `// parameter.cue is used to store addon parameters.
|
||||
//
|
||||
// You can use these parameters in template.cue or in resources/ by 'parameter.myparam'
|
||||
//
|
||||
// For example, you can use parameters to allow the user to customize
|
||||
// container images, ports, and etc.
|
||||
parameter: {
|
||||
// +usage=Custom parameter description
|
||||
myparam: *"myns" | string
|
||||
}
|
||||
`
|
||||
schemaTemplate = `# We put UI Schemas that correspond to Definitions in schemas directory.
|
||||
# References:
|
||||
# - https://kubevela.net/docs/platform-engineers/addon/intro#schemas-directoryoptional
|
||||
# - https://kubevela.net/docs/reference/ui-schema
|
||||
- jsonKey: myparam
|
||||
label: MyParam
|
||||
validate:
|
||||
required: true
|
||||
`
|
||||
appTemplate = `output: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
spec: {
|
||||
components: []
|
||||
policies: []
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
109
pkg/addon/init_test.go
Normal file
109
pkg/addon/init_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2022 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 addon
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestCheckAddonName(t *testing.T) {
|
||||
var err error
|
||||
|
||||
err = CheckAddonName("")
|
||||
assert.ErrorContains(t, err, "should not be empty")
|
||||
|
||||
invalidNames := []string{
|
||||
"-addon",
|
||||
"addon-",
|
||||
"Caps",
|
||||
"=",
|
||||
".",
|
||||
}
|
||||
for _, name := range invalidNames {
|
||||
err = CheckAddonName(name)
|
||||
assert.ErrorContains(t, err, "should only")
|
||||
}
|
||||
|
||||
validNames := []string{
|
||||
"addon-name",
|
||||
"3-addon-name",
|
||||
"addon-name-3",
|
||||
"addon",
|
||||
}
|
||||
for _, name := range validNames {
|
||||
err = CheckAddonName(name)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCmd_CreateScaffold(t *testing.T) {
|
||||
var err error
|
||||
|
||||
// empty addon name or path
|
||||
cmd := InitCmd{}
|
||||
err = cmd.CreateScaffold()
|
||||
assert.ErrorContains(t, err, "be empty")
|
||||
|
||||
// invalid addon name
|
||||
cmd = InitCmd{
|
||||
AddonName: "-name",
|
||||
Path: "name",
|
||||
}
|
||||
err = cmd.CreateScaffold()
|
||||
assert.ErrorContains(t, err, "should only")
|
||||
|
||||
// dir already exists
|
||||
cmd = InitCmd{
|
||||
AddonName: "name",
|
||||
Path: "testdata",
|
||||
}
|
||||
err = cmd.CreateScaffold()
|
||||
assert.ErrorContains(t, err, "cannot create")
|
||||
|
||||
// with helm component
|
||||
cmd = InitCmd{
|
||||
AddonName: "with-helm",
|
||||
Path: "with-helm",
|
||||
HelmRepoURL: "https://charts.bitnami.com/bitnami",
|
||||
HelmChartVersion: "12.0.0",
|
||||
HelmChartName: "nginx",
|
||||
}
|
||||
err = cmd.CreateScaffold()
|
||||
assert.NilError(t, err)
|
||||
defer os.RemoveAll("with-helm")
|
||||
_, err = os.Stat(filepath.Join("with-helm", ResourcesDirName, "helm.cue"))
|
||||
assert.NilError(t, err)
|
||||
|
||||
// with ref-obj
|
||||
cmd = InitCmd{
|
||||
AddonName: "with-refobj",
|
||||
Path: "with-refobj",
|
||||
RefObjURLs: []string{"https:"},
|
||||
}
|
||||
err = cmd.CreateScaffold()
|
||||
assert.ErrorContains(t, err, "not a valid url")
|
||||
cmd.RefObjURLs[0] = "https://some.com"
|
||||
err = cmd.CreateScaffold()
|
||||
assert.NilError(t, err)
|
||||
defer os.RemoveAll("with-refobj")
|
||||
_, err = os.Stat(filepath.Join("with-refobj", ResourcesDirName, "from-url.cue"))
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
346
pkg/addon/render.go
Normal file
346
pkg/addon/render.go
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
Copyright 2022 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 addon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
cuemodel "github.com/oam-dev/kubevela/pkg/cue/model"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
|
||||
)
|
||||
|
||||
const (
|
||||
specifyAddonClustersTopologyPolicy = "deploy-addon-to-specified-clusters"
|
||||
addonAllClusterPolicy = "deploy-addon-to-all-clusters"
|
||||
renderOutputCuePath = "output"
|
||||
renderAuxiliaryOutputsPath = "outputs"
|
||||
)
|
||||
|
||||
type addonCueTemplateRender struct {
|
||||
addon *InstallPackage
|
||||
inputArgs map[string]interface{}
|
||||
}
|
||||
|
||||
func (a addonCueTemplateRender) formatContext() (string, error) {
|
||||
args := a.inputArgs
|
||||
if args == nil {
|
||||
args = map[string]interface{}{}
|
||||
}
|
||||
bt, err := json.Marshal(args)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
paramFile := fmt.Sprintf("%s: %s", cuemodel.ParameterFieldName, string(bt))
|
||||
|
||||
var contextFile = strings.Builder{}
|
||||
// user custom parameter but be the first data and generated data should be appended at last
|
||||
// in case the user defined data has packages
|
||||
contextFile.WriteString(a.addon.Parameters + "\n")
|
||||
|
||||
// addon metadata context
|
||||
metadataJSON, err := json.Marshal(a.addon.Meta)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
contextFile.WriteString(fmt.Sprintf("context: metadata: %s\n", string(metadataJSON)))
|
||||
// parameter definition
|
||||
contextFile.WriteString(paramFile + "\n")
|
||||
|
||||
return contextFile.String(), nil
|
||||
}
|
||||
|
||||
// This func can be used for addon render component.
|
||||
// Please notice the result will be stored in object parameter, so object must be a pointer type
|
||||
func (a addonCueTemplateRender) toObject(cueTemplate string, path string, object interface{}) error {
|
||||
contextFile, err := a.formatContext()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := value.NewValue(contextFile, nil, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := v.LookupByScript(cueTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outputContent, err := out.LookupValue(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return outputContent.UnmarshalTo(object)
|
||||
}
|
||||
|
||||
// renderApp will render Application from CUE files
|
||||
func (a addonCueTemplateRender) renderApp() (*v1beta1.Application, error) {
|
||||
var app v1beta1.Application
|
||||
|
||||
contextFile, err := a.formatContext()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "format context for app render")
|
||||
}
|
||||
var files = []string{contextFile}
|
||||
for _, cuef := range a.addon.CUETemplates {
|
||||
files = append(files, cuef.Data)
|
||||
}
|
||||
|
||||
// TODO(wonderflow): add package discover to support vela own packages if needed
|
||||
v, err := value.NewValueWithFiles(a.addon.AppCueTemplate.Data, files, nil, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "load app template with CUE files")
|
||||
}
|
||||
outputContent, err := v.LookupValue(renderOutputCuePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "render app from output field from CUE")
|
||||
}
|
||||
err = outputContent.UnmarshalTo(&app)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode app from CUE")
|
||||
}
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
// generateAppFramework generate application from yaml defined by template.yaml or cue file from template.cue
|
||||
func generateAppFramework(addon *InstallPackage, parameters map[string]interface{}) (*v1beta1.Application, error) {
|
||||
if len(addon.AppCueTemplate.Data) != 0 && addon.AppTemplate != nil {
|
||||
return nil, ErrBothCueAndYamlTmpl
|
||||
}
|
||||
|
||||
var app *v1beta1.Application
|
||||
var err error
|
||||
if len(addon.AppCueTemplate.Data) != 0 {
|
||||
app, err = renderAppAccordingToCueTemplate(addon, parameters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
app = addon.AppTemplate
|
||||
if app == nil {
|
||||
app = &v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1beta1.SchemeGroupVersion.String(), Kind: v1beta1.ApplicationKind},
|
||||
}
|
||||
}
|
||||
if app.Spec.Components == nil {
|
||||
app.Spec.Components = []common2.ApplicationComponent{}
|
||||
}
|
||||
}
|
||||
|
||||
app.Name = addonutil.Addon2AppName(addon.Name)
|
||||
// force override the namespace defined vela with DefaultVelaNS,this value can be modified by Env
|
||||
app.SetNamespace(types.DefaultKubeVelaNS)
|
||||
if app.Labels == nil {
|
||||
app.Labels = make(map[string]string)
|
||||
}
|
||||
app.Labels[oam.LabelAddonName] = addon.Name
|
||||
app.Labels[oam.LabelAddonVersion] = addon.Version
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func renderAppAccordingToCueTemplate(addon *InstallPackage, args map[string]interface{}) (*v1beta1.Application, error) {
|
||||
r := addonCueTemplateRender{
|
||||
addon: addon,
|
||||
inputArgs: args,
|
||||
}
|
||||
app, err := r.renderApp()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
// renderCompAccordingCUETemplate will return a component from cue template
|
||||
func renderCompAccordingCUETemplate(cueTemplate ElementFile, addon *InstallPackage, args map[string]interface{}) (*common2.ApplicationComponent, error) {
|
||||
comp := common2.ApplicationComponent{}
|
||||
|
||||
r := addonCueTemplateRender{
|
||||
addon: addon,
|
||||
inputArgs: args,
|
||||
}
|
||||
if err := r.toObject(cueTemplate.Data, renderOutputCuePath, &comp); err != nil {
|
||||
return nil, fmt.Errorf("error rendering file %s: %w", cueTemplate.Name, err)
|
||||
}
|
||||
// If the name of component has been set, just keep it, otherwise will set with file name.
|
||||
if len(comp.Name) == 0 {
|
||||
fileName := strings.ReplaceAll(cueTemplate.Name, path.Ext(cueTemplate.Name), "")
|
||||
comp.Name = strings.ReplaceAll(fileName, ".", "-")
|
||||
}
|
||||
return &comp, nil
|
||||
}
|
||||
|
||||
// RenderApp render a K8s application
|
||||
func RenderApp(ctx context.Context, addon *InstallPackage, k8sClient client.Client, args map[string]interface{}) (*v1beta1.Application, error) {
|
||||
if args == nil {
|
||||
args = map[string]interface{}{}
|
||||
}
|
||||
app, err := generateAppFramework(addon, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.Spec.Components = append(app.Spec.Components, renderNeededNamespaceAsComps(addon)...)
|
||||
|
||||
resources, err := renderResources(addon, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.Spec.Components = append(app.Spec.Components, resources...)
|
||||
|
||||
// for legacy addons those hasn't define policy in template.cue but still want to deploy runtime cluster
|
||||
// attach topology policy to application.
|
||||
if checkNeedAttachTopologyPolicy(app, addon) {
|
||||
if err := attachPolicyForLegacyAddon(ctx, app, addon, args, k8sClient); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func attachPolicyForLegacyAddon(ctx context.Context, app *v1beta1.Application, addon *InstallPackage, args map[string]interface{}, k8sClient client.Client) error {
|
||||
deployClusters, err := checkDeployClusters(ctx, k8sClient, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isDeployToRuntime(addon) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(deployClusters) == 0 {
|
||||
// empty cluster args deploy to all clusters
|
||||
clusterSelector := map[string]interface{}{
|
||||
// empty labelSelector means deploy resources to all clusters
|
||||
ClusterLabelSelector: map[string]string{},
|
||||
}
|
||||
properties, err := json.Marshal(clusterSelector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
policy := v1beta1.AppPolicy{
|
||||
Name: addonAllClusterPolicy,
|
||||
Type: v1alpha1.TopologyPolicyType,
|
||||
Properties: &runtime.RawExtension{Raw: properties},
|
||||
}
|
||||
app.Spec.Policies = append(app.Spec.Policies, policy)
|
||||
} else {
|
||||
var found bool
|
||||
for _, c := range deployClusters {
|
||||
if c == multicluster.ClusterLocalName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
deployClusters = append(deployClusters, multicluster.ClusterLocalName)
|
||||
}
|
||||
// deploy to specified clusters
|
||||
if app.Spec.Policies == nil {
|
||||
app.Spec.Policies = []v1beta1.AppPolicy{}
|
||||
}
|
||||
body, err := json.Marshal(map[string][]string{types.ClustersArg: deployClusters})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app.Spec.Policies = append(app.Spec.Policies, v1beta1.AppPolicy{
|
||||
Name: specifyAddonClustersTopologyPolicy,
|
||||
Type: v1alpha1.TopologyPolicyType,
|
||||
Properties: &runtime.RawExtension{Raw: body},
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func renderResources(addon *InstallPackage, args map[string]interface{}) ([]common2.ApplicationComponent, error) {
|
||||
var resources []common2.ApplicationComponent
|
||||
if len(addon.YAMLTemplates) != 0 {
|
||||
comp, err := renderK8sObjectsComponent(addon.YAMLTemplates, addon.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resources = append(resources, *comp)
|
||||
}
|
||||
|
||||
for _, tmpl := range addon.CUETemplates {
|
||||
comp, err := renderCompAccordingCUETemplate(tmpl, addon, args)
|
||||
if err != nil && strings.Contains(err.Error(), "var(path=output) not exist") {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, NewAddonError(fmt.Sprintf("fail to render cue template %s", err.Error()))
|
||||
}
|
||||
resources = append(resources, *comp)
|
||||
}
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
// checkNeedAttachTopologyPolicy will check this addon want to deploy to runtime-cluster, but application template doesn't specify the
|
||||
// topology policy, then will attach the policy to application automatically.
|
||||
func checkNeedAttachTopologyPolicy(app *v1beta1.Application, addon *InstallPackage) bool {
|
||||
if !isDeployToRuntime(addon) {
|
||||
return false
|
||||
}
|
||||
for _, policy := range app.Spec.Policies {
|
||||
if policy.Type == v1alpha1.TopologyPolicyType {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func renderOutputs(addon *InstallPackage, args map[string]interface{}) ([]*unstructured.Unstructured, error) {
|
||||
outputs := map[string]interface{}{}
|
||||
r := addonCueTemplateRender{
|
||||
addon: addon,
|
||||
inputArgs: args,
|
||||
}
|
||||
if err := r.toObject(addon.AppCueTemplate.Data, renderAuxiliaryOutputsPath, &outputs); err != nil {
|
||||
if isErrorCueRenderPathNotFound(err, renderAuxiliaryOutputsPath) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var res []*unstructured.Unstructured
|
||||
for _, o := range outputs {
|
||||
if ao, ok := o.(map[string]interface{}); ok {
|
||||
res = append(res, &unstructured.Unstructured{Object: ao})
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func isDeployToRuntime(addon *InstallPackage) bool {
|
||||
if addon.DeployTo == nil {
|
||||
return false
|
||||
}
|
||||
return addon.DeployTo.RuntimeCluster || addon.DeployTo.LegacyRuntimeCluster
|
||||
}
|
||||
438
pkg/addon/render_test.go
Normal file
438
pkg/addon/render_test.go
Normal file
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
Copyright 2022 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 addon
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
)
|
||||
|
||||
func TestRenderAppTemplate(t *testing.T) {
|
||||
paraDefined := `parameter: {
|
||||
// +usage=The clusters to install
|
||||
clusters?: [...string]
|
||||
namespace: string
|
||||
}`
|
||||
resourceComponent1 := `
|
||||
myref: {
|
||||
type: "ref-objects"
|
||||
properties: {
|
||||
urls: ["https://hello.yaml"]
|
||||
}
|
||||
}
|
||||
`
|
||||
appTemplate := `output: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: "velaux"
|
||||
namespace: "vela-system"
|
||||
}
|
||||
spec: {
|
||||
components: [{
|
||||
type: "k8s-objects"
|
||||
name: "vela-namespace"
|
||||
properties: objects: [{
|
||||
apiVersion: "v1"
|
||||
kind: "Namespace"
|
||||
metadata: name: parameter.namespace
|
||||
}]
|
||||
},myref]
|
||||
policies: [{
|
||||
type: "shared-resource"
|
||||
name: "namespace"
|
||||
properties: rules: [{selector: resourceTypes: ["Namespace"]}]
|
||||
}, {
|
||||
type: "topology"
|
||||
name: "deploy-topology"
|
||||
properties: {
|
||||
if parameter.clusters != _|_ {
|
||||
clusters: parameter.clusters
|
||||
}
|
||||
if parameter.clusters == _|_ {
|
||||
clusterLabelSelector: {}
|
||||
}
|
||||
namespace: parameter.namespace
|
||||
}
|
||||
}]
|
||||
}
|
||||
}`
|
||||
addon := &InstallPackage{
|
||||
Meta: Meta{
|
||||
Name: "velaux",
|
||||
DeployTo: &DeployTo{
|
||||
RuntimeCluster: true,
|
||||
},
|
||||
},
|
||||
Parameters: paraDefined,
|
||||
CUETemplates: []ElementFile{{Data: resourceComponent1}},
|
||||
AppCueTemplate: ElementFile{Data: appTemplate},
|
||||
}
|
||||
|
||||
render := addonCueTemplateRender{
|
||||
addon: addon,
|
||||
inputArgs: map[string]interface{}{
|
||||
"namespace": "vela-system",
|
||||
},
|
||||
}
|
||||
app, err := render.renderApp()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(app.Spec.Components), 2)
|
||||
str, err := json.Marshal(app.Spec.Components[0].Properties)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, strings.Contains(string(str), `{"name":"vela-system"}`))
|
||||
str2, err := json.Marshal(app.Spec.Components[1].Properties)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, strings.Contains(string(str2), `{"urls":["https://hello.yaml"]}`))
|
||||
|
||||
assert.Equal(t, len(app.Spec.Policies), 2)
|
||||
str, err = json.Marshal(app.Spec.Policies)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, strings.Contains(string(str), `"clusterLabelSelector":{}`))
|
||||
|
||||
addon.CUETemplates = []ElementFile{{Data: resourceComponent1}}
|
||||
addon.AppCueTemplate = ElementFile{Data: "package main\n" + appTemplate}
|
||||
app, err = render.renderApp()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(app.Spec.Components), 2)
|
||||
|
||||
addon.Parameters = "package main\n" + paraDefined
|
||||
app, err = render.renderApp()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(app.Spec.Components), 2)
|
||||
|
||||
addon.CUETemplates = []ElementFile{{Data: "package hello\n" + resourceComponent1}}
|
||||
addon.AppCueTemplate = ElementFile{Data: "package main\n" + appTemplate}
|
||||
_, err = render.renderApp()
|
||||
assert.Equal(t, err.Error(), `load app template with CUE files: reference "myref" not found`)
|
||||
|
||||
addon.CUETemplates = []ElementFile{{Data: "package hello\n" + resourceComponent1}}
|
||||
addon.Parameters = paraDefined
|
||||
addon.AppCueTemplate = ElementFile{Data: appTemplate}
|
||||
_, err = render.renderApp()
|
||||
assert.Equal(t, err.Error(), `load app template with CUE files: reference "myref" not found`)
|
||||
|
||||
}
|
||||
|
||||
func TestOutputsRender(t *testing.T) {
|
||||
appTemplate := `output: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: "velaux"
|
||||
namespace: "vela-system"
|
||||
}
|
||||
spec: {
|
||||
components: [{
|
||||
type: "k8s-objects"
|
||||
name: "vela-namespace"
|
||||
properties: objects: [{
|
||||
apiVersion: "v1"
|
||||
kind: "Namespace"
|
||||
metadata: name: parameter.namespace
|
||||
}]
|
||||
}]
|
||||
policies: [{
|
||||
type: "shared-resource"
|
||||
name: "namespace"
|
||||
properties: rules: [{selector: resourceTypes: ["Namespace"]}]
|
||||
}, {
|
||||
type: "topology"
|
||||
name: "deploy-topology"
|
||||
properties: {
|
||||
if parameter.clusters != _|_ {
|
||||
clusters: parameter.clusters
|
||||
}
|
||||
if parameter.clusters == _|_ {
|
||||
clusterLabelSelector: {}
|
||||
}
|
||||
namespace: parameter.namespace
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
outputs: configmap: {
|
||||
apiVersion: "v1"
|
||||
kind: "Configmap"
|
||||
metadata: {
|
||||
name: "test-cm"
|
||||
namespace: "default"
|
||||
}
|
||||
data: parameter.data
|
||||
}
|
||||
`
|
||||
paraDefined := `parameter: {
|
||||
// +usage=The clusters to install
|
||||
data: "myData"
|
||||
}`
|
||||
appTemplateNoOutputs := `output: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: "velaux"
|
||||
namespace: "vela-system"
|
||||
}
|
||||
spec: {
|
||||
components: [{
|
||||
type: "k8s-objects"
|
||||
name: "vela-namespace"
|
||||
properties: objects: [{
|
||||
apiVersion: "v1"
|
||||
kind: "Namespace"
|
||||
metadata: name: parameter.namespace
|
||||
}]
|
||||
}]
|
||||
policies: [{
|
||||
type: "shared-resource"
|
||||
name: "namespace"
|
||||
properties: rules: [{selector: resourceTypes: ["Namespace"]}]
|
||||
}, {
|
||||
type: "topology"
|
||||
name: "deploy-topology"
|
||||
properties: {
|
||||
if parameter.clusters != _|_ {
|
||||
clusters: parameter.clusters
|
||||
}
|
||||
if parameter.clusters == _|_ {
|
||||
clusterLabelSelector: {}
|
||||
}
|
||||
namespace: parameter.namespace
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
`
|
||||
|
||||
addon := &InstallPackage{
|
||||
Meta: Meta{
|
||||
Name: "velaux",
|
||||
DeployTo: &DeployTo{
|
||||
RuntimeCluster: true,
|
||||
},
|
||||
},
|
||||
Parameters: paraDefined,
|
||||
AppCueTemplate: ElementFile{Data: appTemplate},
|
||||
}
|
||||
list, err := renderOutputs(addon, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, len(list) == 1)
|
||||
|
||||
addon = &InstallPackage{
|
||||
Meta: Meta{
|
||||
Name: "velaux",
|
||||
DeployTo: &DeployTo{
|
||||
RuntimeCluster: true,
|
||||
},
|
||||
},
|
||||
Parameters: paraDefined,
|
||||
AppCueTemplate: ElementFile{Data: appTemplateNoOutputs},
|
||||
}
|
||||
_, err = renderOutputs(addon, nil)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestAppComponentRender(t *testing.T) {
|
||||
paraDefined := `parameter: {
|
||||
image: string
|
||||
}`
|
||||
compTemplate := `output: {
|
||||
type: "webservice"
|
||||
name: "velaux"
|
||||
properties: {
|
||||
image: parameter.image}
|
||||
}`
|
||||
addon := &InstallPackage{
|
||||
Meta: Meta{
|
||||
Name: "velaux",
|
||||
DeployTo: &DeployTo{
|
||||
RuntimeCluster: true,
|
||||
},
|
||||
},
|
||||
Parameters: paraDefined,
|
||||
}
|
||||
|
||||
render := addonCueTemplateRender{
|
||||
addon: addon,
|
||||
inputArgs: map[string]interface{}{
|
||||
"image": "1.4.1",
|
||||
},
|
||||
}
|
||||
comp := common.ApplicationComponent{}
|
||||
err := render.toObject(compTemplate, renderOutputCuePath, &comp)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, comp.Name, "velaux")
|
||||
assert.Equal(t, comp.Type, "webservice")
|
||||
str, err := json.Marshal(comp.Properties)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `{"image":"1.4.1"}`, string(str))
|
||||
}
|
||||
|
||||
func TestCheckNeedAttachTopologyPolicy(t *testing.T) {
|
||||
addon1 := &InstallPackage{
|
||||
Meta: Meta{
|
||||
DeployTo: nil,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, checkNeedAttachTopologyPolicy(nil, addon1), false)
|
||||
|
||||
addon2 := &InstallPackage{
|
||||
Meta: Meta{
|
||||
DeployTo: &DeployTo{RuntimeCluster: false},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, checkNeedAttachTopologyPolicy(nil, addon2), false)
|
||||
|
||||
addon3 := &InstallPackage{
|
||||
Meta: Meta{
|
||||
DeployTo: &DeployTo{RuntimeCluster: true},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, checkNeedAttachTopologyPolicy(&v1beta1.Application{Spec: v1beta1.ApplicationSpec{Policies: []v1beta1.AppPolicy{{
|
||||
Type: v1alpha1.TopologyPolicyType,
|
||||
}}}}, addon3), false)
|
||||
|
||||
addon4 := &InstallPackage{
|
||||
Meta: Meta{
|
||||
DeployTo: &DeployTo{RuntimeCluster: true},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, checkNeedAttachTopologyPolicy(&v1beta1.Application{Spec: v1beta1.ApplicationSpec{Policies: []v1beta1.AppPolicy{{
|
||||
Type: v1alpha1.SharedResourcePolicyType,
|
||||
}}}}, addon4), true)
|
||||
}
|
||||
|
||||
func TestGenerateAppFrameworkWithCue(t *testing.T) {
|
||||
paraDefined := `parameter: {
|
||||
// +usage=The clusters to install
|
||||
clusters?: [...string]
|
||||
namespace: string
|
||||
}`
|
||||
cueTemplate := `output: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: "velaux"
|
||||
namespace: "vela-system"
|
||||
}
|
||||
spec: {
|
||||
components: [{
|
||||
type: "k8s-objects"
|
||||
name: "vela-namespace"
|
||||
properties: objects: [{
|
||||
apiVersion: "v1"
|
||||
kind: "Namespace"
|
||||
metadata: name: parameter.namespace
|
||||
}]
|
||||
}]
|
||||
policies: [{
|
||||
type: "shared-resource"
|
||||
name: "namespace"
|
||||
properties: rules: [{selector: resourceTypes: ["Namespace"]}]
|
||||
}, {
|
||||
type: "topology"
|
||||
name: "deploy-topology"
|
||||
properties: {
|
||||
if parameter.clusters != _|_ {
|
||||
clusters: parameter.clusters
|
||||
}
|
||||
if parameter.clusters == _|_ {
|
||||
clusterLabelSelector: {}
|
||||
}
|
||||
namespace: parameter.namespace
|
||||
}
|
||||
}]
|
||||
}
|
||||
}`
|
||||
cueAddon := &InstallPackage{
|
||||
Meta: Meta{Name: "velaux", DeployTo: &DeployTo{RuntimeCluster: true}},
|
||||
AppCueTemplate: ElementFile{Data: cueTemplate},
|
||||
Parameters: paraDefined,
|
||||
}
|
||||
app, err := generateAppFramework(cueAddon, map[string]interface{}{
|
||||
"namespace": "vela-system",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(app.Spec.Components), 1)
|
||||
str, err := json.Marshal(app.Spec.Components[0].Properties)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, strings.Contains(string(str), `{"name":"vela-system"}`))
|
||||
assert.Equal(t, len(app.Spec.Policies), 2)
|
||||
str, err = json.Marshal(app.Spec.Policies)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, strings.Contains(string(str), `"clusterLabelSelector":{}`))
|
||||
assert.Equal(t, len(app.Labels), 2)
|
||||
}
|
||||
|
||||
func TestGenerateAppFrameworkWithYamlTemplate(t *testing.T) {
|
||||
yamlAddon := &InstallPackage{
|
||||
Meta: Meta{Name: "velaux"},
|
||||
AppTemplate: nil,
|
||||
}
|
||||
app, err := generateAppFramework(yamlAddon, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, app.Spec.Components != nil, true)
|
||||
assert.Equal(t, len(app.Labels), 2)
|
||||
|
||||
noCompAddon := &InstallPackage{
|
||||
Meta: Meta{Name: "velaux"},
|
||||
AppTemplate: &v1beta1.Application{},
|
||||
}
|
||||
app, err = generateAppFramework(noCompAddon, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, app.Spec.Components != nil, true)
|
||||
assert.Equal(t, len(app.Labels), 2)
|
||||
}
|
||||
|
||||
func TestRenderCueResourceError(t *testing.T) {
|
||||
cueTemplate1 := `output: {
|
||||
type: "webservice"
|
||||
name: "velaux"
|
||||
}`
|
||||
cueTemplate2 := `output: {
|
||||
type: "webservice"
|
||||
name: "velaux2"
|
||||
}`
|
||||
cueTemplate3 := `nooutput: {
|
||||
type: "webservice"
|
||||
name: "velaux3"
|
||||
}`
|
||||
comp, err := renderResources(&InstallPackage{
|
||||
CUETemplates: []ElementFile{
|
||||
{
|
||||
Data: cueTemplate1,
|
||||
Name: "tmplaate1.cue",
|
||||
},
|
||||
{
|
||||
Data: cueTemplate2,
|
||||
Name: "tmplaate2.cue",
|
||||
},
|
||||
{
|
||||
Data: cueTemplate3,
|
||||
Name: "tmplaate3.cue",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(comp), 2)
|
||||
}
|
||||
@@ -78,9 +78,10 @@ type GitlabAddonSource struct {
|
||||
|
||||
// HelmSource defines the information about the helm repo addon source
|
||||
type HelmSource struct {
|
||||
URL string `json:"url,omitempty" validate:"required"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
URL string `json:"url,omitempty" validate:"required"`
|
||||
InsecureSkipTLS bool `json:"insecureSkipTLS,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
// SafeCopier is an interface to copy Struct without sensitive fields, such as Token, Username, Password
|
||||
@@ -318,6 +319,9 @@ func (r *Registry) GetUIData(meta *SourceMeta, opt ListOptions) (*UIData, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(addon.GlobalParameters) != 0 {
|
||||
addon.Parameters = addon.GlobalParameters
|
||||
}
|
||||
return addon, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -21,20 +21,18 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
|
||||
ocmclusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
ocmworkv1 "open-cluster-management.io/api/work/v1"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
v12 "k8s.io/api/core/v1"
|
||||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/discovery"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
|
||||
ocmclusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
|
||||
ocmworkv1 "open-cluster-management.io/api/work/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
|
||||
@@ -42,6 +40,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
coreoam "github.com/oam-dev/kubevela/apis/core.oam.dev"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/cue/packages"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
// +kubebuilder:scaffold:imports
|
||||
@@ -105,6 +104,9 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Name: testns,
|
||||
}}))
|
||||
|
||||
err = stepHelmHttpServer()
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
close(done)
|
||||
}, 120)
|
||||
|
||||
|
||||
12
pkg/addon/testdata/example-legacy/Chart.yaml
vendored
Normal file
12
pkg/addon/testdata/example-legacy/Chart.yaml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: v2
|
||||
appVersion: 1.0.1
|
||||
description: Extended workload to do continuous and progressive delivery
|
||||
home: https://fluxcd.io
|
||||
icon: https://raw.githubusercontent.com/fluxcd/flux/master/docs/_files/weave-flux.png
|
||||
keywords:
|
||||
- extended_workload
|
||||
- gitops
|
||||
- only_example
|
||||
name: example-legacy
|
||||
type: library
|
||||
version: 1.0.1
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user