├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── pr-title-checker-config.json └── workflows │ ├── back-port.yaml │ ├── chart.yaml │ ├── codeql-analysis.yaml │ ├── commit-lint.yaml │ ├── e2e.yaml │ ├── go.yaml │ ├── issue-commands.yml │ ├── post-submit.yaml │ └── unit-test.yaml ├── .gitignore ├── .golangci.yml ├── Dockerfile ├── Dockerfile.e2e ├── LICENSE ├── Makefile ├── README.md ├── api ├── condition │ ├── condition.go │ ├── condition_test.go │ ├── doc.go │ └── zz_generated.deepcopy.go └── v1alpha1 │ ├── doc.go │ ├── event.go │ ├── register.go │ ├── types.go │ └── zz_generated.deepcopy.go ├── charts └── vela-workflow │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── crds │ ├── core.oam.dev_workflowruns.yaml │ ├── core.oam.dev_workflows.yaml │ ├── core.oam.dev_workflowstepdefinitions.yaml │ └── cue.oam.dev_packages.yaml │ ├── templates │ ├── _helpers.tpl │ ├── admission-webhooks │ │ ├── certmanager.yaml │ │ ├── job-patch │ │ │ ├── clusterrole.yaml │ │ │ ├── clusterrolebinding.yaml │ │ │ ├── job-createSecret.yaml │ │ │ ├── job-patchWebhook.yaml │ │ │ ├── role.yaml │ │ │ ├── rolebinding.yaml │ │ │ └── serviceaccount.yaml │ │ ├── mutatingWebhookConfiguration.yaml │ │ ├── validatingWebhookConfiguration.yaml │ │ └── webhookService.yaml │ ├── definitions │ │ ├── addon-operation.yaml │ │ ├── apply-app.yaml │ │ ├── apply-deployment.yaml │ │ ├── apply-job.yaml │ │ ├── apply-object.yaml │ │ ├── apply-terraform-config.yaml │ │ ├── apply-terraform-provider.yaml │ │ ├── build-push-image.yaml │ │ ├── chat-gpt.yaml │ │ ├── clean-jobs.yaml │ │ ├── create-config.yaml │ │ ├── delete-config.yaml │ │ ├── export2config.yaml │ │ ├── export2secret.yaml │ │ ├── list-config.yaml │ │ ├── notification.yaml │ │ ├── print-message-in-status.yaml │ │ ├── read-app.yaml │ │ ├── read-config.yaml │ │ ├── read-object.yaml │ │ ├── request.yaml │ │ ├── step-group.yaml │ │ ├── suspend.yaml │ │ └── vela-cli.yaml │ └── workflow-controller.yaml │ └── values.yaml ├── cmd ├── main.go └── main_e2e_test.go ├── codecov.yml ├── config └── crd │ ├── kustomization.yaml │ └── kustomizeconfig.yaml ├── controllers ├── backup_controller.go ├── backup_test.go ├── suite_test.go ├── testdata │ ├── apply-object.yaml │ ├── failed-render.yaml │ ├── multi-suspend.yaml │ ├── save-process-context.yaml │ ├── suspend-and-deploy.yaml │ └── test-apply.yaml ├── workflow_test.go └── workflowrun_controller.go ├── e2e ├── definition_test.go ├── e2e_suite_test.go └── test-data │ ├── config-workflow-run.yaml │ ├── definitions │ ├── create-config.yaml │ ├── delete-config.yaml │ ├── list-config.yaml │ ├── read-config.yaml │ └── write-message.yaml │ └── message-workflow-run.yaml ├── examples ├── initialize-env.md ├── job-orchestration.md ├── multiple-apps.md ├── request-and-notify.md ├── run-with-template.md ├── static │ ├── slack-fail.png │ └── slack-success.png ├── workflow-run │ ├── apply-applications.yaml │ ├── apply-terraform-resource.yaml │ ├── build-push-image.yaml │ ├── chat-gpt.yaml │ ├── custom-context.yaml │ ├── deploy-workflowrun.yaml │ └── request.yaml └── workflow-template │ └── deploy-workflow.yaml ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt └── e2e │ ├── end_e2e.sh │ └── modify_charts.sh ├── makefiles ├── const.mk ├── dependency.mk └── e2e.mk ├── pkg ├── backup │ ├── interface.go │ ├── interface_test.go │ └── sls │ │ ├── producer.go │ │ └── producer_test.go ├── common │ └── logs.go ├── context │ ├── context.go │ ├── context_test.go │ ├── interface.go │ └── storage.go ├── cue │ ├── README.md │ ├── model │ │ ├── instance.go │ │ ├── instance_test.go │ │ ├── keyword.go │ │ ├── sets │ │ │ ├── operation.go │ │ │ ├── operation_test.go │ │ │ ├── utils.go │ │ │ ├── utils_test.go │ │ │ ├── walk.go │ │ │ └── walk_test.go │ │ └── value │ │ │ ├── value.go │ │ │ └── value_test.go │ ├── process │ │ ├── contexthook.go │ │ ├── datamanager.go │ │ ├── handle.go │ │ └── handle_test.go │ └── testdata │ │ ├── traits │ │ ├── manualscaler.cue │ │ ├── rollout.cue │ │ └── route.cue │ │ └── workloads │ │ ├── deployment.cue │ │ ├── empty.cue │ │ ├── metrics.cue │ │ ├── test-param.cue │ │ └── webservice.cue ├── debug │ ├── context.go │ └── context_test.go ├── errors │ └── errors.go ├── executor │ ├── interface.go │ ├── options.go │ ├── suit_test.go │ ├── workflow.go │ └── workflow_test.go ├── features │ └── controller_features.go ├── generator │ ├── generator.go │ ├── generator_test.go │ └── suit_test.go ├── hooks │ ├── data_passing.go │ └── data_passing_test.go ├── mock │ └── mock.go ├── monitor │ ├── metrics │ │ └── workflow.go │ └── watcher │ │ └── workflow.go ├── providers │ ├── builtin │ │ ├── workspace.cue │ │ ├── workspace.go │ │ └── workspace_test.go │ ├── compiler.go │ ├── email │ │ ├── email.cue │ │ ├── email.go │ │ └── email_test.go │ ├── http │ │ ├── http.cue │ │ ├── http.go │ │ ├── http_test.go │ │ ├── ratelimiter │ │ │ ├── ratelimiter.go │ │ │ └── ratelimiter_test.go │ │ └── testdata │ │ │ └── certs.go │ ├── kube │ │ ├── kube.cue │ │ ├── kube.go │ │ └── kube_test.go │ ├── legacy │ │ ├── email │ │ │ ├── email.cue │ │ │ ├── email.go │ │ │ └── email_test.go │ │ ├── http │ │ │ ├── http.cue │ │ │ ├── http.go │ │ │ ├── http_test.go │ │ │ ├── ratelimiter │ │ │ │ ├── ratelimiter.go │ │ │ │ └── ratelimiter_test.go │ │ │ └── testdata │ │ │ │ └── certs.go │ │ ├── kube │ │ │ ├── kube.cue │ │ │ ├── kube.go │ │ │ └── kube_test.go │ │ ├── legacy.go │ │ ├── metrics │ │ │ ├── metrics.cue │ │ │ ├── prom_check.go │ │ │ └── prom_check_test.go │ │ ├── time │ │ │ ├── time.cue │ │ │ ├── time.go │ │ │ └── time_test.go │ │ ├── util │ │ │ ├── util.cue │ │ │ ├── util.go │ │ │ └── util_test.go │ │ └── workspace │ │ │ ├── workspace.cue │ │ │ ├── workspace.go │ │ │ └── workspace_test.go │ ├── metrics │ │ ├── metrics.cue │ │ ├── prom_check.go │ │ └── prom_check_test.go │ ├── time │ │ ├── time.cue │ │ ├── time.go │ │ └── time_test.go │ ├── types │ │ └── types.go │ └── util │ │ ├── util.cue │ │ ├── util.go │ │ └── util_test.go ├── tasks │ ├── builtin │ │ ├── step_group.go │ │ └── step_group_test.go │ ├── custom │ │ ├── action.go │ │ ├── task.go │ │ └── task_test.go │ ├── discover.go │ ├── discover_test.go │ └── template │ │ ├── load.go │ │ ├── load_test.go │ │ └── static │ │ ├── builtin-apply-component.cue │ │ └── suspend.cue ├── types │ └── types.go ├── utils │ ├── main_test.go │ ├── operation.go │ ├── operation_test.go │ ├── recycle.go │ ├── recycle_test.go │ ├── test_utils.go │ ├── test_utils_test.go │ ├── utils.go │ └── utils_test.go └── webhook │ ├── register.go │ └── v1alpha1 │ └── workflowrun │ ├── mutating_handler.go │ ├── mutating_handler_test.go │ ├── suite_test.go │ ├── validating_handler.go │ ├── validating_handler_test.go │ └── validation.go ├── staticcheck.conf └── version └── version.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file is a github code protect rule follow the codeowners https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners#example-of-a-codeowners-file 2 | 3 | * @FogDong @wonderflow @leejanee @Somefive @anoop2811 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: kind/bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 14 | 15 | **To Reproduce** 16 | 23 | 24 | **Expected behavior** 25 | 28 | 29 | **Screenshots** 30 | 33 | 34 | **Workflow Version** 35 | 36 | 39 | 40 | **Cluster information** 41 | 45 | 46 | **Additional context** 47 | 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Questions & Help 4 | url: https://github.com/kubevela/workflow/discussions 5 | about: Please ask and answer questions here. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature request" 3 | about: Suggest an idea for this project 4 | title: "[Feature]" 5 | labels: kind/feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 14 | 15 | **Describe the solution you'd like** 16 | 19 | 20 | **Describe alternatives you've considered** 21 | 24 | 25 | **Additional context** 26 | 29 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | ### Description of your changes 3 | 4 | 11 | 12 | Fixes # 13 | 14 | I have: 15 | 16 | - [ ] Read and followed KubeVela's [contribution process](https://github.com/kubevela/kubevela/blob/master/contribute/create-pull-request.md). 17 | - [ ] [Related Docs](https://github.com/kubevela/kubevela.io) updated properly. In a new feature or configuration option, an update to the documentation is necessary. 18 | - [ ] Run `make reviewable` to ensure this PR is ready for review. 19 | - [ ] Added `backport release-x.y` labels to auto-backport this PR if necessary. 20 | 21 | ### How has this code been tested 22 | 23 | 28 | 29 | 30 | ### Special notes for your reviewer 31 | 32 | -------------------------------------------------------------------------------- /.github/pr-title-checker-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "LABEL": { 3 | "name": "title-needs-formatting", 4 | "color": "EEEEEE" 5 | }, 6 | "CHECKS": { 7 | "prefixes": ["Fix: ", "Feat: ", "Docs: ", "Test: ", "Chore: ", "CI: ", "Perf: ", "Refactor: ", "Revert: ", "Style: ", "Test: ", 8 | "Fix(", "Feat(", "Docs(", "Test(", "Chore(", "CI(", "Perf(", "Refactor(", "Revert(", "Style(", "Test(", "[Backport"] 9 | } 10 | } -------------------------------------------------------------------------------- /.github/workflows/back-port.yaml: -------------------------------------------------------------------------------- 1 | name: Backport 2 | on: 3 | pull_request_target: 4 | types: 5 | - closed 6 | 7 | jobs: 8 | # align with crossplane's choice https://github.com/crossplane/crossplane/blob/master/.github/workflows/backport.yml 9 | open-pr: 10 | runs-on: ubuntu-22.04 11 | if: github.event.pull_request.merged 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: Open Backport PR 19 | uses: zeebe-io/backport-action@v0.0.6 20 | with: 21 | github_token: ${{ secrets.GITHUB_TOKEN }} 22 | github_workspace: ${{ github.workspace }} 23 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yaml: -------------------------------------------------------------------------------- 1 | name: CodeQL 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release-* 8 | pull_request: 9 | branches: 10 | - main 11 | - release-* 12 | 13 | jobs: 14 | images: 15 | name: Image Scan 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v2 20 | 21 | - name: Build Vela Workflow image from Dockerfile 22 | run: | 23 | docker build --build-arg GOPROXY=https://proxy.golang.org -t docker.io/oamdev/vela-workflow:${{ github.sha }} -f ./Dockerfile . 24 | - name: Run Trivy vulnerability scanner for vela workflow 25 | uses: aquasecurity/trivy-action@master 26 | with: 27 | image-ref: 'docker.io/oamdev/vela-workflow:${{ github.sha }}' 28 | format: 'sarif' 29 | output: 'trivy-results.sarif' 30 | 31 | - name: Upload Trivy scan results to GitHub Security tab 32 | uses: github/codeql-action/upload-sarif@v1 33 | if: always() 34 | with: 35 | sarif_file: 'trivy-results.sarif' 36 | 37 | analyze: 38 | name: Analyze 39 | runs-on: ubuntu-latest 40 | 41 | strategy: 42 | fail-fast: false 43 | matrix: 44 | language: [ 'go' ] 45 | 46 | steps: 47 | - name: Checkout repository 48 | uses: actions/checkout@v2 49 | 50 | - name: Initialize CodeQL 51 | uses: github/codeql-action/init@v1 52 | with: 53 | languages: ${{ matrix.language }} 54 | 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | - name: Perform CodeQL Analysis 59 | uses: github/codeql-action/analyze@v1 -------------------------------------------------------------------------------- /.github/workflows/commit-lint.yaml: -------------------------------------------------------------------------------- 1 | name: PR Title Checker 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | - edited 7 | - synchronize 8 | - labeled 9 | - unlabeled 10 | 11 | jobs: 12 | check: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: thehanimo/pr-title-checker@v1.3.4 16 | with: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | pass_on_octokit_error: false 19 | configuration_path: ".github/pr-title-checker-config.json" 20 | -------------------------------------------------------------------------------- /.github/workflows/go.yaml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release-* 8 | workflow_dispatch: {} 9 | pull_request: 10 | branches: 11 | - main 12 | - release-* 13 | 14 | env: 15 | # Common versions 16 | GO_VERSION: '1.23.8' 17 | GOLANGCI_VERSION: 'v1.60.1' 18 | 19 | jobs: 20 | 21 | detect-noop: 22 | runs-on: ubuntu-22.04 23 | outputs: 24 | noop: ${{ steps.noop.outputs.should_skip }} 25 | steps: 26 | - name: Detect No-op Changes 27 | id: noop 28 | uses: fkirc/skip-duplicate-actions@v5.3.0 29 | with: 30 | github_token: ${{ secrets.GITHUB_TOKEN }} 31 | paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]' 32 | do_not_skip: '["workflow_dispatch", "schedule", "push"]' 33 | concurrent_skipping: false 34 | 35 | staticcheck: 36 | runs-on: ubuntu-22.04 37 | needs: detect-noop 38 | if: needs.detect-noop.outputs.noop != 'true' 39 | 40 | steps: 41 | - name: Setup Go 42 | uses: actions/setup-go@v2 43 | with: 44 | go-version: ${{ env.GO_VERSION }} 45 | 46 | - name: Checkout 47 | uses: actions/checkout@v2 48 | with: 49 | submodules: true 50 | 51 | - name: Cache Go Dependencies 52 | uses: actions/cache@v3 53 | with: 54 | path: .work/pkg 55 | key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} 56 | restore-keys: ${{ runner.os }}-pkg- 57 | 58 | - name: Install StaticCheck 59 | run: go install honnef.co/go/tools/cmd/staticcheck@v0.5.1 60 | 61 | - name: Static Check 62 | run: staticcheck ./... 63 | 64 | lint: 65 | runs-on: ubuntu-22.04 66 | needs: detect-noop 67 | if: needs.detect-noop.outputs.noop != 'true' 68 | 69 | steps: 70 | - name: Setup Go 71 | uses: actions/setup-go@v2 72 | with: 73 | go-version: ${{ env.GO_VERSION }} 74 | 75 | - name: Checkout 76 | uses: actions/checkout@v2 77 | with: 78 | submodules: true 79 | 80 | - name: Cache Go Dependencies 81 | uses: actions/cache@v3 82 | with: 83 | path: .work/pkg 84 | key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} 85 | restore-keys: ${{ runner.os }}-pkg- 86 | 87 | # This action uses its own setup-go, which always seems to use the latest 88 | # stable version of Go. We could run 'make lint' to ensure our desired Go 89 | # version, but we prefer this action because it leaves 'annotations' (i.e. 90 | # it comments on PRs to point out linter violations). 91 | - name: Lint 92 | uses: golangci/golangci-lint-action@v3 93 | with: 94 | version: ${{ env.GOLANGCI_VERSION }} 95 | -------------------------------------------------------------------------------- /.github/workflows/issue-commands.yml: -------------------------------------------------------------------------------- 1 | name: Run commands when issues are labeled or comments added 2 | on: 3 | issues: 4 | types: [labeled, opened] 5 | issue_comment: 6 | types: [created] 7 | 8 | jobs: 9 | bot: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - name: Checkout Actions 13 | uses: actions/checkout@v2 14 | with: 15 | repository: "oam-dev/kubevela-github-actions" 16 | path: ./actions 17 | ref: v0.4.2 18 | - name: Setup Node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: '14' 22 | cache: 'npm' 23 | cache-dependency-path: ./actions/package-lock.json 24 | - name: Install Dependencies 25 | run: npm ci --production --prefix ./actions 26 | - name: Run Commands 27 | uses: ./actions/commands 28 | with: 29 | token: ${{secrets.GITHUB_TOKEN}} 30 | configPath: issue-commands 31 | 32 | backport: 33 | runs-on: ubuntu-22.04 34 | if: github.event.issue.pull_request && contains(github.event.comment.body, '/backport') 35 | steps: 36 | - name: Extract Command 37 | id: command 38 | uses: xt0rted/slash-command-action@v1 39 | with: 40 | repo-token: ${{ secrets.GITHUB_TOKEN }} 41 | command: backport 42 | reaction: "true" 43 | reaction-type: "eyes" 44 | allow-edits: "false" 45 | permission-level: read 46 | - name: Handle Command 47 | uses: actions/github-script@v4 48 | env: 49 | VERSION: ${{ steps.command.outputs.command-arguments }} 50 | with: 51 | github-token: ${{ secrets.GITHUB_TOKEN }} 52 | script: | 53 | const version = process.env.VERSION 54 | let label = "backport release-" + version 55 | if (version.includes("release")) { 56 | label = "backport " + version 57 | } 58 | // Add our backport label. 59 | github.issues.addLabels({ 60 | // Every pull request is an issue, but not every issue is a pull request. 61 | issue_number: context.issue.number, 62 | owner: context.repo.owner, 63 | repo: context.repo.repo, 64 | labels: [label] 65 | }) 66 | console.log("Added '" + label + "' label.") 67 | - name: Checkout 68 | uses: actions/checkout@v3 69 | with: 70 | fetch-depth: 0 71 | - name: Open Backport PR 72 | uses: zeebe-io/backport-action@v0.0.8 73 | with: 74 | github_token: ${{ secrets.GITHUB_TOKEN }} 75 | github_workspace: ${{ github.workspace }} 76 | -------------------------------------------------------------------------------- /.github/workflows/post-submit.yaml: -------------------------------------------------------------------------------- 1 | name: PostSubmit 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: {} 8 | 9 | env: 10 | GO_VERSION: '1.23.8' 11 | 12 | jobs: 13 | 14 | detect-noop: 15 | runs-on: ubuntu-22.04 16 | outputs: 17 | noop: ${{ steps.noop.outputs.should_skip }} 18 | steps: 19 | - name: Detect No-op Changes 20 | id: noop 21 | uses: fkirc/skip-duplicate-actions@v5.3.0 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]' 25 | do_not_skip: '["workflow_dispatch", "schedule", "push"]' 26 | concurrent_skipping: false 27 | 28 | image-multi-arch: 29 | runs-on: ubuntu-22.04 30 | needs: detect-noop 31 | if: needs.detect-noop.outputs.noop != 'true' 32 | strategy: 33 | matrix: 34 | arch: [ amd64, arm64 ] 35 | 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v2 39 | with: 40 | submodules: true 41 | 42 | - name: Setup Go 43 | uses: actions/setup-go@v2 44 | with: 45 | go-version: ${{ env.GO_VERSION }} 46 | 47 | - name: Build Image 48 | run: | 49 | IMG_TAG=latest-${{ matrix.arch }} \ 50 | OS=linux \ 51 | ARCH=${{ matrix.arch }} \ 52 | make docker-build 53 | - name: Push Image 54 | run: | 55 | echo ${{ secrets.DOCKER_PASSWORD }} | docker login --username ${{ secrets.DOCKER_USER }} --password-stdin 56 | docker push oamdev/vela-workflow:latest-${{ matrix.arch }} 57 | docker push oamdev/vela-workflow:latest-${{ matrix.arch }} 58 | image-manifest: 59 | runs-on: ubuntu-latest 60 | needs: [ image-multi-arch ] 61 | steps: 62 | 63 | - name: Checkout 64 | uses: actions/checkout@v2 65 | with: 66 | submodules: true 67 | 68 | - name: Create Manifest 69 | run: | 70 | echo ${{ secrets.DOCKER_PASSWORD }} | docker login --username ${{ secrets.DOCKER_USER }} --password-stdin 71 | docker manifest create oamdev/vela-workflow:latest \ 72 | oamdev/vela-workflow:latest-amd64 \ 73 | oamdev/vela-workflow:latest-arm64 74 | - name: Annotate Manifest 75 | run: | 76 | docker manifest annotate oamdev/vela-workflow:latest \ 77 | oamdev/vela-workflow:latest-amd64 --arch amd64 78 | docker manifest annotate oamdev/vela-workflow:latest \ 79 | oamdev/vela-workflow:latest-arm64 --arch arm64 80 | - name: Push Manifest 81 | run: | 82 | docker manifest push oamdev/vela-workflow:latest -------------------------------------------------------------------------------- /.github/workflows/unit-test.yaml: -------------------------------------------------------------------------------- 1 | name: Unit-Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release-* 8 | workflow_dispatch: {} 9 | pull_request: 10 | branches: 11 | - main 12 | - release-* 13 | 14 | env: 15 | # Common versions 16 | GO_VERSION: '1.23.8' 17 | GOLANGCI_VERSION: 'v1.60.1' 18 | KIND_VERSION: 'v0.23.0' 19 | 20 | jobs: 21 | 22 | detect-noop: 23 | runs-on: ubuntu-22.04 24 | outputs: 25 | noop: ${{ steps.noop.outputs.should_skip }} 26 | steps: 27 | - name: Detect No-op Changes 28 | id: noop 29 | uses: fkirc/skip-duplicate-actions@v5.3.0 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | paths_ignore: '["**.md", "**.mdx", "**.png", "**.jpg"]' 33 | do_not_skip: '["workflow_dispatch", "schedule", "push"]' 34 | concurrent_skipping: false 35 | 36 | unit-tests: 37 | runs-on: ubuntu-22.04 38 | needs: detect-noop 39 | if: needs.detect-noop.outputs.noop != 'true' 40 | 41 | steps: 42 | - name: Set up Go 43 | uses: actions/setup-go@v1 44 | with: 45 | go-version: ${{ env.GO_VERSION }} 46 | id: go 47 | 48 | - name: Check out code into the Go module directory 49 | uses: actions/checkout@v2 50 | with: 51 | submodules: true 52 | 53 | - name: Cache Go Dependencies 54 | uses: actions/cache@v3 55 | with: 56 | path: .work/pkg 57 | key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} 58 | restore-keys: ${{ runner.os }}-pkg- 59 | 60 | - name: Install ginkgo 61 | run: | 62 | sudo apt-get install -y golang-ginkgo-dev 63 | 64 | - name: install Kubebuilder 65 | uses: RyanSiu1995/kubebuilder-action@v1.2 66 | with: 67 | version: 3.15.1 68 | kubebuilderOnly: false 69 | kubernetesVersion: v1.29.0 70 | 71 | - name: Run Make test 72 | run: make test 73 | 74 | - name: Upload coverage report 75 | uses: codecov/codecov-action@v1 76 | with: 77 | token: ${{ secrets.CODECOV_TOKEN }} 78 | file: ./coverage.txt 79 | flags: unit-test 80 | name: codecov-umbrella 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin 8 | _bin 9 | e2e/vela 10 | 11 | # Test binary, build with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | coverage.txt 17 | 18 | # Kubernetes Generated files - skip generated files, except for vendored files 19 | 20 | !vendor/**/zz_generated.* 21 | 22 | # editor and IDE paraphernalia 23 | .idea 24 | *.swp 25 | *.swo 26 | *~ 27 | .DS_Store 28 | _.yaml 29 | 30 | # Dependency directories (remove the comment below to include it) 31 | vendor/ 32 | 33 | # Vscode files 34 | .vscode 35 | .history 36 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | # Build the manager binary 3 | FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.23.8-alpine3.21 as builder 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | # cache deps before building and copying source so that we don't need to re-download as much 9 | # and so that source changes don't invalidate our downloaded layer 10 | RUN go mod download 11 | 12 | # Copy the go source 13 | COPY cmd/main.go cmd/main.go 14 | COPY api/ api/ 15 | COPY controllers/ controllers/ 16 | COPY pkg/ pkg/ 17 | COPY version/ version/ 18 | 19 | # Build 20 | ARG TARGETARCH 21 | ARG VERSION 22 | ARG GITVERSION 23 | RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \ 24 | go build -a -ldflags "-s -w -X github.com/kubevela/workflow/version.VelaVersion=${VERSION:-undefined} -X github.com/kubevela/workflow/version.GitRevision=${GITVERSION:-undefined}" \ 25 | -o vela-workflow-${TARGETARCH} cmd/main.go 26 | 27 | FROM ${BASE_IMAGE:-alpine:3.15} 28 | # This is required by daemon connecting with cri 29 | RUN apk add --no-cache ca-certificates bash expat 30 | 31 | WORKDIR / 32 | 33 | ARG TARGETARCH 34 | COPY --from=builder /workspace/vela-workflow-${TARGETARCH} /usr/local/bin/vela-workflow 35 | 36 | ENTRYPOINT ["vela-workflow"] 37 | -------------------------------------------------------------------------------- /Dockerfile.e2e: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | # Build the manager binary 3 | FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.23.8-alpine3.21 as builder 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | # cache deps before building and copying source so that we don't need to re-download as much 9 | # and so that source changes don't invalidate our downloaded layer 10 | RUN go mod download 11 | 12 | # Copy the go source 13 | COPY cmd/main.go main.go 14 | COPY cmd/main_e2e_test.go main_e2e_test.go 15 | COPY api/ api/ 16 | COPY controllers/ controllers/ 17 | COPY pkg/ pkg/ 18 | COPY version/ version/ 19 | 20 | # Build 21 | ARG TARGETARCH 22 | 23 | RUN apk add gcc musl-dev libc-dev ;\ 24 | GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \ 25 | go test -c -o vela-workflow-${TARGETARCH} -cover -covermode=atomic -coverpkg ./... . 26 | 27 | FROM ${BASE_IMAGE:-alpine:3.15} 28 | # This is required by daemon connecting with cri 29 | RUN apk add --no-cache ca-certificates bash expat 30 | 31 | WORKDIR / 32 | 33 | ARG TARGETARCH 34 | COPY --from=builder /workspace/vela-workflow-${TARGETARCH} /usr/local/bin/vela-workflow 35 | 36 | VOLUME ["/workspace/data"] 37 | ENTRYPOINT ["vela-workflow"] 38 | -------------------------------------------------------------------------------- /api/condition/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package condition contains condition types 18 | // +kubebuilder:object:generate=true 19 | package condition 20 | -------------------------------------------------------------------------------- /api/condition/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | 3 | /* 4 | Copyright 2022 The KubeVela Authors. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | // Code generated by controller-gen. DO NOT EDIT. 20 | 21 | package condition 22 | 23 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 24 | func (in *Condition) DeepCopyInto(out *Condition) { 25 | *out = *in 26 | in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) 27 | } 28 | 29 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. 30 | func (in *Condition) DeepCopy() *Condition { 31 | if in == nil { 32 | return nil 33 | } 34 | out := new(Condition) 35 | in.DeepCopyInto(out) 36 | return out 37 | } 38 | 39 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 40 | func (in *ConditionedStatus) DeepCopyInto(out *ConditionedStatus) { 41 | *out = *in 42 | if in.Conditions != nil { 43 | in, out := &in.Conditions, &out.Conditions 44 | *out = make([]Condition, len(*in)) 45 | for i := range *in { 46 | (*in)[i].DeepCopyInto(&(*out)[i]) 47 | } 48 | } 49 | } 50 | 51 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConditionedStatus. 52 | func (in *ConditionedStatus) DeepCopy() *ConditionedStatus { 53 | if in == nil { 54 | return nil 55 | } 56 | out := new(ConditionedStatus) 57 | in.DeepCopyInto(out) 58 | return out 59 | } 60 | -------------------------------------------------------------------------------- /api/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022. The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains resources relating to the Open Application Model. 18 | // See https://github.com/oam-dev/spec for more details. 19 | // +kubebuilder:object:generate=true 20 | // +groupName=core.oam.dev 21 | // +versionName=v1alpha1 22 | package v1alpha1 23 | -------------------------------------------------------------------------------- /api/v1alpha1/event.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | const ( 20 | // ReasonExecute is the reason for executing a workflow 21 | ReasonExecute = "Execute" 22 | // ReasonGenerate is the reason for generating a workflow 23 | ReasonGenerate = "Generate" 24 | ) 25 | 26 | const ( 27 | // MessageSuccessfully is the message for successfully 28 | MessageSuccessfully = "WorkflowRun finished successfully" 29 | // MessageTerminated is the message for terminated 30 | MessageTerminated = "WorkflowRun finished with termination" 31 | // MessageFailed is the message for failed 32 | MessageFailed = "WorkflowRun finished with failure" 33 | // MessageFailedGenerate is the message for failed to generate 34 | MessageFailedGenerate = "fail to generate workflow runners" 35 | // MessageFailedExecute is the message for failed to execute 36 | MessageFailedExecute = "fail to execute" 37 | ) 38 | -------------------------------------------------------------------------------- /api/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/runtime/schema" 21 | "sigs.k8s.io/controller-runtime/pkg/scheme" 22 | ) 23 | 24 | // Package type metadata. 25 | const ( 26 | Group = "core.oam.dev" 27 | Version = "v1alpha1" 28 | ) 29 | 30 | var ( 31 | // SchemeGroupVersion is group version used to register these objects 32 | SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} 33 | 34 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 35 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 36 | 37 | // AddToScheme is a global function that registers this API group & version to a scheme 38 | AddToScheme = SchemeBuilder.AddToScheme 39 | ) 40 | 41 | // Workflow meta 42 | var ( 43 | WorkflowKind = "Workflow" 44 | WorkflowGroupVersionKind = SchemeGroupVersion.WithKind(WorkflowKind) 45 | ) 46 | 47 | // WorkflowRun meta 48 | var ( 49 | WorkflowRunKind = "WorkflowRun" 50 | WorkflowRunGroupVersionKind = SchemeGroupVersion.WithKind(WorkflowRunKind) 51 | ) 52 | 53 | func init() { 54 | SchemeBuilder.Register(&Workflow{}, &WorkflowList{}) 55 | SchemeBuilder.Register(&WorkflowRun{}, &WorkflowRunList{}) 56 | } 57 | -------------------------------------------------------------------------------- /charts/vela-workflow/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/vela-workflow/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: vela-workflow 3 | description: A Helm chart for KubeVela Workflow 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | version: 0.1.0 18 | 19 | # This is the version number of the application being deployed. This version number should be 20 | # incremented each time you make changes to the application. 21 | appVersion: 0.1.0 22 | 23 | home: https://kubevela.io 24 | icon: https://kubevela.io/img/logo.svg 25 | -------------------------------------------------------------------------------- /charts/vela-workflow/crds/cue.oam.dev_packages.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.11.3 7 | creationTimestamp: null 8 | name: packages.cue.oam.dev 9 | spec: 10 | group: cue.oam.dev 11 | names: 12 | kind: Package 13 | listKind: PackageList 14 | plural: packages 15 | shortNames: 16 | - pkg 17 | - cpkg 18 | - cuepkg 19 | - cuepackage 20 | singular: package 21 | scope: Namespaced 22 | versions: 23 | - additionalPrinterColumns: 24 | - jsonPath: .spec.path 25 | name: PATH 26 | type: string 27 | - jsonPath: .spec.provider.protocol 28 | name: PROTO 29 | type: string 30 | - jsonPath: .spec.provider.endpoint 31 | name: ENDPOINT 32 | type: string 33 | name: v1alpha1 34 | schema: 35 | openAPIV3Schema: 36 | description: Package is an extension for cuex engine 37 | properties: 38 | apiVersion: 39 | description: 'APIVersion defines the versioned schema of this representation 40 | of an object. Servers should convert recognized schemas to the latest 41 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 42 | type: string 43 | kind: 44 | description: 'Kind is a string value representing the REST resource this 45 | object represents. Servers may infer this from the endpoint the client 46 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 47 | type: string 48 | metadata: 49 | type: object 50 | spec: 51 | description: PackageSpec the spec for Package 52 | properties: 53 | path: 54 | type: string 55 | provider: 56 | description: Provider the external Provider in Package for cuex to 57 | run functions 58 | properties: 59 | endpoint: 60 | type: string 61 | protocol: 62 | description: ProviderProtocol the protocol type for external Provider 63 | type: string 64 | required: 65 | - endpoint 66 | - protocol 67 | type: object 68 | templates: 69 | additionalProperties: 70 | type: string 71 | type: object 72 | required: 73 | - path 74 | - templates 75 | type: object 76 | required: 77 | - spec 78 | type: object 79 | served: true 80 | storage: true 81 | subresources: {} 82 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "kubevela.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "kubevela.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "kubevela.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "kubevela.labels" -}} 38 | helm.sh/chart: {{ include "kubevela.chart" . }} 39 | {{ include "kubevela.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end -}} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "kubevela.selectorLabels" -}} 50 | app.kubernetes.io/name: {{ include "kubevela.name" . }} 51 | app.kubernetes.io/instance: {{ .Release.Name }} 52 | {{- end -}} 53 | 54 | {{/* 55 | Create the name of the service account to use 56 | */}} 57 | {{- define "kubevela.serviceAccountName" -}} 58 | {{- if .Values.serviceAccount.create -}} 59 | {{ default (include "kubevela.fullname" .) .Values.serviceAccount.name }} 60 | {{- else -}} 61 | {{ default "default" .Values.serviceAccount.name }} 62 | {{- end -}} 63 | {{- end -}} 64 | 65 | {{/* 66 | systemDefinitionNamespace value defaulter 67 | */}} 68 | {{- define "systemDefinitionNamespace" -}} 69 | {{- if .Values.systemDefinitionNamespace -}} 70 | {{ .Values.systemDefinitionNamespace }} 71 | {{- else -}} 72 | {{ .Release.Namespace }} 73 | {{- end -}} 74 | {{- end -}} -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/certmanager.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.admissionWebhooks.certManager.enabled -}} 2 | 3 | # The following manifests contain a self-signed issuer CR and a certificate CR. 4 | # More document can be found at https://docs.cert-manager.io 5 | apiVersion: cert-manager.io/v1 6 | kind: Issuer 7 | metadata: 8 | name: {{ template "kubevela.fullname" . }}-self-signed-issuer 9 | spec: 10 | selfSigned: {} 11 | 12 | --- 13 | # Generate a CA Certificate used to sign certificates for the webhook 14 | apiVersion: cert-manager.io/v1 15 | kind: Certificate 16 | metadata: 17 | name: {{ template "kubevela.fullname" . }}-root-cert 18 | spec: 19 | secretName: {{ template "kubevela.fullname" . }}-root-cert 20 | duration: 43800h # 5y 21 | revisionHistoryLimit: {{ .Values.admissionWebhooks.certManager.revisionHistoryLimit }} 22 | issuerRef: 23 | name: {{ template "kubevela.fullname" . }}-self-signed-issuer 24 | commonName: "ca.webhook.kubevela" 25 | isCA: true 26 | 27 | --- 28 | # Create an Issuer that uses the above generated CA certificate to issue certs 29 | apiVersion: cert-manager.io/v1 30 | kind: Issuer 31 | metadata: 32 | name: {{ template "kubevela.fullname" . }}-root-issuer 33 | namespace: {{ .Release.Namespace }} 34 | spec: 35 | ca: 36 | secretName: {{ template "kubevela.fullname" . }}-root-cert 37 | 38 | --- 39 | # generate a serving certificate for the apiservices to use 40 | apiVersion: cert-manager.io/v1 41 | kind: Certificate 42 | metadata: 43 | name: {{ template "kubevela.fullname" . }}-admission 44 | namespace: {{ .Release.Namespace }} 45 | spec: 46 | secretName: {{ template "kubevela.fullname" . }}-admission 47 | duration: 8760h # 1y 48 | revisionHistoryLimit: {{ .Values.admissionWebhooks.certManager.revisionHistoryLimit }} 49 | issuerRef: 50 | name: {{ template "kubevela.fullname" . }}-root-issuer 51 | dnsNames: 52 | - {{ template "kubevela.name" . }}-webhook.{{ .Release.Namespace }}.svc 53 | - {{ template "kubevela.name" . }}-webhook.{{ .Release.Namespace }}.svc.cluster.local 54 | 55 | {{- end }} 56 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/job-patch/clusterrole.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.admissionWebhooks.enabled .Values.admissionWebhooks.patch.enabled .Values.rbac.create (not .Values.admissionWebhooks.certManager.enabled) }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: {{ template "kubevela.fullname" . }}-admission 6 | annotations: 7 | "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade 8 | "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded 9 | labels: 10 | app: {{ template "kubevela.name" . }}-admission 11 | {{- include "kubevela.labels" . | nindent 4 }} 12 | rules: 13 | - apiGroups: 14 | - admissionregistration.k8s.io 15 | resources: 16 | - validatingwebhookconfigurations 17 | - mutatingwebhookconfigurations 18 | verbs: 19 | - get 20 | - update 21 | - apiGroups: 22 | - apiextensions.k8s.io 23 | resources: 24 | - customresourcedefinitions 25 | verbs: 26 | - get 27 | - update 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/job-patch/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.admissionWebhooks.enabled .Values.admissionWebhooks.patch.enabled .Values.rbac.create (not .Values.admissionWebhooks.certManager.enabled) }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: {{ template "kubevela.fullname" . }}-admission 6 | annotations: 7 | "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade 8 | "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded 9 | labels: 10 | app: {{ template "kubevela.name" . }}-admission 11 | {{- include "kubevela.labels" . | nindent 4 }} 12 | roleRef: 13 | apiGroup: rbac.authorization.k8s.io 14 | kind: ClusterRole 15 | name: {{ template "kubevela.fullname" . }}-admission 16 | subjects: 17 | - kind: ServiceAccount 18 | name: {{ template "kubevela.fullname" . }}-admission 19 | namespace: {{ .Release.Namespace }} 20 | {{- end }} 21 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/job-patch/job-createSecret.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.admissionWebhooks.enabled .Values.admissionWebhooks.patch.enabled (not .Values.admissionWebhooks.certManager.enabled) }} 2 | apiVersion: batch/v1 3 | kind: Job 4 | metadata: 5 | name: {{ template "kubevela.fullname" . }}-admission-create 6 | namespace: {{ .Release.Namespace }} 7 | annotations: 8 | "helm.sh/hook": pre-install,pre-upgrade 9 | "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded 10 | labels: 11 | app: {{ template "kubevela.name" . }}-admission-create 12 | {{- include "kubevela.labels" . | nindent 4 }} 13 | spec: 14 | {{- if .Capabilities.APIVersions.Has "batch/v1alpha1" }} 15 | # Alpha feature since k8s 1.12 16 | ttlSecondsAfterFinished: 0 17 | {{- end }} 18 | template: 19 | metadata: 20 | name: {{ template "kubevela.fullname" . }}-admission-create 21 | labels: 22 | app: {{ template "kubevela.name" . }}-admission-create 23 | {{- include "kubevela.labels" . | nindent 8 }} 24 | spec: 25 | {{- with .Values.imagePullSecrets }} 26 | imagePullSecrets: 27 | {{- toYaml . | nindent 8 }} 28 | {{- end }} 29 | containers: 30 | - name: create 31 | image: {{ .Values.imageRegistry }}{{ .Values.admissionWebhooks.patch.image.repository }}:{{ .Values.admissionWebhooks.patch.image.tag }} 32 | imagePullPolicy: {{ .Values.admissionWebhooks.patch.image.pullPolicy }} 33 | args: 34 | - create 35 | - --host={{ template "kubevela.name" . }}-webhook,{{ template "kubevela.name" . }}-webhook.{{ .Release.Namespace }}.svc 36 | - --namespace={{ .Release.Namespace }} 37 | - --secret-name={{ template "kubevela.fullname" . }}-admission 38 | - --key-name=tls.key 39 | - --cert-name=tls.crt 40 | restartPolicy: OnFailure 41 | serviceAccountName: {{ template "kubevela.fullname" . }}-admission 42 | {{- with .Values.admissionWebhooks.patch.nodeSelector }} 43 | nodeSelector: 44 | {{- toYaml . | nindent 8 }} 45 | {{- end }} 46 | {{- with .Values.admissionWebhooks.patch.affinity }} 47 | affinity: 48 | {{ toYaml . | indent 8 }} 49 | {{- end }} 50 | {{- with .Values.admissionWebhooks.patch.tolerations }} 51 | tolerations: 52 | {{ toYaml . | indent 8 }} 53 | {{- end }} 54 | securityContext: 55 | runAsGroup: 2000 56 | runAsNonRoot: true 57 | runAsUser: 2000 58 | {{- end }} 59 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/job-patch/job-patchWebhook.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.admissionWebhooks.enabled .Values.admissionWebhooks.patch.enabled (not .Values.admissionWebhooks.certManager.enabled) }} 2 | apiVersion: batch/v1 3 | kind: Job 4 | metadata: 5 | name: {{ template "kubevela.fullname" . }}-admission-patch 6 | namespace: {{ .Release.Namespace }} 7 | annotations: 8 | "helm.sh/hook": post-install,post-upgrade 9 | "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded 10 | labels: 11 | app: {{ template "kubevela.name" . }}-admission-patch 12 | {{- include "kubevela.labels" . | nindent 4 }} 13 | spec: 14 | {{- if .Capabilities.APIVersions.Has "batch/v1alpha1" }} 15 | # Alpha feature since k8s 1.12 16 | ttlSecondsAfterFinished: 0 17 | {{- end }} 18 | template: 19 | metadata: 20 | name: {{ template "kubevela.fullname" . }}-admission-patch 21 | labels: 22 | app: {{ template "kubevela.name" . }}-admission-patch 23 | {{- include "kubevela.labels" . | nindent 8 }} 24 | spec: 25 | {{- with .Values.imagePullSecrets }} 26 | imagePullSecrets: 27 | {{- toYaml . | nindent 8 }} 28 | {{- end }} 29 | containers: 30 | - name: patch 31 | image: {{ .Values.imageRegistry }}{{ .Values.admissionWebhooks.patch.image.repository }}:{{ .Values.admissionWebhooks.patch.image.tag }} 32 | imagePullPolicy: {{ .Values.admissionWebhooks.patch.image.pullPolicy }} 33 | args: 34 | - patch 35 | - --webhook-name={{ template "kubevela.fullname" . }}-admission 36 | - --namespace={{ .Release.Namespace }} 37 | - --secret-name={{ template "kubevela.fullname" . }}-admission 38 | - --patch-failure-policy={{ .Values.admissionWebhooks.failurePolicy }} 39 | restartPolicy: OnFailure 40 | serviceAccountName: {{ template "kubevela.fullname" . }}-admission 41 | {{- with .Values.admissionWebhooks.patch.affinity }} 42 | affinity: 43 | {{ toYaml . | indent 8 }} 44 | {{- end }} 45 | {{- with .Values.admissionWebhooks.patch.tolerations }} 46 | tolerations: 47 | {{ toYaml . | indent 8 }} 48 | {{- end }} 49 | securityContext: 50 | runAsGroup: 2000 51 | runAsNonRoot: true 52 | runAsUser: 2000 53 | {{- end }} 54 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/job-patch/role.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.admissionWebhooks.enabled .Values.admissionWebhooks.patch.enabled .Values.rbac.create (not .Values.admissionWebhooks.certManager.enabled) }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: {{ template "kubevela.fullname" . }}-admission 6 | namespace: {{ .Release.Namespace }} 7 | annotations: 8 | "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade 9 | "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded 10 | labels: 11 | app: {{ template "kubevela.name" . }}-admission 12 | {{- include "kubevela.labels" . | nindent 4 }} 13 | rules: 14 | - apiGroups: 15 | - "" 16 | resources: 17 | - secrets 18 | verbs: 19 | - get 20 | - create 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/job-patch/rolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.admissionWebhooks.enabled .Values.admissionWebhooks.patch.enabled .Values.rbac.create (not .Values.admissionWebhooks.certManager.enabled) }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: RoleBinding 4 | metadata: 5 | name: {{ template "kubevela.fullname" . }}-admission 6 | namespace: {{ .Release.Namespace }} 7 | annotations: 8 | "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade 9 | "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded 10 | labels: 11 | app: {{ template "kubevela.name" . }}-admission 12 | {{- include "kubevela.labels" . | nindent 4 }} 13 | roleRef: 14 | apiGroup: rbac.authorization.k8s.io 15 | kind: Role 16 | name: {{ template "kubevela.fullname" . }}-admission 17 | subjects: 18 | - kind: ServiceAccount 19 | name: {{ template "kubevela.fullname" . }}-admission 20 | namespace: {{ .Release.Namespace }} 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/job-patch/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.admissionWebhooks.enabled .Values.admissionWebhooks.patch.enabled .Values.rbac.create (not .Values.admissionWebhooks.certManager.enabled) }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ template "kubevela.fullname" . }}-admission 6 | namespace: {{ .Release.Namespace }} 7 | annotations: 8 | "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade 9 | "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded 10 | labels: 11 | app: {{ template "kubevela.name" . }}-admission 12 | {{- include "kubevela.labels" . | nindent 4 }} 13 | {{- end }} 14 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/mutatingWebhookConfiguration.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.admissionWebhooks.enabled -}} 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: MutatingWebhookConfiguration 4 | metadata: 5 | name: {{ template "kubevela.fullname" . }}-admission 6 | namespace: {{ .Release.Namespace }} 7 | {{- if .Values.admissionWebhooks.certManager.enabled }} 8 | annotations: 9 | cert-manager.io/inject-ca-from: {{ printf "%s/%s-root-cert" .Release.Namespace (include "kubevela.fullname" .) | quote }} 10 | {{- end }} 11 | webhooks: 12 | - clientConfig: 13 | caBundle: Cg== 14 | service: 15 | name: {{ template "kubevela.name" . }}-webhook 16 | namespace: {{ .Release.Namespace }} 17 | path: /mutating-core-oam-dev-v1alpha1-workflowruns 18 | {{- if .Values.admissionWebhooks.patch.enabled }} 19 | failurePolicy: Ignore 20 | {{- else }} 21 | failurePolicy: Fail 22 | {{- end }} 23 | name: mutating.core.oam.dev.v1alpha1.workflowruns 24 | sideEffects: None 25 | rules: 26 | - apiGroups: 27 | - core.oam.dev 28 | apiVersions: 29 | - v1alpha1 30 | operations: 31 | - CREATE 32 | - UPDATE 33 | resources: 34 | - workflowruns 35 | scope: Namespaced 36 | admissionReviewVersions: 37 | - v1beta1 38 | - v1 39 | timeoutSeconds: 5 40 | 41 | {{- end -}} 42 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/validatingWebhookConfiguration.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.admissionWebhooks.enabled -}} 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: ValidatingWebhookConfiguration 4 | metadata: 5 | name: {{ template "kubevela.fullname" . }}-admission 6 | namespace: {{ .Release.Namespace }} 7 | {{- if .Values.admissionWebhooks.certManager.enabled }} 8 | annotations: 9 | cert-manager.io/inject-ca-from: {{ printf "%s/%s-root-cert" .Release.Namespace (include "kubevela.fullname" .) | quote }} 10 | {{- end }} 11 | webhooks: 12 | - clientConfig: 13 | caBundle: Cg== 14 | service: 15 | name: {{ template "kubevela.name" . }}-webhook 16 | namespace: {{ .Release.Namespace }} 17 | path: /validating-core-oam-dev-v1alpha1-workflowruns 18 | {{- if .Values.admissionWebhooks.patch.enabled }} 19 | failurePolicy: Ignore 20 | {{- else }} 21 | failurePolicy: {{ .Values.admissionWebhooks.failurePolicy }} 22 | {{- end }} 23 | name: validating.core.oam.dev.v1alpha2.applicationconfigurations 24 | sideEffects: None 25 | rules: 26 | - apiGroups: 27 | - core.oam.dev 28 | apiVersions: 29 | - v1alpha1 30 | operations: 31 | - CREATE 32 | - UPDATE 33 | resources: 34 | - workflowruns 35 | scope: Namespaced 36 | admissionReviewVersions: 37 | - v1beta1 38 | - v1 39 | timeoutSeconds: 5 40 | {{- end -}} -------------------------------------------------------------------------------- /charts/vela-workflow/templates/admission-webhooks/webhookService.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.admissionWebhooks.enabled -}} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ template "kubevela.name" . }}-webhook 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | {{- include "kubevela.labels" . | nindent 4 }} 9 | spec: 10 | type: {{ .Values.webhookService.type }} 11 | ports: 12 | - port: 443 13 | targetPort: {{ .Values.webhookService.port }} 14 | protocol: TCP 15 | name: https 16 | selector: 17 | {{ include "kubevela.selectorLabels" . | nindent 6 }} 18 | 19 | {{- end -}} 20 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/apply-app.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/apply-app.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/alias: "" 8 | definition.oam.dev/description: Apply application from data or ref to the cluster 9 | definition.oam.dev/example-url: https://raw.githubusercontent.com/kubevela/workflow/main/examples/workflow-run/apply-applications.yaml 10 | labels: 11 | custom.definition.oam.dev/scope: WorkflowRun 12 | name: apply-app 13 | namespace: {{ include "systemDefinitionNamespace" . }} 14 | spec: 15 | schematic: 16 | cue: 17 | template: | 18 | import ( 19 | "vela/op" 20 | "encoding/yaml" 21 | ) 22 | 23 | app: op.#Steps & { 24 | if parameter.data != _|_ { 25 | apply: op.#Apply & { 26 | value: parameter.data 27 | } 28 | } 29 | if parameter.ref != _|_ { 30 | if parameter.ref.type == "configMap" { 31 | cm: op.#Read & { 32 | value: { 33 | apiVersion: "v1" 34 | kind: "ConfigMap" 35 | metadata: { 36 | name: parameter.ref.name 37 | namespace: parameter.ref.namespace 38 | } 39 | } 40 | } 41 | template: cm.value.data[parameter.ref.key] 42 | apply: op.#Apply & { 43 | value: yaml.Unmarshal(template) 44 | } 45 | } 46 | } 47 | } 48 | wait: op.#ConditionalWait & { 49 | continue: app.apply.value.status.status == "running" && app.apply.value.status.observedGeneration == app.apply.value.metadata.generation 50 | } 51 | parameter: close({ 52 | data?: {...} 53 | }) | close({ 54 | ref?: { 55 | name: string 56 | namespace: *context.namespace | string 57 | type: *"configMap" | string 58 | key: *"application" | string 59 | } 60 | }) 61 | 62 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/apply-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/apply-deployment.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/alias: "" 8 | definition.oam.dev/description: Apply deployment with specified image and cmd. 9 | name: apply-deployment 10 | namespace: {{ include "systemDefinitionNamespace" . }} 11 | spec: 12 | schematic: 13 | cue: 14 | template: | 15 | import ( 16 | "strconv" 17 | "strings" 18 | "vela/op" 19 | ) 20 | 21 | output: op.#Apply & { 22 | value: { 23 | apiVersion: "apps/v1" 24 | kind: "Deployment" 25 | metadata: { 26 | name: context.stepName 27 | namespace: context.namespace 28 | } 29 | spec: { 30 | selector: matchLabels: "workflow.oam.dev/step-name": "\(context.name)-\(context.stepName)" 31 | template: { 32 | metadata: labels: "workflow.oam.dev/step-name": "\(context.name)-\(context.stepName)" 33 | spec: containers: [{ 34 | name: context.stepName 35 | image: parameter.image 36 | if parameter["cmd"] != _|_ { 37 | command: parameter.cmd 38 | } 39 | }] 40 | } 41 | } 42 | } 43 | } 44 | wait: op.#ConditionalWait & { 45 | continue: output.value.status != _|_ && output.value.status.updatedReplicas == output.value.status.availableReplicas && output.value.status.observedGeneration == output.value.metadata.generation 46 | } 47 | parameter: { 48 | image: string 49 | cmd?: [...string] 50 | } 51 | 52 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/apply-job.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/apply-job.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | custom.definition.oam.dev/category: Resource Management 8 | definition.oam.dev/description: Apply job 9 | name: apply-job 10 | namespace: {{ include "systemDefinitionNamespace" . }} 11 | spec: 12 | schematic: 13 | cue: 14 | template: | 15 | import ( 16 | "vela/op" 17 | ) 18 | 19 | // apply the job 20 | apply: op.#Apply & { 21 | value: parameter.value 22 | cluster: parameter.cluster 23 | } 24 | 25 | // fail the step if the job fails 26 | if apply.status.failed > 0 { 27 | fail: op.#Fail & { 28 | message: "Job failed" 29 | } 30 | } 31 | 32 | // wait the job to be ready 33 | wait: op.#ConditionalWait & { 34 | continue: apply.status.succeeded == apply.spec.completions 35 | } 36 | 37 | parameter: { 38 | // +usage=Specify Kubernetes job object to be applied 39 | value: { 40 | apiVersion: "batch/v1" 41 | kind: "Job" 42 | ... 43 | } 44 | // +usage=The cluster you want to apply the resource to, default is the current control plane cluster 45 | cluster: *"" | string 46 | } 47 | 48 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/apply-object.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/apply-object.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: Apply raw kubernetes objects for your workflow steps 8 | labels: 9 | custom.definition.oam.dev/ui-hidden: "true" 10 | name: apply-object 11 | namespace: {{ include "systemDefinitionNamespace" . }} 12 | spec: 13 | schematic: 14 | cue: 15 | template: | 16 | import ( 17 | "vela/op" 18 | ) 19 | 20 | apply: op.#Apply & { 21 | value: parameter.value 22 | cluster: parameter.cluster 23 | } 24 | parameter: { 25 | // +usage=Specify Kubernetes native resource object to be applied 26 | value: {...} 27 | // +usage=The cluster you want to apply the resource to, default is the current control plane cluster 28 | cluster: *"" | string 29 | } 30 | 31 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/clean-jobs.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/clean-jobs.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: clean applied jobs in the cluster 8 | name: clean-jobs 9 | namespace: {{ include "systemDefinitionNamespace" . }} 10 | spec: 11 | schematic: 12 | cue: 13 | template: | 14 | import ( 15 | "vela/op" 16 | ) 17 | 18 | parameter: { 19 | labelselector?: {...} 20 | namespace: *context.namespace | string 21 | } 22 | cleanJobs: op.#Delete & { 23 | value: { 24 | apiVersion: "batch/v1" 25 | kind: "Job" 26 | metadata: { 27 | name: context.name 28 | namespace: parameter.namespace 29 | } 30 | } 31 | filter: { 32 | namespace: parameter.namespace 33 | if parameter.labelselector != _|_ { 34 | matchingLabels: parameter.labelselector 35 | } 36 | if parameter.labelselector == _|_ { 37 | matchingLabels: "workflow.oam.dev/name": context.name 38 | } 39 | } 40 | } 41 | cleanPods: op.#Delete & { 42 | value: { 43 | apiVersion: "v1" 44 | kind: "pod" 45 | metadata: { 46 | name: context.name 47 | namespace: parameter.namespace 48 | } 49 | } 50 | filter: { 51 | namespace: parameter.namespace 52 | if parameter.labelselector != _|_ { 53 | matchingLabels: parameter.labelselector 54 | } 55 | if parameter.labelselector == _|_ { 56 | matchingLabels: "workflow.oam.dev/name": context.name 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/create-config.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/create-config.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: Create or update a config 8 | name: create-config 9 | namespace: {{ include "systemDefinitionNamespace" . }} 10 | spec: 11 | schematic: 12 | cue: 13 | template: | 14 | import ( 15 | "vela/op" 16 | ) 17 | 18 | deploy: op.#CreateConfig & { 19 | name: parameter.name 20 | if parameter.namespace != _|_ { 21 | namespace: parameter.namespace 22 | } 23 | if parameter.namespace == _|_ { 24 | namespace: context.namespace 25 | } 26 | if parameter.template != _|_ { 27 | template: parameter.template 28 | } 29 | config: parameter.config 30 | } 31 | parameter: { 32 | //+usage=Specify the name of the config. 33 | name: string 34 | 35 | //+usage=Specify the namespace of the config. 36 | namespace?: string 37 | 38 | //+usage=Specify the template of the config. 39 | template?: string 40 | 41 | //+usage=Specify the content of the config. 42 | config: {...} 43 | } 44 | 45 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/delete-config.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/delete-config.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: Delete a config 8 | name: delete-config 9 | namespace: {{ include "systemDefinitionNamespace" . }} 10 | spec: 11 | schematic: 12 | cue: 13 | template: | 14 | import ( 15 | "vela/op" 16 | ) 17 | 18 | deploy: op.#DeleteConfig & { 19 | name: parameter.name 20 | if parameter.namespace != _|_ { 21 | namespace: parameter.namespace 22 | } 23 | if parameter.namespace == _|_ { 24 | namespace: context.namespace 25 | } 26 | } 27 | parameter: { 28 | //+usage=Specify the name of the config. 29 | name: string 30 | 31 | //+usage=Specify the namespace of the config. 32 | namespace?: string 33 | } 34 | 35 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/export2config.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/export2config.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: Export data to specified Kubernetes ConfigMap in your workflow. 8 | labels: 9 | custom.definition.oam.dev/ui-hidden: "true" 10 | name: export2config 11 | namespace: {{ include "systemDefinitionNamespace" . }} 12 | spec: 13 | schematic: 14 | cue: 15 | template: | 16 | import ( 17 | "vela/op" 18 | ) 19 | 20 | apply: op.#Apply & { 21 | value: { 22 | apiVersion: "v1" 23 | kind: "ConfigMap" 24 | metadata: { 25 | name: parameter.configName 26 | if parameter.namespace != _|_ { 27 | namespace: parameter.namespace 28 | } 29 | if parameter.namespace == _|_ { 30 | namespace: context.namespace 31 | } 32 | } 33 | data: parameter.data 34 | } 35 | cluster: parameter.cluster 36 | } 37 | parameter: { 38 | // +usage=Specify the name of the config map 39 | configName: string 40 | // +usage=Specify the namespace of the config map 41 | namespace?: string 42 | // +usage=Specify the data of config map 43 | data: {} 44 | // +usage=Specify the cluster of the config map 45 | cluster: *"" | string 46 | } 47 | 48 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/export2secret.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/export2secret.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: Export data to Kubernetes Secret in your workflow. 8 | labels: 9 | custom.definition.oam.dev/ui-hidden: "true" 10 | name: export2secret 11 | namespace: {{ include "systemDefinitionNamespace" . }} 12 | spec: 13 | schematic: 14 | cue: 15 | template: | 16 | import ( 17 | "vela/op" 18 | "encoding/base64" 19 | "encoding/json" 20 | ) 21 | 22 | secret: op.#Steps & { 23 | data: *parameter.data | {} 24 | if parameter.kind == "docker-registry" && parameter.dockerRegistry != _|_ { 25 | registryData: auths: "\(parameter.dockerRegistry.server)": { 26 | username: parameter.dockerRegistry.username 27 | password: parameter.dockerRegistry.password 28 | auth: base64.Encode(null, "\(parameter.dockerRegistry.username):\(parameter.dockerRegistry.password)") 29 | } 30 | data: ".dockerconfigjson": json.Marshal(registryData) 31 | } 32 | apply: op.#Apply & { 33 | value: { 34 | apiVersion: "v1" 35 | kind: "Secret" 36 | if parameter.type == _|_ && parameter.kind == "docker-registry" { 37 | type: "kubernetes.io/dockerconfigjson" 38 | } 39 | if parameter.type != _|_ { 40 | type: parameter.type 41 | } 42 | metadata: { 43 | name: parameter.secretName 44 | if parameter.namespace != _|_ { 45 | namespace: parameter.namespace 46 | } 47 | if parameter.namespace == _|_ { 48 | namespace: context.namespace 49 | } 50 | } 51 | stringData: data 52 | } 53 | cluster: parameter.cluster 54 | } 55 | } 56 | parameter: { 57 | // +usage=Specify the name of the secret 58 | secretName: string 59 | // +usage=Specify the namespace of the secret 60 | namespace?: string 61 | // +usage=Specify the type of the secret 62 | type?: string 63 | // +usage=Specify the data of secret 64 | data: {} 65 | // +usage=Specify the cluster of the secret 66 | cluster: *"" | string 67 | // +usage=Specify the kind of the secret 68 | kind: *"generic" | "docker-registry" 69 | // +usage=Specify the docker data 70 | dockerRegistry?: { 71 | // +usage=Specify the username of the docker registry 72 | username: string 73 | // +usage=Specify the password of the docker registry 74 | password: string 75 | // +usage=Specify the server of the docker registry 76 | server: *"https://index.docker.io/v1/" | string 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/list-config.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/list-config.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: List the configs 8 | name: list-config 9 | namespace: {{ include "systemDefinitionNamespace" . }} 10 | spec: 11 | schematic: 12 | cue: 13 | template: | 14 | import ( 15 | "vela/op" 16 | ) 17 | 18 | output: op.#ListConfig & { 19 | if parameter.namespace != _|_ { 20 | namespace: parameter.namespace 21 | } 22 | if parameter.namespace == _|_ { 23 | namespace: context.namespace 24 | } 25 | template: parameter.template 26 | } 27 | parameter: { 28 | //+usage=Specify the template of the config. 29 | template: string 30 | //+usage=Specify the namespace of the config. 31 | namespace?: string 32 | } 33 | 34 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/print-message-in-status.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/print-message-in-status.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: print message in workflow step status 8 | labels: 9 | custom.definition.oam.dev/ui-hidden: "true" 10 | name: print-message-in-status 11 | namespace: {{ include "systemDefinitionNamespace" . }} 12 | spec: 13 | schematic: 14 | cue: 15 | template: | 16 | import ( 17 | "vela/op" 18 | ) 19 | 20 | parameter: message: string 21 | msg: op.#Message & { 22 | message: parameter.message 23 | } 24 | 25 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/read-app.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/read-app.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/alias: "" 8 | definition.oam.dev/description: Read application from the cluster 9 | definition.oam.dev/example-url: https://raw.githubusercontent.com/kubevela/workflow/main/examples/workflow-run/apply-applications.yaml 10 | labels: 11 | custom.definition.oam.dev/scope: WorkflowRun 12 | name: read-app 13 | namespace: {{ include "systemDefinitionNamespace" . }} 14 | spec: 15 | schematic: 16 | cue: 17 | template: | 18 | import ( 19 | "vela/op" 20 | "encoding/yaml" 21 | "strings" 22 | ) 23 | 24 | read: op.#Read & { 25 | value: { 26 | apiVersion: "core.oam.dev/v1beta1" 27 | kind: "Application" 28 | metadata: { 29 | name: parameter.name 30 | namespace: parameter.namespace 31 | } 32 | } 33 | } 34 | message: op.#Steps & { 35 | if read.err != _|_ { 36 | if strings.Contains(read.err, "not found") { 37 | msg: op.#Message & { 38 | message: "Application not found" 39 | } 40 | } 41 | } 42 | } 43 | parameter: { 44 | name: string 45 | namespace: *context.namespace | string 46 | } 47 | 48 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/read-config.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/read-config.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: Read a config 8 | name: read-config 9 | namespace: {{ include "systemDefinitionNamespace" . }} 10 | spec: 11 | schematic: 12 | cue: 13 | template: | 14 | import ( 15 | "vela/op" 16 | ) 17 | 18 | output: op.#ReadConfig & { 19 | name: parameter.name 20 | if parameter.namespace != _|_ { 21 | namespace: parameter.namespace 22 | } 23 | if parameter.namespace == _|_ { 24 | namespace: context.namespace 25 | } 26 | } 27 | parameter: { 28 | //+usage=Specify the name of the config. 29 | name: string 30 | 31 | //+usage=Specify the namespace of the config. 32 | namespace?: string 33 | } 34 | 35 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/read-object.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/read-object.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: Read Kubernetes objects from cluster for your workflow steps 8 | labels: 9 | custom.definition.oam.dev/ui-hidden: "true" 10 | name: read-object 11 | namespace: {{ include "systemDefinitionNamespace" . }} 12 | spec: 13 | schematic: 14 | cue: 15 | template: | 16 | import ( 17 | "vela/op" 18 | ) 19 | 20 | output: { 21 | if parameter.apiVersion == _|_ && parameter.kind == _|_ { 22 | op.#Read & { 23 | value: { 24 | apiVersion: "core.oam.dev/v1beta1" 25 | kind: "Application" 26 | metadata: { 27 | name: parameter.name 28 | if parameter.namespace != _|_ { 29 | namespace: parameter.namespace 30 | } 31 | } 32 | } 33 | cluster: parameter.cluster 34 | } 35 | } 36 | if parameter.apiVersion != _|_ || parameter.kind != _|_ { 37 | op.#Read & { 38 | value: { 39 | apiVersion: parameter.apiVersion 40 | kind: parameter.kind 41 | metadata: { 42 | name: parameter.name 43 | if parameter.namespace != _|_ { 44 | namespace: parameter.namespace 45 | } 46 | } 47 | } 48 | cluster: parameter.cluster 49 | } 50 | } 51 | } 52 | parameter: { 53 | // +usage=Specify the apiVersion of the object, defaults to 'core.oam.dev/v1beta1' 54 | apiVersion?: string 55 | // +usage=Specify the kind of the object, defaults to Application 56 | kind?: string 57 | // +usage=Specify the name of the object 58 | name: string 59 | // +usage=The namespace of the resource you want to read 60 | namespace?: *"default" | string 61 | // +usage=The cluster you want to apply the resource to, default is the current control plane cluster 62 | cluster: *"" | string 63 | } 64 | 65 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/request.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/request.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/alias: "" 8 | definition.oam.dev/description: Send request to the url 9 | definition.oam.dev/example-url: https://raw.githubusercontent.com/kubevela/workflow/main/examples/workflow-run/request.yaml 10 | name: request 11 | namespace: {{ include "systemDefinitionNamespace" . }} 12 | spec: 13 | schematic: 14 | cue: 15 | template: | 16 | import ( 17 | "vela/op" 18 | "encoding/json" 19 | ) 20 | 21 | http: op.#HTTPDo & { 22 | method: parameter.method 23 | url: parameter.url 24 | request: { 25 | if parameter.body != _|_ { 26 | body: json.Marshal(parameter.body) 27 | } 28 | if parameter.header != _|_ { 29 | header: parameter.header 30 | } 31 | } 32 | } 33 | fail: op.#Steps & { 34 | if http.response.statusCode > 400 { 35 | requestFail: op.#Fail & { 36 | message: "request of \(parameter.url) is fail: \(http.response.statusCode)" 37 | } 38 | } 39 | } 40 | response: json.Unmarshal(http.response.body) 41 | parameter: { 42 | url: string 43 | method: *"GET" | "POST" | "PUT" | "DELETE" 44 | body?: {...} 45 | header?: [string]: string 46 | } 47 | 48 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/step-group.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/step-group.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: A special step that you can declare 'subSteps' in it, 'subSteps' is an array containing any step type whose valid parameters do not include the `step-group` step type itself. The sub steps were executed in parallel. 8 | labels: 9 | custom.definition.oam.dev/ui-hidden: "true" 10 | name: step-group 11 | namespace: {{ include "systemDefinitionNamespace" . }} 12 | spec: 13 | schematic: 14 | cue: 15 | template: | 16 | // no parameters, the nop only to make the template not empty or it's invalid 17 | nop: {} 18 | 19 | -------------------------------------------------------------------------------- /charts/vela-workflow/templates/definitions/suspend.yaml: -------------------------------------------------------------------------------- 1 | # Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file. 2 | # Definition source cue file: vela-templates/definitions/internal/suspend.cue 3 | apiVersion: core.oam.dev/v1beta1 4 | kind: WorkflowStepDefinition 5 | metadata: 6 | annotations: 7 | definition.oam.dev/description: Suspend the current workflow, it can be resumed by 'vela workflow resume' command. 8 | name: suspend 9 | namespace: {{ include "systemDefinitionNamespace" . }} 10 | spec: 11 | schematic: 12 | cue: 13 | template: | 14 | parameter: { 15 | // +usage=Specify the wait duration time to resume workflow such as "30s", "1min" or "2m15s" 16 | duration?: string 17 | } 18 | 19 | -------------------------------------------------------------------------------- /cmd/main_e2e_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "os/signal" 23 | "strings" 24 | "syscall" 25 | "testing" 26 | ) 27 | 28 | func TestE2EMain(t *testing.T) { 29 | fmt.Println("this is e2e test") 30 | var ( 31 | args []string 32 | run bool 33 | ) 34 | 35 | for _, arg := range os.Args { 36 | switch { 37 | case strings.HasPrefix(arg, "__DEVEL__E2E"): 38 | run = true 39 | case strings.HasPrefix(arg, "-test"): 40 | default: 41 | args = append(args, arg) 42 | } 43 | } 44 | 45 | if !run { 46 | return 47 | } 48 | 49 | waitCh := make(chan int, 1) 50 | 51 | //args=append(args, "leader-election-namespace='someNS'") 52 | os.Args = args 53 | go func() { 54 | main() 55 | close(waitCh) 56 | }() 57 | 58 | signalCh := make(chan os.Signal, 1) 59 | signal.Notify(signalCh, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGHUP) 60 | select { 61 | case <-signalCh: 62 | case <-waitCh: 63 | } 64 | fmt.Println("exit test e2e main") 65 | } 66 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | threshold: 0.1% 6 | patch: 7 | default: 8 | target: 70% 9 | ignore: 10 | - "**/zz_generated.deepcopy.go" 11 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/core.oam.dev_workflows.yaml 6 | - bases/core.oam.dev_workflowruns.yaml 7 | #+kubebuilder:scaffold:crdkustomizeresource 8 | 9 | patchesStrategicMerge: 10 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 11 | # patches here are for enabling the conversion webhook for each CRD 12 | #- patches/webhook_in_workflows.yaml 13 | #+kubebuilder:scaffold:crdkustomizewebhookpatch 14 | 15 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 16 | # patches here are for enabling the CA injection for each CRD 17 | #- patches/cainjection_in_workflows.yaml 18 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch 19 | 20 | # the following config is for teaching kustomize how to do kustomization for CRDs. 21 | configurations: 22 | - kustomizeconfig.yaml 23 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /controllers/testdata/apply-object.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: WorkflowStepDefinition 3 | metadata: 4 | name: apply-object 5 | namespace: vela-system 6 | spec: 7 | schematic: 8 | cue: 9 | template: | 10 | import ( 11 | "vela/kube" 12 | ) 13 | 14 | apply: kube.#Apply & { 15 | $params: { 16 | value: parameter.value 17 | cluster: parameter.cluster 18 | } 19 | } 20 | parameter: { 21 | // +usage=Specify Kubernetes native resource object to be applied 22 | value: {...} 23 | // +usage=The cluster you want to apply the resource to, default is the current control plane cluster 24 | cluster: *"" | string 25 | } 26 | -------------------------------------------------------------------------------- /controllers/testdata/failed-render.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: WorkflowStepDefinition 3 | metadata: 4 | name: failed-render 5 | namespace: vela-system 6 | spec: 7 | schematic: 8 | cue: 9 | template: ":" -------------------------------------------------------------------------------- /controllers/testdata/multi-suspend.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: core.oam.dev/v1beta1 3 | kind: WorkflowStepDefinition 4 | metadata: 5 | name: multi-suspend 6 | namespace: vela-system 7 | spec: 8 | schematic: 9 | cue: 10 | template: | 11 | import ( 12 | "vela/builtin" 13 | ) 14 | suspend1: builtin.#Suspend & {} 15 | suspend2: builtin.#Suspend & {} 16 | -------------------------------------------------------------------------------- /controllers/testdata/save-process-context.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: WorkflowStepDefinition 3 | metadata: 4 | name: save-process-context 5 | namespace: vela-system 6 | spec: 7 | schematic: 8 | cue: 9 | template: | 10 | import "vela/op" 11 | 12 | cm: op.#Apply & { 13 | value: { 14 | apiVersion: "v1" 15 | kind: "ConfigMap" 16 | metadata: { 17 | name: parameter.name 18 | labels: { 19 | "process.context.data": "true" 20 | } 21 | } 22 | data: context 23 | } 24 | } 25 | 26 | parameter: name: string -------------------------------------------------------------------------------- /controllers/testdata/suspend-and-deploy.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: core.oam.dev/v1beta1 3 | kind: WorkflowStepDefinition 4 | metadata: 5 | name: suspend-and-deploy 6 | namespace: vela-system 7 | spec: 8 | schematic: 9 | cue: 10 | template: | 11 | import ( 12 | "vela/kube" 13 | "vela/builtin" 14 | ) 15 | 16 | suspend: builtin.#Suspend & {$params: duration: "1s"} 17 | output: kube.#Apply & { 18 | $params: { 19 | cluster: parameter.cluster 20 | value: { 21 | apiVersion: "apps/v1" 22 | kind: "Deployment" 23 | metadata: { 24 | name: context.stepName 25 | namespace: context.namespace 26 | } 27 | spec: { 28 | selector: matchLabels: "workflow.oam.dev/step-name": "\(context.name)-\(context.stepName)" 29 | replicas: parameter.replicas 30 | template: { 31 | metadata: labels: "workflow.oam.dev/step-name": "\(context.name)-\(context.stepName)" 32 | spec: containers: [{ 33 | name: context.stepName 34 | image: parameter.image 35 | if parameter["cmd"] != _|_ { 36 | command: parameter.cmd 37 | } 38 | }] 39 | } 40 | } 41 | } 42 | } 43 | } 44 | wait: builtin.#ConditionalWait & { 45 | $params: continue: output.$returns.value.status.readyReplicas == parameter.replicas 46 | } 47 | parameter: { 48 | image: string 49 | replicas: *1 | int 50 | cluster: *"" | string 51 | cmd?: [...string] 52 | } 53 | -------------------------------------------------------------------------------- /controllers/testdata/test-apply.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: WorkflowStepDefinition 3 | metadata: 4 | name: test-apply 5 | namespace: vela-system 6 | spec: 7 | schematic: 8 | cue: 9 | template: | 10 | import ( 11 | "vela/kube" 12 | "vela/builtin" 13 | ) 14 | 15 | output: kube.#Apply & { 16 | $params: value: { 17 | apiVersion: "apps/v1" 18 | kind: "Deployment" 19 | metadata: { 20 | name: context.stepName 21 | namespace: context.namespace 22 | } 23 | spec: { 24 | selector: matchLabels: wr: context.stepName 25 | template: { 26 | metadata: labels: wr: context.stepName 27 | spec: containers: [{ 28 | name: context.stepName 29 | image: parameter.image 30 | if parameter["cmd"] != _|_ { 31 | command: parameter.cmd 32 | } 33 | if parameter["message"] != _|_ { 34 | env: [{ 35 | name: "MESSAGE" 36 | value: parameter.message 37 | }] 38 | } 39 | }] 40 | } 41 | } 42 | } 43 | } 44 | wait: builtin.#ConditionalWait & { 45 | if len(output.$returns.value.status) > 0 if output.$returns.value.status.readyReplicas == 1 { 46 | $params: continue: true 47 | } 48 | } 49 | parameter: { 50 | image: string 51 | cmd?: [...string] 52 | message?: string 53 | } 54 | 55 | -------------------------------------------------------------------------------- /e2e/e2e_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package e2e 18 | 19 | import ( 20 | "context" 21 | "os" 22 | "path/filepath" 23 | "strings" 24 | "testing" 25 | 26 | . "github.com/onsi/ginkgo" 27 | . "github.com/onsi/gomega" 28 | "k8s.io/apimachinery/pkg/runtime" 29 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 30 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 31 | "sigs.k8s.io/controller-runtime/pkg/client" 32 | "sigs.k8s.io/controller-runtime/pkg/client/config" 33 | 34 | "github.com/kubevela/pkg/util/singleton" 35 | "github.com/kubevela/pkg/util/test/definition" 36 | 37 | "github.com/kubevela/workflow/api/v1alpha1" 38 | "github.com/kubevela/workflow/pkg/utils" 39 | ) 40 | 41 | var ( 42 | scheme = runtime.NewScheme() 43 | ) 44 | 45 | func init() { 46 | utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 47 | 48 | utilruntime.Must(v1alpha1.AddToScheme(scheme)) 49 | //+kubebuilder:scaffold:scheme 50 | } 51 | 52 | func TestE2e(t *testing.T) { 53 | RegisterFailHandler(Fail) 54 | RunSpecs(t, "E2e Suite") 55 | } 56 | 57 | var k8sClient client.Client 58 | 59 | var _ = BeforeSuite(func() { 60 | conf, err := config.GetConfig() 61 | Expect(err).Should(BeNil()) 62 | singleton.KubeConfig.Set(conf) 63 | 64 | k8sClient, err = client.New(conf, client.Options{Scheme: scheme}) 65 | Expect(err).Should(BeNil()) 66 | singleton.KubeClient.Set(k8sClient) 67 | 68 | prepareWorkflowDefinitions() 69 | }) 70 | 71 | func prepareWorkflowDefinitions() { 72 | var files []string 73 | err := filepath.Walk("./test-data/definitions/", func(path string, info os.FileInfo, err error) error { 74 | if strings.HasSuffix(path, "yaml") { 75 | files = append(files, path) 76 | } 77 | return nil 78 | }) 79 | Expect(err).Should(BeNil()) 80 | for _, f := range files { 81 | Expect(definition.InstallDefinitionFromYAML(context.TODO(), k8sClient, f, nil)).Should(SatisfyAny(BeNil(), &utils.AlreadyExistMatcher{})) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /e2e/test-data/config-workflow-run.yaml: -------------------------------------------------------------------------------- 1 | kind: WorkflowRun 2 | apiVersion: core.oam.dev/v1alpha1 3 | metadata: 4 | name: test-config 5 | namespace: "config-e2e-test" 6 | spec: 7 | workflowSpec: 8 | steps: 9 | - name: write-config 10 | type: create-config 11 | properties: 12 | name: test 13 | config: 14 | key1: value1 15 | key2: 2 16 | key3: true 17 | key4: 18 | key5: value5 19 | - name: read-config 20 | type: read-config 21 | properties: 22 | name: test 23 | outputs: 24 | - fromKey: config 25 | name: read-config 26 | - name: delete-config 27 | type: delete-config 28 | properties: 29 | name: test -------------------------------------------------------------------------------- /e2e/test-data/definitions/create-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: WorkflowStepDefinition 3 | metadata: 4 | annotations: 5 | definition.oam.dev/description: Create or update a config 6 | name: create-config 7 | namespace: vela-system 8 | spec: 9 | schematic: 10 | cue: 11 | template: | 12 | import ( 13 | "vela/op" 14 | ) 15 | deploy: op.#CreateConfig & { 16 | name: parameter.name 17 | if parameter.namespace != _|_ { 18 | namespace: parameter.namespace 19 | } 20 | if parameter.namespace == _|_ { 21 | namespace: context.namespace 22 | } 23 | if parameter.template != _|_ { 24 | template: parameter.template 25 | } 26 | config: parameter.config 27 | } 28 | parameter: { 29 | //+usage=Specify the name of the config. 30 | name: string 31 | //+usage=Specify the namespace of the config. 32 | namespace?: string 33 | //+usage=Specify the template of the config. 34 | template?: string 35 | //+usage=Specify the content of the config. 36 | config: {...} 37 | } -------------------------------------------------------------------------------- /e2e/test-data/definitions/delete-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: WorkflowStepDefinition 3 | metadata: 4 | annotations: 5 | definition.oam.dev/description: Delete a config 6 | name: delete-config 7 | namespace: vela-system 8 | spec: 9 | schematic: 10 | cue: 11 | template: | 12 | import ( 13 | "vela/op" 14 | ) 15 | deploy: op.#DeleteConfig & { 16 | name: parameter.name 17 | if parameter.namespace != _|_ { 18 | namespace: parameter.namespace 19 | } 20 | if parameter.namespace == _|_ { 21 | namespace: context.namespace 22 | } 23 | } 24 | parameter: { 25 | //+usage=Specify the name of the config. 26 | name: string 27 | //+usage=Specify the namespace of the config. 28 | namespace?: string 29 | } -------------------------------------------------------------------------------- /e2e/test-data/definitions/list-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: WorkflowStepDefinition 3 | metadata: 4 | annotations: 5 | definition.oam.dev/description: List the configs 6 | name: list-config 7 | namespace: vela-system 8 | spec: 9 | schematic: 10 | cue: 11 | template: | 12 | import ( 13 | "vela/op" 14 | ) 15 | output: op.#ListConfig & { 16 | if parameter.namespace != _|_ { 17 | namespace: parameter.namespace 18 | } 19 | if parameter.namespace == _|_ { 20 | namespace: context.namespace 21 | } 22 | template: parameter.template 23 | } 24 | parameter: { 25 | //+usage=Specify the template of the config. 26 | template: string 27 | //+usage=Specify the namespace of the config. 28 | namespace?: string 29 | } -------------------------------------------------------------------------------- /e2e/test-data/definitions/read-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: WorkflowStepDefinition 3 | metadata: 4 | annotations: 5 | definition.oam.dev/description: Read a config 6 | name: read-config 7 | namespace: vela-system 8 | spec: 9 | schematic: 10 | cue: 11 | template: | 12 | import ( 13 | "vela/op" 14 | ) 15 | output: op.#ReadConfig & { 16 | name: parameter.name 17 | if parameter.namespace != _|_ { 18 | namespace: parameter.namespace 19 | } 20 | if parameter.namespace == _|_ { 21 | namespace: context.namespace 22 | } 23 | } 24 | parameter: { 25 | //+usage=Specify the name of the config. 26 | name: string 27 | //+usage=Specify the namespace of the config. 28 | namespace?: string 29 | } 30 | -------------------------------------------------------------------------------- /e2e/test-data/definitions/write-message.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: WorkflowStepDefinition 3 | metadata: 4 | annotations: 5 | definition.oam.dev/alias: "" 6 | name: message 7 | namespace: vela-system 8 | spec: 9 | schematic: 10 | cue: 11 | template: | 12 | import ( 13 | "vela/op" 14 | ) 15 | 16 | msg: op.#Message & { 17 | parameter 18 | } 19 | parameter: message: string -------------------------------------------------------------------------------- /e2e/test-data/message-workflow-run.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1alpha1 2 | kind: WorkflowRun 3 | metadata: 4 | name: message 5 | namespace: default 6 | spec: 7 | workflowSpec: 8 | steps: 9 | - name: step1 10 | type: message 11 | properties: 12 | message: "hello" -------------------------------------------------------------------------------- /examples/multiple-apps.md: -------------------------------------------------------------------------------- 1 | # Control the delivery process of multiple applications 2 | 3 | Apply the following workflow to control the delivery process of multiple applications: 4 | 5 | ```yaml 6 | apiVersion: core.oam.dev/v1alpha1 7 | kind: WorkflowRun 8 | metadata: 9 | name: apply-applications 10 | namespace: default 11 | annotations: 12 | workflowrun.oam.dev/debug: "true" 13 | spec: 14 | workflowSpec: 15 | steps: 16 | - name: check-app-exist 17 | type: read-app 18 | properties: 19 | name: webservice-app 20 | - name: apply-app1 21 | type: apply-app 22 | if: status["check-app-exist"].message == "Application not found" 23 | properties: 24 | data: 25 | apiVersion: core.oam.dev/v1beta1 26 | kind: Application 27 | metadata: 28 | name: webservice-app 29 | spec: 30 | components: 31 | - name: express-server 32 | type: webservice 33 | properties: 34 | image: crccheck/hello-world 35 | ports: 36 | - port: 8000 37 | - name: suspend 38 | type: suspend 39 | timeout: 24h 40 | - name: apply-app2 41 | type: apply-app 42 | properties: 43 | ref: 44 | name: my-app 45 | key: application 46 | type: configMap 47 | --- 48 | apiVersion: v1 49 | kind: ConfigMap 50 | metadata: 51 | name: my-app 52 | namespace: default 53 | data: 54 | application: | 55 | apiVersion: core.oam.dev/v1beta1 56 | kind: Application 57 | metadata: 58 | name: webservice-app2 59 | spec: 60 | components: 61 | - name: express-server2 62 | type: webservice 63 | properties: 64 | image: crccheck/hello-world 65 | ports: 66 | - port: 8000 67 | ``` 68 | 69 | Above workflow will first try to read the Application called `webservice-app` from the cluster, if the Application is not found, this step's status message will be `Application not found`. The second step will deploy the `webservice-app` Application if `webservice-app` is not exist in the cluster. After that, the `suspend` step will suspend the delivery process for manual approve. 70 | 71 | ``` 72 | $ kubectl get wr 73 | NAME PHASE AGE 74 | apply-applications suspending 2s 75 | ``` 76 | 77 | You can use `vela workflow resume` to resume this workflow, note that if the workflow has not been resumed in 24 hours, the workflow will failed of timeout: 78 | 79 | ``` 80 | vela workflow resume apply-applications 81 | ``` 82 | 83 | After the workflow is resumed, it will deploy an another Application from the data in ConfigMap with key `application`. 84 | -------------------------------------------------------------------------------- /examples/request-and-notify.md: -------------------------------------------------------------------------------- 1 | # Use Workflow for request and notify 2 | 3 | Apply the following workflow for request a specified URL first and then use the response as a message to your slack channel. 4 | 5 | ```yaml 6 | apiVersion: core.oam.dev/v1alpha1 7 | kind: WorkflowRun 8 | metadata: 9 | name: request-http 10 | namespace: default 11 | spec: 12 | workflowSpec: 13 | steps: 14 | - name: request 15 | type: request 16 | properties: 17 | url: https://api.github.com/repos/kubevela/workflow 18 | outputs: 19 | - name: stars 20 | valueFrom: | 21 | import "strconv" 22 | "Current star count: " + strconv.FormatInt(response["stargazers_count"], 10) 23 | - name: notification 24 | type: notification 25 | inputs: 26 | - from: stars 27 | parameterKey: slack.message.text 28 | properties: 29 | slack: 30 | url: 31 | value: 32 | - name: failed-notification 33 | type: notification 34 | if: status.request.failed 35 | properties: 36 | slack: 37 | url: 38 | value: 39 | message: 40 | text: "Failed to request github" 41 | 42 | ``` 43 | 44 | Above workflow will send a request to the GitHub API and get the star count of the workflow repository as an output, then use the output as a message to send a notification to your Slack channel. 45 | 46 | Apply the WorkflowRun, you can get a message from Slack like: 47 | 48 | ![slack-success](./static/slack-success.png) 49 | 50 | If you change the `url` to an invalid one, you will get a failed notification: 51 | 52 | ![slack-failed](./static/slack-fail.png) -------------------------------------------------------------------------------- /examples/run-with-template.md: -------------------------------------------------------------------------------- 1 | # Run your workflow with template 2 | 3 | You can also create a Workflow Template and run it with a WorkflowRun with different context. 4 | 5 | Apply the following Workflow Template first: 6 | 7 | ```yaml 8 | apiVersion: core.oam.dev/v1alpha1 9 | kind: Workflow 10 | metadata: 11 | name: deploy-template 12 | namespace: default 13 | steps: 14 | - name: apply 15 | type: apply-deployment 16 | if: context.env == "dev" 17 | properties: 18 | image: nginx 19 | - name: apply-test 20 | type: apply-deployment 21 | if: context.env == "test" 22 | properties: 23 | image: crccheck/hello-world 24 | ``` 25 | 26 | Apply the following WorkflowRun: 27 | 28 | ```yaml 29 | apiVersion: core.oam.dev/v1alpha1 30 | kind: WorkflowRun 31 | metadata: 32 | name: deploy-run 33 | namespace: default 34 | spec: 35 | context: 36 | env: dev 37 | workflowRef: deploy-template 38 | ``` 39 | 40 | If you apply the WorkflowRun with `dev` in `context.env`, then you'll get a deployment with `nginx` image. If you change the `env` to `test`, you'll get a deployment with `crccheck/hello-world` image. -------------------------------------------------------------------------------- /examples/static/slack-fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubevela/workflow/d7db9c4ef415d71ee3906b427c9cab4695242b2c/examples/static/slack-fail.png -------------------------------------------------------------------------------- /examples/static/slack-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubevela/workflow/d7db9c4ef415d71ee3906b427c9cab4695242b2c/examples/static/slack-success.png -------------------------------------------------------------------------------- /examples/workflow-run/apply-applications.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1alpha1 2 | kind: WorkflowRun 3 | metadata: 4 | name: apply-applications 5 | namespace: default 6 | annotations: 7 | workflowrun.oam.dev/debug: "true" 8 | spec: 9 | workflowSpec: 10 | steps: 11 | - name: check-app-exist 12 | type: read-app 13 | properties: 14 | name: webservice-app 15 | - name: apply-app1 16 | type: apply-app 17 | if: status["check-app-exist"].message == "Application not found" 18 | properties: 19 | data: 20 | apiVersion: core.oam.dev/v1beta1 21 | kind: Application 22 | metadata: 23 | name: webservice-app 24 | spec: 25 | components: 26 | - name: express-server 27 | type: webservice 28 | properties: 29 | image: crccheck/hello-world 30 | ports: 31 | - port: 8000 32 | - name: suspend 33 | type: suspend 34 | timeout: 24h 35 | - name: apply-app2 36 | type: apply-app 37 | properties: 38 | ref: 39 | name: my-app 40 | key: application 41 | type: configMap 42 | --- 43 | apiVersion: v1 44 | kind: ConfigMap 45 | metadata: 46 | name: my-app 47 | namespace: default 48 | data: 49 | application: | 50 | apiVersion: core.oam.dev/v1beta1 51 | kind: Application 52 | metadata: 53 | name: webservice-app2 54 | spec: 55 | components: 56 | - name: express-server2 57 | type: webservice 58 | properties: 59 | image: crccheck/hello-world 60 | ports: 61 | - port: 8000 62 | -------------------------------------------------------------------------------- /examples/workflow-run/apply-terraform-resource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1alpha1 2 | kind: WorkflowRun 3 | metadata: 4 | name: apply-terraform-resource 5 | namespace: default 6 | spec: 7 | workflowSpec: 8 | steps: 9 | - name: provider 10 | type: apply-terraform-provider 11 | properties: 12 | type: alibaba 13 | name: my-alibaba-provider 14 | accessKey: 15 | secretKey: 16 | region: cn-hangzhou 17 | - name: configuration 18 | type: apply-terraform-config 19 | properties: 20 | source: 21 | path: alibaba/cs/dedicated-kubernetes 22 | remote: https://github.com/FogDong/terraform-modules 23 | providerRef: 24 | name: my-alibaba-provider 25 | writeConnectionSecretToRef: 26 | name: my-terraform-secret 27 | namespace: vela-system 28 | variable: 29 | name: regular-check-ack 30 | new_nat_gateway: true 31 | vpc_name: "tf-k8s-vpc-regular-check" 32 | vpc_cidr: "10.0.0.0/8" 33 | vswitch_name_prefix: "tf-k8s-vsw-regualr-check" 34 | vswitch_cidrs: [ "10.1.0.0/16", "10.2.0.0/16", "10.3.0.0/16" ] 35 | k8s_name_prefix: "tf-k8s-regular-check" 36 | k8s_version: 1.24.6-aliyun.1 37 | k8s_pod_cidr: "192.168.5.0/24" 38 | k8s_service_cidr: "192.168.2.0/24" 39 | k8s_worker_number: 2 40 | cpu_core_count: 4 41 | memory_size: 8 42 | tags: 43 | created_by: "Terraform-of-KubeVela" 44 | created_from: "module-tf-alicloud-ecs-instance" 45 | - name: add-cluster 46 | type: vela-cli 47 | if: always 48 | properties: 49 | storage: 50 | secret: 51 | - name: secret-mount 52 | secretName: my-terraform-secret 53 | mountPath: /kubeconfig/ack 54 | command: 55 | - vela 56 | - cluster 57 | - join 58 | - /kubeconfig/ack/KUBECONFIG 59 | - --name=ack 60 | - name: clean-cli-jobs 61 | type: clean-jobs 62 | properties: 63 | namespace: vela-system 64 | labelSelector: 65 | "workflow.oam.dev/step-name": apply-terraform-resource-add-cluster 66 | - name: distribute-config 67 | type: apply-object 68 | properties: 69 | cluster: ack 70 | value: 71 | apiVersion: v1 72 | kind: ConfigMap 73 | metadata: 74 | name: my-cm 75 | namespace: default 76 | data: 77 | test-key: test-value -------------------------------------------------------------------------------- /examples/workflow-run/build-push-image.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1alpha1 2 | kind: WorkflowRun 3 | metadata: 4 | name: build-push-image 5 | namespace: default 6 | spec: 7 | workflowSpec: 8 | steps: 9 | # or use kubectl create secret generic git-token --from-literal='GIT_TOKEN=' 10 | - name: create-git-secret 11 | type: export2secret 12 | properties: 13 | secretName: git-secret 14 | data: 15 | token: 16 | # or use kubectl create secret docker-registry docker-regcred \ 17 | # --docker-server=https://index.docker.io/v1/ \ 18 | # --docker-username= \ 19 | # --docker-password= 20 | - name: create-image-secret 21 | type: export2secret 22 | properties: 23 | secretName: image-secret 24 | kind: docker-registry 25 | dockerRegistry: 26 | username: 27 | password: 28 | - name: build-push 29 | type: build-push-image 30 | properties: 31 | # use your kaniko executor image like below, if not set, it will use default image oamdev/kaniko-executor:v1.9.1 32 | # kanikoExecutor: gcr.io/kaniko-project/executor:latest 33 | # you can use context with git and branch or directly specify the context, please refer to https://github.com/GoogleContainerTools/kaniko#kaniko-build-contexts 34 | context: 35 | git: github.com/FogDong/simple-web-demo 36 | branch: main 37 | image: fogdong/simple-web-demo:v1 38 | # specify your dockerfile, if not set, it will use default dockerfile ./Dockerfile 39 | # dockerfile: ./Dockerfile 40 | credentials: 41 | image: 42 | name: image-secret 43 | # buildArgs: 44 | # - key="value" 45 | # platform: linux/arm 46 | - name: apply-app 47 | type: apply-app 48 | properties: 49 | data: 50 | apiVersion: core.oam.dev/v1beta1 51 | kind: Application 52 | metadata: 53 | name: my-app 54 | spec: 55 | components: 56 | - name: my-web 57 | type: webservice 58 | properties: 59 | image: fogdong/simple-web-demo:v1 60 | ports: 61 | - port: 80 62 | expose: true -------------------------------------------------------------------------------- /examples/workflow-run/chat-gpt.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1alpha1 2 | kind: WorkflowRun 3 | metadata: 4 | name: chat-gpt 5 | namespace: default 6 | spec: 7 | workflowSpec: 8 | steps: 9 | # apply a deployment with invalid image, this step will fail because of timeout 10 | # the resource will be passed to chat-gpt step to anaylze 11 | - name: apply 12 | type: apply-deployment 13 | timeout: 3s 14 | outputs: 15 | - name: resource 16 | valueFrom: output.value 17 | properties: 18 | image: invalid 19 | 20 | # if apply step failed, send the resource to chat-gpt to diagnose 21 | - name: chat-diagnose 22 | if: status.apply.failed 23 | type: chat-gpt 24 | inputs: 25 | - from: resource 26 | parameterKey: prompt.content 27 | properties: 28 | token: 29 | # specify your token 30 | value: 31 | prompt: 32 | type: diagnose 33 | 34 | # if apply step succeeded, send the resource to chat-gpt to audit 35 | - name: chat-audit 36 | if: status.apply.succeeded 37 | type: chat-gpt 38 | inputs: 39 | - from: resource 40 | parameterKey: prompt.content 41 | properties: 42 | token: 43 | # or read your token from secret 44 | secretRef: 45 | name: chat-gpt-token-secret 46 | key: token 47 | prompt: 48 | type: audit 49 | lang: Chinese -------------------------------------------------------------------------------- /examples/workflow-run/custom-context.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1alpha1 2 | kind: WorkflowRun 3 | metadata: 4 | name: custom-context 5 | namespace: default 6 | spec: 7 | context: 8 | env: dev 9 | name: override 10 | workflowSpec: 11 | steps: 12 | - name: apply 13 | type: apply-deployment 14 | if: context.env == "dev" 15 | properties: 16 | image: nginx -------------------------------------------------------------------------------- /examples/workflow-run/deploy-workflowrun.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1alpha1 2 | kind: WorkflowRun 3 | metadata: 4 | name: deploy-run 5 | namespace: default 6 | spec: 7 | context: 8 | env: test 9 | workflowRef: deploy-template -------------------------------------------------------------------------------- /examples/workflow-run/request.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1alpha1 2 | kind: WorkflowRun 3 | metadata: 4 | name: request-http 5 | namespace: default 6 | spec: 7 | workflowSpec: 8 | steps: 9 | - name: request 10 | type: request 11 | properties: 12 | url: https://api.github.com/repos/kubevela/notfound 13 | outputs: 14 | - name: stars 15 | valueFrom: | 16 | import "strconv" 17 | "Current star count: " + strconv.FormatInt(response["stargazers_count"], 10) 18 | - name: notification 19 | type: notification 20 | inputs: 21 | - from: stars 22 | parameterKey: slack.message.text 23 | properties: 24 | slack: 25 | url: 26 | value: 27 | - name: failed-notification 28 | type: notification 29 | if: status.request.failed 30 | properties: 31 | slack: 32 | url: 33 | value: 34 | message: 35 | text: "Failed to request github" 36 | -------------------------------------------------------------------------------- /examples/workflow-template/deploy-workflow.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1alpha1 2 | kind: Workflow 3 | metadata: 4 | name: deploy-template 5 | namespace: default 6 | steps: 7 | - name: apply 8 | type: apply-deployment 9 | if: context.env == "dev" 10 | properties: 11 | image: nginx 12 | - name: apply-test 13 | type: apply-deployment 14 | if: context.env == "test" 15 | properties: 16 | image: crccheck/hello-world -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /hack/e2e/end_e2e.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONTAINER_ID=$(docker exec k3d-k3s-default-server-0 crictl ps | grep vela-workflow | grep --regexp '^.............' -o) 4 | DOCKER_DIR=$(docker exec k3d-k3s-default-server-0 crictl inspect --output go-template --template '{{range .info.runtimeSpec.mounts}}{{if (eq .destination "/workspace/data")}}{{.source}}{{end}}{{end}}' "${CONTAINER_ID}") 5 | echo "${CONTAINER_ID}" 6 | echo "${DOCKER_DIR}" 7 | 8 | docker exec k3d-k3s-default-server-0 crictl exec "${CONTAINER_ID}" kill -2 1 9 | 10 | file=$DOCKER_DIR/e2e-profile.out 11 | echo "$file" 12 | n=1 13 | while [ $n -le 60 ];do 14 | if_exist=$(docker exec k3d-k3s-default-server-0 sh -c "test -f $file && echo 'ok'") 15 | echo "$if_exist" 16 | if [ -n "$if_exist" ];then 17 | docker exec k3d-k3s-default-server-0 cat "$file" > /tmp/e2e-profile.out 18 | break 19 | fi 20 | echo file not generated yet 21 | n="$(expr $n + 1)" 22 | sleep 1 23 | done -------------------------------------------------------------------------------- /hack/e2e/modify_charts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | { 3 | echo ' - "-test.coverprofile=/workspace/data/e2e-profile.out"' 4 | echo ' - "__DEVEL__E2E"' 5 | echo ' - "-test.run=E2EMain"' 6 | echo ' - "-test.coverpkg=$(go list ./pkg/...| tr '"'"'\n'"'"' '"'"','"'"'| sed '"'"'s/,$//g'"'"')"' 7 | } > tmp_add.txt 8 | sed '/ args:/r tmp_add.txt' ./charts/vela-workflow/templates/workflow-controller.yaml > tmp.yaml 9 | rm ./charts/vela-workflow/templates/workflow-controller.yaml 10 | cat tmp.yaml 11 | mv tmp.yaml ./charts/vela-workflow/templates/workflow-controller.yaml 12 | -------------------------------------------------------------------------------- /makefiles/const.mk: -------------------------------------------------------------------------------- 1 | # Image URL to use all building/pushing image targets 2 | IMG ?= oamdev/vela-workflow 3 | IMG_TAG ?= latest 4 | OS ?= linux 5 | ARCH ?= amd64 6 | # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. 7 | ENVTEST_K8S_VERSION = 1.29.0 8 | 9 | # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) 10 | ifeq (,$(shell go env GOBIN)) 11 | GOBIN=$(shell go env GOPATH)/bin 12 | else 13 | GOBIN=$(shell go env GOBIN) 14 | endif 15 | 16 | # Setting SHELL to bash allows bash commands to be executed by recipes. 17 | # This is a requirement for 'setup-envtest.sh' in the test target. 18 | # Options are set to exit when a recipe line exits non-zero or a piped command fails. 19 | SHELL = /usr/bin/env bash -o pipefail 20 | .SHELLFLAGS = -ec 21 | 22 | GOBUILD_ENV = GO111MODULE=on CGO_ENABLED=0 23 | GOX = go run github.com/mitchellh/gox 24 | TARGETS := darwin/amd64 linux/amd64 windows/amd64 25 | DIST_DIRS := find * -type d -exec 26 | 27 | TIME_LONG = `date +%Y-%m-%d' '%H:%M:%S` 28 | TIME_SHORT = `date +%H:%M:%S` 29 | TIME = $(TIME_SHORT) 30 | 31 | BLUE := $(shell printf "\033[34m") 32 | YELLOW := $(shell printf "\033[33m") 33 | RED := $(shell printf "\033[31m") 34 | GREEN := $(shell printf "\033[32m") 35 | CNone := $(shell printf "\033[0m") 36 | 37 | INFO = echo ${TIME} ${BLUE}[ .. ]${CNone} 38 | WARN = echo ${TIME} ${YELLOW}[WARN]${CNone} 39 | ERR = echo ${TIME} ${RED}[FAIL]${CNone} 40 | OK = echo ${TIME} ${GREEN}[ OK ]${CNone} 41 | FAIL = (echo ${TIME} ${RED}[FAIL]${CNone} && false) 42 | 43 | 44 | 45 | # Vela version 46 | VELA_VERSION ?= master 47 | # Repo info 48 | GIT_COMMIT ?= git-$(shell git rev-parse --short HEAD) 49 | GIT_COMMIT_LONG ?= $(shell git rev-parse HEAD) 50 | VELA_VERSION_KEY := github.com/kubevela/workflow/version.VelaVersion 51 | VELA_GITVERSION_KEY := github.com/kubevela/workflow/version.GitRevision 52 | LDFLAGS ?= "-s -w -X $(VELA_VERSION_KEY)=$(VELA_VERSION) -X $(VELA_GITVERSION_KEY)=$(GIT_COMMIT)" 53 | 54 | 55 | 56 | # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) 57 | ifeq (,$(shell go env GOBIN)) 58 | GOBIN=$(shell go env GOPATH)/bin 59 | else 60 | GOBIN=$(shell go env GOBIN) 61 | endif 62 | 63 | # Image URL to use all building/pushing image targets 64 | VELA_CORE_IMAGE ?= vela-core:latest 65 | VELA_CLI_IMAGE ?= oamdev/vela-cli:latest 66 | VELA_CORE_TEST_IMAGE ?= vela-core-test:$(GIT_COMMIT) 67 | VELA_APISERVER_IMAGE ?= apiserver:latest 68 | VELA_RUNTIME_ROLLOUT_IMAGE ?= vela-runtime-rollout:latest 69 | VELA_RUNTIME_ROLLOUT_TEST_IMAGE ?= vela-runtime-rollout-test:$(GIT_COMMIT) 70 | RUNTIME_CLUSTER_CONFIG ?= /tmp/worker.client.kubeconfig 71 | RUNTIME_CLUSTER_NAME ?= worker -------------------------------------------------------------------------------- /makefiles/e2e.mk: -------------------------------------------------------------------------------- 1 | .PHONY: e2e-setup-controller-pre-hook 2 | e2e-setup-controller-pre-hook: 3 | sh ./hack/e2e/modify_charts.sh 4 | 5 | 6 | .PHONY: e2e-setup-controller 7 | e2e-setup-controller: 8 | helm upgrade --install \ 9 | --create-namespace \ 10 | --namespace vela-system \ 11 | --set image.repository=oamdev/vela-workflow \ 12 | --set image.tag=latest \ 13 | --set image.pullPolicy=IfNotPresent \ 14 | --wait vela-workflow \ 15 | ./charts/vela-workflow \ 16 | --debug 17 | 18 | .PHONY: end-e2e 19 | end-e2e: 20 | sh ./hack/e2e/end_e2e.sh 21 | 22 | .PHONY: e2e-test 23 | e2e-test: 24 | # Run e2e test 25 | go test -v ./e2e 26 | -------------------------------------------------------------------------------- /pkg/backup/interface.go: -------------------------------------------------------------------------------- 1 | package backup 2 | 3 | import ( 4 | "fmt" 5 | 6 | monitorContext "github.com/kubevela/pkg/monitor/context" 7 | "github.com/kubevela/workflow/api/v1alpha1" 8 | "github.com/kubevela/workflow/pkg/backup/sls" 9 | ) 10 | 11 | const ( 12 | // PersistTypeSLS is the SLS persister. 13 | PersistTypeSLS string = "sls" 14 | ) 15 | 16 | // NewPersister is a factory method for creating a persister. 17 | func NewPersister(config map[string][]byte, persistType string) (PersistWorkflowRecord, error) { 18 | if config == nil { 19 | return nil, fmt.Errorf("empty config") 20 | } 21 | switch persistType { 22 | case PersistTypeSLS: 23 | return sls.NewSLSHandler(config) 24 | case "": 25 | return nil, nil 26 | default: 27 | return nil, fmt.Errorf("unsupported persist type %s", persistType) 28 | } 29 | } 30 | 31 | // PersistWorkflowRecord is the interface for record persist 32 | type PersistWorkflowRecord interface { 33 | Store(ctx monitorContext.Context, run *v1alpha1.WorkflowRun) error 34 | } 35 | -------------------------------------------------------------------------------- /pkg/backup/interface_test.go: -------------------------------------------------------------------------------- 1 | package backup 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | corev1 "k8s.io/api/core/v1" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | "k8s.io/client-go/kubernetes/scheme" 11 | "sigs.k8s.io/controller-runtime/pkg/client/fake" 12 | ) 13 | 14 | func TestNewPersister(t *testing.T) { 15 | cli := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() 16 | ctx := context.Background() 17 | testCases := map[string]struct { 18 | persistType string 19 | configName string 20 | expectedErr string 21 | secret *corev1.Secret 22 | }{ 23 | "empty config": { 24 | persistType: "sls", 25 | secret: &corev1.Secret{ 26 | ObjectMeta: metav1.ObjectMeta{ 27 | Name: "valid", 28 | Namespace: "default", 29 | }, 30 | }, 31 | expectedErr: "empty config", 32 | }, 33 | "invalid type": { 34 | persistType: "invalid", 35 | secret: &corev1.Secret{ 36 | ObjectMeta: metav1.ObjectMeta{ 37 | Name: "valid", 38 | Namespace: "default", 39 | }, 40 | Data: map[string][]byte{ 41 | "accessKeyID": []byte("accessKeyID"), 42 | }, 43 | }, 44 | expectedErr: "unsupported persist type", 45 | }, 46 | "sls-not-complete": { 47 | persistType: "sls", 48 | secret: &corev1.Secret{ 49 | ObjectMeta: metav1.ObjectMeta{ 50 | Name: "valid", 51 | Namespace: "default", 52 | }, 53 | Data: map[string][]byte{ 54 | "accessKeyID": []byte("accessKeyID"), 55 | }, 56 | }, 57 | expectedErr: "invalid SLS config", 58 | }, 59 | "sls-success": { 60 | persistType: "sls", 61 | secret: &corev1.Secret{ 62 | ObjectMeta: metav1.ObjectMeta{ 63 | Name: "valid", 64 | Namespace: "default", 65 | }, 66 | Data: map[string][]byte{ 67 | "AccessKeyID": []byte("accessKeyID"), 68 | "AccessKeySecret": []byte("accessKeySecret"), 69 | "Endpoint": []byte("endpoint"), 70 | "ProjectName": []byte("project"), 71 | "LogStoreName": []byte("logstore"), 72 | }, 73 | }, 74 | }, 75 | } 76 | for name, tc := range testCases { 77 | t.Run(name, func(t *testing.T) { 78 | r := require.New(t) 79 | if tc.secret != nil { 80 | r.NoError(cli.Create(ctx, tc.secret)) 81 | defer cli.Delete(ctx, tc.secret) 82 | } 83 | _, err := NewPersister(tc.secret.Data, tc.persistType) 84 | if tc.expectedErr != "" { 85 | r.Contains(err.Error(), tc.expectedErr) 86 | return 87 | } 88 | r.NoError(err) 89 | }) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /pkg/backup/sls/producer.go: -------------------------------------------------------------------------------- 1 | package sls 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "time" 7 | 8 | monitorContext "github.com/kubevela/pkg/monitor/context" 9 | 10 | "github.com/aliyun/aliyun-log-go-sdk/producer" 11 | "github.com/kubevela/workflow/api/v1alpha1" 12 | ) 13 | 14 | // Handler is sls config. 15 | type Handler struct { 16 | LogStoreName string 17 | ProjectName string 18 | ProducerConfig *producer.ProducerConfig 19 | } 20 | 21 | // Callback is for sls callback 22 | type Callback struct { 23 | ctx monitorContext.Context 24 | } 25 | 26 | // NewSLSHandler create a new sls handler 27 | func NewSLSHandler(config map[string][]byte) (*Handler, error) { 28 | endpoint := string(config["Endpoint"]) 29 | accessKeyID := string(config["AccessKeyID"]) 30 | accessKeySecret := string(config["AccessKeySecret"]) 31 | projectName := string(config["ProjectName"]) 32 | logStoreName := string(config["LogStoreName"]) 33 | if endpoint == "" || accessKeyID == "" || accessKeySecret == "" || projectName == "" || logStoreName == "" { 34 | return nil, fmt.Errorf("invalid SLS config, please make sure endpoint/ak/sk/project/logstore are both provided correctly") 35 | } 36 | producerConfig := producer.GetDefaultProducerConfig() 37 | producerConfig.Endpoint = endpoint 38 | producerConfig.AccessKeyID = accessKeyID 39 | producerConfig.AccessKeySecret = accessKeySecret 40 | 41 | return &Handler{ 42 | ProducerConfig: producerConfig, 43 | LogStoreName: logStoreName, 44 | ProjectName: projectName, 45 | }, nil 46 | } 47 | 48 | // Fail is fail callback 49 | func (callback *Callback) Fail(result *producer.Result) { 50 | callback.ctx.Error(fmt.Errorf("failed to send log to sls"), result.GetErrorMessage(), "errorCode", result.GetErrorCode(), "requestId", result.GetRequestId()) 51 | } 52 | 53 | // Success is success callback 54 | func (callback *Callback) Success(result *producer.Result) { //nolint:revive,unused 55 | } 56 | 57 | // Store is store workflowRun to sls 58 | func (s *Handler) Store(ctx monitorContext.Context, run *v1alpha1.WorkflowRun) error { 59 | ctx.Info("Start Send workflow record to SLS") 60 | p := producer.InitProducer(s.ProducerConfig) 61 | p.Start() 62 | defer p.SafeClose() 63 | 64 | data, err := json.Marshal(run) 65 | if err != nil { 66 | ctx.Error(err, "Marshal WorkflowRun Content fail") 67 | return err 68 | } 69 | 70 | callback := &Callback{ctx: ctx} 71 | log := producer.GenerateLog(uint32(time.Now().Unix()), map[string]string{"content": string(data)}) 72 | err = p.SendLogWithCallBack(s.ProjectName, s.LogStoreName, "topic", "", log, callback) 73 | if err != nil { 74 | ctx.Error(err, "Send WorkflowRun Content to SLS fail") 75 | return err 76 | } 77 | 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /pkg/backup/sls/producer_test.go: -------------------------------------------------------------------------------- 1 | package sls 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | monitorContext "github.com/kubevela/pkg/monitor/context" 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/kubevela/workflow/api/v1alpha1" 11 | ) 12 | 13 | func TestHandler_Store(t *testing.T) { 14 | tests := []struct { 15 | name string 16 | config map[string][]byte 17 | run *v1alpha1.WorkflowRun 18 | wantErr bool 19 | }{ 20 | { 21 | name: "Success", 22 | config: map[string][]byte{ 23 | "AccessKeyID": []byte("accessKeyID"), 24 | "AccessKeySecret": []byte("accessKeySecret"), 25 | "Endpoint": []byte("endpoint"), 26 | "ProjectName": []byte("project"), 27 | "LogStoreName": []byte("logstore"), 28 | }, 29 | wantErr: false, 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | r := require.New(t) 35 | ctx := monitorContext.NewTraceContext(context.Background(), "test") 36 | s, err := NewSLSHandler(tt.config) 37 | r.NoError(err) 38 | if err := s.Store(ctx, tt.run); (err != nil) != tt.wantErr { 39 | t.Errorf("Store() error = %v, wantErr %v", err, tt.wantErr) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pkg/common/logs.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package common 18 | 19 | import "k8s.io/klog/v2" 20 | 21 | // klog has multiple levels, you can set the log levels by klog.V() 22 | // Basic examples: 23 | // 24 | // klog.V(1).Info("Prepare to repel boarders") 25 | // 26 | // klog.V(2).ErrorS(err, "Initialization failed") 27 | const ( 28 | // LogInfo level is for most info logs, this is the default 29 | // One should just call Info directly. 30 | LogInfo klog.Level = iota 31 | 32 | // LogDebug is for more verbose logs 33 | LogDebug 34 | 35 | // LogDebugWithContent is recommended if one wants to log with the content of the object, 36 | // ie. http body, json/yaml file content 37 | LogDebugWithContent 38 | 39 | // LogTrace is the most verbose log level, don't add anything after this 40 | LogTrace = 100 41 | ) 42 | -------------------------------------------------------------------------------- /pkg/context/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package context 18 | 19 | import ( 20 | "context" 21 | 22 | "cuelang.org/go/cue" 23 | corev1 "k8s.io/api/core/v1" 24 | ) 25 | 26 | // Context is workflow context interface 27 | type Context interface { 28 | GetVar(paths ...string) (cue.Value, error) 29 | SetVar(v cue.Value, paths ...string) error 30 | GetStore() *corev1.ConfigMap 31 | GetMutableValue(path ...string) string 32 | SetMutableValue(data string, path ...string) 33 | DeleteMutableValue(paths ...string) 34 | IncreaseCountValueInMemory(paths ...string) int 35 | SetValueInMemory(data interface{}, paths ...string) 36 | GetValueInMemory(paths ...string) (interface{}, bool) 37 | DeleteValueInMemory(paths ...string) 38 | Commit(ctx context.Context) error 39 | StoreRef() *corev1.ObjectReference 40 | } 41 | -------------------------------------------------------------------------------- /pkg/context/storage.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package context 18 | 19 | import ( 20 | "fmt" 21 | "sync" 22 | 23 | v1 "k8s.io/api/core/v1" 24 | ) 25 | 26 | var ( 27 | // EnableInMemoryContext optimize workflow context storage by storing it in memory instead of etcd 28 | EnableInMemoryContext = false 29 | ) 30 | 31 | type inMemoryContextStorage struct { 32 | mu sync.Mutex 33 | contexts map[string]*v1.ConfigMap 34 | } 35 | 36 | // MemStore store in-memory context 37 | var MemStore = &inMemoryContextStorage{ 38 | contexts: map[string]*v1.ConfigMap{}, 39 | } 40 | 41 | func (o *inMemoryContextStorage) getKey(cm *v1.ConfigMap) string { 42 | ns := cm.GetNamespace() 43 | if ns == "" { 44 | ns = "default" 45 | } 46 | name := cm.GetName() 47 | return ns + "/" + name 48 | } 49 | 50 | func (o *inMemoryContextStorage) GetOrCreateInMemoryContext(cm *v1.ConfigMap) { 51 | if obj := o.GetInMemoryContext(cm.Name, cm.Namespace); obj != nil { 52 | obj.DeepCopyInto(cm) 53 | } else { 54 | o.CreateInMemoryContext(cm) 55 | } 56 | } 57 | 58 | func (o *inMemoryContextStorage) GetInMemoryContext(name, ns string) *v1.ConfigMap { 59 | return o.contexts[ns+"/"+name] 60 | } 61 | 62 | func (o *inMemoryContextStorage) CreateInMemoryContext(cm *v1.ConfigMap) { 63 | o.mu.Lock() 64 | defer o.mu.Unlock() 65 | cm.Data = map[string]string{} 66 | o.contexts[o.getKey(cm)] = cm 67 | } 68 | 69 | func (o *inMemoryContextStorage) UpdateInMemoryContext(cm *v1.ConfigMap) { 70 | o.mu.Lock() 71 | defer o.mu.Unlock() 72 | o.contexts[o.getKey(cm)] = cm 73 | } 74 | 75 | func (o *inMemoryContextStorage) DeleteInMemoryContext(appName string) { 76 | o.mu.Lock() 77 | defer o.mu.Unlock() 78 | key := fmt.Sprintf("workflow-%s-context", appName) 79 | delete(o.contexts, key) 80 | } 81 | -------------------------------------------------------------------------------- /pkg/cue/README.md: -------------------------------------------------------------------------------- 1 | # Develop Tips 2 | 3 | The following packages need to be tested without external/tool dependencies, So that the cue-sh/unity can do verification 4 | 5 | - github.com/kubevela/workflow/pkg/cue/definition 6 | - github.com/kubevela/workflow/pkg/cue/model 7 | - github.com/kubevela/workflow/pkg/cue/model/sets 8 | - github.com/kubevela/workflow/pkg/cue/process 9 | - github.com/kubevela/workflow/pkg/cue/task 10 | -------------------------------------------------------------------------------- /pkg/cue/model/keyword.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package model 18 | 19 | const ( 20 | // OutputFieldName is the reference of context base object 21 | OutputFieldName = "output" 22 | // OutputsFieldName is the reference of context Auxiliaries 23 | OutputsFieldName = "outputs" 24 | // ConfigFieldName is the reference of context config 25 | ConfigFieldName = "config" 26 | // ParameterFieldName is the keyword in CUE template to define users' input and the reference to the context parameter 27 | ParameterFieldName = "parameter" 28 | // ContextFieldName is the keyword in CUE template to define context 29 | ContextFieldName = "context" 30 | // ContextName is the name of context 31 | ContextName = "name" 32 | // ContextNamespace is the namespace of the app 33 | ContextNamespace = "namespace" 34 | // ContextPublishVersion is the publish version of the app 35 | ContextPublishVersion = "publishVersion" 36 | // ContextWorkflowName is the name of the workflow 37 | ContextWorkflowName = "workflowName" 38 | // ContextStepSessionID is the session id of the step 39 | ContextStepSessionID = "stepSessionID" 40 | // ContextStepName is the name of the step 41 | ContextStepName = "stepName" 42 | // ContextStepGroupName is the name of the stepGroup 43 | ContextStepGroupName = "stepGroupName" 44 | // ContextSpanID is name for span id. 45 | ContextSpanID = "spanID" 46 | // OutputSecretName is used to store all secret names which are generated by cloud resource components 47 | OutputSecretName = "outputSecretName" 48 | ) 49 | -------------------------------------------------------------------------------- /pkg/cue/model/sets/walk_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package sets 18 | 19 | import ( 20 | "testing" 21 | 22 | "cuelang.org/go/cue/ast" 23 | "cuelang.org/go/cue/cuecontext" 24 | "cuelang.org/go/cue/parser" 25 | "github.com/stretchr/testify/require" 26 | ) 27 | 28 | func TestWalk(t *testing.T) { 29 | 30 | testCases := []string{ 31 | `x: "124"`, 32 | 33 | `{ x: y: string }`, 34 | 35 | `x: {y: 124}`, 36 | 37 | `kind: "Deployment" 38 | metadata: name: "test" 39 | spec: replicas: 12`, 40 | 41 | `sidecar: { 42 | name: "agent" 43 | image: "test.com/agent:0.1" 44 | } 45 | containers: [{ 46 | name: "main" 47 | image: "webserver:0.2" 48 | },sidecar] 49 | `, 50 | 51 | ` x: 12 52 | if x==12 { 53 | y: "test string" 54 | } 55 | `, 56 | 57 | ` item1: { 58 | x: 12 59 | if x==12 { 60 | y: "test string" 61 | } 62 | } 63 | output: [item1] 64 | `, 65 | `import "strings" 66 | 67 | #User: { 68 | tags_str: string 69 | tags_map: { 70 | for k, v in strings.Split(tags_str, " ") { 71 | "\(v)": string 72 | } 73 | "{a}": string 74 | } 75 | } 76 | 77 | user: { 78 | #User 79 | tags_str: "b {c}" 80 | } 81 | `, 82 | `import "strings" 83 | 84 | b: string 85 | user: { 86 | tags_str: strings.Compare(b,"c") 87 | } 88 | `, 89 | `a: [1, 2, 3]`, 90 | } 91 | 92 | for _, src := range testCases { 93 | r := require.New(t) 94 | inst := cuecontext.New().CompileString(src) 95 | nsrc, err := toString(inst.Value()) 96 | r.NoError(err) 97 | f, err := parser.ParseFile("-", nsrc) 98 | r.NoError(err) 99 | 100 | newWalker(func(node ast.Node, ctx walkCtx) { 101 | if len(ctx.Pos()) == 0 { 102 | return 103 | } 104 | 105 | if _, ok := node.(ast.Expr); !ok { 106 | return 107 | } 108 | if _, ok := node.(*ast.CallExpr); ok { 109 | return 110 | } 111 | 112 | n, err := lookUp(f, ctx.Pos()...) 113 | r.NoError(err) 114 | 115 | r.Equal(n, node, nsrc) 116 | }).walk(f) 117 | } 118 | 119 | } 120 | 121 | func TestRemoveTmpVar(t *testing.T) { 122 | src := `spec: { 123 | _tmp: "x" 124 | list: [{ 125 | _tmp: "x" 126 | retain: "y" 127 | }, { 128 | _tmp: "x" 129 | retain: "z" 130 | }] 131 | retain: "y" 132 | } 133 | ` 134 | r := require.New(t) 135 | v := cuecontext.New().CompileString(src) 136 | s, err := toString(v, removeTmpVar) 137 | r.NoError(err) 138 | r.Equal(`spec: { 139 | list: [{ 140 | retain: "y" 141 | }, { 142 | retain: "z" 143 | }] 144 | retain: "y" 145 | } 146 | `, s) 147 | } 148 | -------------------------------------------------------------------------------- /pkg/cue/process/contexthook.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package process 18 | 19 | import "github.com/kubevela/workflow/pkg/cue/model" 20 | 21 | // BaseHook defines function to be invoked before setting base to a 22 | // process.Context 23 | type BaseHook interface { 24 | Exec(Context, model.Instance) error 25 | } 26 | 27 | // BaseHookFn implements BaseHook interface 28 | type BaseHookFn func(Context, model.Instance) error 29 | 30 | // Exec will be invoked before settiing 'base' into ctx.Base 31 | func (fn BaseHookFn) Exec(ctx Context, base model.Instance) error { 32 | return fn(ctx, base) 33 | } 34 | 35 | // AuxiliaryHook defines function to be invoked before appending auxiliaries to 36 | // a process.Context 37 | type AuxiliaryHook interface { 38 | Exec(Context, []Auxiliary) error 39 | } 40 | 41 | // AuxiliaryHookFn implements AuxiliaryHook interface 42 | type AuxiliaryHookFn func(Context, []Auxiliary) error 43 | 44 | // Exec will be invoked before appending 'auxs' into ctx.Auxiliaries 45 | func (fn AuxiliaryHookFn) Exec(ctx Context, auxs []Auxiliary) error { 46 | return fn(ctx, auxs) 47 | } 48 | -------------------------------------------------------------------------------- /pkg/cue/process/datamanager.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import "github.com/kubevela/workflow/pkg/cue/model" 4 | 5 | // DataManager is in charge of injecting and removing runtime context for ContextData 6 | type DataManager interface { 7 | Fill(ctx Context, kvs []StepMetaKV) 8 | Remove(ctx Context, keys []string) 9 | } 10 | 11 | // StepRunTimeMeta manage step runtime metadata 12 | type StepRunTimeMeta struct{} 13 | 14 | // StepMetaKV store the key and value of step runtime metadata 15 | type StepMetaKV struct { 16 | Key string 17 | Value interface{} 18 | } 19 | 20 | // WithSessionID return stepSessionID of the step 21 | func WithSessionID(id string) StepMetaKV { 22 | return StepMetaKV{ 23 | Key: model.ContextStepSessionID, 24 | Value: id, 25 | } 26 | } 27 | 28 | // WithName return stepName of the step 29 | func WithName(name string) StepMetaKV { 30 | return StepMetaKV{ 31 | Key: model.ContextStepName, 32 | Value: name, 33 | } 34 | } 35 | 36 | // WithGroupName return stepGroupName of the step 37 | func WithGroupName(name string) StepMetaKV { 38 | return StepMetaKV{ 39 | Key: model.ContextStepGroupName, 40 | Value: name, 41 | } 42 | } 43 | 44 | // WithSpanID return spanID of the step 45 | func WithSpanID(id string) StepMetaKV { 46 | return StepMetaKV{ 47 | Key: model.ContextSpanID, 48 | Value: id, 49 | } 50 | } 51 | 52 | // NewStepRunTimeMeta create step runtime metadata manager 53 | func NewStepRunTimeMeta() DataManager { 54 | return &StepRunTimeMeta{} 55 | } 56 | 57 | // Fill will fill step runtime metadata for ContextData 58 | func (s *StepRunTimeMeta) Fill(ctx Context, kvs []StepMetaKV) { 59 | for _, kv := range kvs { 60 | ctx.PushData(kv.Key, kv.Value) 61 | } 62 | } 63 | 64 | // Remove remove step runtime metadata of ContextData 65 | func (s *StepRunTimeMeta) Remove(ctx Context, keys []string) { 66 | for _, key := range keys { 67 | ctx.RemoveData(key) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /pkg/cue/testdata/traits/manualscaler.cue: -------------------------------------------------------------------------------- 1 | outputs: scaler: { 2 | apiVersion: "core.oam.dev/v1alpha2" 3 | kind: "ManualScalerTrait" 4 | spec: { 5 | replicaCount: parameter.replicas 6 | } 7 | } 8 | parameter: { 9 | //+short=r 10 | replicas: *2 | int 11 | } 12 | -------------------------------------------------------------------------------- /pkg/cue/testdata/traits/rollout.cue: -------------------------------------------------------------------------------- 1 | outputs: rollout: { 2 | apiVersion: "extend.oam.dev/v1alpha2" 3 | kind: "SimpleRolloutTrait" 4 | spec: { 5 | replica: parameter.replica 6 | maxUnavailable: parameter.maxUnavailable 7 | batch: parameter.batch 8 | } 9 | } 10 | 11 | parameter: { 12 | replica: *3 | int 13 | maxUnavailable: *1 | int 14 | batch: *2 | int 15 | } 16 | -------------------------------------------------------------------------------- /pkg/cue/testdata/traits/route.cue: -------------------------------------------------------------------------------- 1 | outputs: ingress: { 2 | apiVersion: "networking.k8s.io/v1beta1" 3 | kind: "Ingress" 4 | spec: { 5 | rules: [{ 6 | host: parameter.domain 7 | http: paths: [{ 8 | backend: { 9 | serviceName: parameter.service 10 | servicePort: parameter.port 11 | }}] 12 | }] 13 | } 14 | } 15 | parameter: { 16 | domain: string 17 | port: *80 | int 18 | service: string 19 | } 20 | -------------------------------------------------------------------------------- /pkg/cue/testdata/workloads/deployment.cue: -------------------------------------------------------------------------------- 1 | #deployment: { 2 | name: string 3 | // +usage=Which image would you like to use for your service 4 | // +short=i 5 | image: string 6 | // +usage=Which port do you want customer traffic sent to 7 | // +short=p 8 | port: *8080 | int 9 | env: [...{ 10 | name: string 11 | value: string 12 | }] 13 | cpu?: string 14 | } 15 | output: { 16 | apiVersion: "apps/v1" 17 | kind: "Deployment" 18 | metadata: name: parameter.name 19 | spec: { 20 | selector: 21 | matchLabels: 22 | app: parameter.name 23 | template: { 24 | metadata: 25 | labels: 26 | app: parameter.name 27 | spec: containers: [{ 28 | image: parameter.image 29 | name: parameter.name 30 | env: parameter.env 31 | ports: [{ 32 | containerPort: parameter.port 33 | protocol: "TCP" 34 | name: "default" 35 | }] 36 | if parameter["cpu"] != _|_ { 37 | resources: { 38 | limits: 39 | cpu: parameter.cpu 40 | requests: 41 | cpu: parameter.cpu 42 | } 43 | } 44 | }] 45 | } 46 | } 47 | } 48 | parameter: #deployment 49 | -------------------------------------------------------------------------------- /pkg/cue/testdata/workloads/empty.cue: -------------------------------------------------------------------------------- 1 | output: {} 2 | parameter: {} 3 | -------------------------------------------------------------------------------- /pkg/cue/testdata/workloads/metrics.cue: -------------------------------------------------------------------------------- 1 | #metrics: { 2 | // +usage=format of the metrics, default as prometheus 3 | // +short=f 4 | format: *"prometheus" | string 5 | enabled: *true | bool 6 | port?: *8080 | >=1024 & <=65535 & int 7 | // +usage=the label selector for the pods, default is the workload labels 8 | selector?: [string]: string 9 | } 10 | outputs: metrics: { 11 | apiVersion: "standard.oam.dev/v1alpha1" 12 | kind: "MetricsTrait" 13 | spec: { 14 | scrapeService: parameter 15 | } 16 | } 17 | parameter: #metrics 18 | -------------------------------------------------------------------------------- /pkg/cue/testdata/workloads/test-param.cue: -------------------------------------------------------------------------------- 1 | Template: { 2 | } 3 | 4 | parameter: { 5 | name: string 6 | // +usage=Which image would you like to use for your service 7 | // +short=i 8 | image: string 9 | // +usage=Which port do you want customer traffic sent to 10 | // +short=p 11 | port: *8080 | int 12 | env: [...{ 13 | name: string 14 | value: string 15 | }] 16 | enable: *false | bool 17 | fval: *64.3 | number 18 | nval: number 19 | } 20 | -------------------------------------------------------------------------------- /pkg/cue/testdata/workloads/webservice.cue: -------------------------------------------------------------------------------- 1 | output: { 2 | apiVersion: "apps/v1" 3 | kind: "Deployment" 4 | spec: { 5 | selector: matchLabels: { 6 | "app.oam.dev/component": context.name 7 | } 8 | template: { 9 | metadata: labels: { 10 | "app.oam.dev/component": context.name 11 | if parameter.addRevisionLabel { 12 | "app.oam.dev/appRevision": context.appRevision 13 | } 14 | } 15 | spec: { 16 | containers: [{ 17 | name: context.name 18 | image: parameter.image 19 | if parameter["env"] != _|_ { 20 | env: parameter.env 21 | } 22 | }] 23 | } 24 | } 25 | } 26 | } 27 | parameter: { 28 | // +usage=Which image would you like to use for your service 29 | // +short=i 30 | image: string 31 | // +usage=Define arguments by using environment variables 32 | env?: [...{ 33 | // +usage=Environment variable name 34 | name: string 35 | // +usage=The value of the environment variable 36 | value?: string 37 | // +usage=Specifies a source the value of this var should come from 38 | valueFrom?: { 39 | // +usage=Selects a key of a secret in the pod's namespace 40 | secretKeyRef: { 41 | // +usage=The name of the secret in the pod's namespace to select from 42 | name: string 43 | // +ignore 44 | // +usage=The key of the secret to select from. Must be a valid secret key 45 | secretKey: string 46 | } 47 | } 48 | }] 49 | // +ignore 50 | // +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods 51 | addRevisionLabel: *false | bool 52 | } 53 | 54 | -------------------------------------------------------------------------------- /pkg/debug/context.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package debug 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "cuelang.org/go/cue" 24 | corev1 "k8s.io/api/core/v1" 25 | "k8s.io/apimachinery/pkg/api/errors" 26 | "k8s.io/apimachinery/pkg/types" 27 | 28 | "github.com/kubevela/pkg/cue/util" 29 | "github.com/kubevela/pkg/util/singleton" 30 | wfTypes "github.com/kubevela/workflow/pkg/types" 31 | ) 32 | 33 | // ContextImpl is workflow debug context interface 34 | type ContextImpl interface { 35 | Set(v cue.Value) error 36 | } 37 | 38 | // Context is debug context. 39 | type Context struct { 40 | instance *wfTypes.WorkflowInstance 41 | id string 42 | } 43 | 44 | // Set sets debug content into context 45 | func (d *Context) Set(v cue.Value) error { 46 | data, err := util.ToString(v) 47 | if err != nil { 48 | return err 49 | } 50 | err = setStore(context.Background(), d.instance, d.id, data) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | return nil 56 | } 57 | 58 | func setStore(ctx context.Context, instance *wfTypes.WorkflowInstance, id, data string) error { 59 | cm := &corev1.ConfigMap{} 60 | cli := singleton.KubeClient.Get() 61 | if err := cli.Get(ctx, types.NamespacedName{ 62 | Namespace: instance.Namespace, 63 | Name: GenerateContextName(instance.Name, id, string(instance.UID)), 64 | }, cm); err != nil { 65 | if errors.IsNotFound(err) { 66 | cm.Name = GenerateContextName(instance.Name, id, string(instance.UID)) 67 | cm.Namespace = instance.Namespace 68 | cm.Data = map[string]string{ 69 | "debug": data, 70 | } 71 | cm.Labels = map[string]string{} 72 | cm.SetOwnerReferences(instance.ChildOwnerReferences) 73 | return cli.Create(ctx, cm) 74 | } 75 | return err 76 | } 77 | cm.Data = map[string]string{ 78 | "debug": data, 79 | } 80 | 81 | return cli.Update(ctx, cm) 82 | } 83 | 84 | // NewContext new workflow context without initialize data. 85 | func NewContext(instance *wfTypes.WorkflowInstance, id string) ContextImpl { 86 | return &Context{ 87 | instance: instance, 88 | id: id, 89 | } 90 | } 91 | 92 | // GenerateContextName generate context name 93 | func GenerateContextName(name, id, suffix string) string { 94 | if len(suffix) > 5 { 95 | suffix = suffix[len(suffix)-5:] 96 | } 97 | return fmt.Sprintf("%s-%s-debug-%s", name, id, suffix) 98 | } 99 | -------------------------------------------------------------------------------- /pkg/debug/context_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package debug 18 | 19 | import ( 20 | "context" 21 | "testing" 22 | 23 | "cuelang.org/go/cue/cuecontext" 24 | "github.com/crossplane/crossplane-runtime/pkg/test" 25 | "github.com/stretchr/testify/require" 26 | corev1 "k8s.io/api/core/v1" 27 | kerrors "k8s.io/apimachinery/pkg/api/errors" 28 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 | "sigs.k8s.io/controller-runtime/pkg/client" 30 | 31 | "github.com/kubevela/pkg/util/singleton" 32 | "github.com/kubevela/workflow/pkg/types" 33 | ) 34 | 35 | func TestSetContext(t *testing.T) { 36 | r := require.New(t) 37 | newCliForTest(&corev1.ConfigMap{ 38 | ObjectMeta: metav1.ObjectMeta{ 39 | Name: GenerateContextName("test", "step1", "123456"), 40 | }, 41 | Data: map[string]string{ 42 | "debug": "test", 43 | }, 44 | }) 45 | cuectx := cuecontext.New() 46 | // test update 47 | debugCtx := NewContext(&types.WorkflowInstance{ 48 | WorkflowMeta: types.WorkflowMeta{ 49 | Name: "test", 50 | }, 51 | }, "step1") 52 | v := cuectx.CompileString(`test: "test"`) 53 | err := debugCtx.Set(v) 54 | r.NoError(err) 55 | // test create 56 | debugCtx = NewContext(&types.WorkflowInstance{ 57 | WorkflowMeta: types.WorkflowMeta{ 58 | Name: "test", 59 | }, 60 | }, "step2") 61 | v = cuectx.CompileString(`test2: "test2"`) 62 | err = debugCtx.Set(v) 63 | r.NoError(err) 64 | } 65 | 66 | func newCliForTest(wfCm *corev1.ConfigMap) { 67 | cli := &test.MockClient{ 68 | MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 69 | o, ok := obj.(*corev1.ConfigMap) 70 | if ok { 71 | switch key.Name { 72 | case GenerateContextName("test", "step1", "123456"): 73 | if wfCm != nil { 74 | *o = *wfCm 75 | return nil 76 | } 77 | default: 78 | return kerrors.NewNotFound(corev1.Resource("configMap"), o.Name) 79 | } 80 | } 81 | return kerrors.NewNotFound(corev1.Resource("configMap"), key.Name) 82 | }, 83 | MockUpdate: func(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { 84 | o, ok := obj.(*corev1.ConfigMap) 85 | if ok { 86 | if wfCm == nil { 87 | return kerrors.NewNotFound(corev1.Resource("configMap"), o.Name) 88 | } 89 | *wfCm = *o 90 | } 91 | return nil 92 | }, 93 | MockCreate: func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { 94 | return nil 95 | }, 96 | } 97 | singleton.KubeClient.Set(cli) 98 | } 99 | -------------------------------------------------------------------------------- /pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package errors 18 | 19 | import "fmt" 20 | 21 | // ActionType is the type of action 22 | type ActionType string 23 | 24 | const ( 25 | // ActionSuspend is the action type of suspend 26 | ActionSuspend ActionType = "suspend" 27 | // ActionTerminate is the action type of terminate 28 | ActionTerminate ActionType = "terminate" 29 | // ActionWait is the action type of wait 30 | ActionWait ActionType = "wait" 31 | ) 32 | 33 | // GenericActionError is the error type of action 34 | type GenericActionError ActionType 35 | 36 | func (e GenericActionError) Error() string { 37 | return "" 38 | } 39 | 40 | // LookUpNotFoundErr is the error type of lookup 41 | type LookUpNotFoundErr string 42 | 43 | // Error . 44 | func (e LookUpNotFoundErr) Error() string { 45 | return fmt.Sprintf("failed to lookup value: var(path=%s) not exist", string(e)) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/executor/interface.go: -------------------------------------------------------------------------------- 1 | /*Copyright 2022 The KubeVela Authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package executor 17 | 18 | import ( 19 | "time" 20 | 21 | monitorContext "github.com/kubevela/pkg/monitor/context" 22 | 23 | "github.com/kubevela/workflow/api/v1alpha1" 24 | "github.com/kubevela/workflow/pkg/types" 25 | ) 26 | 27 | // WorkflowExecutor is used to execute the workflow steps 28 | type WorkflowExecutor interface { 29 | // ExecuteSteps executes the steps 30 | ExecuteRunners(ctx monitorContext.Context, taskRunners []types.TaskRunner) (state v1alpha1.WorkflowRunPhase, err error) 31 | 32 | // GetBackoffWaitTime returns the wait time for next retry. 33 | GetBackoffWaitTime() time.Duration 34 | 35 | GetSuspendBackoffWaitTime() time.Duration 36 | } 37 | -------------------------------------------------------------------------------- /pkg/executor/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package executor 18 | 19 | import ( 20 | "github.com/kubevela/workflow/pkg/types" 21 | ) 22 | 23 | // Option is the option of executor 24 | type Option interface { 25 | ApplyTo(*workflowExecutor) 26 | } 27 | 28 | type withStatusPatcher struct { 29 | patcher types.StatusPatcher 30 | } 31 | 32 | func (w *withStatusPatcher) ApplyTo(e *workflowExecutor) { 33 | e.patcher = w.patcher 34 | } 35 | 36 | // WithStatusPatcher set the status patcher 37 | func WithStatusPatcher(patcher types.StatusPatcher) Option { 38 | return &withStatusPatcher{patcher: patcher} 39 | } 40 | -------------------------------------------------------------------------------- /pkg/executor/suit_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package executor 18 | 19 | import ( 20 | "testing" 21 | "time" 22 | 23 | "github.com/kubevela/pkg/util/singleton" 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | 27 | cuexv1alpha1 "github.com/kubevela/pkg/apis/cue/v1alpha1" 28 | crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 29 | "k8s.io/apimachinery/pkg/runtime" 30 | "k8s.io/client-go/dynamic/fake" 31 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 32 | "k8s.io/client-go/rest" 33 | "k8s.io/utils/ptr" 34 | "sigs.k8s.io/controller-runtime/pkg/client" 35 | "sigs.k8s.io/controller-runtime/pkg/envtest" 36 | ) 37 | 38 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 39 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 40 | 41 | var cfg *rest.Config 42 | var k8sClient client.Client 43 | var testEnv *envtest.Environment 44 | var scheme = runtime.NewScheme() 45 | 46 | func TestWorkflow(t *testing.T) { 47 | RegisterFailHandler(Fail) 48 | 49 | RunSpecs(t, "Test Definition Suite") 50 | } 51 | 52 | var _ = BeforeSuite(func(done Done) { 53 | By("Bootstrapping test environment") 54 | testEnv = &envtest.Environment{ 55 | ControlPlaneStartTimeout: time.Minute, 56 | ControlPlaneStopTimeout: time.Minute, 57 | UseExistingCluster: ptr.To(false), 58 | } 59 | var err error 60 | cfg, err = testEnv.Start() 61 | Expect(err).ToNot(HaveOccurred()) 62 | Expect(cfg).ToNot(BeNil()) 63 | Expect(clientgoscheme.AddToScheme(scheme)).Should(BeNil()) 64 | Expect(crdv1.AddToScheme(scheme)).Should(BeNil()) 65 | Expect(cuexv1alpha1.AddToScheme(scheme)).Should(BeNil()) 66 | // +kubebuilder:scaffold:scheme 67 | By("Create the k8s client") 68 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) 69 | Expect(err).ToNot(HaveOccurred()) 70 | Expect(k8sClient).ToNot(BeNil()) 71 | singleton.KubeClient.Set(k8sClient) 72 | fakeDynamicClient := fake.NewSimpleDynamicClient(scheme) 73 | singleton.DynamicClient.Set(fakeDynamicClient) 74 | 75 | close(done) 76 | }, 60) 77 | 78 | var _ = AfterSuite(func() { 79 | By("Tearing down the test environment") 80 | err := testEnv.Stop() 81 | Expect(err).ToNot(HaveOccurred()) 82 | }) 83 | -------------------------------------------------------------------------------- /pkg/features/controller_features.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package features 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/util/runtime" 21 | "k8s.io/apiserver/pkg/util/feature" 22 | "k8s.io/component-base/featuregate" 23 | ) 24 | 25 | const ( 26 | // EnableSuspendOnFailure enable suspend on workflow failure 27 | EnableSuspendOnFailure featuregate.Feature = "EnableSuspendOnFailure" 28 | // EnableBackupWorkflowRecord enable backup workflow record 29 | EnableBackupWorkflowRecord featuregate.Feature = "EnableBackupWorkflowRecord" 30 | // EnablePatchStatusAtOnce enable patch status at once 31 | EnablePatchStatusAtOnce featuregate.Feature = "EnablePatchStatusAtOnce" 32 | // EnableWatchEventListener enable watch event listener 33 | EnableWatchEventListener featuregate.Feature = "EnableWatchEventListener" 34 | ) 35 | 36 | var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ 37 | EnableSuspendOnFailure: {Default: false, PreRelease: featuregate.Alpha}, 38 | EnableBackupWorkflowRecord: {Default: false, PreRelease: featuregate.Alpha}, 39 | EnablePatchStatusAtOnce: {Default: false, PreRelease: featuregate.Alpha}, 40 | EnableWatchEventListener: {Default: false, PreRelease: featuregate.Alpha}, 41 | } 42 | 43 | func init() { 44 | runtime.Must(feature.DefaultMutableFeatureGate.Add(defaultFeatureGates)) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/generator/suit_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package generator 18 | 19 | import ( 20 | "testing" 21 | "time" 22 | 23 | "github.com/kubevela/pkg/util/singleton" 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | 27 | cuexv1alpha1 "github.com/kubevela/pkg/apis/cue/v1alpha1" 28 | crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 29 | "k8s.io/apimachinery/pkg/runtime" 30 | "k8s.io/client-go/dynamic/fake" 31 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 32 | "k8s.io/client-go/rest" 33 | "k8s.io/utils/ptr" 34 | "sigs.k8s.io/controller-runtime/pkg/client" 35 | "sigs.k8s.io/controller-runtime/pkg/envtest" 36 | ) 37 | 38 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 39 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 40 | 41 | var cfg *rest.Config 42 | var k8sClient client.Client 43 | var testEnv *envtest.Environment 44 | var scheme = runtime.NewScheme() 45 | 46 | func TestSteps(t *testing.T) { 47 | RegisterFailHandler(Fail) 48 | 49 | RunSpecs(t, "Test Definition Suite") 50 | } 51 | 52 | var _ = BeforeSuite(func(done Done) { 53 | By("Bootstrapping test environment") 54 | testEnv = &envtest.Environment{ 55 | ControlPlaneStartTimeout: time.Minute, 56 | ControlPlaneStopTimeout: time.Minute, 57 | UseExistingCluster: ptr.To(false), 58 | } 59 | var err error 60 | cfg, err = testEnv.Start() 61 | Expect(err).ToNot(HaveOccurred()) 62 | Expect(cfg).ToNot(BeNil()) 63 | Expect(clientgoscheme.AddToScheme(scheme)).Should(BeNil()) 64 | Expect(crdv1.AddToScheme(scheme)).Should(BeNil()) 65 | Expect(cuexv1alpha1.AddToScheme(scheme)).Should(BeNil()) 66 | // +kubebuilder:scaffold:scheme 67 | By("Create the k8s client") 68 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) 69 | Expect(err).ToNot(HaveOccurred()) 70 | Expect(k8sClient).ToNot(BeNil()) 71 | singleton.KubeClient.Set(k8sClient) 72 | fakeDynamicClient := fake.NewSimpleDynamicClient(scheme) 73 | singleton.DynamicClient.Set(fakeDynamicClient) 74 | 75 | close(done) 76 | }, 60) 77 | 78 | var _ = AfterSuite(func() { 79 | By("Tearing down the test environment") 80 | err := testEnv.Stop() 81 | Expect(err).ToNot(HaveOccurred()) 82 | }) 83 | -------------------------------------------------------------------------------- /pkg/mock/mock.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mock 18 | 19 | import "github.com/kubevela/workflow/api/v1alpha1" 20 | 21 | // Action ... 22 | type Action struct { 23 | Phase string 24 | Msg string 25 | } 26 | 27 | // Suspend makes the step suspend 28 | func (act *Action) Suspend(message string) { 29 | act.Phase = "Suspend" 30 | if message != "" { 31 | act.Msg = message 32 | } 33 | } 34 | 35 | // GetStatus returns the step status 36 | func (act *Action) GetStatus() v1alpha1.StepStatus { 37 | return v1alpha1.StepStatus{} 38 | } 39 | 40 | // Resume makes the step resume 41 | func (act *Action) Resume(message string) { 42 | act.Phase = "Resume" 43 | if message != "" { 44 | act.Msg = message 45 | } 46 | } 47 | 48 | // Terminate makes the step terminate 49 | func (act *Action) Terminate(message string) { 50 | act.Phase = "Terminate" 51 | if message != "" { 52 | act.Msg = message 53 | } 54 | } 55 | 56 | // Wait makes the step wait 57 | func (act *Action) Wait(message string) { 58 | act.Phase = "Wait" 59 | if message != "" { 60 | act.Msg = message 61 | } 62 | } 63 | 64 | // Fail makes the step fail 65 | func (act *Action) Fail(message string) { 66 | act.Phase = "Fail" 67 | if message != "" { 68 | act.Msg = message 69 | } 70 | } 71 | 72 | // Message write message to step status 73 | func (act *Action) Message(message string) { 74 | act.Phase = "Fail" 75 | if message != "" { 76 | act.Msg = message 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pkg/providers/builtin/workspace.cue: -------------------------------------------------------------------------------- 1 | // workspace.cue 2 | 3 | #DoVar: { 4 | #do: "var" 5 | #provider: "builtin" 6 | 7 | $params: { 8 | // +usage=The method to call on the variable 9 | method: *"Get" | "Put" 10 | // +usage=The path to the variable 11 | path: string 12 | // +usage=The value of the variable 13 | value?: _ 14 | } 15 | 16 | $returns?: { 17 | // +usage=The value of the variable 18 | value: _ 19 | } 20 | } 21 | 22 | #ConditionalWait: { 23 | #do: "wait" 24 | #provider: "builtin" 25 | 26 | $params: { 27 | // +usage=If continue is false, the step will wait for continue to be true. 28 | continue: *false | bool 29 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 30 | message?: string 31 | } 32 | } 33 | 34 | #Suspend: { 35 | #do: "suspend" 36 | #provider: "builtin" 37 | 38 | $params: { 39 | // +usage=Specify the wait duration time to resume automaticlly such as "30s", "1min" or "2m15s" 40 | duration?: string 41 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 42 | message?: string 43 | } 44 | } 45 | 46 | #Break: { 47 | #do: "break" 48 | #provider: "builtin" 49 | 50 | $params: { 51 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 52 | message?: string 53 | } 54 | } 55 | 56 | #Fail: { 57 | #do: "fail" 58 | #provider: "builtin" 59 | 60 | $params: { 61 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 62 | message?: string 63 | } 64 | } 65 | 66 | #Message: { 67 | #do: "message" 68 | #provider: "builtin" 69 | 70 | $params: { 71 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 72 | message?: string 73 | } 74 | } 75 | 76 | #Steps: { 77 | ... 78 | } 79 | 80 | -------------------------------------------------------------------------------- /pkg/providers/email/email.cue: -------------------------------------------------------------------------------- 1 | // email.cue 2 | 3 | #SendEmail: { 4 | #do: "send" 5 | #provider: "email" 6 | 7 | $params: { 8 | // +usage=The info of the sender 9 | from: { 10 | // +usage=The address of the sender 11 | address: string 12 | // +usage=The alias of the sender 13 | alias?: string 14 | // +usage=The password of the sender 15 | password: string 16 | // +usage=The host of the sender server 17 | host: string 18 | // +usage=The port of the sender server 19 | port: int 20 | } 21 | // +usgae=The email address list of the recievers 22 | to: [...string] 23 | // +usage=The content of the email 24 | content: { 25 | // +usage=The subject of the email 26 | subject: string 27 | // +usage=The body of the email 28 | body: string 29 | } 30 | } 31 | // this provider has no returns 32 | ... 33 | } 34 | -------------------------------------------------------------------------------- /pkg/providers/http/http.cue: -------------------------------------------------------------------------------- 1 | // http.cue 2 | 3 | #HTTPDo: { 4 | #do: "do" 5 | #provider: "http" 6 | 7 | $params: { 8 | // +usage=The method of HTTP request 9 | method: *"GET" | "POST" | "PUT" | "DELETE" 10 | // +usage=The url to request 11 | url: string 12 | // +usage=The request config 13 | request?: { 14 | // +usage=The timeout of this request 15 | timeout?: string 16 | // +usage=The request body 17 | body?: string 18 | // +usage=The header of the request 19 | header?: [string]: string 20 | // +usage=The trailer of the request 21 | trailer?: [string]: string 22 | // +usage=The rate limiter of the request 23 | ratelimiter?: { 24 | limit: int 25 | period: string 26 | } 27 | ... 28 | } 29 | // +usgae=The tls config of the request 30 | tls_config?: { 31 | secret: string 32 | namespace?: string 33 | } 34 | } 35 | 36 | $returns?: { 37 | // +usage=The response of the request will be filled in this field after the action is executed 38 | response: { 39 | // +usage=The body of the response 40 | body: string 41 | // +usage=The header of the response 42 | header?: [string]: [...string] 43 | // +usage=The trailer of the response 44 | trailer?: [string]: [...string] 45 | // +usage=The status code of the response 46 | statusCode: int 47 | ... 48 | } 49 | } 50 | ... 51 | } 52 | 53 | #HTTPGet: #HTTPDo & {method: "GET"} 54 | 55 | #HTTPPost: #HTTPDo & {method: "POST"} 56 | 57 | #HTTPPut: #HTTPDo & {method: "PUT"} 58 | 59 | #HTTPDelete: #HTTPDo & {method: "DELETE"} 60 | -------------------------------------------------------------------------------- /pkg/providers/http/ratelimiter/ratelimiter.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package ratelimiter 18 | 19 | import ( 20 | "time" 21 | 22 | "github.com/golang/groupcache/lru" 23 | "golang.org/x/time/rate" 24 | ) 25 | 26 | // RateLimiter is the rate limiter. 27 | type RateLimiter struct { 28 | store *lru.Cache 29 | } 30 | 31 | // NewRateLimiter returns a new rate limiter. 32 | func NewRateLimiter(len int) *RateLimiter { 33 | store := lru.New(len) 34 | store.Clear() 35 | return &RateLimiter{store: store} 36 | } 37 | 38 | // Allow returns true if the operation is allowed. 39 | func (rl *RateLimiter) Allow(id string, limit int, duration time.Duration) bool { 40 | if l, ok := rl.store.Get(id); ok { 41 | limiter := l.(*rate.Limiter) 42 | if limiter.Limit() == rate.Every(duration) && limiter.Burst() == limit { 43 | return limiter.Allow() 44 | } 45 | } 46 | limiter := rate.NewLimiter(rate.Every(duration), limit) 47 | rl.store.Add(id, limiter) 48 | return limiter.Allow() 49 | } 50 | -------------------------------------------------------------------------------- /pkg/providers/http/ratelimiter/ratelimiter_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package ratelimiter 18 | 19 | import ( 20 | "testing" 21 | "time" 22 | 23 | "github.com/stretchr/testify/require" 24 | ) 25 | 26 | func TestRateLimiter(t *testing.T) { 27 | rl := NewRateLimiter(2) 28 | r := require.New(t) 29 | duration := time.Second 30 | testCases := []struct { 31 | id string 32 | limit int 33 | expected bool 34 | }{ 35 | { 36 | id: "1", 37 | limit: 2, 38 | }, 39 | { 40 | id: "2", 41 | limit: 2, 42 | }, 43 | { 44 | id: "3", 45 | limit: 2, 46 | }, 47 | { 48 | id: "2", 49 | limit: 3, 50 | }, 51 | } 52 | for _, tc := range testCases { 53 | for i := 0; i < tc.limit; i++ { 54 | allow := rl.Allow(tc.id, tc.limit, duration) 55 | r.Equal(true, allow) 56 | } 57 | allow := rl.Allow(tc.id, tc.limit, duration) 58 | r.Equal(false, allow) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/providers/legacy/email/email.cue: -------------------------------------------------------------------------------- 1 | // email.cue 2 | 3 | #SendEmail: { 4 | #do: "send" 5 | #provider: "op" 6 | 7 | // +usage=The info of the sender 8 | from: { 9 | // +usage=The address of the sender 10 | address: string 11 | // +usage=The alias of the sender 12 | alias?: string 13 | // +usage=The password of the sender 14 | password: string 15 | // +usage=The host of the sender server 16 | host: string 17 | // +usage=The port of the sender server 18 | port: int 19 | } 20 | // +usgae=The email address list of the recievers 21 | to: [...string] 22 | // +usage=The content of the email 23 | content: { 24 | // +usage=The subject of the email 25 | subject: string 26 | // +usage=The body of the email 27 | body: string 28 | } 29 | stepID: context.stepSessionID 30 | ... 31 | } 32 | -------------------------------------------------------------------------------- /pkg/providers/legacy/http/http.cue: -------------------------------------------------------------------------------- 1 | // http.cue 2 | 3 | #HTTPDo: { 4 | #do: "do" 5 | #provider: "op" 6 | 7 | // +usage=The method of HTTP request 8 | method: *"GET" | "POST" | "PUT" | "DELETE" 9 | // +usage=The url to request 10 | url: string 11 | // +usage=The request config 12 | request?: { 13 | // +usage=The timeout of this request 14 | timeout?: string 15 | // +usage=The request body 16 | body?: string 17 | // +usage=The header of the request 18 | header?: [string]: string 19 | // +usage=The trailer of the request 20 | trailer?: [string]: string 21 | // +usage=The rate limiter of the request 22 | ratelimiter?: { 23 | limit: int 24 | period: string 25 | } 26 | ... 27 | } 28 | // +usgae=The tls config of the request 29 | tls_config?: { 30 | secret: string 31 | namespace?: string 32 | } 33 | // +usage=The response of the request will be filled in this field after the action is executed 34 | response: { 35 | // +usage=The body of the response 36 | body: string 37 | // +usage=The header of the response 38 | header?: [string]: [...string] 39 | // +usage=The trailer of the response 40 | trailer?: [string]: [...string] 41 | // +usage=The status code of the response 42 | statusCode: int 43 | ... 44 | } 45 | ... 46 | } 47 | 48 | #HTTPGet: #HTTPDo & {method: "GET"} 49 | 50 | #HTTPPost: #HTTPDo & {method: "POST"} 51 | 52 | #HTTPPut: #HTTPDo & {method: "PUT"} 53 | 54 | #HTTPDelete: #HTTPDo & {method: "DELETE"} 55 | -------------------------------------------------------------------------------- /pkg/providers/legacy/http/ratelimiter/ratelimiter.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package ratelimiter 18 | 19 | import ( 20 | "time" 21 | 22 | "github.com/golang/groupcache/lru" 23 | "golang.org/x/time/rate" 24 | ) 25 | 26 | // RateLimiter is the rate limiter. 27 | type RateLimiter struct { 28 | store *lru.Cache 29 | } 30 | 31 | // NewRateLimiter returns a new rate limiter. 32 | func NewRateLimiter(len int) *RateLimiter { 33 | store := lru.New(len) 34 | store.Clear() 35 | return &RateLimiter{store: store} 36 | } 37 | 38 | // Allow returns true if the operation is allowed. 39 | func (rl *RateLimiter) Allow(id string, limit int, duration time.Duration) bool { 40 | if l, ok := rl.store.Get(id); ok { 41 | limiter := l.(*rate.Limiter) 42 | if limiter.Limit() == rate.Every(duration) && limiter.Burst() == limit { 43 | return limiter.Allow() 44 | } 45 | } 46 | limiter := rate.NewLimiter(rate.Every(duration), limit) 47 | rl.store.Add(id, limiter) 48 | return limiter.Allow() 49 | } 50 | -------------------------------------------------------------------------------- /pkg/providers/legacy/http/ratelimiter/ratelimiter_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package ratelimiter 18 | 19 | import ( 20 | "testing" 21 | "time" 22 | 23 | "github.com/stretchr/testify/require" 24 | ) 25 | 26 | func TestRateLimiter(t *testing.T) { 27 | rl := NewRateLimiter(2) 28 | r := require.New(t) 29 | duration := time.Second 30 | testCases := []struct { 31 | id string 32 | limit int 33 | expected bool 34 | }{ 35 | { 36 | id: "1", 37 | limit: 2, 38 | }, 39 | { 40 | id: "2", 41 | limit: 2, 42 | }, 43 | { 44 | id: "3", 45 | limit: 2, 46 | }, 47 | { 48 | id: "2", 49 | limit: 3, 50 | }, 51 | } 52 | for _, tc := range testCases { 53 | for i := 0; i < tc.limit; i++ { 54 | allow := rl.Allow(tc.id, tc.limit, duration) 55 | r.Equal(true, allow) 56 | } 57 | allow := rl.Allow(tc.id, tc.limit, duration) 58 | r.Equal(false, allow) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/providers/legacy/kube/kube.cue: -------------------------------------------------------------------------------- 1 | // kube.cue 2 | 3 | #Apply: { 4 | #do: "apply" 5 | #provider: "op" 6 | 7 | // +usage=The cluster to use 8 | cluster: *"" | string 9 | // +usage=The resource to apply 10 | value: {...} 11 | // +usage=The patcher that will be applied to the resource, you can define the strategy of list merge through comments. Reference doc here: https://kubevela.io/docs/platform-engineers/traits/patch-trait#patch-in-workflow-step 12 | patch?: {...} 13 | ... 14 | } 15 | 16 | #Patch: { 17 | #do: "patch" 18 | #provider: "op" 19 | 20 | // +usage=The cluster to use 21 | cluster: *"" | string 22 | // +usage=The resource to patch, we'll first get the resource from the cluster, then apply the patcher to it 23 | value: {...} 24 | // +usage=The patcher that will be applied to the resource, you can define the strategy of list merge through comments. Reference doc here: https://kubevela.io/docs/platform-engineers/traits/patch-trait#patch-in-workflow-step 25 | patch: {...} 26 | // +usage=The resource after applied will be filled in this field after the action is executed 27 | result?: {...} 28 | ... 29 | } 30 | 31 | #ApplyInParallel: { 32 | #do: "apply-in-parallel" 33 | #provider: "op" 34 | 35 | // +usage=The cluster to use 36 | cluster: *"" | string 37 | // +usage=The resources to apply in parallel 38 | value: [...{...}] 39 | ... 40 | } 41 | 42 | #Read: { 43 | #do: "read" 44 | #provider: "op" 45 | 46 | // +usage=The cluster to use 47 | cluster: *"" | string 48 | // +usage=The resource to read, this field will be filled with the resource read from the cluster after the action is executed 49 | value: {...} 50 | ... 51 | } 52 | 53 | #List: { 54 | #do: "list" 55 | #provider: "op" 56 | 57 | // +usage=The cluster to use 58 | cluster: *"" | string 59 | // +usage=The resource to list 60 | resource: { 61 | // +usage=The api version of the resource 62 | apiVersion: string 63 | // +usage=The kind of the resource 64 | kind: string 65 | } 66 | // +usage=The filter to list the resources 67 | filter?: { 68 | // +usage=The namespace to list the resources 69 | namespace: *"" | string 70 | // +usage=The label selector to filter the resources 71 | matchingLabels?: {...} 72 | } 73 | // +usage=The listed resources will be filled in this field after the action is executed 74 | list?: {...} 75 | ... 76 | } 77 | 78 | #Delete: { 79 | #do: "delete" 80 | #provider: "op" 81 | 82 | // +usage=The cluster to use 83 | cluster: *"" | string 84 | // +usage=The resource to delete 85 | value: { 86 | // +usage=The api version of the resource 87 | apiVersion: string 88 | // +usage=The kind of the resource 89 | kind: string 90 | // +usage=The metadata of the resource 91 | metadata: { 92 | // +usage=The name of the resource 93 | name?: string 94 | // +usage=The namespace of the resource 95 | namespace: *"default" | string 96 | } 97 | } 98 | // +usage=The filter to delete the resources 99 | filter?: { 100 | // +usage=The namespace to list the resources 101 | namespace?: string 102 | // +usage=The label selector to filter the resources 103 | matchingLabels?: {...} 104 | } 105 | ... 106 | } 107 | -------------------------------------------------------------------------------- /pkg/providers/legacy/legacy.go: -------------------------------------------------------------------------------- 1 | package legacy 2 | 3 | import ( 4 | "strings" 5 | 6 | cuexruntime "github.com/kubevela/pkg/cue/cuex/runtime" 7 | 8 | "github.com/kubevela/workflow/pkg/providers/legacy/email" 9 | "github.com/kubevela/workflow/pkg/providers/legacy/http" 10 | "github.com/kubevela/workflow/pkg/providers/legacy/kube" 11 | "github.com/kubevela/workflow/pkg/providers/legacy/metrics" 12 | "github.com/kubevela/workflow/pkg/providers/legacy/time" 13 | "github.com/kubevela/workflow/pkg/providers/legacy/util" 14 | "github.com/kubevela/workflow/pkg/providers/legacy/workspace" 15 | ) 16 | 17 | func registerProviders(providers map[string]cuexruntime.ProviderFn, new map[string]cuexruntime.ProviderFn) map[string]cuexruntime.ProviderFn { 18 | for k, v := range new { 19 | providers[k] = v 20 | } 21 | return providers 22 | } 23 | 24 | // GetLegacyProviders get legacy providers 25 | func GetLegacyProviders() map[string]cuexruntime.ProviderFn { 26 | providers := make(map[string]cuexruntime.ProviderFn, 0) 27 | registerProviders(providers, email.GetProviders()) 28 | registerProviders(providers, http.GetProviders()) 29 | registerProviders(providers, kube.GetProviders()) 30 | registerProviders(providers, metrics.GetProviders()) 31 | registerProviders(providers, time.GetProviders()) 32 | registerProviders(providers, util.GetProviders()) 33 | registerProviders(providers, workspace.GetProviders()) 34 | return providers 35 | } 36 | 37 | // GetLegacyTemplate get legacy template 38 | func GetLegacyTemplate() string { 39 | return strings.Join([]string{ 40 | email.GetTemplate(), 41 | http.GetTemplate(), 42 | kube.GetTemplate(), 43 | metrics.GetTemplate(), 44 | time.GetTemplate(), 45 | util.GetTemplate(), 46 | workspace.GetTemplate(), 47 | }, 48 | "\n") 49 | } 50 | -------------------------------------------------------------------------------- /pkg/providers/legacy/metrics/metrics.cue: -------------------------------------------------------------------------------- 1 | // metrics.cue 2 | 3 | #PromCheck: { 4 | #do: "promCheck" 5 | #provider: "op" 6 | 7 | query: string 8 | metricEndpoint: *"http://prometheus-server.o11y-system.svc:9090" | string 9 | condition: string 10 | failDuration: *"2m" | string 11 | duration: *"5m" | string 12 | ... 13 | } 14 | -------------------------------------------------------------------------------- /pkg/providers/legacy/time/time.cue: -------------------------------------------------------------------------------- 1 | // time.cue 2 | 3 | #DateToTimestamp: { 4 | #do: "timestamp" 5 | #provider: "op" 6 | 7 | date: string 8 | layout: *"" | string 9 | 10 | timestamp?: int64 11 | ... 12 | } 13 | 14 | #TimestampToDate: { 15 | #do: "date" 16 | #provider: "op" 17 | 18 | timestamp: int64 19 | layout: *"" | string 20 | 21 | date?: string 22 | ... 23 | } 24 | -------------------------------------------------------------------------------- /pkg/providers/legacy/time/time.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package time 18 | 19 | import ( 20 | "context" 21 | _ "embed" 22 | "fmt" 23 | "time" 24 | 25 | cuexruntime "github.com/kubevela/pkg/cue/cuex/runtime" 26 | providertypes "github.com/kubevela/workflow/pkg/providers/types" 27 | ) 28 | 29 | // Vars . 30 | type Vars struct { 31 | Date string `json:"date,omitempty"` 32 | Timestamp int64 `json:"timestamp,omitempty"` 33 | Layout string `json:"layout,omitempty"` 34 | } 35 | 36 | // Params . 37 | type Params = providertypes.LegacyParams[Vars] 38 | 39 | // Timestamp convert date to timestamp 40 | func Timestamp(_ context.Context, params *Params) (*Vars, error) { 41 | date := params.Params.Date 42 | layout := params.Params.Layout 43 | if date == "" { 44 | return nil, fmt.Errorf("empty date to convert") 45 | } 46 | if layout == "" { 47 | layout = time.RFC3339 48 | } 49 | t, err := time.Parse(layout, date) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return &Vars{ 54 | Timestamp: t.Unix(), 55 | }, nil 56 | } 57 | 58 | // Date convert timestamp to date 59 | func Date(_ context.Context, params *Params) (*Vars, error) { 60 | timestamp := params.Params.Timestamp 61 | layout := params.Params.Layout 62 | if layout == "" { 63 | layout = time.RFC3339 64 | } 65 | t := time.Unix(timestamp, 0) 66 | return &Vars{ 67 | Date: t.UTC().Format(layout), 68 | }, nil 69 | } 70 | 71 | //go:embed time.cue 72 | var template string 73 | 74 | // GetTemplate return the template 75 | func GetTemplate() string { 76 | return template 77 | } 78 | 79 | // GetProviders return the provider 80 | func GetProviders() map[string]cuexruntime.ProviderFn { 81 | return map[string]cuexruntime.ProviderFn{ 82 | "timestamp": providertypes.LegacyGenericProviderFn[Vars, Vars](Timestamp), 83 | "date": providertypes.LegacyGenericProviderFn[Vars, Vars](Date), 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /pkg/providers/legacy/util/util.cue: -------------------------------------------------------------------------------- 1 | // util.cue 2 | 3 | #PatchK8sObject: { 4 | #do: "patch-k8s-object" 5 | #provider: "op" 6 | value: {...} 7 | patch: {...} 8 | result: {...} 9 | ... 10 | } 11 | 12 | #ConvertString: { 13 | #do: "string" 14 | #provider: "op" 15 | 16 | bt: bytes 17 | str?: string 18 | ... 19 | } 20 | 21 | #Log: { 22 | #do: "log" 23 | #provider: "op" 24 | 25 | // +usage=The data to print in the controller logs 26 | data?: {...} | string 27 | // +usage=The log level of the data 28 | level: *3 | int 29 | // +usage=The log source of this step. You can specify it from a url or resources. Note that if you set source in multiple op.#Log, only the latest one will work 30 | source?: close({ 31 | // +usage=Specify the log source url of this step 32 | url: string 33 | }) | close({ 34 | // +usage=Specify the log resources of this step 35 | resources?: [...{ 36 | // +usage=Specify the name of the resource 37 | name?: string 38 | // +usage=Specify the cluster of the resource 39 | cluster?: string 40 | // +usage=Specify the namespace of the resource 41 | namespace?: string 42 | // +usage=Specify the label selector of the resource 43 | labelSelector?: {...} 44 | }] 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/providers/legacy/workspace/workspace.cue: -------------------------------------------------------------------------------- 1 | // workspace.cue 2 | 3 | #DoVar: { 4 | #do: "var" 5 | #provider: "op" 6 | 7 | // +usage=The method to call on the variable 8 | method: *"Get" | "Put" 9 | // +usage=The path to the variable 10 | path: string 11 | // +usage=The value of the variable 12 | value?: _ 13 | } 14 | 15 | #ConditionalWait: { 16 | #do: "wait" 17 | #provider: "op" 18 | 19 | // +usage=If continue is false, the step will wait for continue to be true. 20 | continue: *false | bool 21 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 22 | message?: string 23 | } 24 | 25 | #Suspend: { 26 | #do: "suspend" 27 | #provider: "op" 28 | 29 | // +usage=Specify the wait duration time to resume automaticlly such as "30s", "1min" or "2m15s" 30 | duration?: string 31 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 32 | message?: string 33 | } 34 | 35 | #Break: { 36 | #do: "break" 37 | #provider: "op" 38 | 39 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 40 | message?: string 41 | } 42 | 43 | #Fail: { 44 | #do: "fail" 45 | #provider: "op" 46 | 47 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 48 | message?: string 49 | } 50 | 51 | #Message: { 52 | #do: "message" 53 | #provider: "op" 54 | 55 | // +usage=Optional message that will be shown in workflow step status, note that the message might be override by other actions. 56 | message?: string 57 | } 58 | 59 | #Steps: { 60 | ... 61 | } 62 | NoExist: _|_ 63 | context: _ 64 | -------------------------------------------------------------------------------- /pkg/providers/metrics/metrics.cue: -------------------------------------------------------------------------------- 1 | // metrics.cue 2 | 3 | #PromCheck: { 4 | #do: "promCheck" 5 | #provider: "metrics" 6 | 7 | $params: { 8 | query: string 9 | metricEndpoint: *"http://prometheus-server.o11y-system.svc:9090" | string 10 | condition: string 11 | failDuration: *"2m" | string 12 | duration: *"5m" | string 13 | } 14 | 15 | $returns?: { 16 | message?: string 17 | failed: bool 18 | result: bool 19 | } 20 | ... 21 | } 22 | -------------------------------------------------------------------------------- /pkg/providers/time/time.cue: -------------------------------------------------------------------------------- 1 | // time.cue 2 | 3 | #DateToTimestamp: { 4 | #do: "timestamp" 5 | #provider: "time" 6 | 7 | $params: { 8 | date: string 9 | layout: *"" | string 10 | } 11 | 12 | $returns?: { 13 | timestamp: int64 14 | } 15 | ... 16 | } 17 | 18 | #TimestampToDate: { 19 | #do: "date" 20 | #provider: "time" 21 | 22 | $params: { 23 | timestamp: int64 24 | layout: *"" | string 25 | } 26 | 27 | $returns?: { 28 | date: string 29 | } 30 | ... 31 | } 32 | -------------------------------------------------------------------------------- /pkg/providers/util/util.cue: -------------------------------------------------------------------------------- 1 | // util.cue 2 | 3 | #PatchK8sObject: { 4 | #do: "patch-k8s-object" 5 | #provider: "util" 6 | 7 | $params: { 8 | value: {...} 9 | patch: {...} 10 | } 11 | 12 | $returns?: { 13 | result: {...} 14 | } 15 | ... 16 | } 17 | 18 | #ConvertString: { 19 | #do: "string" 20 | #provider: "util" 21 | 22 | $params: { 23 | bt: bytes 24 | } 25 | 26 | $returns?: { 27 | str: string 28 | } 29 | ... 30 | } 31 | 32 | #Log: { 33 | #do: "log" 34 | #provider: "util" 35 | 36 | $params: { 37 | // +usage=The data to print in the controller logs 38 | data?: {...} | string 39 | // +usage=The log level of the data 40 | level: *3 | int 41 | // +usage=The log source of this step. You can specify it from a url or resources. Note that if you set source in multiple util.#Log, only the latest one will work 42 | source?: close({ 43 | // +usage=Specify the log source url of this step 44 | url: string 45 | }) | close({ 46 | // +usage=Specify the log resources of this step 47 | resources?: [...{ 48 | // +usage=Specify the name of the resource 49 | name?: string 50 | // +usage=Specify the cluster of the resource 51 | cluster?: string 52 | // +usage=Specify the namespace of the resource 53 | namespace?: string 54 | // +usage=Specify the label selector of the resource 55 | labelSelector?: {...} 56 | }] 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pkg/tasks/discover.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package tasks 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/pkg/errors" 23 | 24 | monitorContext "github.com/kubevela/pkg/monitor/context" 25 | 26 | "github.com/kubevela/workflow/pkg/tasks/builtin" 27 | "github.com/kubevela/workflow/pkg/tasks/custom" 28 | "github.com/kubevela/workflow/pkg/types" 29 | ) 30 | 31 | type taskDiscover struct { 32 | builtin map[string]types.TaskGenerator 33 | customTaskDiscover *custom.TaskLoader 34 | } 35 | 36 | // NewTaskDiscover new task discover 37 | func NewTaskDiscover(ctx monitorContext.Context, options types.StepGeneratorOptions) types.TaskDiscover { //nolint:revive,unused 38 | return &taskDiscover{ 39 | builtin: map[string]types.TaskGenerator{ 40 | types.WorkflowStepTypeStepGroup: builtin.StepGroup, 41 | }, 42 | customTaskDiscover: custom.NewTaskLoader(options.TemplateLoader.LoadTemplate, options.LogLevel, options.ProcessCtx, options.Compiler), 43 | } 44 | } 45 | 46 | // GetTaskGenerator get task generator by name. 47 | func (td *taskDiscover) GetTaskGenerator(ctx context.Context, name string) (types.TaskGenerator, error) { 48 | tg, ok := td.builtin[name] 49 | if ok { 50 | return tg, nil 51 | } 52 | if td.customTaskDiscover != nil { 53 | var err error 54 | tg, err = td.customTaskDiscover.GetTaskGenerator(ctx, name) 55 | if err != nil { 56 | return nil, err 57 | } 58 | return tg, nil 59 | 60 | } 61 | return nil, errors.Errorf("can't find task generator: %s", name) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/tasks/discover_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package tasks 18 | 19 | import ( 20 | "context" 21 | "testing" 22 | 23 | "github.com/pkg/errors" 24 | "github.com/stretchr/testify/require" 25 | 26 | "github.com/kubevela/workflow/pkg/cue/process" 27 | "github.com/kubevela/workflow/pkg/tasks/builtin" 28 | "github.com/kubevela/workflow/pkg/tasks/custom" 29 | "github.com/kubevela/workflow/pkg/types" 30 | ) 31 | 32 | func TestDiscover(t *testing.T) { 33 | r := require.New(t) 34 | makeErr := func(name string) error { 35 | return errors.Errorf("template %s not found", name) 36 | } 37 | 38 | loadTemplate := func(ctx context.Context, name string) (string, error) { 39 | switch name { 40 | case "foo": 41 | return "", nil 42 | case "crazy": 43 | return "", nil 44 | default: 45 | return "", makeErr(name) 46 | } 47 | } 48 | pCtx := process.NewContext(process.ContextData{ 49 | Namespace: "default", 50 | }) 51 | discover := &taskDiscover{ 52 | builtin: map[string]types.TaskGenerator{ 53 | "stepGroup": builtin.StepGroup, 54 | }, 55 | customTaskDiscover: custom.NewTaskLoader(loadTemplate, 0, pCtx, nil), 56 | } 57 | 58 | _, err := discover.GetTaskGenerator(context.Background(), "stepGroup") 59 | r.NoError(err) 60 | _, err = discover.GetTaskGenerator(context.Background(), "foo") 61 | r.NoError(err) 62 | _, err = discover.GetTaskGenerator(context.Background(), "crazy") 63 | r.NoError(err) 64 | _, err = discover.GetTaskGenerator(context.Background(), "fly") 65 | r.Equal(err.Error(), makeErr("fly").Error()) 66 | 67 | } 68 | -------------------------------------------------------------------------------- /pkg/tasks/template/load_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package template 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "os" 23 | "testing" 24 | 25 | "github.com/crossplane/crossplane-runtime/pkg/test" 26 | "github.com/kubevela/pkg/util/singleton" 27 | "github.com/stretchr/testify/require" 28 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 29 | "sigs.k8s.io/controller-runtime/pkg/client" 30 | "sigs.k8s.io/yaml" 31 | ) 32 | 33 | func TestLoad(t *testing.T) { 34 | cli := &test.MockClient{ 35 | MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 36 | o, ok := obj.(*unstructured.Unstructured) 37 | if !ok { 38 | return nil 39 | } 40 | var d map[string]interface{} 41 | js, err := yaml.YAMLToJSON([]byte(stepDefYaml)) 42 | if err != nil { 43 | return err 44 | } 45 | if err := json.Unmarshal(js, &d); err != nil { 46 | return err 47 | } 48 | o.Object = d 49 | return nil 50 | }, 51 | } 52 | singleton.KubeClient.Set(cli) 53 | loader := NewWorkflowStepTemplateLoader() 54 | 55 | r := require.New(t) 56 | tmpl, err := loader.LoadTemplate(context.Background(), "builtin-apply-component") 57 | r.NoError(err) 58 | expected, err := os.ReadFile("./static/builtin-apply-component.cue") 59 | r.NoError(err) 60 | r.Equal(tmpl, string(expected)) 61 | 62 | tmpl, err = loader.LoadTemplate(context.Background(), "apply-oam-component") 63 | r.NoError(err) 64 | r.Equal(tmpl, `import ( 65 | "vela/op" 66 | ) 67 | 68 | // apply components and traits 69 | apply: op.#ApplyComponent & { 70 | component: parameter.component 71 | } 72 | parameter: { 73 | // +usage=Declare the name of the component 74 | component: string 75 | }`) 76 | } 77 | 78 | var ( 79 | stepDefYaml = `apiVersion: core.oam.dev/v1beta1 80 | kind: WorkflowStepDefinition 81 | metadata: 82 | annotations: 83 | definition.oam.dev/description: Apply components and traits for your workflow steps 84 | name: apply-oam-component 85 | namespace: vela-system 86 | spec: 87 | schematic: 88 | cue: 89 | template: | 90 | import ( 91 | "vela/op" 92 | ) 93 | 94 | // apply components and traits 95 | apply: op.#ApplyComponent & { 96 | component: parameter.component 97 | } 98 | parameter: { 99 | // +usage=Declare the name of the component 100 | component: string 101 | }` 102 | ) 103 | -------------------------------------------------------------------------------- /pkg/tasks/template/static/builtin-apply-component.cue: -------------------------------------------------------------------------------- 1 | import ( 2 | "vela/op" 3 | ) 4 | 5 | oam: op.oam 6 | // apply component and traits 7 | apply: oam.#ApplyComponent & { 8 | value: parameter 9 | } 10 | 11 | if apply.output != _|_ { 12 | output: apply.output 13 | } 14 | 15 | if apply.outputs != _|_ { 16 | outputs: apply.outputs 17 | } 18 | parameter: {...} 19 | -------------------------------------------------------------------------------- /pkg/tasks/template/static/suspend.cue: -------------------------------------------------------------------------------- 1 | import ( 2 | "vela/op" 3 | ) 4 | 5 | suspend: op.#Suspend & { 6 | if parameter.duration != _|_ { 7 | duration: parameter.duration 8 | } 9 | if parameter.message != _|_ { 10 | message: parameter.message 11 | } 12 | } 13 | 14 | parameter: { 15 | duration?: string 16 | message?: string 17 | } 18 | -------------------------------------------------------------------------------- /pkg/utils/main_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import ( 20 | "testing" 21 | 22 | "k8s.io/client-go/kubernetes/scheme" 23 | "sigs.k8s.io/controller-runtime/pkg/client" 24 | "sigs.k8s.io/controller-runtime/pkg/client/fake" 25 | 26 | "github.com/kubevela/pkg/util/singleton" 27 | "github.com/kubevela/workflow/api/v1alpha1" 28 | ) 29 | 30 | var ( 31 | cli client.Client 32 | ) 33 | 34 | func TestMain(m *testing.M) { 35 | sc := scheme.Scheme 36 | _ = v1alpha1.AddToScheme(sc) 37 | cli = fake.NewClientBuilder().WithScheme(sc).WithStatusSubresource(&v1alpha1.WorkflowRun{}).Build() 38 | singleton.KubeClient.Set(cli) 39 | m.Run() 40 | } 41 | -------------------------------------------------------------------------------- /pkg/utils/recycle_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "testing" 23 | "time" 24 | 25 | "github.com/stretchr/testify/require" 26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | 28 | "github.com/kubevela/workflow/api/v1alpha1" 29 | ) 30 | 31 | func TestRecycleCronJob(t *testing.T) { 32 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 33 | defer cancel() 34 | r := require.New(t) 35 | for i := 1; i < 7; i++ { 36 | run := &v1alpha1.WorkflowRun{ 37 | ObjectMeta: metav1.ObjectMeta{ 38 | Name: fmt.Sprintf("workflow-test-%d", i), 39 | Namespace: "default", 40 | }, 41 | } 42 | if i%5 != 0 { 43 | run.Labels = map[string]string{ 44 | "pipeline.oam.dev/name": "test", 45 | } 46 | } 47 | err := cli.Create(ctx, run) 48 | r.NoError(err) 49 | run.Status.Finished = i%6 != 0 50 | run.Status.EndTime = metav1.Time{Time: time.Now().AddDate(0, 0, -i)} 51 | err = cli.Status().Update(ctx, run) 52 | r.NoError(err) 53 | defer cli.Delete(ctx, run) 54 | } 55 | 56 | runner := NewRecycleCronJob(cli, time.Hour, "@every 1s", "pipeline.oam.dev/name") 57 | err := runner.Start(ctx) 58 | r.NoError(err) 59 | runs := &v1alpha1.WorkflowRunList{} 60 | err = cli.List(ctx, runs) 61 | r.NoError(err) 62 | r.Equal(3, len(runs.Items)) 63 | } 64 | -------------------------------------------------------------------------------- /pkg/webhook/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package webhook 18 | 19 | import ( 20 | "sigs.k8s.io/controller-runtime/pkg/manager" 21 | "sigs.k8s.io/controller-runtime/pkg/webhook/conversion" 22 | 23 | "github.com/kubevela/workflow/controllers" 24 | "github.com/kubevela/workflow/pkg/webhook/v1alpha1/workflowrun" 25 | ) 26 | 27 | // Register will be called in main and register all validation handlers 28 | func Register(mgr manager.Manager, args controllers.Args) { 29 | workflowrun.RegisterValidatingHandler(mgr, args) 30 | workflowrun.RegisterMutatingHandler(mgr) 31 | 32 | server := mgr.GetWebhookServer() 33 | server.Register("/convert", conversion.NewWebhookHandler(mgr.GetScheme())) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/webhook/v1alpha1/workflowrun/mutating_handler.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package workflowrun 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | "net/http" 24 | 25 | "sigs.k8s.io/controller-runtime/pkg/manager" 26 | "sigs.k8s.io/controller-runtime/pkg/webhook" 27 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 28 | 29 | "github.com/kubevela/workflow/api/v1alpha1" 30 | ) 31 | 32 | // MutatingHandler adding user info to application annotations 33 | type MutatingHandler struct { 34 | Decoder *admission.Decoder 35 | } 36 | 37 | var _ admission.Handler = &MutatingHandler{} 38 | 39 | // Handle mutate application 40 | func (h *MutatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response { //nolint:revive,unused 41 | wr := &v1alpha1.WorkflowRun{} 42 | if err := h.Decoder.Decode(req, wr); err != nil { 43 | return admission.Errored(http.StatusBadRequest, err) 44 | } 45 | if wr.Spec.WorkflowSpec != nil { 46 | for i, step := range wr.Spec.WorkflowSpec.Steps { 47 | if step.Name == "" { 48 | wr.Spec.WorkflowSpec.Steps[i].Name = fmt.Sprintf("step-%d", i) 49 | } 50 | for j, sub := range step.SubSteps { 51 | if sub.Name == "" { 52 | wr.Spec.WorkflowSpec.Steps[i].SubSteps[j].Name = fmt.Sprintf("step-%d-%d", i, j) 53 | } 54 | } 55 | } 56 | } 57 | bs, err := json.Marshal(wr) 58 | if err != nil { 59 | return admission.Errored(http.StatusInternalServerError, err) 60 | } 61 | return admission.PatchResponseFromRaw(req.AdmissionRequest.Object.Raw, bs) 62 | } 63 | 64 | // RegisterMutatingHandler will register workflow mutation handler to the webhook 65 | func RegisterMutatingHandler(mgr manager.Manager) { 66 | server := mgr.GetWebhookServer() 67 | server.Register("/mutating-core-oam-dev-v1alpha1-workflowruns", &webhook.Admission{Handler: &MutatingHandler{ 68 | Decoder: admission.NewDecoder(mgr.GetScheme()), 69 | }}) 70 | } 71 | -------------------------------------------------------------------------------- /pkg/webhook/v1alpha1/workflowrun/mutating_handler_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package workflowrun 18 | 19 | import ( 20 | . "github.com/onsi/ginkgo" 21 | . "github.com/onsi/gomega" 22 | "gomodules.xyz/jsonpatch/v2" 23 | admissionv1 "k8s.io/api/admission/v1" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/runtime" 26 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 27 | ) 28 | 29 | var _ = Describe("Test WorkflowRun Mutator", func() { 30 | 31 | var mutatingHandler *MutatingHandler 32 | 33 | BeforeEach(func() { 34 | mutatingHandler = &MutatingHandler{ 35 | Decoder: decoder, 36 | } 37 | }) 38 | 39 | It("Test WorkflowRun Mutator [bad request]", func() { 40 | req := admission.Request{ 41 | AdmissionRequest: admissionv1.AdmissionRequest{ 42 | Operation: admissionv1.Create, 43 | Resource: metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1alpha1", Resource: "workflowruns"}, 44 | Object: runtime.RawExtension{Raw: []byte("bad request")}, 45 | }, 46 | } 47 | resp := mutatingHandler.Handle(ctx, req) 48 | Expect(resp.Allowed).Should(BeFalse()) 49 | }) 50 | 51 | It("Test WorkflowRun Mutator [with patch]", func() { 52 | req := admission.Request{ 53 | AdmissionRequest: admissionv1.AdmissionRequest{ 54 | Operation: admissionv1.Create, 55 | Resource: metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1alpha1", Resource: "workflowruns"}, 56 | Object: runtime.RawExtension{ 57 | Raw: []byte( 58 | `{"apiVersion":"core.oam.dev/v1alpha1","kind":"WorkflowRun","metadata":{"name":"wr-sample"},"spec":{"workflowSpec":{"steps":[{"properties":{"duration":"3s"},"type":"suspend"}]}}}`), 59 | }, 60 | }, 61 | } 62 | resp := mutatingHandler.Handle(ctx, req) 63 | Expect(resp.Allowed).Should(BeTrue()) 64 | Expect(resp.Patches).Should(ContainElement(jsonpatch.JsonPatchOperation{ 65 | Operation: "add", 66 | Path: "/spec/workflowSpec/steps/0/name", 67 | Value: "step-0", 68 | })) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /pkg/webhook/v1alpha1/workflowrun/validating_handler.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package workflowrun 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "net/http" 23 | 24 | admissionv1 "k8s.io/api/admission/v1" 25 | "k8s.io/apimachinery/pkg/util/validation/field" 26 | "sigs.k8s.io/controller-runtime/pkg/client" 27 | "sigs.k8s.io/controller-runtime/pkg/manager" 28 | "sigs.k8s.io/controller-runtime/pkg/webhook" 29 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 30 | 31 | "github.com/kubevela/workflow/api/v1alpha1" 32 | "github.com/kubevela/workflow/pkg/types" 33 | 34 | "github.com/kubevela/workflow/controllers" 35 | ) 36 | 37 | var _ admission.Handler = &ValidatingHandler{} 38 | 39 | // ValidatingHandler handles application 40 | type ValidatingHandler struct { 41 | Client client.Client 42 | // Decoder decodes objects 43 | Decoder *admission.Decoder 44 | } 45 | 46 | func mergeErrors(errs field.ErrorList) error { 47 | s := "" 48 | for _, err := range errs { 49 | s += fmt.Sprintf("field \"%s\": %s error encountered, %s. ", err.Field, err.Type, err.Detail) 50 | } 51 | return fmt.Errorf("%s", s) 52 | } 53 | 54 | // Handle validate Application Spec here 55 | func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response { 56 | wr := &v1alpha1.WorkflowRun{} 57 | if err := h.Decoder.Decode(req, wr); err != nil { 58 | return admission.Errored(http.StatusBadRequest, err) 59 | } 60 | ctx = types.SetNamespaceInCtx(ctx, wr.Namespace) 61 | switch req.Operation { 62 | case admissionv1.Create: 63 | if allErrs := h.ValidateWorkflow(ctx, wr); len(allErrs) > 0 { 64 | // http.StatusUnprocessableEntity will NOT report any error descriptions 65 | // to the client, use generic http.StatusBadRequest instead. 66 | return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs)) 67 | } 68 | case admissionv1.Update: 69 | if wr.ObjectMeta.DeletionTimestamp.IsZero() { 70 | if allErrs := h.ValidateWorkflow(ctx, wr); len(allErrs) > 0 { 71 | return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs)) 72 | } 73 | } 74 | default: 75 | // Do nothing for DELETE and CONNECT 76 | } 77 | return admission.ValidationResponse(true, "") 78 | } 79 | 80 | // RegisterValidatingHandler will register application validate handler to the webhook 81 | func RegisterValidatingHandler(mgr manager.Manager, _ controllers.Args) { 82 | server := mgr.GetWebhookServer() 83 | server.Register("/validating-core-oam-dev-v1alpha1-workflowruns", &webhook.Admission{Handler: &ValidatingHandler{ 84 | Client: mgr.GetClient(), 85 | Decoder: admission.NewDecoder(mgr.GetScheme()), 86 | }}) 87 | } 88 | -------------------------------------------------------------------------------- /pkg/webhook/v1alpha1/workflowrun/validation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package workflowrun 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "time" 23 | 24 | "k8s.io/apimachinery/pkg/util/validation/field" 25 | "sigs.k8s.io/controller-runtime/pkg/client" 26 | 27 | "github.com/kubevela/workflow/api/v1alpha1" 28 | ) 29 | 30 | // ValidateWorkflow validates the Application workflow 31 | func (h *ValidatingHandler) ValidateWorkflow(ctx context.Context, wr *v1alpha1.WorkflowRun) field.ErrorList { 32 | var errs field.ErrorList 33 | var steps []v1alpha1.WorkflowStep 34 | if wr.Spec.WorkflowSpec != nil { 35 | steps = wr.Spec.WorkflowSpec.Steps 36 | } else { 37 | w := &v1alpha1.Workflow{} 38 | if err := h.Client.Get(ctx, client.ObjectKey{Namespace: wr.Namespace, Name: wr.Spec.WorkflowRef}, w); err != nil { 39 | errs = append(errs, field.Invalid(field.NewPath("spec", "workflowRef"), wr.Spec.WorkflowRef, fmt.Sprintf("failed to get workflow ref: %v", err))) 40 | return errs 41 | } 42 | steps = w.Steps 43 | } 44 | stepName := make(map[string]interface{}) 45 | for _, step := range steps { 46 | if step.Name == "" { 47 | errs = append(errs, field.Invalid(field.NewPath("spec", "workflowSpec", "steps", "name"), step.Name, "empty step name")) 48 | } 49 | if _, ok := stepName[step.Name]; ok { 50 | errs = append(errs, field.Invalid(field.NewPath("spec", "workflowSpec", "steps"), step.Name, "duplicated step name")) 51 | } 52 | stepName[step.Name] = nil 53 | if step.Timeout != "" { 54 | errs = append(errs, h.ValidateTimeout(step.Name, step.Timeout)...) 55 | } 56 | for _, sub := range step.SubSteps { 57 | if sub.Name == "" { 58 | errs = append(errs, field.Invalid(field.NewPath("spec", "workflowSpec", "steps", "subSteps", "name"), sub.Name, "empty step name")) 59 | } 60 | if _, ok := stepName[sub.Name]; ok { 61 | errs = append(errs, field.Invalid(field.NewPath("spec", "workflowSpec", "steps", "subSteps"), sub.Name, "duplicated step name")) 62 | } 63 | stepName[sub.Name] = nil 64 | if step.Timeout != "" { 65 | errs = append(errs, h.ValidateTimeout(step.Name, step.Timeout)...) 66 | } 67 | } 68 | } 69 | return errs 70 | } 71 | 72 | // ValidateTimeout validates the timeout of steps 73 | func (h *ValidatingHandler) ValidateTimeout(name, timeout string) field.ErrorList { 74 | var errs field.ErrorList 75 | _, err := time.ParseDuration(timeout) 76 | if err != nil { 77 | errs = append(errs, field.Invalid(field.NewPath("spec", "workflowSpec", "steps", "timeout"), name, "invalid timeout, please use the format of timeout like 1s, 1m, 1h or 1d")) 78 | } 79 | return errs 80 | } 81 | -------------------------------------------------------------------------------- /staticcheck.conf: -------------------------------------------------------------------------------- 1 | # This is config file for staticcheck. 2 | # If you need to add ignored checks, pls also add explaination in comments. 3 | 4 | checks = ["all", "-ST1000", "-ST1003", "-ST1016", "-SA1019"] 5 | 6 | # ST1000 - Incorrect or missing package comment (non-default) 7 | # ST1003 – Poorly chosen identifier (non-default) 8 | # ST1016 – Use consistent method receiver names (non-default) 9 | # SA1019 – Using a deprecated function, variable, constant or field (non-default), ignore it for now for cue upgrades (TODO: remove it after CUE compatible) -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The KubeVela Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package version 18 | 19 | import "github.com/hashicorp/go-version" 20 | 21 | // GitRevision is the commit of repo 22 | var GitRevision = "UNKNOWN" 23 | 24 | // VelaVersion is the version of cli. 25 | var VelaVersion = "UNKNOWN" 26 | 27 | // IsOfficialWorkflowVersion checks whether the provided version string follows a KubeVela Workflow version pattern 28 | func IsOfficialWorkflowVersion(versionStr string) bool { 29 | _, err := version.NewSemver(versionStr) 30 | return err == nil 31 | } 32 | 33 | // GetOfficialWorkflowVersion extracts the KubeVela Workflow version from the provided string 34 | // More precisely, this method returns the segments and prerelease info w/o metadata 35 | func GetOfficialWorkflowVersion(versionStr string) (string, error) { 36 | s, err := version.NewSemver(versionStr) 37 | if err != nil { 38 | return "", err 39 | } 40 | v := s.String() 41 | metadata := s.Metadata() 42 | if metadata != "" { 43 | metadata = "+" + metadata 44 | } 45 | return v[:len(v)-len(metadata)], nil 46 | } 47 | --------------------------------------------------------------------------------