mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-23 14:23:54 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a783393ebd | ||
|
|
a19ed0b510 | ||
|
|
03223aa786 | ||
|
|
55c8dad116 | ||
|
|
38c57c38c8 | ||
|
|
7f734e9479 | ||
|
|
7814232b7c | ||
|
|
b1cc06b0f3 | ||
|
|
ed9d53b448 | ||
|
|
ad83e59865 | ||
|
|
b62eeca3f9 | ||
|
|
5d9757fcb8 | ||
|
|
4d653951a1 | ||
|
|
bcda4976a9 | ||
|
|
a01d0e773a | ||
|
|
f0e3304c17 | ||
|
|
e9f1e21d55 | ||
|
|
de127b7311 | ||
|
|
9f0558c62e | ||
|
|
0f547fa158 | ||
|
|
84155d06fb | ||
|
|
bc7e31f979 | ||
|
|
f406936dce | ||
|
|
c2ecc71941 | ||
|
|
c1efd3f056 | ||
|
|
7002182072 | ||
|
|
554a06e35e | ||
|
|
4ffb7e6707 | ||
|
|
caeb334340 | ||
|
|
275b61d427 | ||
|
|
11904a6f60 | ||
|
|
4b4e4f8530 | ||
|
|
0121e8b6ef | ||
|
|
382510aa67 | ||
|
|
7ae7d2a5ef | ||
|
|
0736e85e07 | ||
|
|
f01e6d9723 | ||
|
|
2d7d4ef99d | ||
|
|
6bbce07a21 | ||
|
|
12ba4631c1 | ||
|
|
d5b4f9ae5d | ||
|
|
d62185315a | ||
|
|
12f0cebc6c | ||
|
|
284a7d08b2 | ||
|
|
c91850ce0d | ||
|
|
e13b31d00e | ||
|
|
71d0d7344f | ||
|
|
247845db0a | ||
|
|
427809cea7 | ||
|
|
6c29b7b088 | ||
|
|
77e85472fa | ||
|
|
c60df945c3 | ||
|
|
28488a4e9b | ||
|
|
1ae7ba1e1e | ||
|
|
2076c2f937 |
2
.github/workflows/apiserver-test.yaml
vendored
2
.github/workflows/apiserver-test.yaml
vendored
@@ -176,10 +176,12 @@ jobs:
|
||||
make e2e-cleanup
|
||||
make e2e-setup-core
|
||||
bin/vela addon enable fluxcd
|
||||
bin/vela addon enable vela-workflow
|
||||
timeout 600s bash -c -- 'while true; do kubectl get ns flux-system; if [ $? -eq 0 ] ; then break; else sleep 5; fi;done'
|
||||
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=vela-core,app.kubernetes.io/instance=kubevela -n vela-system --timeout=600s
|
||||
kubectl wait --for=condition=Ready pod -l app=source-controller -n flux-system --timeout=600s
|
||||
kubectl wait --for=condition=Ready pod -l app=helm-controller -n flux-system --timeout=600s
|
||||
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=vela-workflow -n vela-system --timeout=600s
|
||||
|
||||
- name: Run api server e2e test
|
||||
run: |
|
||||
|
||||
44
.github/workflows/definition-lint.yml
vendored
Normal file
44
.github/workflows/definition-lint.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Definition-Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
workflow_dispatch: {}
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.19'
|
||||
|
||||
jobs:
|
||||
definition-doc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup K3d
|
||||
uses: nolar/setup-k3d-k3s@v1.0.9
|
||||
with:
|
||||
version: v1.20
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Definition Doc generate check
|
||||
run: |
|
||||
go build -o docgen hack/docgen/def/gen.go
|
||||
./docgen --type=comp --force-example-doc --path=./comp-def-check.md
|
||||
./docgen --type=trait --force-example-doc --path=./trait-def-check.md
|
||||
./docgen --type=wf --force-example-doc --path=./wf-def-check.md
|
||||
./docgen --type=policy --force-example-doc --path=./policy-def-check.md
|
||||
32
.github/workflows/go.yml
vendored
32
.github/workflows/go.yml
vendored
@@ -141,3 +141,35 @@ jobs:
|
||||
|
||||
- name: Cleanup binary
|
||||
run: make build-cleanup
|
||||
|
||||
check-windows:
|
||||
runs-on: windows-latest
|
||||
needs: detect-noop
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- 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: Run Build CLI
|
||||
run: make vela-cli
|
||||
|
||||
- name: Run CLI for version
|
||||
shell: cmd
|
||||
run: |
|
||||
move .\bin\vela .\bin\vela.exe
|
||||
.\bin\vela.exe version
|
||||
|
||||
@@ -189,8 +189,6 @@ type Status struct {
|
||||
type ApplicationPhase string
|
||||
|
||||
const (
|
||||
// ApplicationRollingOut means the app is in the middle of rolling out
|
||||
ApplicationRollingOut ApplicationPhase = "rollingOut"
|
||||
// ApplicationStarting means the app is preparing for reconcile
|
||||
ApplicationStarting ApplicationPhase = "starting"
|
||||
// ApplicationRendering means the app is rendering
|
||||
@@ -205,8 +203,6 @@ const (
|
||||
ApplicationWorkflowTerminated ApplicationPhase = "workflowTerminated"
|
||||
// ApplicationWorkflowFailed means the app's workflow is failed
|
||||
ApplicationWorkflowFailed ApplicationPhase = "workflowFailed"
|
||||
// ApplicationWorkflowFinished means the app's workflow is finished
|
||||
ApplicationWorkflowFinished ApplicationPhase = "workflowFinished"
|
||||
// ApplicationRunning means the app finished rendering and applied result to the cluster
|
||||
ApplicationRunning ApplicationPhase = "running"
|
||||
// ApplicationUnhealthy means the app finished rendering and applied result to the cluster, but still unhealthy
|
||||
|
||||
@@ -23,8 +23,17 @@ import (
|
||||
const (
|
||||
// ApplyOncePolicyType refers to the type of configuration drift policy
|
||||
ApplyOncePolicyType = "apply-once"
|
||||
// ApplyOnceStrategyOnAppUpdate policy takes effect on application updating
|
||||
ApplyOnceStrategyOnAppUpdate ApplyOnceAffectStrategy = "onUpdate"
|
||||
// ApplyOnceStrategyOnAppStateKeep policy takes effect on application state keep
|
||||
ApplyOnceStrategyOnAppStateKeep ApplyOnceAffectStrategy = "onStateKeep"
|
||||
// ApplyOnceStrategyAlways policy takes effect always
|
||||
ApplyOnceStrategyAlways ApplyOnceAffectStrategy = "always"
|
||||
)
|
||||
|
||||
// ApplyOnceAffectStrategy is a string that mark the policy effective stage
|
||||
type ApplyOnceAffectStrategy string
|
||||
|
||||
// ApplyOncePolicySpec defines the spec of preventing configuration drift
|
||||
type ApplyOncePolicySpec struct {
|
||||
Enable bool `json:"enable"`
|
||||
@@ -45,6 +54,9 @@ type ApplyOnceStrategy struct {
|
||||
// Path the specified path that allow configuration drift
|
||||
// like 'spec.template.spec.containers[0].resources' and '*' means the whole target allow configuration drift
|
||||
Path []string `json:"path"`
|
||||
// ApplyOnceAffectStrategy Decide when the strategy will take effect
|
||||
// like affect:onUpdate/onStateKeep/always
|
||||
ApplyOnceAffectStrategy ApplyOnceAffectStrategy `json:"affect"`
|
||||
}
|
||||
|
||||
// FindStrategy find apply-once strategy for target resource
|
||||
|
||||
@@ -64,6 +64,8 @@ const (
|
||||
LabelDefinitionDeprecated = "custom.definition.oam.dev/deprecated"
|
||||
// LabelDefinitionHidden is the label which describe whether the capability is hidden by UI
|
||||
LabelDefinitionHidden = "custom.definition.oam.dev/ui-hidden"
|
||||
// LabelDefinitionScope is the label which describe whether the capability's scope
|
||||
LabelDefinitionScope = "custom.definition.oam.dev/scope"
|
||||
// LabelNodeRoleGateway gateway role of node
|
||||
LabelNodeRoleGateway = "node-role.kubernetes.io/gateway"
|
||||
// LabelNodeRoleWorker worker role of node
|
||||
|
||||
@@ -97,6 +97,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `featureGates.gzipResourceTracker` | if enabled, resourceTracker will be compressed using gzip before being stored | `false` |
|
||||
| `featureGates.zstdResourceTracker` | if enabled, resourceTracker will be compressed using zstd before being stored. It is much faster and more efficient than gzip. If both gzip and zstd are enabled, zstd will be used. | `false` |
|
||||
| `featureGates.applyOnce` | if enabled, the apply-once feature will be applied to all applications, no state-keep and no resource data storage in ResourceTracker | `false` |
|
||||
| `featureGates.multiStageComponentApply` | if enabled, the multiStageComponentApply feature will be combined with the stage field in TraitDefinition to complete the multi-stage apply. | `false` |
|
||||
|
||||
|
||||
### MultiCluster parameters
|
||||
|
||||
@@ -2209,10 +2209,11 @@ spec:
|
||||
execution
|
||||
properties:
|
||||
steps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: SubSteps is the mode of workflow sub
|
||||
steps execution
|
||||
type: string
|
||||
type: object
|
||||
ref:
|
||||
@@ -4008,6 +4009,17 @@ spec:
|
||||
namespace:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: WorkflowExecuteMode defines the mode of workflow
|
||||
execution
|
||||
properties:
|
||||
steps:
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: SubSteps is the mode of workflow sub steps execution
|
||||
type: string
|
||||
type: object
|
||||
steps:
|
||||
items:
|
||||
description: WorkflowStep defines how to execute a workflow
|
||||
|
||||
@@ -1020,10 +1020,10 @@ spec:
|
||||
execution
|
||||
properties:
|
||||
steps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: SubSteps is the mode of workflow sub steps execution
|
||||
type: string
|
||||
type: object
|
||||
ref:
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.6.2
|
||||
controller-gen.kubebuilder.io/version: v0.9.0
|
||||
creationTimestamp: null
|
||||
name: workflows.core.oam.dev
|
||||
spec:
|
||||
group: core.oam.dev
|
||||
@@ -34,6 +34,16 @@ spec:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
mode:
|
||||
description: WorkflowExecuteMode defines the mode of workflow execution
|
||||
properties:
|
||||
steps:
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: SubSteps is the mode of workflow sub steps execution
|
||||
type: string
|
||||
type: object
|
||||
steps:
|
||||
items:
|
||||
description: WorkflowStep defines how to execute a workflow step.
|
||||
@@ -161,153 +171,3 @@ spec:
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
- name: v1beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Workflow defines workflow steps and other attributes
|
||||
properties:
|
||||
mode:
|
||||
description: WorkflowExecuteMode defines the mode of workflow execution
|
||||
properties:
|
||||
steps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
type: string
|
||||
subSteps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
type: string
|
||||
type: object
|
||||
ref:
|
||||
type: string
|
||||
steps:
|
||||
items:
|
||||
description: WorkflowStep defines how to execute a workflow step.
|
||||
properties:
|
||||
dependsOn:
|
||||
description: DependsOn is the dependency of the step
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
if:
|
||||
description: If is the if condition of the step
|
||||
type: string
|
||||
inputs:
|
||||
description: Inputs is the inputs of the step
|
||||
items:
|
||||
properties:
|
||||
from:
|
||||
type: string
|
||||
parameterKey:
|
||||
type: string
|
||||
required:
|
||||
- from
|
||||
- parameterKey
|
||||
type: object
|
||||
type: array
|
||||
meta:
|
||||
description: Meta is the meta data of the workflow step.
|
||||
properties:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
outputs:
|
||||
description: Outputs is the outputs of the step
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
valueFrom:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- valueFrom
|
||||
type: object
|
||||
type: array
|
||||
properties:
|
||||
description: Properties is the properties of the step
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
subSteps:
|
||||
items:
|
||||
description: WorkflowStepBase defines the workflow step base
|
||||
properties:
|
||||
dependsOn:
|
||||
description: DependsOn is the dependency of the step
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
if:
|
||||
description: If is the if condition of the step
|
||||
type: string
|
||||
inputs:
|
||||
description: Inputs is the inputs of the step
|
||||
items:
|
||||
properties:
|
||||
from:
|
||||
type: string
|
||||
parameterKey:
|
||||
type: string
|
||||
required:
|
||||
- from
|
||||
- parameterKey
|
||||
type: object
|
||||
type: array
|
||||
meta:
|
||||
description: Meta is the meta data of the workflow step.
|
||||
properties:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
outputs:
|
||||
description: Outputs is the outputs of the step
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
valueFrom:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- valueFrom
|
||||
type: object
|
||||
type: array
|
||||
properties:
|
||||
description: Properties is the properties of the step
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
timeout:
|
||||
description: Timeout is the timeout of the step
|
||||
type: string
|
||||
type:
|
||||
description: Type is the type of the workflow step.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
timeout:
|
||||
description: Timeout is the timeout of the step
|
||||
type: string
|
||||
type:
|
||||
description: Type is the type of the workflow step.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
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/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-application-in-parallel
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
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/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-application
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/apply-component.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply a specific component and its corresponding traits in application
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-component
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
parameter: {
|
||||
// +usage=Specify the component name to apply
|
||||
component: string
|
||||
// +usage=Specify the cluster
|
||||
cluster: *"" | string
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ spec:
|
||||
cue:
|
||||
template: |
|
||||
#ApplyOnceStrategy: {
|
||||
// +usage=When the strategy takes effect,e.g. onUpdate、onStateKeep
|
||||
affect?: string
|
||||
// +usage=Specify the path of the resource that allow configuration drift
|
||||
path: [...string]
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
definition.oam.dev/description: Apply remaining components and traits
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-remaining
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Deploy cloud resource and deliver secret to multi clusters.
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
name: deploy-cloud-resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: A powerful and unified deploy step for components multi-cluster delivery with policies.
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
name: deploy
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
definition.oam.dev/description: Deploy env binding component to target env
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: deploy2env
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
definition.oam.dev/description: Deploy application to runtime clusters
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/scope: Application
|
||||
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: pring message in workflow status
|
||||
definition.oam.dev/description: print message in workflow step status
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: print-message-in-status
|
||||
|
||||
@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Sync secrets created by terraform component to runtime clusters so that runtime clusters can share the created cloud resource.
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
name: share-cloud-resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -13,6 +13,7 @@ spec:
|
||||
template: |
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
mountsArray: [
|
||||
@@ -167,7 +168,11 @@ spec:
|
||||
name: v.name
|
||||
}
|
||||
if v.name == _|_ {
|
||||
name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
_name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
name: *_name | string
|
||||
if v.protocol != "TCP" {
|
||||
name: _name + "-" + strings.ToLower(v.protocol)
|
||||
}
|
||||
}
|
||||
}}]
|
||||
}
|
||||
@@ -283,11 +288,18 @@ spec:
|
||||
name: v.name
|
||||
}
|
||||
if v.name == _|_ {
|
||||
name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
_name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
name: *_name | string
|
||||
if v.protocol != "TCP" {
|
||||
name: _name + "-" + strings.ToLower(v.protocol)
|
||||
}
|
||||
}
|
||||
if v.nodePort != _|_ && parameter.exposeType == "NodePort" {
|
||||
nodePort: v.nodePort
|
||||
}
|
||||
if v.protocol != _|_ {
|
||||
protocol: v.protocol
|
||||
}
|
||||
},
|
||||
]
|
||||
outputs: {
|
||||
|
||||
@@ -116,6 +116,39 @@ subjects:
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
|
||||
---
|
||||
# permissions to read the view of VelaQL, schemas, and templates.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:template-reader-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps/status
|
||||
verbs:
|
||||
- get
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:template-reader-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: {{ include "kubevela.fullname" . }}:template-reader-role
|
||||
subjects:
|
||||
- kind: Group
|
||||
name: template-reader
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -221,6 +254,7 @@ spec:
|
||||
- "--feature-gates=GzipResourceTracker={{- .Values.featureGates.gzipResourceTracker | toString -}}"
|
||||
- "--feature-gates=ZstdResourceTracker={{- .Values.featureGates.zstdResourceTracker | toString -}}"
|
||||
- "--feature-gates=ApplyOnce={{- .Values.featureGates.applyOnce | toString -}}"
|
||||
- "--feature-gates=MultiStageComponentApply= {{- .Values.featureGates.multiStageComponentApply | toString -}}"
|
||||
{{ if .Values.authentication.enabled }}
|
||||
{{ if .Values.authentication.withUser }}
|
||||
- "--authentication-with-user"
|
||||
|
||||
@@ -113,11 +113,13 @@ optimize:
|
||||
##@param featureGates.gzipResourceTracker if enabled, resourceTracker will be compressed using gzip before being stored
|
||||
##@param featureGates.zstdResourceTracker if enabled, resourceTracker will be compressed using zstd before being stored. It is much faster and more efficient than gzip. If both gzip and zstd are enabled, zstd will be used.
|
||||
##@param featureGates.applyOnce if enabled, the apply-once feature will be applied to all applications, no state-keep and no resource data storage in ResourceTracker
|
||||
##@param featureGates.multiStageComponentApply if enabled, the multiStageComponentApply feature will be combined with the stage field in TraitDefinition to complete the multi-stage apply.
|
||||
featureGates:
|
||||
enableLegacyComponentRevision: false
|
||||
gzipResourceTracker: false
|
||||
zstdResourceTracker: false
|
||||
applyOnce: false
|
||||
multiStageComponentApply: false
|
||||
|
||||
## @section MultiCluster parameters
|
||||
|
||||
|
||||
@@ -2209,10 +2209,11 @@ spec:
|
||||
execution
|
||||
properties:
|
||||
steps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: SubSteps is the mode of workflow sub
|
||||
steps execution
|
||||
type: string
|
||||
type: object
|
||||
ref:
|
||||
@@ -4008,6 +4009,17 @@ spec:
|
||||
namespace:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: WorkflowExecuteMode defines the mode of workflow
|
||||
execution
|
||||
properties:
|
||||
steps:
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: SubSteps is the mode of workflow sub steps execution
|
||||
type: string
|
||||
type: object
|
||||
steps:
|
||||
items:
|
||||
description: WorkflowStep defines how to execute a workflow
|
||||
|
||||
@@ -1020,10 +1020,10 @@ spec:
|
||||
execution
|
||||
properties:
|
||||
steps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: SubSteps is the mode of workflow sub steps execution
|
||||
type: string
|
||||
type: object
|
||||
ref:
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
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/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-application-in-parallel
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
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/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-application
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/apply-component.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply a specific component and its corresponding traits in application
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-component
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
parameter: {
|
||||
// +usage=Specify the component name to apply
|
||||
component: string
|
||||
// +usage=Specify the cluster
|
||||
cluster: *"" | string
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ spec:
|
||||
cue:
|
||||
template: |
|
||||
#ApplyOnceStrategy: {
|
||||
// +usage=When the strategy takes effect,e.g. onUpdate、onStateKeep
|
||||
affect?: string
|
||||
// +usage=Specify the path of the resource that allow configuration drift
|
||||
path: [...string]
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
definition.oam.dev/description: Apply remaining components and traits
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-remaining
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
|
||||
@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Deploy cloud resource and deliver secret to multi clusters.
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
name: deploy-cloud-resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: A powerful and unified deploy step for components multi-cluster delivery with policies.
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
name: deploy
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata:
|
||||
definition.oam.dev/description: Deploy application to runtime clusters
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/scope: Application
|
||||
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: pring message in workflow status
|
||||
definition.oam.dev/description: print message in workflow step status
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: print-message-in-status
|
||||
|
||||
@@ -5,6 +5,8 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Sync secrets created by terraform component to runtime clusters so that runtime clusters can share the created cloud resource.
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
name: share-cloud-resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -13,6 +13,7 @@ spec:
|
||||
template: |
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
mountsArray: [
|
||||
@@ -167,7 +168,11 @@ spec:
|
||||
name: v.name
|
||||
}
|
||||
if v.name == _|_ {
|
||||
name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
_name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
name: *_name | string
|
||||
if v.protocol != "TCP" {
|
||||
name: _name + "-" + strings.ToLower(v.protocol)
|
||||
}
|
||||
}
|
||||
}}]
|
||||
}
|
||||
@@ -283,11 +288,18 @@ spec:
|
||||
name: v.name
|
||||
}
|
||||
if v.name == _|_ {
|
||||
name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
_name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
name: *_name | string
|
||||
if v.protocol != "TCP" {
|
||||
name: _name + "-" + strings.ToLower(v.protocol)
|
||||
}
|
||||
}
|
||||
if v.nodePort != _|_ && parameter.exposeType == "NodePort" {
|
||||
nodePort: v.nodePort
|
||||
}
|
||||
if v.protocol != _|_ {
|
||||
protocol: v.protocol
|
||||
}
|
||||
},
|
||||
]
|
||||
outputs: {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -120,4 +120,61 @@ EOF
|
||||
|
||||
In the `apply-once-app-3` case, any changes of `hello-cosmos` deployment will not be brought back and any changes
|
||||
of `hello-cosmos` service will be brought back in the next reconcile loop. In the same time, any changes
|
||||
of `hello-world` component will be brought back in the next reconcile loop.
|
||||
of `hello-world` component will be brought back in the next reconcile loop.
|
||||
|
||||
```shell
|
||||
$ cat <<EOF | kubectl apply -f -
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: apply-once-app-4
|
||||
spec:
|
||||
components:
|
||||
- name: hello-world
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8080
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 1
|
||||
- name: hello-cosmos
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8080
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 1
|
||||
policies:
|
||||
- name: apply-once
|
||||
type: apply-once
|
||||
properties:
|
||||
enable: true
|
||||
rules:
|
||||
- selector:
|
||||
componentNames: [ "hello-cosmos" ]
|
||||
resourceTypes: [ "Deployment" ]
|
||||
strategy:
|
||||
affect: onStateKeep
|
||||
path: [ "spec.replicas"]
|
||||
EOF
|
||||
```
|
||||
|
||||
By default, KubeVela executes the apply-once policy in two phases: application update and cycle state maintenance,
|
||||
allowing configuration drift depending on the policy configuration.
|
||||
|
||||
If you have special requirements, you can set the affect to determine the phase of policy execution .
|
||||
affect supported configurations: onUpdate/onStateKeep/always (default)
|
||||
|
||||
When affect=always, or not set, the policy is executed in two phase.
|
||||
|
||||
When affect=onStateKeep, the policy is executed only during the stateKeep phase. In the case of `apply-once-app-4`, any
|
||||
changes to the deployed copy of `hello-cosmos` will not be brought back to the next state keeping loop, but will be
|
||||
brought back to the next application update.
|
||||
|
||||
When affect=onUpdate, the policy is only executed when the application is updated. In the case of `
|
||||
apply-once-app-4`, if affect=onUpdate is set, any changes to the deployed copy of `hello-cosmos` will not be brought
|
||||
back in the next application update, but will be brought back in the next state keeping loop.
|
||||
|
||||
43
docs/examples/multi-stage-component-apply/README.md
Normal file
43
docs/examples/multi-stage-component-apply/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# MultiStageComponentApply
|
||||
|
||||
This example shows how to enable MultiStageComponentApply, the MultiStageComponentApply feature will be combined with the stage field in TraitDefinition to complete the multi-stage apply. Currently, the stage field in TraitDefinition is an optional parameter, which provides `PreDispatch` and `PostDispatch`.
|
||||
|
||||
## How to use multi-stage
|
||||
> The future-gate is still in alpha stage, and it is recommended to use it only in short-term test clusters.
|
||||
|
||||
The `MultiStageComponentApply` is not enabled by default, you need some extra works to use it.
|
||||
|
||||
1. Add an args `--feature-gates=MultiStageComponentApply=ture` in KubeVela controller's deployment like:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --feature-gates=MultiStageComponentApply=true
|
||||
...
|
||||
```
|
||||
|
||||
2. Sometime, you have multi-stage apply requirements inside the component, and it is the `outputs` resource defined in the trait. In this case, you can use the `stage` with the value `PreDispatch` or `PostDispatch` like:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add storages on K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
name: storage
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
- statefulsets.apps
|
||||
- daemonsets.apps
|
||||
- jobs.batch
|
||||
podDisruptive: true
|
||||
stage: PreDispatch
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
...
|
||||
```
|
||||
|
||||
8
e2e/addon/mock/testdata/mock-dep-addon/metadata.yaml
vendored
Normal file
8
e2e/addon/mock/testdata/mock-dep-addon/metadata.yaml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
name: mock-dep-addon
|
||||
version: v1.0.0
|
||||
description: Vela test addon named mock-dep-addon
|
||||
icon: https://www.test.com/icon
|
||||
url: https://www.test.com
|
||||
|
||||
dependencies:
|
||||
- name: mock-be-dep-addon
|
||||
19
e2e/addon/mock/testdata/not-match-addon/metadata.yaml
vendored
Normal file
19
e2e/addon/mock/testdata/not-match-addon/metadata.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: not-match-addon
|
||||
version: 1.0.0
|
||||
description: Extended workload to do continuous and progressive delivery
|
||||
icon: https://raw.githubusercontent.com/fluxcd/flux/master/docs/_files/weave-flux.png
|
||||
url: https://fluxcd.io
|
||||
|
||||
tags:
|
||||
- mock
|
||||
dependencies: []
|
||||
#- name: addon_name
|
||||
|
||||
# set invisible means this won't be list and will be enabled when depended on
|
||||
# for example, terraform-alibaba depends on terraform which is invisible,
|
||||
# when terraform-alibaba is enabled, terraform will be enabled automatically
|
||||
# default: false
|
||||
invisible: false
|
||||
|
||||
system:
|
||||
kubernetes: "<=v1.3.0"
|
||||
BIN
e2e/addon/mock/testrepo/helm-repo/bar-v1.0.0.tgz
Normal file
BIN
e2e/addon/mock/testrepo/helm-repo/bar-v1.0.0.tgz
Normal file
Binary file not shown.
BIN
e2e/addon/mock/testrepo/helm-repo/bar-v2.0.0.tgz
Normal file
BIN
e2e/addon/mock/testrepo/helm-repo/bar-v2.0.0.tgz
Normal file
Binary file not shown.
BIN
e2e/addon/mock/testrepo/helm-repo/foo-v1.0.0.tgz
Normal file
BIN
e2e/addon/mock/testrepo/helm-repo/foo-v1.0.0.tgz
Normal file
Binary file not shown.
@@ -23,4 +23,50 @@ entries:
|
||||
annotations:
|
||||
system.vela: ">=1.5.0"
|
||||
system.kubernetes: ">=1.30.0"
|
||||
vela-workflow:
|
||||
- annotations:
|
||||
system.vela: '>=v1.6.0-beta.1'
|
||||
created: "2022-10-29T09:11:16.865230605Z"
|
||||
description: vela-workflow provides the capability to run a standalone workflow
|
||||
home: https://github.com/kubevela/workflow
|
||||
icon: https://static.kubevela.net/images/logos/KubeVela%20-03.png
|
||||
name: vela-workflow
|
||||
urls:
|
||||
- http://127.0.0.1:9098/helm/vela-workflow-v0.3.1.tgz
|
||||
version: v0.3.1
|
||||
foo:
|
||||
- created: "2022-10-29T09:11:16.865230605Z"
|
||||
description: Vela test addon named foo
|
||||
home: https://www.foo.com/icon
|
||||
icon: https://www.foo.com
|
||||
name: foo
|
||||
urls:
|
||||
- http://127.0.0.1:9098/helm/foo-v1.0.0.tgz
|
||||
version: v1.0.0
|
||||
bar:
|
||||
- created: "2022-10-29T09:11:16.865230605Z"
|
||||
description: Vela test addon named bar
|
||||
home: https://www.bar.com/icon
|
||||
icon: https://www.bar.com
|
||||
name: foo
|
||||
urls:
|
||||
- http://127.0.0.1:9098/helm/bar-v1.0.0.tgz
|
||||
version: v1.0.0
|
||||
- created: "2022-10-29T09:11:16.865230605Z"
|
||||
description: Vela test addon named bar
|
||||
home: https://www.bar.com/icon
|
||||
icon: https://www.bar.com
|
||||
name: foo
|
||||
urls:
|
||||
- http://127.0.0.1:9098/helm/bar-v2.0.0.tgz
|
||||
version: v2.0.0
|
||||
mock-be-dep-addon:
|
||||
- created: "2022-10-29T09:11:16.865230605Z"
|
||||
description: Vela test addon named mock-be-dep-addon
|
||||
home: https://www.test.com/icon
|
||||
icon: https://www.test.com
|
||||
name: mock-be-dep-addon
|
||||
urls:
|
||||
- http://127.0.0.1:9098/helm/mock-be-dep-addon-v1.0.0.tgz
|
||||
version: v1.0.0
|
||||
generated: "2022-06-15T13:17:04.733573+08:00"
|
||||
BIN
e2e/addon/mock/testrepo/helm-repo/mock-be-dep-addon-v1.0.0.tgz
Normal file
BIN
e2e/addon/mock/testrepo/helm-repo/mock-be-dep-addon-v1.0.0.tgz
Normal file
Binary file not shown.
BIN
e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.3.1.tgz
Normal file
BIN
e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.3.1.tgz
Normal file
Binary file not shown.
@@ -22,9 +22,9 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
@@ -108,24 +108,55 @@ var ossHandler http.HandlerFunc = func(rw http.ResponseWriter, req *http.Request
|
||||
var helmHandler http.HandlerFunc = func(rw http.ResponseWriter, req *http.Request) {
|
||||
switch {
|
||||
case strings.Contains(req.URL.Path, "index.yaml"):
|
||||
file, err := ioutil.ReadFile("./e2e/addon/mock/testrepo/helm-repo/index.yaml")
|
||||
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/index.yaml")
|
||||
if err != nil {
|
||||
_, _ = rw.Write([]byte(err.Error()))
|
||||
}
|
||||
rw.Write(file)
|
||||
case strings.Contains(req.URL.Path, "fluxcd-test-version-1.0.0.tgz"):
|
||||
file, err := ioutil.ReadFile("./e2e/addon/mock/testrepo/helm-repo/fluxcd-test-version-1.0.0.tgz")
|
||||
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/fluxcd-test-version-1.0.0.tgz")
|
||||
if err != nil {
|
||||
_, _ = rw.Write([]byte(err.Error()))
|
||||
}
|
||||
rw.Write(file)
|
||||
case strings.Contains(req.URL.Path, "fluxcd-test-version-2.0.0.tgz"):
|
||||
file, err := ioutil.ReadFile("./e2e/addon/mock/testrepo/helm-repo/fluxcd-test-version-2.0.0.tgz")
|
||||
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/fluxcd-test-version-2.0.0.tgz")
|
||||
if err != nil {
|
||||
_, _ = rw.Write([]byte(err.Error()))
|
||||
}
|
||||
rw.Write(file)
|
||||
case strings.Contains(req.URL.Path, "vela-workflow-v0.3.1.tgz"):
|
||||
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/vela-workflow-v0.3.1.tgz")
|
||||
if err != nil {
|
||||
_, _ = rw.Write([]byte(err.Error()))
|
||||
}
|
||||
rw.Write(file)
|
||||
case strings.Contains(req.URL.Path, "foo-v1.0.0.tgz"):
|
||||
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/foo-v1.0.0.tgz")
|
||||
if err != nil {
|
||||
_, _ = rw.Write([]byte(err.Error()))
|
||||
}
|
||||
rw.Write(file)
|
||||
case strings.Contains(req.URL.Path, "bar-v1.0.0.tgz"):
|
||||
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/bar-v1.0.0.tgz")
|
||||
if err != nil {
|
||||
_, _ = rw.Write([]byte(err.Error()))
|
||||
}
|
||||
rw.Write(file)
|
||||
case strings.Contains(req.URL.Path, "bar-v2.0.0.tgz"):
|
||||
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/bar-v2.0.0.tgz")
|
||||
if err != nil {
|
||||
_, _ = rw.Write([]byte(err.Error()))
|
||||
}
|
||||
rw.Write(file)
|
||||
case strings.Contains(req.URL.Path, "mock-be-dep-addon-v1.0.0.tgz"):
|
||||
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/mock-be-dep-addon-v1.0.0.tgz")
|
||||
if err != nil {
|
||||
_, _ = rw.Write([]byte(err.Error()))
|
||||
}
|
||||
rw.Write(file)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -47,6 +47,9 @@ var (
|
||||
appbasicJsonAppFile = `{"name":"app-basic","services":{"app-basic":{"type":"webservice","image":"nginx:1.9.4","ports":[{port: 80, expose: true}]}}}`
|
||||
appbasicAddTraitJsonAppFile = `{"name":"app-basic","services":{"app-basic":{"type":"webservice","image":"nginx:1.9.4","ports":[{port: 80, expose: true}],"scaler":{"replicas":2}}}}`
|
||||
velaQL = "test-component-pod-view{appNs=default,appName=nginx-vela,name=nginx}"
|
||||
|
||||
waitAppfileToSuccess = `{"name":"app-wait-success","services":{"app-basic1":{"type":"webservice","image":"nginx:1.9.4","ports":[{port: 80, expose: true}]}}}`
|
||||
waitAppfileToFail = `{"name":"app-wait-fail","services":{"app-basic2":{"type":"webservice","image":"nginx:fail","ports":[{port: 80, expose: true}]}}}`
|
||||
)
|
||||
|
||||
var _ = ginkgo.Describe("Test Vela Application", func() {
|
||||
@@ -75,6 +78,9 @@ var _ = ginkgo.Describe("Test Vela Application", func() {
|
||||
|
||||
e2e.JsonAppFileContext("json appfile apply", testDeleteJsonAppFile)
|
||||
VelaQLPodListContext("ql", velaQL)
|
||||
|
||||
e2e.JsonAppFileContextWithWait("json appfile apply with wait", waitAppfileToSuccess)
|
||||
e2e.JsonAppFileContextWithTimeout("json appfile apply with wait but timeout", waitAppfileToFail, "3s")
|
||||
})
|
||||
|
||||
var ApplicationStatusContext = func(context string, applicationName string, workloadType string) bool {
|
||||
@@ -182,7 +188,7 @@ var ApplicationInitIntercativeCliContext = func(context string, appName string,
|
||||
c.ExpectEOF()
|
||||
})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("Checking Status"))
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("Waiting app to be healthy"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -84,6 +84,30 @@ var (
|
||||
})
|
||||
}
|
||||
|
||||
JsonAppFileContextWithWait = func(context, jsonAppFile string) bool {
|
||||
return ginkgo.Context(context, func() {
|
||||
ginkgo.It("Start the application through the app file in JSON format.", func() {
|
||||
writeStatus := os.WriteFile("vela.json", []byte(jsonAppFile), 0644)
|
||||
gomega.Expect(writeStatus).NotTo(gomega.HaveOccurred())
|
||||
output, err := Exec("vela up -f vela.json --wait")
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("Application Deployed Successfully!"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
JsonAppFileContextWithTimeout = func(context, jsonAppFile, duration string) bool {
|
||||
return ginkgo.Context(context, func() {
|
||||
ginkgo.It("Start the application through the app file in JSON format.", func() {
|
||||
writeStatus := os.WriteFile("vela.json", []byte(jsonAppFile), 0644)
|
||||
gomega.Expect(writeStatus).NotTo(gomega.HaveOccurred())
|
||||
output, err := Exec("vela up -f vela.json --wait --timeout=" + duration)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("Timeout waiting Application to be healthy!"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
DeleteEnvFunc = func(context string, envName string) bool {
|
||||
return ginkgo.Context(context, func() {
|
||||
ginkgo.It("should print env does not exist message", func() {
|
||||
|
||||
20
go.mod
20
go.mod
@@ -3,7 +3,7 @@ module github.com/oam-dev/kubevela
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
cuelang.org/go v0.4.4-0.20220915174651-ad253ed099e9
|
||||
cuelang.org/go v0.5.0-alpha.1
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1
|
||||
github.com/FogDong/uitable v0.0.5
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
@@ -50,16 +50,18 @@ require (
|
||||
github.com/gosuri/uilive v0.0.4
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/hashicorp/go-version v1.3.0
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/hashicorp/hcl/v2 v2.9.1
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/klauspost/compress v1.15.9
|
||||
github.com/klauspost/compress v1.15.11
|
||||
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
|
||||
github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2
|
||||
github.com/kubevela/prism v1.5.1-0.20220915071949-6bf3ad33f84f
|
||||
github.com/kubevela/workflow v0.0.0-20221019093241-b5b7a0d79051
|
||||
github.com/kubevela/workflow v0.3.3
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.1
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
|
||||
github.com/oam-dev/cluster-gateway v1.4.0
|
||||
github.com/oam-dev/cluster-register v1.0.4-0.20220928064144-5f76a9d7ca8c
|
||||
github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28
|
||||
@@ -77,7 +79,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/tidwall/gjson v1.9.3
|
||||
github.com/wercker/stern v0.0.0-20190705090245-4fa46dd6987f
|
||||
github.com/wonderflow/cert-manager-api v1.0.4-0.20210304051430-e08aa76f6c5f
|
||||
@@ -85,7 +87,7 @@ require (
|
||||
github.com/xlab/treeprint v1.1.0
|
||||
go.mongodb.org/mongo-driver v1.5.1
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/text v0.3.8
|
||||
@@ -239,7 +241,6 @@ require (
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
@@ -292,12 +293,12 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9 // indirect
|
||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/grpc v1.48.0 // indirect
|
||||
@@ -341,6 +342,7 @@ require (
|
||||
)
|
||||
|
||||
replace (
|
||||
cuelang.org/go => github.com/kubevela/cue v0.4.4-0.20221107123854-a976b0e340be
|
||||
github.com/docker/cli => github.com/docker/cli v20.10.9+incompatible
|
||||
github.com/docker/docker => github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible
|
||||
github.com/wercker/stern => github.com/oam-dev/stern v1.13.2
|
||||
|
||||
30
go.sum
30
go.sum
@@ -73,8 +73,6 @@ cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq
|
||||
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs=
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
|
||||
cuelang.org/go v0.4.4-0.20220915174651-ad253ed099e9 h1:4mfDNgtdb398g0bekqiW8J8tw+JN3/U/3wh+Jw/I4Yk=
|
||||
cuelang.org/go v0.4.4-0.20220915174651-ad253ed099e9/go.mod h1:nxWFAPWKYvZJ+eYayxArWqKKjdBTeU1N52vJpML/c6w=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eimREzLWI=
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
|
||||
@@ -1182,6 +1180,7 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
@@ -1304,8 +1303,8 @@ github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
|
||||
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
|
||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@@ -1330,12 +1329,14 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kubevela/cue v0.4.4-0.20221107123854-a976b0e340be h1:0xj/Rh4yVy54mUD2nLmAuN1AYgBkkHxBh4PoLGbIg5g=
|
||||
github.com/kubevela/cue v0.4.4-0.20221107123854-a976b0e340be/go.mod h1:Ya12qn7FZc+LSN0qgEhzEpnzQsvnGHVgoDrqe9i3eNg=
|
||||
github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2 h1:C3cAfrxst1+dIWgLLhUQt1TQvEEpp1UTq9ZQB2xKbeI=
|
||||
github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2/go.mod h1:TgIGEB/r0NOy63Jzem7WsL3AIr34l+ClH9dmPqcZ4d4=
|
||||
github.com/kubevela/prism v1.5.1-0.20220915071949-6bf3ad33f84f h1:1lUtU1alPThdcsn4MI6XjPb7eJLuZPpmlEdgjtnUMKw=
|
||||
github.com/kubevela/prism v1.5.1-0.20220915071949-6bf3ad33f84f/go.mod h1:m724/7ANnB/iukyHW20+DicpeJMEC/JA0ZhgsHY10MA=
|
||||
github.com/kubevela/workflow v0.0.0-20221019093241-b5b7a0d79051 h1:ET01t1GCjbERb+uwgGZnLHoLo4ceE1+gHnmgM/9or5g=
|
||||
github.com/kubevela/workflow v0.0.0-20221019093241-b5b7a0d79051/go.mod h1:1XyGmfIkD6gPAegUkeDBXXModeiu8NVUWIgersTqwr8=
|
||||
github.com/kubevela/workflow v0.3.3 h1:NSbQGcABWJIzUV5wfWFJsrO/ffZ4mCVfofUtUHCTojQ=
|
||||
github.com/kubevela/workflow v0.3.3/go.mod h1:5jfZ8T1m/En44wDGRf2YqCSlODfEnAV+9PnzoLoDlFs=
|
||||
github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U=
|
||||
github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4=
|
||||
github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
@@ -1923,8 +1924,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
@@ -2137,8 +2139,9 @@ go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
|
||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
@@ -2198,8 +2201,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8=
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A=
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -2517,8 +2520,8 @@ golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -2550,8 +2553,9 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
@@ -42,6 +42,7 @@ func main() {
|
||||
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")
|
||||
forceExample := flag.Bool("force-example-doc", false, "example must be provided for definitions")
|
||||
flag.Parse()
|
||||
|
||||
if *i18nfile != "" {
|
||||
@@ -52,21 +53,29 @@ func main() {
|
||||
fmt.Println("you must specify a type with definition ref path specified ")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
opt := mods.Options{
|
||||
Path: *path,
|
||||
Location: *location,
|
||||
DefDir: *defdir,
|
||||
ForceExamples: *forceExample,
|
||||
}
|
||||
|
||||
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)
|
||||
mods.ComponentDef(ctx, c, opt)
|
||||
case types.TypeTrait:
|
||||
mods.TraitDef(ctx, c, path, location, *defdir)
|
||||
mods.TraitDef(ctx, c, opt)
|
||||
case types.TypePolicy:
|
||||
mods.PolicyDef(ctx, c, path, location, *defdir)
|
||||
mods.PolicyDef(ctx, c, opt)
|
||||
case types.TypeWorkflowStep, "workflow", "wf":
|
||||
mods.WorkflowDef(ctx, c, path, location, *defdir)
|
||||
mods.WorkflowDef(ctx, c, opt)
|
||||
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)
|
||||
mods.ComponentDef(ctx, c, opt)
|
||||
mods.TraitDef(ctx, c, opt)
|
||||
mods.PolicyDef(ctx, c, opt)
|
||||
mods.WorkflowDef(ctx, c, opt)
|
||||
default:
|
||||
fmt.Printf("type %s not supported\n", *tp)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -19,7 +19,6 @@ package mods
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -60,12 +59,13 @@ 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
|
||||
func ComponentDef(ctx context.Context, c common.Args, opt Options) {
|
||||
if opt.DefDir == "" {
|
||||
opt.DefDir = ComponentDefDir
|
||||
}
|
||||
ref := &docgen.MarkdownReference{
|
||||
AllInOne: true,
|
||||
AllInOne: true,
|
||||
ForceExample: opt.ForceExamples,
|
||||
Filter: func(capability types.Capability) bool {
|
||||
if capability.Type != types.TypeComponentDefinition || capability.Category != types.CUECategory {
|
||||
return false
|
||||
@@ -74,9 +74,9 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
|
||||
return false
|
||||
}
|
||||
// only print capability which contained in cue def
|
||||
files, err := ioutil.ReadDir(defdir)
|
||||
files, err := os.ReadDir(opt.DefDir)
|
||||
if err != nil {
|
||||
fmt.Println("read dir err", defdir, err)
|
||||
fmt.Println("read dir err", opt.DefDir, err)
|
||||
return false
|
||||
}
|
||||
for _, f := range files {
|
||||
@@ -96,19 +96,20 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
|
||||
return
|
||||
}
|
||||
ref.DiscoveryMapper = dm
|
||||
if *path != "" {
|
||||
if opt.Path != "" {
|
||||
ref.I18N = &docgen.En
|
||||
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
|
||||
if strings.Contains(opt.Location, "zh") || strings.Contains(opt.Location, "chinese") {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomComponentHeaderZH
|
||||
}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, opt.Path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
|
||||
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), opt.Path)
|
||||
return
|
||||
}
|
||||
if *location == "" || *location == "en" {
|
||||
if opt.Location == "" || opt.Location == "en" {
|
||||
ref.I18N = &docgen.En
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, ComponentDefRefPath); err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -116,7 +117,7 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
|
||||
}
|
||||
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), ComponentDefRefPath)
|
||||
}
|
||||
if *location == "" || *location == "zh" {
|
||||
if opt.Location == "" || opt.Location == "zh" {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomComponentHeaderZH
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, ComponentDefRefPathZh); err != nil {
|
||||
|
||||
@@ -19,7 +19,6 @@ package mods
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -58,12 +57,13 @@ 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
|
||||
func PolicyDef(ctx context.Context, c common.Args, opt Options) {
|
||||
if opt.DefDir == "" {
|
||||
opt.DefDir = PolicyDefDir
|
||||
}
|
||||
ref := &docgen.MarkdownReference{
|
||||
AllInOne: true,
|
||||
AllInOne: true,
|
||||
ForceExample: opt.ForceExamples,
|
||||
Filter: func(capability types.Capability) bool {
|
||||
if capability.Type != types.TypePolicy || capability.Category != types.CUECategory {
|
||||
return false
|
||||
@@ -72,9 +72,9 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
|
||||
return false
|
||||
}
|
||||
// only print capability which contained in cue def
|
||||
files, err := ioutil.ReadDir(defdir)
|
||||
files, err := os.ReadDir(opt.DefDir)
|
||||
if err != nil {
|
||||
fmt.Println("read dir err", defdir, err)
|
||||
fmt.Println("read dir err", opt.DefDir, err)
|
||||
return false
|
||||
}
|
||||
for _, f := range files {
|
||||
@@ -86,20 +86,21 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
|
||||
},
|
||||
CustomDocHeader: CustomPolicyHeaderEN,
|
||||
}
|
||||
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
|
||||
if *path != "" {
|
||||
ref.Local = &docgen.FromLocal{Path: PolicyDefDir}
|
||||
if opt.Path != "" {
|
||||
ref.I18N = &docgen.En
|
||||
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
|
||||
if strings.Contains(opt.Location, "zh") || strings.Contains(opt.Location, "chinese") {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomPolicyHeaderZH
|
||||
}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, opt.Path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
|
||||
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), opt.Path)
|
||||
return
|
||||
}
|
||||
if *location == "" || *location == "en" {
|
||||
if opt.Location == "" || opt.Location == "en" {
|
||||
ref.I18N = &docgen.En
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, PolicyDefRefPath); err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -107,7 +108,7 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
|
||||
}
|
||||
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), PolicyDefRefPath)
|
||||
}
|
||||
if *location == "" || *location == "zh" {
|
||||
if opt.Location == "" || opt.Location == "zh" {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomPolicyHeaderZH
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, PolicyDefRefPathZh); err != nil {
|
||||
|
||||
@@ -19,7 +19,6 @@ package mods
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -58,12 +57,13 @@ 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
|
||||
func TraitDef(ctx context.Context, c common.Args, opt Options) {
|
||||
if opt.DefDir == "" {
|
||||
opt.DefDir = TraitDefDir
|
||||
}
|
||||
ref := &docgen.MarkdownReference{
|
||||
AllInOne: true,
|
||||
AllInOne: true,
|
||||
ForceExample: opt.ForceExamples,
|
||||
Filter: func(capability types.Capability) bool {
|
||||
if capability.Type != types.TypeTrait || capability.Category != types.CUECategory {
|
||||
return false
|
||||
@@ -72,9 +72,9 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
|
||||
return false
|
||||
}
|
||||
// only print capability which contained in cue def
|
||||
files, err := ioutil.ReadDir(defdir)
|
||||
files, err := os.ReadDir(opt.DefDir)
|
||||
if err != nil {
|
||||
fmt.Println("read dir err", defdir, err)
|
||||
fmt.Println("read dir err", opt.DefDir, err)
|
||||
return false
|
||||
}
|
||||
for _, f := range files {
|
||||
@@ -90,20 +90,20 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
|
||||
Path: TraitDefDir,
|
||||
}
|
||||
|
||||
if *path != "" {
|
||||
if opt.Path != "" {
|
||||
ref.I18N = &docgen.En
|
||||
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
|
||||
if strings.Contains(opt.Location, "zh") || strings.Contains(opt.Location, "chinese") {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomTraitHeaderZH
|
||||
}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, opt.Path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
|
||||
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), opt.Path)
|
||||
} else {
|
||||
// Generate to default path depends on language
|
||||
if *location == "" || *location == "en" {
|
||||
if opt.Location == "" || opt.Location == "en" {
|
||||
ref.I18N = &docgen.En
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, TraitDefRefPath); err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -111,7 +111,7 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
|
||||
}
|
||||
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), TraitDefRefPath)
|
||||
}
|
||||
if *location == "" || *location == "zh" {
|
||||
if opt.Location == "" || opt.Location == "zh" {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomTraitHeaderZH
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, TraitDefRefPathZh); err != nil {
|
||||
|
||||
25
hack/docgen/def/mods/types.go
Normal file
25
hack/docgen/def/mods/types.go
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
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
|
||||
|
||||
// Options defines the doc generate options
|
||||
type Options struct {
|
||||
Path string
|
||||
Location string
|
||||
DefDir string
|
||||
ForceExamples bool
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package mods
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -58,12 +57,13 @@ 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
|
||||
func WorkflowDef(ctx context.Context, c common.Args, opt Options) {
|
||||
if opt.DefDir == "" {
|
||||
opt.DefDir = WorkflowDefDir
|
||||
}
|
||||
ref := &docgen.MarkdownReference{
|
||||
AllInOne: true,
|
||||
AllInOne: true,
|
||||
ForceExample: opt.ForceExamples,
|
||||
Filter: func(capability types.Capability) bool {
|
||||
|
||||
if capability.Type != types.TypeWorkflowStep || capability.Category != types.CUECategory {
|
||||
@@ -74,9 +74,9 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
|
||||
return false
|
||||
}
|
||||
// only print capability which contained in cue def
|
||||
files, err := ioutil.ReadDir(defdir)
|
||||
files, err := os.ReadDir(opt.DefDir)
|
||||
if err != nil {
|
||||
fmt.Println("read dir err", defdir, err)
|
||||
fmt.Println("read dir err", opt.DefDir, err)
|
||||
return false
|
||||
}
|
||||
for _, f := range files {
|
||||
@@ -88,21 +88,22 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
|
||||
},
|
||||
CustomDocHeader: CustomWorkflowHeaderEN,
|
||||
}
|
||||
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
|
||||
ref.Local = &docgen.FromLocal{Path: WorkflowDefDir}
|
||||
|
||||
if *path != "" {
|
||||
if opt.Path != "" {
|
||||
ref.I18N = &docgen.En
|
||||
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
|
||||
if strings.Contains(opt.Location, "zh") || strings.Contains(opt.Location, "chinese") {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomWorkflowHeaderZH
|
||||
}
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, opt.Path); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
|
||||
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), opt.Path)
|
||||
return
|
||||
}
|
||||
if *location == "" || *location == "en" {
|
||||
if opt.Location == "" || opt.Location == "en" {
|
||||
ref.I18N = &docgen.En
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, WorkflowDefRefPath); err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -110,7 +111,7 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
|
||||
}
|
||||
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), WorkflowDefRefPath)
|
||||
}
|
||||
if *location == "" || *location == "zh" {
|
||||
if opt.Location == "" || opt.Location == "zh" {
|
||||
ref.I18N = &docgen.Zh
|
||||
ref.CustomDocHeader = CustomWorkflowHeaderZH
|
||||
if err := ref.GenerateReferenceDocs(ctx, c, WorkflowDefRefPathZh); err != nil {
|
||||
|
||||
@@ -2209,10 +2209,11 @@ spec:
|
||||
execution
|
||||
properties:
|
||||
steps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: SubSteps is the mode of workflow sub
|
||||
steps execution
|
||||
type: string
|
||||
type: object
|
||||
ref:
|
||||
@@ -4008,6 +4009,17 @@ spec:
|
||||
namespace:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: WorkflowExecuteMode defines the mode of workflow
|
||||
execution
|
||||
properties:
|
||||
steps:
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: SubSteps is the mode of workflow sub steps execution
|
||||
type: string
|
||||
type: object
|
||||
steps:
|
||||
items:
|
||||
description: WorkflowStep defines how to execute a workflow
|
||||
|
||||
@@ -1021,10 +1021,10 @@ spec:
|
||||
execution
|
||||
properties:
|
||||
steps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
description: SubSteps is the mode of workflow sub steps execution
|
||||
type: string
|
||||
type: object
|
||||
ref:
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.6.2
|
||||
controller-gen.kubebuilder.io/version: v0.9.0
|
||||
name: workflows.core.oam.dev
|
||||
spec:
|
||||
group: core.oam.dev
|
||||
@@ -34,6 +33,16 @@ spec:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
mode:
|
||||
description: WorkflowExecuteMode defines the mode of workflow execution
|
||||
properties:
|
||||
steps:
|
||||
description: Steps is the mode of workflow steps execution
|
||||
type: string
|
||||
subSteps:
|
||||
description: SubSteps is the mode of workflow sub steps execution
|
||||
type: string
|
||||
type: object
|
||||
steps:
|
||||
items:
|
||||
description: WorkflowStep defines how to execute a workflow step.
|
||||
@@ -161,297 +170,3 @@ spec:
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Workflow is the Schema for the workflow API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
steps:
|
||||
items:
|
||||
description: WorkflowStep defines how to execute a workflow step.
|
||||
properties:
|
||||
dependsOn:
|
||||
description: DependsOn is the dependency of the step
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
if:
|
||||
description: If is the if condition of the step
|
||||
type: string
|
||||
inputs:
|
||||
description: Inputs is the inputs of the step
|
||||
items:
|
||||
properties:
|
||||
from:
|
||||
type: string
|
||||
parameterKey:
|
||||
type: string
|
||||
required:
|
||||
- from
|
||||
- parameterKey
|
||||
type: object
|
||||
type: array
|
||||
meta:
|
||||
description: Meta is the meta data of the workflow step.
|
||||
properties:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
outputs:
|
||||
description: Outputs is the outputs of the step
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
valueFrom:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- valueFrom
|
||||
type: object
|
||||
type: array
|
||||
properties:
|
||||
description: Properties is the properties of the step
|
||||
type: object
|
||||
|
||||
subSteps:
|
||||
items:
|
||||
description: WorkflowStepBase defines the workflow step base
|
||||
properties:
|
||||
dependsOn:
|
||||
description: DependsOn is the dependency of the step
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
if:
|
||||
description: If is the if condition of the step
|
||||
type: string
|
||||
inputs:
|
||||
description: Inputs is the inputs of the step
|
||||
items:
|
||||
properties:
|
||||
from:
|
||||
type: string
|
||||
parameterKey:
|
||||
type: string
|
||||
required:
|
||||
- from
|
||||
- parameterKey
|
||||
type: object
|
||||
type: array
|
||||
meta:
|
||||
description: Meta is the meta data of the workflow step.
|
||||
properties:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
outputs:
|
||||
description: Outputs is the outputs of the step
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
valueFrom:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- valueFrom
|
||||
type: object
|
||||
type: array
|
||||
properties:
|
||||
description: Properties is the properties of the step
|
||||
type: object
|
||||
|
||||
timeout:
|
||||
description: Timeout is the timeout of the step
|
||||
type: string
|
||||
type:
|
||||
description: Type is the type of the workflow step.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
timeout:
|
||||
description: Timeout is the timeout of the step
|
||||
type: string
|
||||
type:
|
||||
description: Type is the type of the workflow step.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
- name: v1beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Workflow defines workflow steps and other attributes
|
||||
properties:
|
||||
mode:
|
||||
description: WorkflowExecuteMode defines the mode of workflow execution
|
||||
properties:
|
||||
steps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
type: string
|
||||
subSteps:
|
||||
description: WorkflowMode describes the mode of workflow
|
||||
type: string
|
||||
type: object
|
||||
ref:
|
||||
type: string
|
||||
steps:
|
||||
items:
|
||||
description: WorkflowStep defines how to execute a workflow step.
|
||||
properties:
|
||||
dependsOn:
|
||||
description: DependsOn is the dependency of the step
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
if:
|
||||
description: If is the if condition of the step
|
||||
type: string
|
||||
inputs:
|
||||
description: Inputs is the inputs of the step
|
||||
items:
|
||||
properties:
|
||||
from:
|
||||
type: string
|
||||
parameterKey:
|
||||
type: string
|
||||
required:
|
||||
- from
|
||||
- parameterKey
|
||||
type: object
|
||||
type: array
|
||||
meta:
|
||||
description: Meta is the meta data of the workflow step.
|
||||
properties:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
outputs:
|
||||
description: Outputs is the outputs of the step
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
valueFrom:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- valueFrom
|
||||
type: object
|
||||
type: array
|
||||
properties:
|
||||
description: Properties is the properties of the step
|
||||
type: object
|
||||
|
||||
subSteps:
|
||||
items:
|
||||
description: WorkflowStepBase defines the workflow step base
|
||||
properties:
|
||||
dependsOn:
|
||||
description: DependsOn is the dependency of the step
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
if:
|
||||
description: If is the if condition of the step
|
||||
type: string
|
||||
inputs:
|
||||
description: Inputs is the inputs of the step
|
||||
items:
|
||||
properties:
|
||||
from:
|
||||
type: string
|
||||
parameterKey:
|
||||
type: string
|
||||
required:
|
||||
- from
|
||||
- parameterKey
|
||||
type: object
|
||||
type: array
|
||||
meta:
|
||||
description: Meta is the meta data of the workflow step.
|
||||
properties:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
outputs:
|
||||
description: Outputs is the outputs of the step
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
valueFrom:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- valueFrom
|
||||
type: object
|
||||
type: array
|
||||
properties:
|
||||
description: Properties is the properties of the step
|
||||
type: object
|
||||
|
||||
timeout:
|
||||
description: Timeout is the timeout of the step
|
||||
type: string
|
||||
type:
|
||||
description: Type is the type of the workflow step.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
timeout:
|
||||
description: Timeout is the timeout of the step
|
||||
type: string
|
||||
type:
|
||||
description: Type is the type of the workflow step.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
||||
@@ -6,8 +6,8 @@ e2e-setup-core-pre-hook:
|
||||
e2e-setup-core-post-hook:
|
||||
kubectl wait --for=condition=Available deployment/kubevela-vela-core -n vela-system --timeout=180s
|
||||
helm install kruise https://github.com/openkruise/charts/releases/download/kruise-1.1.0/kruise-1.1.0.tgz --set featureGates="PreDownloadImageForInPlaceUpdate=true" --set daemon.socketLocation=/run/k3s/containerd/
|
||||
kill -9 $(lsof -it:9098) || true
|
||||
go run ./e2e/addon/mock &
|
||||
sleep 15
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/fluxcd
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/rollout
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/terraform
|
||||
@@ -82,14 +82,9 @@ e2e-api-test:
|
||||
ginkgo -v -skipPackage capability,setup,application -r e2e
|
||||
ginkgo -v -r e2e/application
|
||||
|
||||
ADDONSERVER = $(shell pgrep vela_addon_mock_server)
|
||||
|
||||
|
||||
.PHONY: e2e-apiserver-test
|
||||
e2e-apiserver-test:
|
||||
pkill vela_addon_mock_server || true
|
||||
go run ./e2e/addon/mock/vela_addon_mock_server.go &
|
||||
sleep 15
|
||||
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_apiserver_test.out ./test/e2e-apiserver-test
|
||||
@$(OK) tests pass
|
||||
|
||||
|
||||
@@ -612,7 +612,7 @@ func unmarshalToContent(content []byte) (fileContent *github.RepositoryContent,
|
||||
}
|
||||
|
||||
func genAddonAPISchema(addonRes *UIData) error {
|
||||
cueScript := script.CUE([]byte(addonRes.Parameters))
|
||||
cueScript := script.CUE(addonRes.Parameters)
|
||||
schema, err := cueScript.ParsePropertiesToSchema()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -932,10 +932,12 @@ type Installer struct {
|
||||
|
||||
dryRun bool
|
||||
dryRunBuff *bytes.Buffer
|
||||
|
||||
registries []Registry
|
||||
}
|
||||
|
||||
// NewAddonInstaller will create an installer for addon
|
||||
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache, opts ...InstallOption) Installer {
|
||||
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache, registries []Registry, opts ...InstallOption) Installer {
|
||||
i := Installer{
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
@@ -946,6 +948,7 @@ func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *
|
||||
cache: cache,
|
||||
dc: discoveryClient,
|
||||
dryRunBuff: &bytes.Buffer{},
|
||||
registries: registries,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&i)
|
||||
@@ -1042,16 +1045,41 @@ func (h *Installer) installDependency(addon *InstallPackage) error {
|
||||
if h.dryRun {
|
||||
continue
|
||||
}
|
||||
// always install addon's latest version
|
||||
depAddon, err := h.loadInstallPackage(dep.Name, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
depHandler := *h
|
||||
depHandler.args = nil
|
||||
if err = depHandler.enableAddon(depAddon); err != nil {
|
||||
return errors.Wrap(err, "fail to dispatch dependent addon resource")
|
||||
var depAddon *InstallPackage
|
||||
// try to install the dependent addon from the same registry with the current addon
|
||||
depAddon, err = h.loadInstallPackage(dep.Name, dep.Version)
|
||||
if err == nil {
|
||||
if err = depHandler.enableAddon(depAddon); err != nil {
|
||||
return errors.Wrap(err, "fail to dispatch dependent addon resource")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !errors.Is(err, ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
for _, registry := range h.registries {
|
||||
// try to install dependent addon from other registries
|
||||
depHandler.r = &Registry{
|
||||
Name: registry.Name, Helm: registry.Helm, OSS: registry.OSS, Git: registry.Git, Gitee: registry.Gitee, Gitlab: registry.Gitlab,
|
||||
}
|
||||
depAddon, err = depHandler.loadInstallPackage(dep.Name, dep.Version)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if errors.Is(err, ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
if err = depHandler.enableAddon(depAddon); err != nil {
|
||||
return errors.Wrap(err, "fail to dispatch dependent addon resource")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("dependency addon: %s with version: %s cannot be found from all registries", dep.Name, dep.Version)
|
||||
}
|
||||
if h.dryRun && len(dependencies) > 0 {
|
||||
klog.Warningf("dry run addon won't install dependencies, please make sure your system has already installed these addons: %v", strings.Join(dependencies, ", "))
|
||||
|
||||
@@ -355,7 +355,7 @@ var _ = Describe("func addon update ", func() {
|
||||
}, time.Millisecond*500, 30*time.Second).Should(BeNil())
|
||||
|
||||
pkg := &InstallPackage{Meta: Meta{Name: "test-update", Version: "1.3.0"}}
|
||||
h := NewAddonInstaller(context.Background(), k8sClient, nil, nil, nil, &Registry{Name: "test"}, nil, nil)
|
||||
h := NewAddonInstaller(context.Background(), k8sClient, nil, nil, nil, &Registry{Name: "test"}, nil, nil, nil)
|
||||
h.addon = pkg
|
||||
Expect(h.dispatchAddonResource(pkg)).Should(BeNil())
|
||||
|
||||
@@ -418,7 +418,7 @@ var _ = Describe("test dry-run addon from local dir", func() {
|
||||
pkg, err := GetInstallPackageFromReader(r, &meta, UIData)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
h := NewAddonInstaller(ctx, k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, &Registry{Name: LocalAddonRegistryName}, map[string]interface{}{"example": "test-dry-run"}, nil, DryRunAddon)
|
||||
h := NewAddonInstaller(ctx, k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, &Registry{Name: LocalAddonRegistryName}, map[string]interface{}{"example": "test-dry-run"}, nil, nil, DryRunAddon)
|
||||
|
||||
err = h.enableAddon(pkg)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -55,8 +55,8 @@ const (
|
||||
)
|
||||
|
||||
// EnableAddon will enable addon with dependency check, source is where addon from.
|
||||
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache, opts ...InstallOption) error {
|
||||
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache, opts...)
|
||||
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache, registries []Registry, opts ...InstallOption) error {
|
||||
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache, registries, opts...)
|
||||
pkg, err := h.loadInstallPackage(name, version)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -113,7 +113,7 @@ func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli cli
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil, opts...)
|
||||
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil, nil, opts...)
|
||||
needEnableAddonNames, err := h.checkDependency(pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -105,7 +105,8 @@ type DeployTo struct {
|
||||
|
||||
// Dependency defines the other addons it depends on
|
||||
type Dependency struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// ElementFile can be addon's definition or addon's component
|
||||
|
||||
@@ -187,7 +187,7 @@ func findLegacyAddonDefs(ctx context.Context, k8sClient client.Client, addonName
|
||||
if registry.Name == registryName {
|
||||
var uiData *UIData
|
||||
if !IsVersionRegistry(registry) {
|
||||
installer := NewAddonInstaller(ctx, k8sClient, nil, nil, config, ®istries[i], nil, nil)
|
||||
installer := NewAddonInstaller(ctx, k8sClient, nil, nil, config, ®istries[i], nil, nil, nil)
|
||||
metas, err := installer.getAddonMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -502,3 +502,17 @@ func checkBondComponentExist(u unstructured.Unstructured, app v1beta1.Applicatio
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FilterDependencyRegistries will return all registries besides the target registry itself
|
||||
func FilterDependencyRegistries(i int, rs []Registry) []Registry {
|
||||
if i >= len(rs) {
|
||||
return rs
|
||||
}
|
||||
if i < 0 {
|
||||
return rs
|
||||
}
|
||||
ret := make([]Registry, len(rs)-1)
|
||||
copy(ret, rs[:i])
|
||||
copy(ret[i:], rs[i+1:])
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -329,6 +329,57 @@ func TestCheckObjectBindingComponent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterDependencyRegistries(t *testing.T) {
|
||||
testCases := []struct {
|
||||
registries []Registry
|
||||
index int
|
||||
res []Registry
|
||||
origin []Registry
|
||||
}{
|
||||
{
|
||||
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
index: 0,
|
||||
res: []Registry{{Name: "r2"}, {Name: "r3"}},
|
||||
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
},
|
||||
{
|
||||
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
index: 1,
|
||||
res: []Registry{{Name: "r1"}, {Name: "r3"}},
|
||||
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
},
|
||||
{
|
||||
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
index: 2,
|
||||
res: []Registry{{Name: "r1"}, {Name: "r2"}},
|
||||
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
},
|
||||
{
|
||||
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
index: 3,
|
||||
res: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
},
|
||||
{
|
||||
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
index: -1,
|
||||
res: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
|
||||
},
|
||||
{
|
||||
registries: []Registry{},
|
||||
index: 0,
|
||||
res: []Registry{},
|
||||
origin: []Registry{},
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
res := FilterDependencyRegistries(testCase.index, testCase.registries)
|
||||
assert.Equal(t, res, testCase.res)
|
||||
assert.Equal(t, testCase.registries, testCase.origin)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
compDefYaml = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/pkg/errors"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
|
||||
@@ -146,7 +147,7 @@ func (i versionedRegistry) loadAddon(ctx context.Context, name, version string)
|
||||
sort.Sort(sort.Reverse(versions))
|
||||
addonVersion, availableVersions := chooseVersion(version, versions)
|
||||
if addonVersion == nil {
|
||||
return nil, fmt.Errorf("specified version %s not exist", version)
|
||||
return nil, errors.Errorf("specified version %s not exist", utils.Sanitize(version))
|
||||
}
|
||||
for _, chartURL := range addonVersion.URLs {
|
||||
if !utils.IsValidURL(chartURL) {
|
||||
|
||||
@@ -247,6 +247,9 @@ var RevisionStatusTerminated = "terminated"
|
||||
// RevisionStatusRollback event status rollback
|
||||
var RevisionStatusRollback = "rollback"
|
||||
|
||||
// WorkflowStepPhaseStopped is the stopped phase
|
||||
var WorkflowStepPhaseStopped workflowv1alpha1.WorkflowStepPhase = "stopped"
|
||||
|
||||
// ApplicationRevision be created when an application initiates deployment and describes the phased version of the application.
|
||||
type ApplicationRevision struct {
|
||||
BaseModel
|
||||
@@ -383,6 +386,7 @@ type ApplicationTrigger struct {
|
||||
Type string `json:"type"`
|
||||
PayloadType string `json:"payloadType"`
|
||||
ComponentName string `json:"componentName"`
|
||||
Registry string `json:"registry,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -16,10 +16,60 @@ limitations under the License.
|
||||
|
||||
package model
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kubevela/workflow/api/v1alpha1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterModel(&PipelineContext{})
|
||||
RegisterModel(&Pipeline{})
|
||||
}
|
||||
|
||||
// Structs copied from workflow/api/v1alpha1/types.go
|
||||
|
||||
// WorkflowSpec defines workflow steps and other attributes
|
||||
type WorkflowSpec struct {
|
||||
Mode *v1alpha1.WorkflowExecuteMode `json:"mode,omitempty"`
|
||||
Steps []WorkflowStep `json:"steps,omitempty"`
|
||||
}
|
||||
|
||||
// Pipeline is the model of pipeline
|
||||
type Pipeline struct {
|
||||
BaseModel
|
||||
Spec WorkflowSpec
|
||||
Name string `json:"name"`
|
||||
Project string `json:"project"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (p Pipeline) PrimaryKey() string {
|
||||
return fmt.Sprintf("%s-%s", p.Project, p.Name)
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
func (p Pipeline) TableName() string {
|
||||
return tableNamePrefix + "pipeline"
|
||||
}
|
||||
|
||||
// ShortTableName is the compressed version of table name for kubeapi storage and others
|
||||
func (p Pipeline) ShortTableName() string {
|
||||
return "pipeline"
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (p Pipeline) Index() map[string]string {
|
||||
var index = make(map[string]string)
|
||||
if p.Project != "" {
|
||||
index["project"] = p.Project
|
||||
}
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// Value is a k-v pair
|
||||
@@ -16,8 +16,6 @@ limitations under the License.
|
||||
|
||||
package model
|
||||
|
||||
import "fmt"
|
||||
|
||||
func init() {
|
||||
RegisterModel(&Project{})
|
||||
}
|
||||
@@ -29,11 +27,15 @@ type Project struct {
|
||||
Alias string `json:"alias"`
|
||||
Owner string `json:"owner"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
// GetNamespace get the namespace name of this project.
|
||||
func (p *Project) GetNamespace() string {
|
||||
return fmt.Sprintf("project-%s", p.Name)
|
||||
if p.Namespace != "" {
|
||||
return p.Namespace
|
||||
}
|
||||
return p.Name
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
|
||||
@@ -50,16 +50,25 @@ type Workflow struct {
|
||||
|
||||
// WorkflowStep defines how to execute a workflow step.
|
||||
type WorkflowStep struct {
|
||||
WorkflowStepBase `json:",inline"`
|
||||
SubSteps []WorkflowStepBase `json:"subSteps,omitempty"`
|
||||
}
|
||||
|
||||
// WorkflowStepBase is the step base of workflow
|
||||
type WorkflowStepBase struct {
|
||||
// Name is the unique name of the workflow step.
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
OrderIndex int `json:"orderIndex"`
|
||||
Inputs workflowv1alpha1.StepInputs `json:"inputs,omitempty"`
|
||||
Outputs workflowv1alpha1.StepOutputs `json:"outputs,omitempty"`
|
||||
DependsOn []string `json:"dependsOn"`
|
||||
Properties *JSONStruct `json:"properties,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
OrderIndex int `json:"orderIndex"`
|
||||
Inputs workflowv1alpha1.StepInputs `json:"inputs,omitempty"`
|
||||
Outputs workflowv1alpha1.StepOutputs `json:"outputs,omitempty"`
|
||||
DependsOn []string `json:"dependsOn"`
|
||||
Properties *JSONStruct `json:"properties,omitempty"`
|
||||
Meta *workflowv1alpha1.WorkflowStepMeta `json:"meta,omitempty"`
|
||||
If string `json:"if,omitempty"`
|
||||
Timeout string `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
@@ -114,6 +123,12 @@ type WorkflowRecord struct {
|
||||
|
||||
// WorkflowStepStatus is the workflow step status database model
|
||||
type WorkflowStepStatus struct {
|
||||
StepStatus `json:",inline"`
|
||||
SubStepsStatus []StepStatus `json:"subSteps,omitempty"`
|
||||
}
|
||||
|
||||
// StepStatus is the workflow step status database model
|
||||
type StepStatus struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// ListApplicationPolicies query the application policies
|
||||
@@ -83,7 +84,7 @@ func ListApplicationCommonPolicies(ctx context.Context, store datastore.DataStor
|
||||
|
||||
// DeleteApplicationEnvPolicies delete the policies via app name and env name
|
||||
func DeleteApplicationEnvPolicies(ctx context.Context, store datastore.DataStore, app *model.Application, envName string) error {
|
||||
log.Logger.Debugf("clear the policies via app name %s and env name %s", app.PrimaryKey(), envName)
|
||||
log.Logger.Debugf("clear the policies via app name %s and env name %s", app.PrimaryKey(), utils.Sanitize(envName))
|
||||
policies, err := ListApplicationEnvPolicies(ctx, store, app, envName)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/event/sync/convert"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
|
||||
@@ -599,25 +600,20 @@ func GenEnvWorkflowStepsAndPolicies(ctx context.Context, kubeClient client.Clien
|
||||
}
|
||||
var steps []model.WorkflowStep
|
||||
for _, step := range workflowSteps {
|
||||
base, err := convert.FromCRWorkflowStepBase(step.WorkflowStepBase)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("workflow %s step %s properties is invalid %s", pkgUtils.Sanitize(app.Name), pkgUtils.Sanitize(step.Name), err.Error())
|
||||
continue
|
||||
}
|
||||
targetName := strings.Replace(step.Name, "-cloud-resource", "", 1)
|
||||
s := model.WorkflowStep{
|
||||
Name: step.Name,
|
||||
Type: step.Type,
|
||||
Alias: fmt.Sprintf("Deploy To %s", targetName),
|
||||
Description: fmt.Sprintf("deploy app to delivery target %s", targetName),
|
||||
DependsOn: step.DependsOn,
|
||||
Inputs: step.Inputs,
|
||||
Outputs: step.Outputs,
|
||||
base.Alias = fmt.Sprintf("Deploy To %s", targetName)
|
||||
base.Description = fmt.Sprintf("deploy app to delivery target %s", targetName)
|
||||
ws := model.WorkflowStep{
|
||||
WorkflowStepBase: *base,
|
||||
SubSteps: make([]model.WorkflowStepBase, 0),
|
||||
}
|
||||
if step.Properties != nil {
|
||||
properties, err := model.NewJSONStruct(step.Properties)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("workflow %s step %s properties is invalid %s", pkgUtils.Sanitize(app.Name), pkgUtils.Sanitize(step.Name), err.Error())
|
||||
continue
|
||||
}
|
||||
s.Properties = properties
|
||||
}
|
||||
steps = append(steps, s)
|
||||
// no sub steps handle here
|
||||
steps = append(steps, ws)
|
||||
}
|
||||
return steps, policies
|
||||
}
|
||||
|
||||
@@ -40,54 +40,70 @@ import (
|
||||
func TestCompareWorkflowSteps(t *testing.T) {
|
||||
existSteps := []model.WorkflowStep{
|
||||
{
|
||||
Name: "step1",
|
||||
Type: "deploy2env",
|
||||
Properties: &model.JSONStruct{
|
||||
"policy": "env-policy",
|
||||
"env": "target1",
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Name: "step1",
|
||||
Type: "deploy2env",
|
||||
Properties: &model.JSONStruct{
|
||||
"policy": "env-policy",
|
||||
"env": "target1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "suspend",
|
||||
Type: "suspend",
|
||||
},
|
||||
{
|
||||
Name: "step2",
|
||||
Type: "deploy2env",
|
||||
Properties: &model.JSONStruct{
|
||||
"policy": "env-policy",
|
||||
"env": "target2",
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Name: "suspend",
|
||||
Type: "suspend",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "step3",
|
||||
Type: "deploy2env",
|
||||
Properties: &model.JSONStruct{
|
||||
"policy": "env-policy",
|
||||
"env": "target3",
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Name: "step2",
|
||||
Type: "deploy2env",
|
||||
Properties: &model.JSONStruct{
|
||||
"policy": "env-policy",
|
||||
"env": "target2",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "notify",
|
||||
Type: "notify",
|
||||
Properties: &model.JSONStruct{"message": "dddd"},
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Name: "step3",
|
||||
Type: "deploy2env",
|
||||
Properties: &model.JSONStruct{
|
||||
"policy": "env-policy",
|
||||
"env": "target3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Name: "notify",
|
||||
Type: "notify",
|
||||
Properties: &model.JSONStruct{"message": "dddd"},
|
||||
},
|
||||
},
|
||||
}
|
||||
newSteps := []model.WorkflowStep{
|
||||
{
|
||||
Name: "step1",
|
||||
Type: "deploy",
|
||||
Properties: &model.JSONStruct{"policies": []string{"target1"}},
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Name: "step1",
|
||||
Type: "deploy",
|
||||
Properties: &model.JSONStruct{"policies": []string{"target1"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "step2",
|
||||
Type: "deploy",
|
||||
Properties: &model.JSONStruct{"policies": []string{"target2"}},
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Name: "step2",
|
||||
Type: "deploy",
|
||||
Properties: &model.JSONStruct{"policies": []string{"target2"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "step4",
|
||||
Type: "deploy",
|
||||
Properties: &model.JSONStruct{"policies": []string{"target4"}},
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Name: "step4",
|
||||
Type: "deploy",
|
||||
Properties: &model.JSONStruct{"policies": []string{"target4"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
exist := createWorkflowSteps(existSteps, []datastore.Entity{
|
||||
@@ -368,11 +384,15 @@ var _ = Describe("Test workflow model", func() {
|
||||
|
||||
workflow.Steps = []model.WorkflowStep{
|
||||
workflow.Steps[0], {
|
||||
Type: "suspend",
|
||||
Name: "suspend",
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Type: "suspend",
|
||||
Name: "suspend",
|
||||
},
|
||||
}, workflow.Steps[1], {
|
||||
Type: "notification",
|
||||
Name: "notification",
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Type: "notification",
|
||||
Name: "notification",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -400,8 +400,22 @@ func (u *addonServiceImpl) EnableAddon(ctx context.Context, name string, args ap
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range registries {
|
||||
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache)
|
||||
if len(args.RegistryName) != 0 {
|
||||
foundRegistry := false
|
||||
for _, registry := range registries {
|
||||
if registry.Name == args.RegistryName {
|
||||
foundRegistry = true
|
||||
}
|
||||
}
|
||||
if !foundRegistry {
|
||||
return bcode.ErrAddonRegistryNotExist.SetMessage(fmt.Sprintf("specified registry %s not exist", args.RegistryName))
|
||||
}
|
||||
}
|
||||
for i, r := range registries {
|
||||
if len(args.RegistryName) != 0 && args.RegistryName != r.Name {
|
||||
continue
|
||||
}
|
||||
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -412,14 +426,12 @@ func (u *addonServiceImpl) EnableAddon(ctx context.Context, name string, args ap
|
||||
continue
|
||||
}
|
||||
if strings.Contains(err.Error(), "specified version") {
|
||||
berr := bcode.ErrAddonInvalidVersion
|
||||
berr.Message = err.Error()
|
||||
return berr
|
||||
return bcode.ErrAddonInvalidVersion.SetMessage(err.Error())
|
||||
}
|
||||
|
||||
// wrap this error with special bcode
|
||||
if errors.As(err, &pkgaddon.VersionUnMatchError{}) {
|
||||
return bcode.ErrAddonSystemVersionMismatch
|
||||
return bcode.ErrAddonSystemVersionMismatch.SetMessage(err.Error())
|
||||
}
|
||||
// except `addon not found`, other errors should return directly
|
||||
return err
|
||||
@@ -473,8 +485,8 @@ func (u *addonServiceImpl) UpdateAddon(ctx context.Context, name string, args ap
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range registries {
|
||||
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache)
|
||||
for i, r := range registries {
|
||||
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -418,6 +418,7 @@ func (c *applicationServiceImpl) CreateApplicationTrigger(ctx context.Context, a
|
||||
Type: req.Type,
|
||||
PayloadType: req.PayloadType,
|
||||
ComponentName: req.ComponentName,
|
||||
Registry: req.Registry,
|
||||
Token: genWebhookToken(),
|
||||
}
|
||||
if err := c.Store.Add(ctx, trigger); err != nil {
|
||||
|
||||
@@ -533,7 +533,7 @@ var _ = Describe("Test application service function", func() {
|
||||
Expect(cmp.Diff(compareResponse.TargetAppYAML, "")).Should(BeEmpty())
|
||||
Expect(cmp.Diff(compareResponse.BaseAppYAML, "")).ShouldNot(BeEmpty())
|
||||
|
||||
By("compare when app's env add target, should return true")
|
||||
By("compare when app's env add target, should return false")
|
||||
_, err = targetService.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: "dev-target1", Project: appModel.Project, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: "dev-target1"}})
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = envService.UpdateEnv(context.TODO(), "app-dev",
|
||||
@@ -548,7 +548,8 @@ var _ = Describe("Test application service function", func() {
|
||||
},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
check(compareResponse, true)
|
||||
// Existing applications are not affected after update env.
|
||||
check(compareResponse, false)
|
||||
|
||||
By("compare when update app's trait, should return true")
|
||||
// reset app config
|
||||
@@ -846,19 +847,25 @@ var _ = Describe("Test apiserver policy rest api", func() {
|
||||
EnvName: "default",
|
||||
Steps: []v1.WorkflowStep{
|
||||
{
|
||||
Name: "default",
|
||||
Type: "deploy",
|
||||
Properties: `{"policies":["local"]}`,
|
||||
WorkflowStepBase: v1.WorkflowStepBase{
|
||||
Name: "default",
|
||||
Type: "deploy",
|
||||
Properties: `{"policies":["local"]}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "suspend",
|
||||
Type: "suspend",
|
||||
Properties: `{"duration": "10m"}`,
|
||||
WorkflowStepBase: v1.WorkflowStepBase{
|
||||
Name: "suspend",
|
||||
Type: "suspend",
|
||||
Properties: `{"duration": "10m"}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "second",
|
||||
Type: "deploy",
|
||||
Properties: `{"policies":["cluster1"]}`,
|
||||
WorkflowStepBase: v1.WorkflowStepBase{
|
||||
Name: "second",
|
||||
Type: "deploy",
|
||||
Properties: `{"policies":["cluster1"]}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -870,9 +877,11 @@ var _ = Describe("Test apiserver policy rest api", func() {
|
||||
EnvName: "default",
|
||||
Steps: []v1.WorkflowStep{
|
||||
{
|
||||
Name: "second",
|
||||
Type: "deploy",
|
||||
Properties: `{"policies":["cluster3"]}`,
|
||||
WorkflowStepBase: v1.WorkflowStepBase{
|
||||
Name: "second",
|
||||
Type: "deploy",
|
||||
Properties: `{"policies":["cluster3"]}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -231,6 +231,7 @@ func (c *cloudShellServiceImpl) prepareKubeConfig(ctx context.Context) error {
|
||||
var groups []string
|
||||
for _, p := range projects {
|
||||
permissions, err := c.RBACService.GetUserPermissions(ctx, user, p.Name, false)
|
||||
// The kubernetes permission set is generated based on simple rules, but this is not completely strict.
|
||||
var readOnly bool
|
||||
if err != nil {
|
||||
log.Logger.Errorf("failed to get the user permissions %s", err.Error())
|
||||
@@ -239,7 +240,7 @@ func (c *cloudShellServiceImpl) prepareKubeConfig(ctx context.Context) error {
|
||||
readOnly = checkReadOnly(p.Name, permissions)
|
||||
}
|
||||
if readOnly {
|
||||
groupName, err := c.managePrivilegesForProjectRead(ctx, p.Name, true)
|
||||
groupName, err := c.managePrivilegesForProject(ctx, p, true)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("failed to privileges the user %s", err.Error())
|
||||
}
|
||||
@@ -247,9 +248,16 @@ func (c *cloudShellServiceImpl) prepareKubeConfig(ctx context.Context) error {
|
||||
groups = append(groups, groupName)
|
||||
}
|
||||
} else {
|
||||
groups = append(groups, utils.KubeVelaProjectGroupPrefix+p.Name)
|
||||
groupName, err := c.managePrivilegesForProject(ctx, p, false)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("failed to privileges the user %s", err.Error())
|
||||
}
|
||||
if groupName != "" {
|
||||
groups = append(groups, groupName)
|
||||
}
|
||||
}
|
||||
}
|
||||
groups = append(groups, utils.TemplateReaderGroup)
|
||||
|
||||
if utils.StringsContain(user.UserRoles, "admin") {
|
||||
groups = append(groups, utils.KubeVelaAdminGroupPrefix+"admin")
|
||||
@@ -375,8 +383,9 @@ func checkReadOnly(projectName string, permissions []*model.Permission) bool {
|
||||
return !ra.Match(permissions)
|
||||
}
|
||||
|
||||
// managePrivilegesForProjectRead grant the read privileges for a project
|
||||
func (c *cloudShellServiceImpl) managePrivilegesForProjectRead(ctx context.Context, projectName string, readOnly bool) (string, error) {
|
||||
// managePrivilegesForProject grant the privileges for a project
|
||||
func (c *cloudShellServiceImpl) managePrivilegesForProject(ctx context.Context, project *apisv1.ProjectBase, readOnly bool) (string, error) {
|
||||
projectName := project.Name
|
||||
targets, err := c.TargetService.ListTargets(ctx, 0, 0, projectName)
|
||||
if err != nil {
|
||||
log.Logger.Infof("failed to list the targets by the project name %s :%s", projectName, err.Error())
|
||||
@@ -392,7 +401,14 @@ func (c *cloudShellServiceImpl) managePrivilegesForProjectRead(ctx context.Conte
|
||||
for _, e := range envs.Envs {
|
||||
authPDs = append(authPDs, &auth.ApplicationPrivilege{Cluster: kubevelatypes.ClusterLocalName, Namespace: e.Namespace, ReadOnly: readOnly})
|
||||
}
|
||||
|
||||
// The namespace of the environment: Application and WorkflowRun
|
||||
authPDs = append(authPDs, &auth.ApplicationPrivilege{Cluster: kubevelatypes.ClusterLocalName, Namespace: project.Namespace, ReadOnly: readOnly})
|
||||
|
||||
groupName := utils.KubeVelaProjectReadGroupPrefix + projectName
|
||||
if !readOnly {
|
||||
groupName = utils.KubeVelaProjectGroupPrefix + projectName
|
||||
}
|
||||
identity := &auth.Identity{Groups: []string{groupName}}
|
||||
writer := &bytes.Buffer{}
|
||||
if err := auth.GrantPrivileges(ctx, c.KubeClient, authPDs, identity, writer, auth.WithReplace); err != nil {
|
||||
|
||||
@@ -48,6 +48,7 @@ var _ = Describe("Test cloudshell service function", func() {
|
||||
cloudShellService *cloudShellServiceImpl
|
||||
userService *userServiceImpl
|
||||
projectService *projectServiceImpl
|
||||
envService *envServiceImpl
|
||||
err error
|
||||
database string
|
||||
)
|
||||
@@ -56,7 +57,7 @@ var _ = Describe("Test cloudshell service function", func() {
|
||||
database = "cloudshell-test-kubevela"
|
||||
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: database})
|
||||
Expect(err).Should(Succeed())
|
||||
envService := &envServiceImpl{
|
||||
envService = &envServiceImpl{
|
||||
Store: ds,
|
||||
KubeClient: k8sClient,
|
||||
}
|
||||
@@ -66,7 +67,8 @@ var _ = Describe("Test cloudshell service function", func() {
|
||||
ProjectService: projectService,
|
||||
}
|
||||
projectService = &projectServiceImpl{
|
||||
Store: ds,
|
||||
Store: ds,
|
||||
K8sClient: k8sClient,
|
||||
RbacService: &rbacServiceImpl{
|
||||
Store: ds,
|
||||
},
|
||||
@@ -99,14 +101,13 @@ var _ = Describe("Test cloudshell service function", func() {
|
||||
}
|
||||
})
|
||||
|
||||
It("test prepareKubeConfig", func() {
|
||||
It("Test prepareKubeConfig", func() {
|
||||
err = userService.Init(context.TODO())
|
||||
Expect(err).Should(BeNil())
|
||||
err = projectService.Init(context.TODO())
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
By("test the developer users")
|
||||
|
||||
_, err = userService.CreateUser(context.TODO(), apisv1.CreateUserRequest{Name: "test-dev", Password: "test"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -170,17 +171,37 @@ var _ = Describe("Test cloudshell service function", func() {
|
||||
|
||||
err = cloudShellService.prepareKubeConfig(ctx)
|
||||
Expect(err).Should(BeNil())
|
||||
var cm corev1.ConfigMap
|
||||
err = k8sClient.Get(context.TODO(), types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: makeUserConfigName("admin-test")}, &cm)
|
||||
checkConfig := func() {
|
||||
var cm corev1.ConfigMap
|
||||
err = k8sClient.Get(context.TODO(), types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: makeUserConfigName("admin-test")}, &cm)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(cm.Data["identity"]) > 0).Should(BeTrue())
|
||||
var identity auth.Identity
|
||||
err = yaml.Unmarshal([]byte(cm.Data["identity"]), &identity)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(utils.StringsContain(identity.Groups, utils.KubeVelaAdminGroupPrefix+"admin")).Should(BeTrue())
|
||||
Expect(utils.StringsContain(identity.Groups, utils.TemplateReaderGroup)).Should(BeTrue())
|
||||
}
|
||||
|
||||
checkConfig()
|
||||
|
||||
By("Test other projects")
|
||||
|
||||
_, err = projectService.CreateProject(ctx, apisv1.CreateProjectRequest{Name: "cloudshell"})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(cm.Data["identity"]) > 0).Should(BeTrue())
|
||||
var identity auth.Identity
|
||||
err = yaml.Unmarshal([]byte(cm.Data["identity"]), &identity)
|
||||
_, err = envService.CreateEnv(ctx, apisv1.CreateEnvRequest{Name: "cloudshell-env", Project: "cloudshell"})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(utils.StringsContain(identity.Groups, utils.KubeVelaAdminGroupPrefix+"admin")).Should(BeTrue())
|
||||
err = cloudShellService.prepareKubeConfig(ctx)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "kubevela:writer:application:binding", Namespace: "cloudshell-env"}, &rb)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(rb.Subjects[0].Name).Should(Equal(utils.KubeVelaProjectGroupPrefix + "cloudshell"))
|
||||
|
||||
checkConfig()
|
||||
})
|
||||
|
||||
It("test prepare", func() {
|
||||
It("Test prepare", func() {
|
||||
By("Test with not CRD")
|
||||
_, err = userService.CreateUser(context.TODO(), apisv1.CreateUserRequest{Name: "test", Password: "test"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -69,6 +69,7 @@ type DefinitionQueryOption struct {
|
||||
AppliedWorkloads string `json:"appliedWorkloads"`
|
||||
OwnerAddon string `json:"sourceAddon"`
|
||||
QueryAll bool `json:"queryAll"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
// String return cache key string
|
||||
@@ -109,6 +110,19 @@ func (d *definitionServiceImpl) listDefinitions(ctx context.Context, list *unstr
|
||||
},
|
||||
},
|
||||
}
|
||||
if ops.Scope != "" {
|
||||
var filterScope string
|
||||
if ops.Scope == "Application" {
|
||||
filterScope = "WorkflowRun"
|
||||
} else {
|
||||
filterScope = "Application"
|
||||
}
|
||||
matchLabels.MatchExpressions = append(matchLabels.MatchExpressions, metav1.LabelSelectorRequirement{
|
||||
Key: types.LabelDefinitionScope,
|
||||
Operator: metav1.LabelSelectorOpNotIn,
|
||||
Values: []string{filterScope},
|
||||
})
|
||||
}
|
||||
if !ops.QueryAll {
|
||||
matchLabels.MatchExpressions = append(matchLabels.MatchExpressions, metav1.LabelSelectorRequirement{
|
||||
Key: types.LabelDefinitionHidden,
|
||||
|
||||
@@ -112,6 +112,11 @@ var _ = Describe("Test namespace service functions", func() {
|
||||
Expect(wfstep[0].WorkflowStep.Schematic).ShouldNot(BeNil())
|
||||
Expect(wfstep[0].Alias).Should(Equal("test-alias"))
|
||||
|
||||
wfstep, err = definitionService.ListDefinitions(context.TODO(), DefinitionQueryOption{Type: "workflowstep", Scope: "WorkflowRun"})
|
||||
Expect(err).Should(BeNil())
|
||||
// the definition should be filtered
|
||||
Expect(cmp.Diff(len(wfstep), 1)).Should(BeEmpty())
|
||||
|
||||
step, err = ioutil.ReadFile("./testdata/apply-application-hide.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
var sd2 v1beta1.WorkflowStepDefinition
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
apierror "k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/repository"
|
||||
@@ -184,23 +185,6 @@ func checkEqual(old, new []string) bool {
|
||||
return reflect.DeepEqual(old, new)
|
||||
}
|
||||
|
||||
func (p *envServiceImpl) updateAppWithNewEnv(ctx context.Context, envName string, env *model.Env) error {
|
||||
|
||||
// List all apps inside the env
|
||||
apps, err := listApp(ctx, p.Store, apisv1.ListApplicationOptions{Env: envName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, app := range apps {
|
||||
err = repository.UpdateEnvWorkflow(ctx, p.KubeClient, p.Store, app, env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// UpdateEnv update an env for request
|
||||
func (p *envServiceImpl) UpdateEnv(ctx context.Context, name string, req apisv1.UpdateEnvRequest) (*apisv1.Env, error) {
|
||||
env := &model.Env{}
|
||||
@@ -221,25 +205,33 @@ func (p *envServiceImpl) UpdateEnv(ctx context.Context, name string, req apisv1.
|
||||
if err != nil || !pass {
|
||||
return nil, bcode.ErrEnvTargetConflict
|
||||
}
|
||||
|
||||
var targetChanged bool
|
||||
if len(req.Targets) > 0 && !checkEqual(env.Targets, req.Targets) {
|
||||
targetChanged = true
|
||||
env.Targets = req.Targets
|
||||
}
|
||||
|
||||
targets, err := repository.ListTarget(ctx, p.Store, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var targetMap = make(map[string]*model.Target, len(targets))
|
||||
for i, existTarget := range targets {
|
||||
targetMap[existTarget.Name] = targets[i]
|
||||
}
|
||||
for _, target := range req.Targets {
|
||||
if _, exist := targetMap[target]; !exist {
|
||||
var targets []*model.Target
|
||||
if len(req.Targets) > 0 {
|
||||
_, _, deleted := util.ThreeWaySliceCompare(req.Targets, env.Targets)
|
||||
if len(deleted) > 0 {
|
||||
count, err := p.GetAppCountInEnv(ctx, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
return nil, bcode.ErrEnvTargetNotAllowDelete
|
||||
}
|
||||
}
|
||||
targets, err = repository.ListTarget(ctx, p.Store, "", &datastore.ListOptions{
|
||||
FilterOptions: datastore.FilterOptions{
|
||||
In: []datastore.InQueryOption{{
|
||||
Key: "name",
|
||||
Values: req.Targets,
|
||||
}},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(targets) != len(req.Targets) {
|
||||
return nil, bcode.ErrTargetNotExist
|
||||
}
|
||||
env.Targets = req.Targets
|
||||
}
|
||||
|
||||
// create namespace at first
|
||||
@@ -247,13 +239,6 @@ func (p *envServiceImpl) UpdateEnv(ctx context.Context, name string, req apisv1.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if targetChanged {
|
||||
if err = p.updateAppWithNewEnv(ctx, name, env); err != nil {
|
||||
log.Logger.Errorf("update envbinding failure %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := managePrivilegesForEnvironment(ctx, p.KubeClient, env, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -262,6 +247,14 @@ func (p *envServiceImpl) UpdateEnv(ctx context.Context, name string, req apisv1.
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *envServiceImpl) GetAppCountInEnv(ctx context.Context, env *model.Env) (int, error) {
|
||||
var appList v1beta1.ApplicationList
|
||||
if err := p.KubeClient.List(ctx, &appList, client.InNamespace(env.Namespace), client.MatchingLabels{model.LabelSourceOfTruth: model.FromUX}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(appList.Items), nil
|
||||
}
|
||||
|
||||
// CreateEnv create an env for request
|
||||
func (p *envServiceImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequest) (*apisv1.Env, error) {
|
||||
newEnv := &model.Env{
|
||||
@@ -344,14 +337,23 @@ func convertEnvModel2Base(env *model.Env, targets []*model.Target) *apisv1.Env {
|
||||
UpdateTime: env.UpdateTime,
|
||||
}
|
||||
for _, dt := range env.Targets {
|
||||
var t *model.Target
|
||||
for _, tg := range targets {
|
||||
if dt == tg.Name {
|
||||
data.Targets = append(data.Targets, apisv1.NameAlias{
|
||||
Name: dt,
|
||||
Alias: tg.Alias,
|
||||
})
|
||||
t = tg
|
||||
break
|
||||
}
|
||||
}
|
||||
if t != nil {
|
||||
data.Targets = append(data.Targets, apisv1.NameAlias{
|
||||
Name: dt,
|
||||
Alias: t.Alias,
|
||||
})
|
||||
} else {
|
||||
data.Targets = append(data.Targets, apisv1.NameAlias{
|
||||
Name: dt,
|
||||
})
|
||||
}
|
||||
}
|
||||
return &data
|
||||
}
|
||||
|
||||
@@ -25,8 +25,11 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
@@ -53,6 +56,8 @@ var _ = Describe("Test env service functions", func() {
|
||||
// create target
|
||||
err := ds.Add(context.TODO(), &model.Target{Name: "env-test"})
|
||||
Expect(err).Should(BeNil())
|
||||
err = ds.Add(context.TODO(), &model.Target{Name: "env-test-2"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
req := apisv1.CreateEnvRequest{
|
||||
Name: "test-env",
|
||||
@@ -113,6 +118,35 @@ var _ = Describe("Test env service functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(env.Description, req5.Description)).Should(BeEmpty())
|
||||
|
||||
By("Test update the targets of the env")
|
||||
req6 := apisv1.UpdateEnvRequest{
|
||||
Description: "this is a env description update",
|
||||
Targets: []string{"env-test", "env-test-2"},
|
||||
}
|
||||
env, err = envService.UpdateEnv(context.TODO(), "test-env-2", req6)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(env.Targets), len(req6.Targets))).Should(BeEmpty())
|
||||
|
||||
Expect(k8sClient.Create(context.TODO(), &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "env-app",
|
||||
Namespace: env.Namespace,
|
||||
Labels: map[string]string{
|
||||
model.LabelSourceOfTruth: model.FromUX,
|
||||
},
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{},
|
||||
},
|
||||
})).Should(BeNil())
|
||||
|
||||
req7 := apisv1.UpdateEnvRequest{
|
||||
Description: "this is a env description update",
|
||||
Targets: []string{"env-test"},
|
||||
}
|
||||
_, err = envService.UpdateEnv(context.TODO(), "test-env-2", req7)
|
||||
Expect(err).Should(Equal(bcode.ErrEnvTargetNotAllowDelete))
|
||||
|
||||
// clean up the env
|
||||
err = envService.DeleteEnv(context.TODO(), "test-env")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -83,7 +83,7 @@ var _ = Describe("Test helm repo list", func() {
|
||||
pSec = v1.Secret{}
|
||||
gSec = v1.Secret{}
|
||||
Expect(k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "project-my-project"}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "my-project"}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(yaml.Unmarshal([]byte(projectSecret), &pSec)).Should(BeNil())
|
||||
Expect(yaml.Unmarshal([]byte(globalSecret), &gSec)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &pSec)).Should(BeNil())
|
||||
@@ -390,7 +390,7 @@ apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: project-helm-repo
|
||||
namespace: project-my-project
|
||||
namespace: my-project
|
||||
labels:
|
||||
config.oam.dev/type: helm-repository
|
||||
config.oam.dev/catalog: velacore-config
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
142
pkg/apiserver/domain/service/pipeline_test.go
Normal file
142
pkg/apiserver/domain/service/pipeline_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
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 service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/kubevela/workflow/api/v1alpha1"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultNamespace = "project-default-ns1-test"
|
||||
pipelineService *pipelineServiceImpl
|
||||
pipelineRunService *pipelineRunServiceImpl
|
||||
userService *userServiceImpl
|
||||
contextService *contextServiceImpl
|
||||
projectService *projectServiceImpl
|
||||
ctx context.Context
|
||||
|
||||
pipelineName = "test-pipeline"
|
||||
projectName = "test-project"
|
||||
)
|
||||
var _ = Describe("Test pipeline service functions", func() {
|
||||
It("Init services and project", func() {
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "pipeline-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
pipelineService = NewTestPipelineService(ds, k8sClient, cfg).(*pipelineServiceImpl)
|
||||
pipelineRunService = pipelineService.PipelineRunService.(*pipelineRunServiceImpl)
|
||||
contextService = pipelineService.ContextService.(*contextServiceImpl)
|
||||
projectService = pipelineService.ProjectService.(*projectServiceImpl)
|
||||
userService = &userServiceImpl{Store: ds, K8sClient: k8sClient}
|
||||
|
||||
ctx = context.WithValue(context.TODO(), &apisv1.CtxKeyUser, "admin")
|
||||
err = userService.Init(context.TODO())
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = projectService.CreateProject(ctx, apisv1.CreateProjectRequest{
|
||||
Name: projectName,
|
||||
Owner: "admin",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
projModel, err := projectService.GetProject(context.TODO(), projectName)
|
||||
Expect(err).Should(BeNil())
|
||||
ctx = context.WithValue(ctx, &apisv1.CtxKeyProject, projModel)
|
||||
})
|
||||
|
||||
It("Test create pipeline", func() {
|
||||
props := model.JSONStruct{
|
||||
"url": "https://api.github.com/repos/kubevela/kubevela",
|
||||
}
|
||||
testPipelineSteps := []model.WorkflowStep{
|
||||
{
|
||||
SubSteps: []model.WorkflowStepBase{
|
||||
{
|
||||
Name: "request",
|
||||
Type: "request",
|
||||
Outputs: v1alpha1.StepOutputs{
|
||||
{
|
||||
ValueFrom: "import \"strconv\"\n\"Current star count: \" + strconv.FormatInt(response[\"stargazers_count\"], 10)\n",
|
||||
Name: "stars",
|
||||
},
|
||||
},
|
||||
Properties: &props,
|
||||
},
|
||||
},
|
||||
WorkflowStepBase: model.WorkflowStepBase{
|
||||
Name: "step-group",
|
||||
Type: "step-group",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
By("create pipeline with sub-steps")
|
||||
pipeline, err := pipelineService.CreatePipeline(ctx, apisv1.CreatePipelineRequest{
|
||||
Name: pipelineName,
|
||||
Spec: model.WorkflowSpec{
|
||||
Steps: testPipelineSteps,
|
||||
},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(pipeline.Name).Should(Equal(pipelineName))
|
||||
Expect(pipeline.Spec.Steps[0].Name).Should(Equal("step-group"))
|
||||
})
|
||||
|
||||
It("list pipeline", func() {
|
||||
pipelines, err := pipelineService.ListPipelines(ctx, apisv1.ListPipelineRequest{
|
||||
Detailed: true,
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(pipelines).ShouldNot(BeNil())
|
||||
Expect(pipelines.Total).Should(Equal(1))
|
||||
Expect(len(pipelines.Pipelines)).Should(Equal(1))
|
||||
Expect(pipelines.Pipelines[0].Info).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("get pipeline contexts", func() {
|
||||
By("no context")
|
||||
contexts, err := contextService.ListContexts(ctx, projectName, pipelineName)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(contexts.Total).Should(Equal(0))
|
||||
Expect(len(contexts.Contexts)).Should(Equal(0))
|
||||
|
||||
By("create context")
|
||||
contextName := "test-context"
|
||||
contextKey := "test-key"
|
||||
contextVal := "test-val"
|
||||
ppCtx := apisv1.Context{
|
||||
Name: contextName,
|
||||
Values: []model.Value{
|
||||
{
|
||||
Key: contextKey,
|
||||
Value: contextVal,
|
||||
},
|
||||
},
|
||||
}
|
||||
context, err := contextService.CreateContext(ctx, projectName, pipelineName, ppCtx)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(context.Contexts)).Should(Equal(1))
|
||||
})
|
||||
})
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
@@ -31,6 +32,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// ProjectService project manage service.
|
||||
@@ -73,29 +75,19 @@ func (p *projectServiceImpl) Init(ctx context.Context) error {
|
||||
// the default env and default target both using the `default` namespace in control plane cluster
|
||||
func (p *projectServiceImpl) InitDefaultProjectEnvTarget(ctx context.Context, defaultNamespace string) error {
|
||||
var project = model.Project{}
|
||||
entities, err := p.Store.List(ctx, &project, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{
|
||||
IsNotExist: []datastore.IsNotExistQueryOption{
|
||||
{
|
||||
Key: "owner",
|
||||
},
|
||||
},
|
||||
}})
|
||||
entities, err := p.Store.List(ctx, &project, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{}})
|
||||
if err != nil {
|
||||
return fmt.Errorf("initialize project failed %w", err)
|
||||
}
|
||||
if len(entities) > 0 {
|
||||
for _, project := range entities {
|
||||
pro := project.(*model.Project)
|
||||
var init = pro.Owner == ""
|
||||
pro.Owner = model.DefaultAdminUserName
|
||||
if err := p.Store.Put(ctx, pro); err != nil {
|
||||
return err
|
||||
}
|
||||
// owner is empty, it is old data
|
||||
if init {
|
||||
if err := p.RbacService.InitDefaultRoleAndUsersForProject(ctx, pro); err != nil {
|
||||
return fmt.Errorf("init default role and users for project %s failure %w", pro.Name, err)
|
||||
}
|
||||
if err := p.RbacService.SyncDefaultRoleAndUsersForProject(ctx, pro); err != nil {
|
||||
return fmt.Errorf("fail to sync the default role and users for the project %s %w", pro.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -159,6 +151,13 @@ func (p *projectServiceImpl) GetProject(ctx context.Context, projectName string)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if _, err := utils.GetNamespace(ctx, p.K8sClient, project.GetNamespace()); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
if err := utils.CreateNamespace(ctx, p.K8sClient, projectName); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return nil, bcode.ErrProjectNamespaceFail
|
||||
}
|
||||
}
|
||||
}
|
||||
return project, nil
|
||||
}
|
||||
|
||||
@@ -296,7 +295,6 @@ func (p *projectServiceImpl) DeleteProject(ctx context.Context, name string) err
|
||||
|
||||
// CreateProject create project
|
||||
func (p *projectServiceImpl) CreateProject(ctx context.Context, req apisv1.CreateProjectRequest) (*apisv1.ProjectBase, error) {
|
||||
|
||||
exist, err := p.Store.IsExist(ctx, &model.Project{Name: req.Name})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("check project name is exist failure %s", err.Error())
|
||||
@@ -319,19 +317,24 @@ func (p *projectServiceImpl) CreateProject(ctx context.Context, req apisv1.Creat
|
||||
}
|
||||
}
|
||||
|
||||
if err := utils.CreateNamespace(ctx, p.K8sClient, req.Name); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return nil, bcode.ErrProjectNamespaceFail
|
||||
}
|
||||
|
||||
newProject := &model.Project{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Alias: req.Alias,
|
||||
Owner: owner,
|
||||
Namespace: req.Name,
|
||||
}
|
||||
|
||||
if err := p.Store.Add(ctx, newProject); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.RbacService.InitDefaultRoleAndUsersForProject(ctx, newProject); err != nil {
|
||||
log.Logger.Errorf("init default role and users for project failure %s", err.Error())
|
||||
if err := p.RbacService.SyncDefaultRoleAndUsersForProject(ctx, newProject); err != nil {
|
||||
log.Logger.Errorf("fail to sync the default role and users for the project: %s", err.Error())
|
||||
}
|
||||
|
||||
return ConvertProjectModel2Base(newProject, user), nil
|
||||
@@ -526,6 +529,7 @@ func ConvertProjectModel2Base(project *model.Project, owner *model.User) *apisv1
|
||||
CreateTime: project.CreateTime,
|
||||
UpdateTime: project.UpdateTime,
|
||||
Owner: apisv1.NameAlias{Name: project.Owner},
|
||||
Namespace: project.GetNamespace(),
|
||||
}
|
||||
if owner != nil && owner.Name == project.Owner {
|
||||
base.Owner = apisv1.NameAlias{Name: owner.Name, Alias: owner.Alias}
|
||||
|
||||
@@ -59,6 +59,7 @@ var defaultProjectPermissionTemplate = []*model.PermissionTemplate{
|
||||
"project:{projectName}/permission:*",
|
||||
"project:{projectName}/environment:*",
|
||||
"project:{projectName}/application:*/*",
|
||||
"project:{projectName}/pipeline:*/*",
|
||||
},
|
||||
Actions: []string{"detail", "list"},
|
||||
Effect: "Allow",
|
||||
@@ -89,13 +90,23 @@ var defaultProjectPermissionTemplate = []*model.PermissionTemplate{
|
||||
Scope: "project",
|
||||
},
|
||||
{
|
||||
Name: "configuration-read",
|
||||
Alias: "Environment Management",
|
||||
Name: "config-management",
|
||||
Alias: "Config Management",
|
||||
Resources: []string{"project:{projectName}/config:*", "project:{projectName}/provider:*"},
|
||||
Actions: []string{"list", "detail"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "project",
|
||||
},
|
||||
{
|
||||
Name: "pipeline-management",
|
||||
Alias: "Pipeline Management",
|
||||
Resources: []string{
|
||||
"project:{projectName}/pipeline:*",
|
||||
},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "project",
|
||||
},
|
||||
}
|
||||
|
||||
var defaultPlatformPermission = []*model.PermissionTemplate{
|
||||
@@ -234,6 +245,17 @@ var ResourceMaps = map[string]resourceMetadata{
|
||||
pathName: "configName",
|
||||
},
|
||||
"provider": {},
|
||||
"pipeline": {
|
||||
pathName: "pipelineName",
|
||||
subResources: map[string]resourceMetadata{
|
||||
"context": {
|
||||
pathName: "contextName",
|
||||
},
|
||||
"pipelineRun": {
|
||||
pathName: "pipelineRunName",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pathName: "projectName",
|
||||
},
|
||||
@@ -276,7 +298,7 @@ var ResourceMaps = map[string]resourceMetadata{
|
||||
"configTemplate": {},
|
||||
}
|
||||
|
||||
var existResourcePaths = convert(ResourceMaps)
|
||||
var existResourcePaths = convertSources(ResourceMaps)
|
||||
|
||||
type resourceMetadata struct {
|
||||
subResources map[string]resourceMetadata
|
||||
@@ -320,11 +342,11 @@ func checkResourcePath(resource string) (string, error) {
|
||||
return path, fmt.Errorf("there is no resource %s", resource)
|
||||
}
|
||||
|
||||
func convert(sources map[string]resourceMetadata) map[string]string {
|
||||
func convertSources(sources map[string]resourceMetadata) map[string]string {
|
||||
list := make(map[string]string)
|
||||
for k, v := range sources {
|
||||
if len(v.subResources) > 0 {
|
||||
for sub, subWithPathName := range convert(v.subResources) {
|
||||
for sub, subWithPathName := range convertSources(v.subResources) {
|
||||
if subWithPathName != "" {
|
||||
withPathname := fmt.Sprintf("/%s:*%s", k, subWithPathName)
|
||||
if v.pathName != "" {
|
||||
@@ -383,7 +405,7 @@ type RBACService interface {
|
||||
ListPermissions(ctx context.Context, projectName string) ([]apisv1.PermissionBase, error)
|
||||
CreatePermission(ctx context.Context, projectName string, req apisv1.CreatePermissionRequest) (*apisv1.PermissionBase, error)
|
||||
DeletePermission(ctx context.Context, projectName, permName string) error
|
||||
InitDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error
|
||||
SyncDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error
|
||||
Init(ctx context.Context) error
|
||||
}
|
||||
|
||||
@@ -835,7 +857,17 @@ func (p *rbacServiceImpl) CreatePermission(ctx context.Context, projectName stri
|
||||
return assembler.ConvertPermission2DTO(&permission), nil
|
||||
}
|
||||
|
||||
func (p *rbacServiceImpl) InitDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error {
|
||||
func (p *rbacServiceImpl) SyncDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error {
|
||||
|
||||
permissions, err := p.ListPermissions(ctx, project.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var permissionMap = map[string]apisv1.PermissionBase{}
|
||||
for i, per := range permissions {
|
||||
permissionMap[per.Name] = permissions[i]
|
||||
}
|
||||
|
||||
var batchData []datastore.Entity
|
||||
for _, permissionTemp := range defaultProjectPermissionTemplate {
|
||||
var rra = RequestResourceAction{}
|
||||
@@ -849,39 +881,52 @@ func (p *rbacServiceImpl) InitDefaultRoleAndUsersForProject(ctx context.Context,
|
||||
})
|
||||
formattedResource = append(formattedResource, rra.GetResource().String())
|
||||
}
|
||||
batchData = append(batchData, &model.Permission{
|
||||
permission := &model.Permission{
|
||||
Name: permissionTemp.Name,
|
||||
Alias: permissionTemp.Alias,
|
||||
Project: project.Name,
|
||||
Resources: formattedResource,
|
||||
Actions: permissionTemp.Actions,
|
||||
Effect: permissionTemp.Effect,
|
||||
})
|
||||
}
|
||||
batchData = append(batchData, &model.Role{
|
||||
Name: "app-developer",
|
||||
Alias: "App Developer",
|
||||
Permissions: []string{"project-view", "app-management", "env-management", "configuration-read"},
|
||||
Project: project.Name,
|
||||
}, &model.Role{
|
||||
Name: "project-admin",
|
||||
Alias: "Project Admin",
|
||||
Permissions: []string{"project-view", "app-management", "env-management", "role-management", "configuration-read"},
|
||||
Project: project.Name,
|
||||
}, &model.Role{
|
||||
Name: "project-viewer",
|
||||
Alias: "Project Viewer",
|
||||
Permissions: []string{"project-view"},
|
||||
Project: project.Name,
|
||||
})
|
||||
if project.Owner != "" {
|
||||
var projectUser = &model.ProjectUser{
|
||||
ProjectName: project.Name,
|
||||
UserRoles: []string{"project-admin"},
|
||||
Username: project.Owner,
|
||||
}
|
||||
batchData = append(batchData, projectUser)
|
||||
if perm, exist := permissionMap[permissionTemp.Name]; exist {
|
||||
if !utils.EqualSlice(perm.Resources, permissionTemp.Resources) || utils.EqualSlice(perm.Actions, permissionTemp.Actions) {
|
||||
if err := p.Store.Put(ctx, permission); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
batchData = append(batchData, permission)
|
||||
}
|
||||
|
||||
if len(permissions) == 0 {
|
||||
batchData = append(batchData, &model.Role{
|
||||
Name: "app-developer",
|
||||
Alias: "App Developer",
|
||||
Permissions: []string{"project-view", "app-management", "env-management", "config-management", "pipeline-management"},
|
||||
Project: project.Name,
|
||||
}, &model.Role{
|
||||
Name: "project-admin",
|
||||
Alias: "Project Admin",
|
||||
Permissions: []string{"project-view", "app-management", "env-management", "pipeline-management", "config-management", "role-management"},
|
||||
Project: project.Name,
|
||||
}, &model.Role{
|
||||
Name: "project-viewer",
|
||||
Alias: "Project Viewer",
|
||||
Permissions: []string{"project-view"},
|
||||
Project: project.Name,
|
||||
})
|
||||
if project.Owner != "" {
|
||||
var projectUser = &model.ProjectUser{
|
||||
ProjectName: project.Name,
|
||||
UserRoles: []string{"project-admin"},
|
||||
Username: project.Owner,
|
||||
}
|
||||
batchData = append(batchData, projectUser)
|
||||
}
|
||||
}
|
||||
|
||||
return p.Store.BatchAdd(ctx, batchData)
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ var _ = Describe("Test rbac service", func() {
|
||||
|
||||
err = ds.Add(context.TODO(), &model.Project{Name: "init-test", Owner: "test-user"})
|
||||
Expect(err).Should(BeNil())
|
||||
err = rbacService.InitDefaultRoleAndUsersForProject(context.TODO(), &model.Project{Name: "init-test"})
|
||||
err = rbacService.SyncDefaultRoleAndUsersForProject(context.TODO(), &model.Project{Name: "init-test"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
roles, err := rbacService.ListRole(context.TODO(), "init-test", 0, 0)
|
||||
@@ -198,7 +198,7 @@ var _ = Describe("Test rbac service", func() {
|
||||
|
||||
policies, err := rbacService.ListPermissions(context.TODO(), "init-test")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(policies)).Should(BeEquivalentTo(int64(5)))
|
||||
Expect(len(policies)).Should(BeEquivalentTo(int64(6)))
|
||||
})
|
||||
|
||||
It("Test UpdatePermission", func() {
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kubevela/workflow/api/v1alpha1"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -74,7 +75,10 @@ var _ = BeforeSuite(func(done Done) {
|
||||
|
||||
By("new kube client")
|
||||
cfg.Timeout = time.Minute * 2
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
scheme := common.Scheme
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
By("new kube client success")
|
||||
|
||||
@@ -8,6 +8,8 @@ metadata:
|
||||
definition.oam.dev/alias: test-alias
|
||||
name: apply-application
|
||||
namespace: vela-system
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
|
||||
@@ -52,6 +52,9 @@ func guaranteePolicyNotExist(c []string, policy string) ([]string, bool) {
|
||||
// extractPolicyListAndProperty can extract policy from string-format properties, and return
|
||||
// map-format properties in order to further update operation.
|
||||
func extractPolicyListAndProperty(property string) ([]string, map[string]interface{}, error) {
|
||||
if len(property) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
content := map[string]interface{}{}
|
||||
err := json.Unmarshal([]byte(property), &content)
|
||||
if err != nil {
|
||||
|
||||
@@ -208,6 +208,14 @@ func TestExtractPolicyListAndProperty(t *testing.T) {
|
||||
noError bool
|
||||
}{noError: false},
|
||||
},
|
||||
{
|
||||
input: ``,
|
||||
res: struct {
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noError bool
|
||||
}{policies: nil, properties: nil, noError: true},
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
policy, properties, err := extractPolicyListAndProperty(testCase.input)
|
||||
|
||||
@@ -215,13 +215,16 @@ func (c *customHandlerImpl) install() {
|
||||
}
|
||||
|
||||
func (c *acrHandlerImpl) handle(ctx context.Context, webhookTrigger *model.ApplicationTrigger, app *model.Application) (interface{}, error) {
|
||||
|
||||
component, err := getComponent(ctx, c.w.Store, webhookTrigger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acrReq := c.req
|
||||
image := fmt.Sprintf("registry.%s.aliyuncs.com/%s:%s", acrReq.Repository.Region, acrReq.Repository.RepoFullName, acrReq.PushData.Tag)
|
||||
registry := webhookTrigger.Registry
|
||||
if registry == "" {
|
||||
registry = fmt.Sprintf("registry.%s.aliyuncs.com", acrReq.Repository.Region)
|
||||
}
|
||||
image := fmt.Sprintf("%s/%s:%s", registry, acrReq.Repository.RepoFullName, acrReq.PushData.Tag)
|
||||
if err := c.w.patchComponentProperties(ctx, component, &runtime.RawExtension{
|
||||
Raw: []byte(fmt.Sprintf(`{"image": "%s"}`, image)),
|
||||
}); err != nil {
|
||||
|
||||
@@ -196,6 +196,40 @@ var _ = Describe("Test application service function", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect((*comp.Properties)["image"]).Should(Equal("registry.test-region.aliyuncs.com/test-namespace/test-repo:test-tag"))
|
||||
|
||||
By("Test HandleApplicationWebhook function with ACR payload and registry info")
|
||||
acrTrigger, err = appService.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{
|
||||
Name: "test-acr",
|
||||
PayloadType: "acr",
|
||||
Type: "webhook",
|
||||
ComponentName: "component-name-webhook",
|
||||
Registry: "test-enterprise-registry.test-region.cr.aliyuncs.com",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
acrBody = apisv1.HandleApplicationTriggerACRRequest{
|
||||
PushData: apisv1.ACRPushData{
|
||||
Digest: "test-digest",
|
||||
Tag: "test-tag",
|
||||
},
|
||||
Repository: apisv1.ACRRepository{
|
||||
Name: "test-repo",
|
||||
Namespace: "test-namespace",
|
||||
Region: "test-region",
|
||||
RepoFullName: "test-namespace/test-repo",
|
||||
RepoType: "public",
|
||||
},
|
||||
}
|
||||
body, err = json.Marshal(acrBody)
|
||||
Expect(err).Should(BeNil())
|
||||
httpreq, err = http.NewRequest("post", "/", bytes.NewBuffer(body))
|
||||
httpreq.Header.Add(restful.HEADER_ContentType, "application/json")
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = webhookService.HandleApplicationWebhook(context.TODO(), acrTrigger.Token, restful.NewRequest(httpreq))
|
||||
Expect(err).Should(BeNil())
|
||||
comp, err = appService.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect((*comp.Properties)["image"]).Should(Equal("test-enterprise-registry.test-region.cr.aliyuncs.com/test-namespace/test-repo:test-tag"))
|
||||
|
||||
By("Test HandleApplicationWebhook function with harbor payload")
|
||||
harborTrigger, err := appService.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{
|
||||
Name: "test-harbor",
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/repository"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/event/sync/convert"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
assembler "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/assembler/v1"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
@@ -149,26 +150,12 @@ func (w *workflowServiceImpl) CreateOrUpdateWorkflow(ctx context.Context, app *m
|
||||
if err != nil && errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
var steps []model.WorkflowStep
|
||||
for _, step := range req.Steps {
|
||||
properties, err := model.NewJSONStructByString(step.Properties)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("parse trait properties failire %w", err)
|
||||
return nil, bcode.ErrInvalidProperties
|
||||
}
|
||||
steps = append(steps, model.WorkflowStep{
|
||||
Name: step.Name,
|
||||
Type: step.Type,
|
||||
Alias: step.Alias,
|
||||
Inputs: step.Inputs,
|
||||
Outputs: step.Outputs,
|
||||
Description: step.Description,
|
||||
DependsOn: step.DependsOn,
|
||||
Properties: properties,
|
||||
})
|
||||
modelSteps, err := assembler.CreateWorkflowStepModel(req.Steps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if workflow != nil {
|
||||
workflow.Steps = steps
|
||||
workflow.Steps = modelSteps
|
||||
workflow.Alias = req.Alias
|
||||
workflow.Description = req.Description
|
||||
workflow.Default = req.Default
|
||||
@@ -178,7 +165,7 @@ func (w *workflowServiceImpl) CreateOrUpdateWorkflow(ctx context.Context, app *m
|
||||
} else {
|
||||
// It is allowed to set multiple workflows as default, and only one takes effect.
|
||||
workflow = &model.Workflow{
|
||||
Steps: steps,
|
||||
Steps: modelSteps,
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
@@ -490,6 +477,8 @@ func (w *workflowServiceImpl) syncWorkflowStatus(ctx context.Context, appPrimary
|
||||
status := app.Status.Workflow
|
||||
summaryStatus := model.RevisionStatusRunning
|
||||
switch {
|
||||
case status.Phase == workflowv1alpha1.WorkflowStateFailed:
|
||||
summaryStatus = model.RevisionStatusFail
|
||||
case status.Finished:
|
||||
summaryStatus = model.RevisionStatusComplete
|
||||
case status.Terminated:
|
||||
@@ -497,17 +486,26 @@ func (w *workflowServiceImpl) syncWorkflowStatus(ctx context.Context, appPrimary
|
||||
}
|
||||
|
||||
record.Status = summaryStatus
|
||||
stepStatus := make(map[string]*workflowv1alpha1.WorkflowStepStatus, len(status.Steps))
|
||||
for i, step := range status.Steps {
|
||||
stepStatus[step.Name] = &status.Steps[i]
|
||||
stepStatus := make(map[string]*model.WorkflowStepStatus, len(status.Steps))
|
||||
stepAlias := make(map[string]string)
|
||||
for _, step := range record.Steps {
|
||||
stepAlias[step.Name] = step.Alias
|
||||
for _, sub := range step.SubStepsStatus {
|
||||
stepAlias[sub.Name] = sub.Alias
|
||||
}
|
||||
}
|
||||
for _, step := range status.Steps {
|
||||
stepStatus[step.Name] = &model.WorkflowStepStatus{
|
||||
StepStatus: convert.FromCRWorkflowStepStatus(step.StepStatus, stepAlias[step.Name]),
|
||||
SubStepsStatus: make([]model.StepStatus, 0),
|
||||
}
|
||||
for _, sub := range step.SubStepsStatus {
|
||||
stepStatus[step.Name].SubStepsStatus = append(stepStatus[step.Name].SubStepsStatus, convert.FromCRWorkflowStepStatus(sub, stepAlias[sub.Name]))
|
||||
}
|
||||
}
|
||||
for i, step := range record.Steps {
|
||||
if stepStatus[step.Name] != nil {
|
||||
record.Steps[i].Phase = stepStatus[step.Name].Phase
|
||||
record.Steps[i].Message = stepStatus[step.Name].Message
|
||||
record.Steps[i].Reason = stepStatus[step.Name].Reason
|
||||
record.Steps[i].FirstExecuteTime = stepStatus[step.Name].FirstExecuteTime.Time
|
||||
record.Steps[i].LastExecuteTime = stepStatus[step.Name].LastExecuteTime.Time
|
||||
record.Steps[i] = *stepStatus[step.Name]
|
||||
}
|
||||
}
|
||||
record.Finished = strconv.FormatBool(status.Finished)
|
||||
@@ -542,9 +540,19 @@ func (w *workflowServiceImpl) CreateWorkflowRecord(ctx context.Context, appModel
|
||||
steps := make([]model.WorkflowStepStatus, len(workflow.Steps))
|
||||
for i, step := range workflow.Steps {
|
||||
steps[i] = model.WorkflowStepStatus{
|
||||
Name: step.Name,
|
||||
Alias: step.Alias,
|
||||
Type: step.Type,
|
||||
StepStatus: model.StepStatus{
|
||||
Name: step.Name,
|
||||
Alias: step.Alias,
|
||||
Type: step.Type,
|
||||
},
|
||||
SubStepsStatus: make([]model.StepStatus, 0),
|
||||
}
|
||||
for _, sub := range step.SubSteps {
|
||||
steps[i].SubStepsStatus = append(steps[i].SubStepsStatus, model.StepStatus{
|
||||
Name: sub.Name,
|
||||
Alias: sub.Alias,
|
||||
Type: sub.Type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,7 +623,7 @@ func resetRevisionsAndRecords(ctx context.Context, ds datastore.DataStore, appNa
|
||||
record.Finished = "true"
|
||||
for i, step := range record.Steps {
|
||||
if step.Phase == workflowv1alpha1.WorkflowStepPhaseRunning {
|
||||
record.Steps[i].Phase = workflowv1alpha1.WorkflowStepPhaseStopped
|
||||
record.Steps[i].Phase = model.WorkflowStepPhaseStopped
|
||||
}
|
||||
}
|
||||
if err := ds.Put(ctx, record); err != nil {
|
||||
@@ -709,7 +717,7 @@ func TerminateWorkflow(ctx context.Context, kubecli client.Client, app *v1beta1.
|
||||
switch sub.Phase {
|
||||
case workflowv1alpha1.WorkflowStepPhaseFailed:
|
||||
if sub.Reason != wfTypes.StatusReasonFailedAfterRetries && sub.Reason != wfTypes.StatusReasonTimeout {
|
||||
steps[i].SubStepsStatus[j].Phase = wfTypes.StatusReasonTerminate
|
||||
steps[i].SubStepsStatus[j].Reason = wfTypes.StatusReasonTerminate
|
||||
}
|
||||
case workflowv1alpha1.WorkflowStepPhaseRunning:
|
||||
steps[i].SubStepsStatus[j].Phase = workflowv1alpha1.WorkflowStepPhaseFailed
|
||||
|
||||
@@ -58,7 +58,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
rbacService := &rbacServiceImpl{Store: ds}
|
||||
projectService = &projectServiceImpl{Store: ds, RbacService: rbacService}
|
||||
projectService = &projectServiceImpl{Store: ds, RbacService: rbacService, K8sClient: k8sClient}
|
||||
envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService}
|
||||
envBinding = &envBindingServiceImpl{
|
||||
Store: ds,
|
||||
@@ -123,12 +123,28 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
EnvName: "dev",
|
||||
Steps: []apisv1.WorkflowStep{
|
||||
{
|
||||
Name: "apply-pvc",
|
||||
Alias: "step-alias-1",
|
||||
WorkflowStepBase: apisv1.WorkflowStepBase{
|
||||
Name: "apply-server",
|
||||
Alias: "step-alias-1",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "apply-server",
|
||||
Alias: "step-alias-2",
|
||||
WorkflowStepBase: apisv1.WorkflowStepBase{
|
||||
Name: "apply-server2",
|
||||
Alias: "step-alias-2",
|
||||
},
|
||||
},
|
||||
{
|
||||
WorkflowStepBase: apisv1.WorkflowStepBase{
|
||||
Name: "group",
|
||||
Alias: "group-alias",
|
||||
},
|
||||
SubSteps: []apisv1.WorkflowStepBase{
|
||||
{
|
||||
Name: "suspend",
|
||||
Alias: "my-suspend",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Default: &defaultW,
|
||||
@@ -255,16 +271,20 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
By("check the record")
|
||||
record, err := workflowService.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-233")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(record.Status).Should(Equal(model.RevisionStatusComplete))
|
||||
Expect(record.Status).Should(Equal(model.RevisionStatusFail))
|
||||
Expect(record.Steps[0].Alias).Should(Equal("step-alias-1"))
|
||||
Expect(record.Steps[0].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseSucceeded))
|
||||
Expect(record.Steps[1].Alias).Should(Equal("step-alias-2"))
|
||||
Expect(record.Steps[1].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseSucceeded))
|
||||
Expect(record.Steps[2].Alias).Should(Equal("group-alias"))
|
||||
Expect(record.Steps[2].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseFailed))
|
||||
Expect(record.Steps[2].SubStepsStatus[0].Alias).Should(Equal("my-suspend"))
|
||||
Expect(record.Steps[2].SubStepsStatus[0].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseFailed))
|
||||
|
||||
By("check the application revision")
|
||||
err = workflowService.Store.Get(ctx, revision)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(revision.Status).Should(Equal(model.RevisionStatusComplete))
|
||||
Expect(revision.Status).Should(Equal(model.RevisionStatusFail))
|
||||
|
||||
By("create another workflow record to test sync status from controller revision")
|
||||
app.Status.Workflow.Finished = false
|
||||
@@ -314,12 +334,12 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
By("check the record")
|
||||
anotherRecord, err := workflowService.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-111")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(anotherRecord.Status).Should(Equal(model.RevisionStatusComplete))
|
||||
Expect(anotherRecord.Status).Should(Equal(model.RevisionStatusFail))
|
||||
|
||||
By("check the application revision")
|
||||
err = workflowService.Store.Get(ctx, anotherRevision)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(anotherRevision.Status).Should(Equal(model.RevisionStatusComplete))
|
||||
Expect(anotherRevision.Status).Should(Equal(model.RevisionStatusFail))
|
||||
})
|
||||
|
||||
It("Test CreateRecord function", func() {
|
||||
@@ -541,10 +561,14 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
Finished: "false",
|
||||
Steps: []model.WorkflowStepStatus{
|
||||
{
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseSucceeded,
|
||||
StepStatus: model.StepStatus{
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseSucceeded,
|
||||
},
|
||||
},
|
||||
{
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseRunning,
|
||||
StepStatus: model.StepStatus{
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseRunning,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -562,7 +586,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(record.Status).Should(Equal(model.RevisionStatusTerminated))
|
||||
Expect(record.Finished).Should(Equal("true"))
|
||||
Expect(record.Steps[1].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseStopped))
|
||||
Expect(record.Steps[1].Phase).Should(Equal(model.WorkflowStepPhaseStopped))
|
||||
})
|
||||
|
||||
It("Test deleting workflow", func() {
|
||||
@@ -619,10 +643,10 @@ var yamlStr = `apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
annotations:
|
||||
app.oam.dev/workflowName: test-workflow-2
|
||||
app.oam.dev/appName: app-workflow
|
||||
app.oam.dev/deployVersion: "1234"
|
||||
app.oam.dev/publishVersion: "test-workflow-name-111"
|
||||
app.oam.dev/appName: "app-workflow"
|
||||
app.oam.dev/publishVersion: test-workflow-name-111
|
||||
app.oam.dev/workflowName: test-workflow-2
|
||||
name: app-workflow
|
||||
namespace: default
|
||||
spec:
|
||||
@@ -632,31 +656,71 @@ spec:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
type: webservice
|
||||
- name: express-server2
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
type: webservice
|
||||
workflow:
|
||||
steps:
|
||||
- name: apply-server
|
||||
properties:
|
||||
component: express-server
|
||||
type: apply-component
|
||||
- name: apply-server2
|
||||
properties:
|
||||
component: express-server
|
||||
type: apply-component
|
||||
- name: group
|
||||
subSteps:
|
||||
- name: suspend
|
||||
timeout: 1s
|
||||
type: suspend
|
||||
type: step-group
|
||||
status:
|
||||
status: workflowFailed
|
||||
workflow:
|
||||
appRevision: test-workflow-name-111
|
||||
contextBackend:
|
||||
name: workflow-app-workflow-context
|
||||
namespace: default
|
||||
uid: ef9bcf49-66a7-4c69-b349-150810aa2bac
|
||||
endTime: "2022-10-28T06:45:46Z"
|
||||
finished: true
|
||||
message: The workflow terminates because of the failed steps
|
||||
mode: StepByStep-DAG
|
||||
startTime: "2022-10-28T06:45:37Z"
|
||||
status: failed
|
||||
steps:
|
||||
- firstExecuteTime: "2021-10-26T11:19:33Z"
|
||||
id: t8bpvi88d1
|
||||
lastExecuteTime: "2021-10-26T11:19:33Z"
|
||||
name: apply-pvc
|
||||
phase: succeeded
|
||||
type: apply-object
|
||||
- firstExecuteTime: "2021-10-26T11:19:33Z"
|
||||
id: 9fou7rbq9r
|
||||
lastExecuteTime: "2021-10-26T11:19:33Z"
|
||||
- firstExecuteTime: "2022-10-28T06:45:37Z"
|
||||
id: fg5uiwroe6
|
||||
lastExecuteTime: "2022-10-28T06:45:45Z"
|
||||
name: apply-server
|
||||
phase: succeeded
|
||||
type: apply-component
|
||||
- firstExecuteTime: "2022-10-28T06:45:45Z"
|
||||
id: prouwp48y7
|
||||
lastExecuteTime: "2022-10-28T06:45:45Z"
|
||||
name: apply-server2
|
||||
phase: succeeded
|
||||
type: apply-component
|
||||
- firstExecuteTime: "2022-10-28T06:45:45Z"
|
||||
id: s6o27xnkzq
|
||||
lastExecuteTime: "2022-10-28T06:45:46Z"
|
||||
name: group
|
||||
phase: failed
|
||||
reason: Timeout
|
||||
subSteps:
|
||||
- firstExecuteTime: "2022-10-28T06:45:45Z"
|
||||
id: ctu63esz2m
|
||||
lastExecuteTime: "2022-10-28T06:45:46Z"
|
||||
name: suspend
|
||||
phase: failed
|
||||
reason: Timeout
|
||||
type: suspend
|
||||
type: step-group
|
||||
suspend: false
|
||||
terminated: false
|
||||
finished: true
|
||||
appRevision: "test-workflow-name-111"`
|
||||
terminated: true`
|
||||
|
||||
func (w *workflowServiceImpl) createTestApplicationRevision(ctx context.Context, revision *model.ApplicationRevision) error {
|
||||
if err := w.Store.Add(ctx, revision); err != nil {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user