├── .env.example ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── README.md ├── SUPPORT.md ├── renovate.json └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ ├── prerelease.yml │ ├── publish.yml │ ├── reviewdog.yml │ ├── spec.yml │ ├── test.yml │ ├── validate-pr-title.yml │ └── validate.yml ├── .gitignore ├── .golangci.yml ├── DOCS.md ├── Dockerfile ├── Dockerfile-alpine ├── LICENSE ├── Makefile ├── PROJECT ├── api ├── build.go ├── doc.go ├── executor.go ├── health.go ├── metrics.go ├── pipeline.go ├── register.go ├── repo.go ├── shutdown.go └── version.go ├── cmd └── vela-worker │ ├── client.go │ ├── exec.go │ ├── flags.go │ ├── main.go │ ├── operate.go │ ├── register.go │ ├── run.go │ ├── server.go │ ├── start.go │ ├── validate.go │ └── worker.go ├── codecov.yml ├── docker-compose.yml ├── executor ├── context.go ├── context_test.go ├── doc.go ├── engine.go ├── executor.go ├── executor_test.go ├── flags.go ├── linux │ ├── api.go │ ├── api_test.go │ ├── build.go │ ├── build_test.go │ ├── doc.go │ ├── driver.go │ ├── driver_test.go │ ├── linux.go │ ├── linux_test.go │ ├── opts.go │ ├── opts_test.go │ ├── outputs.go │ ├── outputs_test.go │ ├── secret.go │ ├── secret_test.go │ ├── service.go │ ├── service_test.go │ ├── stage.go │ ├── stage_test.go │ ├── step.go │ ├── step_test.go │ └── testdata │ │ ├── build │ │ ├── empty.yml │ │ ├── secrets │ │ │ ├── basic.yml │ │ │ ├── img_ignorenotfound.yml │ │ │ ├── img_notfound.yml │ │ │ └── name_notfound.yml │ │ ├── services │ │ │ ├── basic.yml │ │ │ ├── img_environmentdynamic.yml │ │ │ ├── img_ignorenotfound.yml │ │ │ ├── img_notfound.yml │ │ │ ├── name_init.yml │ │ │ └── name_notfound.yml │ │ ├── stages │ │ │ ├── basic.yml │ │ │ ├── img_environmentdynamic.yml │ │ │ ├── img_ignorenotfound.yml │ │ │ ├── img_notfound.yml │ │ │ ├── name_init.yml │ │ │ └── name_notfound.yml │ │ └── steps │ │ │ ├── basic.yml │ │ │ ├── basic_secrets.yml │ │ │ ├── img_environmentdynamic.yml │ │ │ ├── img_ignorenotfound.yml │ │ │ ├── img_notfound.yml │ │ │ ├── name_init.yml │ │ │ └── name_notfound.yml │ │ ├── secret │ │ ├── basic.yml │ │ └── name_notfound.yml │ │ └── step │ │ └── secret_text.txt ├── local │ ├── api.go │ ├── api_test.go │ ├── build.go │ ├── build_test.go │ ├── doc.go │ ├── driver.go │ ├── driver_test.go │ ├── local.go │ ├── local_test.go │ ├── opts.go │ ├── opts_test.go │ ├── outputs.go │ ├── outputs_test.go │ ├── service.go │ ├── service_test.go │ ├── stage.go │ ├── stage_test.go │ ├── step.go │ ├── step_test.go │ └── testdata │ │ └── build │ │ ├── empty.yml │ │ ├── services │ │ ├── basic.yml │ │ ├── img_ignorenotfound.yml │ │ ├── img_notfound.yml │ │ └── name_notfound.yml │ │ ├── stages │ │ ├── basic.yml │ │ ├── img_ignorenotfound.yml │ │ ├── img_notfound.yml │ │ └── name_notfound.yml │ │ └── steps │ │ ├── basic.yml │ │ ├── img_ignorenotfound.yml │ │ ├── img_notfound.yml │ │ └── name_notfound.yml ├── setup.go └── setup_test.go ├── go.mod ├── go.sum ├── internal ├── build │ ├── doc.go │ ├── snapshot.go │ ├── snapshot_test.go │ ├── upload.go │ └── upload_test.go ├── context │ ├── context.go │ ├── context_test.go │ └── doc.go ├── doc.go ├── image │ ├── doc.go │ ├── image.go │ └── image_test.go ├── internal.go ├── message │ ├── doc.go │ └── stream.go ├── outputs │ ├── doc.go │ ├── sanitize.go │ └── sanitize_test.go ├── service │ ├── doc.go │ ├── environment.go │ ├── environment_test.go │ ├── load.go │ ├── load_test.go │ ├── snapshot.go │ ├── snapshot_test.go │ ├── upload.go │ └── upload_test.go ├── step │ ├── doc.go │ ├── environment.go │ ├── environment_test.go │ ├── load.go │ ├── load_test.go │ ├── skip.go │ ├── skip_test.go │ ├── snapshot.go │ ├── snapshot_test.go │ ├── upload.go │ └── upload_test.go └── volume │ ├── doc.go │ ├── volume.go │ └── volume_test.go ├── mock ├── doc.go ├── docker │ ├── config.go │ ├── container.go │ ├── distribution.go │ ├── doc.go │ ├── docker.go │ ├── image.go │ ├── mock.go │ ├── network.go │ ├── node.go │ ├── plugin.go │ ├── secret.go │ ├── service.go │ ├── swarm.go │ ├── system.go │ └── volume.go ├── mock.go └── worker │ ├── build.go │ ├── doc.go │ ├── executor.go │ ├── pipeline.go │ ├── register.go │ ├── repo.go │ └── server.go ├── router ├── build.go ├── build_test.go ├── doc.go ├── executor.go ├── executor_test.go ├── middleware │ ├── doc.go │ ├── executor.go │ ├── executor │ │ ├── executor.go │ │ └── executor_test.go │ ├── executor_test.go │ ├── header.go │ ├── header_test.go │ ├── logger.go │ ├── logger_test.go │ ├── payload.go │ ├── payload_test.go │ ├── perm │ │ ├── doc.go │ │ ├── perm.go │ │ └── perm_test.go │ ├── register_token.go │ ├── register_token_test.go │ ├── server.go │ ├── server_test.go │ ├── token │ │ ├── doc.go │ │ ├── token.go │ │ └── token_test.go │ ├── worker.go │ └── worker_test.go ├── pipeline.go ├── pipeline_test.go ├── repo.go ├── repo_test.go ├── router.go └── router_test.go ├── runtime ├── context.go ├── context_test.go ├── doc.go ├── docker │ ├── build.go │ ├── build_test.go │ ├── container.go │ ├── container_test.go │ ├── doc.go │ ├── docker.go │ ├── docker_test.go │ ├── driver.go │ ├── driver_test.go │ ├── image.go │ ├── image_test.go │ ├── network.go │ ├── network_test.go │ ├── opts.go │ ├── opts_test.go │ ├── volume.go │ └── volume_test.go ├── engine.go ├── flags.go ├── kubernetes │ ├── apis │ │ ├── doc.go │ │ └── vela │ │ │ ├── register.go │ │ │ └── v1alpha1 │ │ │ ├── doc.go │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ └── zz_generated.deepcopy.go │ ├── build.go │ ├── build_test.go │ ├── codegen │ │ └── header.go.txt │ ├── container.go │ ├── container_test.go │ ├── doc.go │ ├── driver.go │ ├── driver_test.go │ ├── generated │ │ ├── clientset │ │ │ └── versioned │ │ │ │ ├── clientset.go │ │ │ │ ├── doc.go │ │ │ │ ├── fake │ │ │ │ ├── clientset_generated.go │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ ├── scheme │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ └── typed │ │ │ │ └── vela │ │ │ │ └── v1alpha1 │ │ │ │ ├── doc.go │ │ │ │ ├── fake │ │ │ │ ├── doc.go │ │ │ │ ├── fake_pipelinepodstemplate.go │ │ │ │ └── fake_vela_client.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── pipelinepodstemplate.go │ │ │ │ └── vela_client.go │ │ └── go-vela.github.io_pipelinepodstemplates.yaml │ ├── image.go │ ├── image_test.go │ ├── kubernetes.go │ ├── kubernetes_test.go │ ├── mock.go │ ├── network.go │ ├── network_test.go │ ├── opts.go │ ├── opts_test.go │ ├── pod_tracker.go │ ├── pod_tracker_test.go │ ├── testdata │ │ ├── config │ │ ├── config_empty │ │ ├── pipeline-pods-template-dns.yaml │ │ ├── pipeline-pods-template-empty.yaml │ │ ├── pipeline-pods-template-malformed.yaml │ │ ├── pipeline-pods-template-node-selection.yaml │ │ ├── pipeline-pods-template-security-context.yaml │ │ └── pipeline-pods-template.yaml │ ├── volume.go │ └── volume_test.go ├── runtime.go ├── runtime_test.go ├── setup.go ├── setup_test.go └── testdata │ ├── config │ ├── large.yml │ ├── stages.yml │ └── steps.yml └── version └── version.go /.env.example: -------------------------------------------------------------------------------- 1 | ################## 2 | # __ __ ___ # 3 | # | | | || | # 4 | # | | | || | # 5 | # | |_| || | # 6 | # | || | # 7 | # | || | # 8 | # |_______||___| # 9 | # # 10 | ################## 11 | 12 | # These are used by the ui service defined in the docker compose stack 13 | 14 | # customize the location for the Vela server address 15 | # 16 | # Should match the "VELA_ADDR" value in docker-compose.yml when running locally. 17 | VELA_API=http://localhost:8080 18 | 19 | # customize the location where users can review documentation 20 | # 21 | # default: https://go-vela.github.io/docs 22 | # VELA_DOCS_URL= 23 | 24 | # customize the location where you want users to provide feedback 25 | # 26 | # default: https://github.com/go-vela/ui/issues/new 27 | # VELA_FEEDBACK_URL= 28 | 29 | # customize the number of bytes for size of logs the UI will attempt to render 30 | # 31 | # default: 20000 (2 MB) 32 | # VELA_LOG_BYTES_LIMIT= 33 | 34 | # customize the number of concurrent builds for a repo the UI will allow configuring 35 | # 36 | # default: 30 37 | # VELA_MAX_BUILD_LIMIT= 38 | 39 | ############################################################ 40 | # _______ _______ ______ __ __ _______ ______ # 41 | # | || || _ | | | | || || _ | # 42 | # | _____|| ___|| | || | |_| || ___|| | || # 43 | # | |_____ | |___ | |_||_ | || |___ | |_||_ # 44 | # |_____ || ___|| __ || || ___|| __ | # 45 | # _____| || |___ | | | | | | | |___ | | | | # 46 | # |_______||_______||___| |_| |___| |_______||___| |_| # 47 | # # 48 | ############################################################ 49 | 50 | # These are used by the server service defined in the docker compose stack 51 | 52 | # github web url (only required if using GitHub Enterprise) 53 | # 54 | # default: https://github.com 55 | # VELA_SCM_ADDR= 56 | 57 | # github client id from oauth application 58 | # VELA_SCM_CLIENT= 59 | 60 | # github client secret from oauth application 61 | # VELA_SCM_SECRET= -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in the repo. 2 | * @go-vela/admins 3 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Getting Started 4 | 5 | We'd love to accept your contributions to this project! If you are a first time contributor, please review our [Contributing Guidelines](https://go-vela.github.io/docs/community/contributing_guidelines/) before proceeding. 6 | 7 | ### Prerequisites 8 | 9 | * [Review the local development docs](../DOCS.md) - ensures you have the Vela application stack running locally 10 | * [Review the commit guide we follow](https://chris.beams.io/posts/git-commit/#seven-rules) - ensure your commits follow our standards 11 | * Review our [style guide](https://go-vela.github.io/docs/community/contributing_guidelines/#style-guide) to ensure your code is clean and consistent. 12 | 13 | ### Setup 14 | 15 | * [Fork](/fork) this repository 16 | 17 | * Clone this repository to your workstation: 18 | 19 | ```bash 20 | # clone the project 21 | git clone git@github.com:go-vela/worker.git $HOME/go-vela/worker 22 | ``` 23 | 24 | * Navigate to the repository code: 25 | 26 | ```bash 27 | # change into the cloned project directory 28 | cd $HOME/go-vela/worker 29 | ``` 30 | 31 | * Point the original code at your fork: 32 | 33 | ```bash 34 | # add a remote branch pointing to your fork 35 | git remote add fork https://github.com/your_fork/worker 36 | ``` 37 | 38 | ### Development 39 | 40 | **Please review the [local development documentation](../DOCS.md) for more information.** 41 | 42 | * Navigate to the repository code: 43 | 44 | ```bash 45 | # change into the cloned project directory 46 | cd $HOME/go-vela/worker 47 | ``` 48 | 49 | * Write your code and tests to implement the changes you desire. 50 | 51 | * Run the repository code (ensures your changes perform as you desire): 52 | 53 | ```bash 54 | # execute the `up` target with `make` 55 | make up 56 | ``` 57 | 58 | * Test the repository code (ensures your changes don't break existing functionality): 59 | 60 | ```bash 61 | # execute the `test` target with `make` 62 | make test 63 | ``` 64 | 65 | * Clean the repository code (ensures your code meets the project standards): 66 | 67 | ```bash 68 | # execute the `clean` target with `make` 69 | make clean 70 | ``` 71 | 72 | * Push to your fork: 73 | 74 | ```bash 75 | # push your code up to your fork 76 | git push fork main 77 | ``` 78 | 79 | * Make sure to follow our [PR process](https://go-vela.github.io/docs/community/contributing_guidelines/#development-workflow) when opening a pull request 80 | 81 | Thank you for your contribution! 82 | -------------------------------------------------------------------------------- /.github/README.md: -------------------------------------------------------------------------------- 1 | # worker 2 | 3 | [![license](https://img.shields.io/crates/l/gl.svg)](../LICENSE) 4 | [![GoDoc](https://godoc.org/github.com/go-vela/worker?status.svg)](https://godoc.org/github.com/go-vela/worker) 5 | [![Go Report Card](https://goreportcard.com/badge/go-vela/worker)](https://goreportcard.com/report/go-vela/worker) 6 | [![codecov](https://codecov.io/gh/go-vela/worker/branch/main/graph/badge.svg)](https://codecov.io/gh/go-vela/worker) 7 | 8 | > Vela is in active development and is a pre-release product. 9 | > 10 | > Feel free to send us feedback at https://github.com/go-vela/community/issues/new. 11 | 12 | Vela is a Pipeline Automation (CI/CD) framework built on [Linux container](https://linuxcontainers.org/) technology written in [Golang](https://golang.org/). 13 | 14 | Vela uses a syntax similar to [Docker Compose](https://docs.docker.com/compose/) to define its configuration. This structure for repeated use, within the application, is called a pipeline and a single execution of a pipeline is referenced as a build. 15 | 16 | ## Documentation 17 | 18 | For installation and usage, please [visit our user docs](https://go-vela.github.io/docs). 19 | 20 | For local development, please [visit our repo docs](../DOCS.md). 21 | 22 | ## Contributing 23 | 24 | We are always welcome to new pull requests! 25 | 26 | Please see our [contributing](CONTRIBUTING.md) documentation for further instructions. 27 | 28 | ## Support 29 | 30 | We are always here to help! 31 | 32 | Please see our [support](SUPPORT.md) documentation for further instructions. 33 | 34 | ## Copyright and License 35 | 36 | ``` 37 | Copyright 2019 Target Brands, Inc. 38 | ``` 39 | 40 | [Apache License, Version 2.0](../LICENSE) 41 | 42 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | Welcome to Vela! To get help with a specific aspect of Vela, we've provided the below information. 4 | 5 | ## Bugs or Feature Requests 6 | 7 | We use GitHub for tracking bugs and feature requests. 8 | 9 | Please see our [contributing](CONTRIBUTING.md) documentation for further instructions. 10 | 11 | ## Installation and Usage 12 | 13 | We use GitHub pages for hosting our installation and usage documentation. 14 | 15 | Please see our [documentation](https://go-vela.github.io/docs) site for more information. 16 | 17 | ## Local Development 18 | 19 | Please see our [local development documentation](../DOCS.md) for more information. 20 | 21 | ## Questions 22 | 23 | We use Slack for supporting questions not already covered in the above documents. 24 | 25 | Please join the [#vela](https://gophers.slack.com/app_redirect?channel=CNRRKE8KY) channel. 26 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>go-vela/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # name of the action 2 | name: build 3 | 4 | # trigger on pull_request or push events 5 | on: 6 | pull_request: 7 | push: 8 | 9 | # pipeline to execute 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: clone 16 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 17 | 18 | - name: install go 19 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 20 | with: 21 | # use version from go.mod file 22 | go-version-file: 'go.mod' 23 | cache: true 24 | check-latest: true 25 | 26 | - name: build 27 | run: | 28 | make build 29 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '38 18 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'go' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 39 | 40 | - name: install go 41 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 42 | with: 43 | # use version from go.mod file 44 | go-version-file: 'go.mod' 45 | cache: true 46 | check-latest: true 47 | 48 | # Initializes the CodeQL tools for scanning. 49 | - name: Initialize CodeQL 50 | uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 51 | with: 52 | languages: ${{ matrix.language }} 53 | # If you wish to specify custom queries, you can do so here or in a config file. 54 | # By default, queries listed here will override any specified in a config file. 55 | # Prefix the list here with "+" to use these queries and those in the config file. 56 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 57 | 58 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 59 | # If this step fails, then you should remove it and run the build manually (see below) 60 | - name: Autobuild 61 | uses: github/codeql-action/autobuild@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 62 | 63 | # ℹ️ Command-line programs to run using the OS shell. 64 | # 📚 https://git.io/JvXDl 65 | 66 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 67 | # and modify them (or add more) to build your code if your project 68 | # uses a compiled language 69 | 70 | #- run: | 71 | # make bootstrap 72 | # make release 73 | 74 | - name: Perform CodeQL Analysis 75 | uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 76 | -------------------------------------------------------------------------------- /.github/workflows/prerelease.yml: -------------------------------------------------------------------------------- 1 | # name of the action 2 | name: prerelease 3 | 4 | # trigger on push events with `v*` in tag 5 | on: 6 | push: 7 | tags: 8 | - 'v*' 9 | 10 | # pipeline to execute 11 | jobs: 12 | prerelease: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: clone 17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | with: 19 | # ensures we fetch tag history for the repository 20 | fetch-depth: 0 21 | 22 | - name: install go 23 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 24 | with: 25 | # use version from go.mod file 26 | go-version-file: 'go.mod' 27 | cache: true 28 | check-latest: true 29 | 30 | - name: setup 31 | run: | 32 | # setup git tag in Actions environment 33 | echo "GITHUB_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV 34 | 35 | - name: build 36 | env: 37 | GOOS: linux 38 | CGO_ENABLED: '0' 39 | run: | 40 | make build-static-ci 41 | 42 | - name: publish 43 | uses: elgohr/Publish-Docker-Github-Action@eb53b3ec07136a6ebaed78d8135806da64f7c7e2 # v5 44 | with: 45 | name: target/vela-worker 46 | cache: true 47 | tag_names: true 48 | username: ${{ secrets.DOCKER_USERNAME }} 49 | password: ${{ secrets.DOCKER_PASSWORD }} 50 | 51 | - name: publish-alpine 52 | uses: elgohr/Publish-Docker-Github-Action@eb53b3ec07136a6ebaed78d8135806da64f7c7e2 # v5 53 | with: 54 | name: target/vela-worker 55 | cache: true 56 | tags: "${{ env.GITHUB_TAG }}-alpine" 57 | username: ${{ secrets.DOCKER_USERNAME }} 58 | password: ${{ secrets.DOCKER_PASSWORD }} 59 | dockerfile: Dockerfile-alpine 60 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # name of the action 2 | name: publish 3 | 4 | # trigger on push events with branch main 5 | on: 6 | push: 7 | branches: [ main ] 8 | 9 | # pipeline to execute 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: clone 16 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 17 | with: 18 | # ensures we fetch tag history for the repository 19 | fetch-depth: 0 20 | 21 | - name: install go 22 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 23 | with: 24 | # use version from go.mod file 25 | go-version-file: 'go.mod' 26 | cache: true 27 | check-latest: true 28 | 29 | - name: build 30 | env: 31 | GOOS: linux 32 | CGO_ENABLED: '0' 33 | run: | 34 | make build-static-ci 35 | 36 | - name: publish 37 | uses: elgohr/Publish-Docker-Github-Action@eb53b3ec07136a6ebaed78d8135806da64f7c7e2 # v5 38 | with: 39 | name: target/vela-worker 40 | cache: true 41 | username: ${{ secrets.DOCKER_USERNAME }} 42 | password: ${{ secrets.DOCKER_PASSWORD }} 43 | 44 | - name: publish-alpine 45 | uses: elgohr/Publish-Docker-Github-Action@eb53b3ec07136a6ebaed78d8135806da64f7c7e2 # v5 46 | with: 47 | name: target/vela-worker 48 | cache: true 49 | tags: "latest-alpine" 50 | username: ${{ secrets.DOCKER_USERNAME }} 51 | password: ${{ secrets.DOCKER_PASSWORD }} 52 | dockerfile: Dockerfile-alpine 53 | -------------------------------------------------------------------------------- /.github/workflows/reviewdog.yml: -------------------------------------------------------------------------------- 1 | # name of the action 2 | name: reviewdog 3 | 4 | # trigger on pull_request events 5 | on: 6 | pull_request: 7 | 8 | # pipeline to execute 9 | jobs: 10 | diff-review: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: clone 14 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | 16 | - name: install go 17 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 18 | with: 19 | # use version from go.mod file 20 | go-version-file: "go.mod" 21 | cache: true 22 | check-latest: true 23 | 24 | - name: golangci-lint 25 | uses: reviewdog/action-golangci-lint@f9bba13753278f6a73b27a56a3ffb1bfda90ed71 # v2.8.0 26 | with: 27 | github_token: ${{ secrets.github_token }} 28 | golangci_lint_flags: "--config=.golangci.yml --verbose --timeout=5m" 29 | fail_on_error: true 30 | filter_mode: diff_context 31 | reporter: github-pr-review 32 | 33 | full-review: 34 | runs-on: ubuntu-latest 35 | steps: 36 | - name: clone 37 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 38 | 39 | - name: install go 40 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 41 | with: 42 | # use version from go.mod file 43 | go-version-file: "go.mod" 44 | cache: true 45 | check-latest: true 46 | 47 | - name: golangci-lint 48 | uses: reviewdog/action-golangci-lint@f9bba13753278f6a73b27a56a3ffb1bfda90ed71 # v2.8.0 49 | with: 50 | github_token: ${{ secrets.github_token }} 51 | golangci_lint_flags: "--config=.golangci.yml --verbose --timeout=5m" 52 | fail_on_error: false 53 | filter_mode: nofilter 54 | -------------------------------------------------------------------------------- /.github/workflows/spec.yml: -------------------------------------------------------------------------------- 1 | # name of the action 2 | name: spec 3 | 4 | # trigger on release events 5 | on: 6 | release: 7 | types: [ created ] 8 | 9 | # pipeline to execute 10 | jobs: 11 | schema: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: clone 16 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 17 | 18 | - name: install go 19 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 20 | with: 21 | # use version from go.mod file 22 | go-version-file: 'go.mod' 23 | cache: true 24 | check-latest: true 25 | 26 | - name: tags 27 | run: | 28 | git fetch --tags 29 | 30 | - name: create spec 31 | run: | 32 | sudo make spec-install 33 | sudo make spec 34 | 35 | - name: upload spec 36 | uses: skx/github-action-publish-binaries@master 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | args: 'api-spec.json' 41 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # name of the action 2 | name: test 3 | 4 | # trigger on pull_request or push events 5 | on: 6 | pull_request: 7 | push: 8 | 9 | # pipeline to execute 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: clone 16 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 17 | 18 | - name: install go 19 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 20 | with: 21 | # use version from go.mod file 22 | go-version-file: 'go.mod' 23 | cache: true 24 | check-latest: true 25 | 26 | - name: test 27 | run: | 28 | go test -covermode=atomic -coverprofile=coverage.out ./... 29 | 30 | - name: coverage 31 | uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0 32 | with: 33 | token: ${{ secrets.CODECOV_TOKEN }} 34 | file: coverage.out 35 | -------------------------------------------------------------------------------- /.github/workflows/validate-pr-title.yml: -------------------------------------------------------------------------------- 1 | # name of the action 2 | name: validate PR title 3 | 4 | # trigger on pull_request events of the opened & edited type. 5 | on: 6 | pull_request: 7 | types: [opened, synchronize, edited, reopened] 8 | 9 | # pipeline to execute 10 | jobs: 11 | validate: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: validate title 16 | env: 17 | TITLE: ${{ github.event.pull_request.title }} 18 | run: | 19 | echo "$TITLE" | grep -Eq '^(feat|fix|chore|refactor|enhance|test|docs)(\(.*\)|)!?:\s.+$' && (echo "Pass"; exit 0) || (echo "Incorrect Format. Please see https://go-vela.github.io/docs/community/contributing_guidelines/#development-workflow"; exit 1) 20 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | # name of the action 2 | name: validate 3 | 4 | # trigger on pull_request or push events 5 | on: 6 | pull_request: 7 | push: 8 | 9 | # pipeline to execute 10 | jobs: 11 | validate: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: clone 16 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 17 | 18 | - name: install go 19 | uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 20 | with: 21 | # use version from go.mod file 22 | go-version-file: "go.mod" 23 | cache: true 24 | check-latest: true 25 | 26 | - name: validate 27 | run: | 28 | # Check that go mod tidy produces a zero diff; clean up any changes afterwards. 29 | go mod tidy && git diff --exit-code; code=$?; git checkout -- .; (exit $code) 30 | # Check that go vet ./... produces a zero diff; clean up any changes afterwards. 31 | go vet ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code) 32 | # Check that go fmt ./... produces a zero diff; clean up any changes afterwards. 33 | go fmt ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code) 34 | # Check that go fix ./... produces a zero diff; clean up any changes afterwards. 35 | go fix ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code) 36 | 37 | - name: validate spec 38 | run: | 39 | sudo make spec-install 40 | make spec 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/go 2 | 3 | ### Go ### 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | # install makefile required bins in the bin/ dir 11 | bin 12 | 13 | # Test binary, build with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | ### Go Patch ### 20 | /vendor/ 21 | /Godeps/ 22 | 23 | 24 | # End of https://www.gitignore.io/api/go 25 | 26 | # Binary release folder 27 | release/ 28 | 29 | # IntelliJ project folder 30 | .idea/ 31 | 32 | # IntelliJ project files 33 | *.iml 34 | *.ipr 35 | *.iws 36 | *.xml 37 | 38 | # Secrets environment file 39 | secrets.env 40 | 41 | # Dotenv environment file 42 | .env 43 | .env.test 44 | 45 | # Files to be excluded. 46 | .DS_Store 47 | 48 | api-spec.json 49 | 50 | # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode 51 | # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode 52 | 53 | ### VisualStudioCode ### 54 | .vscode/* 55 | !.vscode/settings.json 56 | !.vscode/tasks.json 57 | !.vscode/launch.json 58 | !.vscode/extensions.json 59 | !.vscode/*.code-snippets 60 | 61 | # Local History for Visual Studio Code 62 | .history/ 63 | 64 | # Built Visual Studio Code Extensions 65 | *.vsix 66 | __debug_bin 67 | 68 | ### VisualStudioCode Patch ### 69 | # Ignore all local history of files 70 | .history 71 | .ionide 72 | 73 | # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode 74 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | output: 3 | formats: 4 | text: 5 | path: stdout 6 | print-linter-name: true 7 | print-issued-lines: true 8 | linters: 9 | default: none 10 | enable: 11 | - bidichk 12 | - bodyclose 13 | - contextcheck 14 | - dupl 15 | - errcheck 16 | - errorlint 17 | - funlen 18 | - goconst 19 | - gocyclo 20 | - godot 21 | - goheader 22 | - gomoddirectives 23 | - goprintffuncname 24 | - gosec 25 | - govet 26 | - ineffassign 27 | - makezero 28 | - misspell 29 | - nakedret 30 | - nilerr 31 | - noctx 32 | - nolintlint 33 | - revive 34 | - staticcheck 35 | - unconvert 36 | - unparam 37 | - unused 38 | - whitespace 39 | - wsl 40 | settings: 41 | dupl: 42 | threshold: 100 43 | funlen: 44 | lines: 160 45 | statements: 70 46 | goheader: 47 | template: "SPDX-License-Identifier: Apache-2.0" 48 | misspell: 49 | locale: US 50 | nolintlint: 51 | require-explanation: true 52 | require-specific: true 53 | allow-unused: false 54 | exclusions: 55 | generated: lax 56 | presets: 57 | - comments 58 | - common-false-positives 59 | - legacy 60 | - std-error-handling 61 | rules: 62 | - linters: 63 | - dupl 64 | - funlen 65 | - goconst 66 | - gocyclo 67 | path: _test\.go 68 | paths: 69 | - third_party$ 70 | - builtin$ 71 | - examples$ 72 | issues: 73 | uniq-by-line: true 74 | formatters: 75 | enable: 76 | - gci 77 | - gofmt 78 | settings: 79 | gci: 80 | sections: 81 | - standard 82 | - default 83 | - blank 84 | - dot 85 | - prefix(github.com/go-vela) 86 | custom-order: true 87 | exclusions: 88 | generated: lax 89 | paths: 90 | - third_party$ 91 | - builtin$ 92 | - examples$ 93 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c as certs 4 | 5 | RUN apk add --update --no-cache ca-certificates 6 | 7 | FROM scratch 8 | 9 | COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 10 | 11 | EXPOSE 8080 12 | 13 | ENV GODEBUG=netdns=go 14 | 15 | ADD release/vela-worker /bin/ 16 | 17 | CMD ["/bin/vela-worker"] 18 | -------------------------------------------------------------------------------- /Dockerfile-alpine: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c 4 | 5 | RUN apk add --update --no-cache ca-certificates 6 | 7 | EXPOSE 8080 8 | 9 | ENV GODEBUG=netdns=go 10 | 11 | ADD release/vela-worker /bin/ 12 | 13 | CMD ["/bin/vela-worker"] 14 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | --- 2 | # This file is used in generating code for the Kubernetes Runtime. 3 | # Look in runtime/kubernetes/apis/ and runtime/kubernetes/generated/ 4 | # for the code that is used or generated by the code generators. 5 | # 6 | # For more about this file see: 7 | # https://book.kubebuilder.io/reference/project-config.html 8 | domain: go-vela.github.io 9 | layout: 10 | - go.kubebuilder.io/v3 11 | projectName: worker 12 | repo: github.com/go-vela/worker 13 | resources: 14 | - api: 15 | crdVersion: v1 16 | namespaced: true 17 | domain: go-vela.github.io 18 | kind: PipelinePodsTemplate 19 | path: pkg/runtime/kubernetes/apis/vela/v1alpha1 20 | version: v1alpha1 21 | version: "3" 22 | -------------------------------------------------------------------------------- /api/build.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package api 4 | 5 | import ( 6 | "fmt" 7 | "net/http" 8 | 9 | "github.com/gin-gonic/gin" 10 | 11 | api "github.com/go-vela/server/api/types" 12 | "github.com/go-vela/worker/router/middleware/executor" 13 | ) 14 | 15 | // swagger:operation GET /api/v1/executors/{executor}/build build GetBuild 16 | // 17 | // Get the currently running build 18 | // 19 | // --- 20 | // produces: 21 | // - application/json 22 | // parameters: 23 | // - in: path 24 | // name: executor 25 | // description: The executor running the build 26 | // required: true 27 | // type: string 28 | // security: 29 | // - ApiKeyAuth: [] 30 | // responses: 31 | // '200': 32 | // description: Successfully retrieved the build 33 | // type: json 34 | // schema: 35 | // "$ref": "#/definitions/Build" 36 | // '500': 37 | // description: Unable to retrieve the build 38 | // schema: 39 | // type: string 40 | 41 | // GetBuild represents the API handler to capture the 42 | // build currently running on an executor. 43 | func GetBuild(c *gin.Context) { 44 | e := executor.Retrieve(c) 45 | 46 | build, err := e.GetBuild() 47 | if err != nil { 48 | msg := fmt.Errorf("unable to read build: %w", err).Error() 49 | 50 | c.AbortWithStatusJSON(http.StatusInternalServerError, api.Error{Message: &msg}) 51 | 52 | return 53 | } 54 | 55 | c.JSON(http.StatusOK, build) 56 | } 57 | 58 | // swagger:operation DELETE /api/v1/executors/{executor}/build/cancel build CancelBuild 59 | // 60 | // Cancel the currently running build 61 | // 62 | // --- 63 | // produces: 64 | // - application/json 65 | // parameters: 66 | // - in: path 67 | // name: executor 68 | // description: The executor running the build 69 | // required: true 70 | // type: string 71 | // security: 72 | // - ApiKeyAuth: [] 73 | // responses: 74 | // '200': 75 | // description: Successfully canceled the build 76 | // type: json 77 | // '500': 78 | // description: Unable to cancel the build 79 | // type: json 80 | 81 | // CancelBuild represents the API handler to cancel a 82 | // build currently running on an executor. 83 | // 84 | // This function performs a hard cancellation of a build on worker. 85 | // Any build running during this time will immediately be stopped. 86 | func CancelBuild(c *gin.Context) { 87 | e := executor.Retrieve(c) 88 | 89 | build, err := e.CancelBuild() 90 | if err != nil { 91 | msg := fmt.Errorf("unable to cancel build: %w", err).Error() 92 | 93 | c.AbortWithStatusJSON(http.StatusInternalServerError, api.Error{Message: &msg}) 94 | 95 | return 96 | } 97 | 98 | c.JSON(http.StatusOK, build) 99 | } 100 | -------------------------------------------------------------------------------- /api/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package api provides the handlers for the Vela API. 4 | // 5 | // Usage: 6 | // 7 | // import "github.com/go-vela/worker/api" 8 | package api 9 | -------------------------------------------------------------------------------- /api/health.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package api 4 | 5 | import ( 6 | "net/http" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // swagger:operation GET /health system Health 12 | // 13 | // Check if the worker API is available 14 | // 15 | // --- 16 | // produces: 17 | // - application/json 18 | // parameters: 19 | // responses: 20 | // '200': 21 | // description: Successful 'ping' of Vela worker API 22 | // schema: 23 | // type: string 24 | 25 | // Health check the status of the application. 26 | func Health(c *gin.Context) { 27 | c.JSON(http.StatusOK, "ok") 28 | } 29 | -------------------------------------------------------------------------------- /api/metrics.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package api 4 | 5 | import ( 6 | "net/http" 7 | 8 | "github.com/prometheus/client_golang/prometheus/promhttp" 9 | ) 10 | 11 | // swagger:operation GET /metrics system Metrics 12 | // 13 | // Retrieve metrics from the worker 14 | // 15 | // --- 16 | // produces: 17 | // - application/json 18 | // parameters: 19 | // responses: 20 | // '200': 21 | // description: Successful retrieval of worker metrics 22 | // schema: 23 | // type: string 24 | 25 | // Metrics returns a Prometheus handler for serving go metrics. 26 | func Metrics() http.Handler { 27 | return promhttp.Handler() 28 | } 29 | -------------------------------------------------------------------------------- /api/pipeline.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package api 4 | 5 | import ( 6 | "fmt" 7 | "net/http" 8 | 9 | "github.com/gin-gonic/gin" 10 | 11 | api "github.com/go-vela/server/api/types" 12 | "github.com/go-vela/worker/router/middleware/executor" 13 | ) 14 | 15 | // swagger:operation GET /api/v1/executors/{executor}/pipeline pipeline GetPipeline 16 | // 17 | // Get a currently running pipeline 18 | // 19 | // --- 20 | // produces: 21 | // - application/json 22 | // parameters: 23 | // - in: path 24 | // name: executor 25 | // description: The executor running the pipeline 26 | // required: true 27 | // type: string 28 | // security: 29 | // - ApiKeyAuth: [] 30 | // responses: 31 | // '200': 32 | // description: Successfully retrieved the pipeline 33 | // type: json 34 | // schema: 35 | // "$ref": "#/definitions/PipelineBuild" 36 | // '500': 37 | // description: Unable to retrieve the pipeline 38 | // type: json 39 | 40 | // GetPipeline represents the API handler to capture the 41 | // pipeline currently running on an executor. 42 | func GetPipeline(c *gin.Context) { 43 | e := executor.Retrieve(c) 44 | 45 | pipeline, err := e.GetPipeline() 46 | if err != nil { 47 | msg := fmt.Errorf("unable to read pipeline: %w", err).Error() 48 | 49 | c.AbortWithStatusJSON(http.StatusInternalServerError, api.Error{Message: &msg}) 50 | 51 | return 52 | } 53 | 54 | c.JSON(http.StatusOK, pipeline) 55 | } 56 | -------------------------------------------------------------------------------- /api/repo.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package api 4 | 5 | import ( 6 | "fmt" 7 | "net/http" 8 | 9 | "github.com/gin-gonic/gin" 10 | 11 | api "github.com/go-vela/server/api/types" 12 | "github.com/go-vela/worker/router/middleware/executor" 13 | ) 14 | 15 | // swagger:operation GET /api/v1/executors/{executor}/repo repo GetRepo 16 | // 17 | // Get a currently running repo 18 | // 19 | // --- 20 | // produces: 21 | // - application/json 22 | // parameters: 23 | // - in: path 24 | // name: executor 25 | // description: The executor running the build 26 | // required: true 27 | // type: string 28 | // security: 29 | // - ApiKeyAuth: [] 30 | // responses: 31 | // '200': 32 | // description: Successfully retrieved the repo 33 | // type: json 34 | // schema: 35 | // "$ref": "#/definitions/Repo" 36 | // '500': 37 | // description: Unable to retrieve the repo 38 | // type: json 39 | 40 | // GetRepo represents the API handler to capture the 41 | // repo currently running on an executor. 42 | func GetRepo(c *gin.Context) { 43 | e := executor.Retrieve(c) 44 | 45 | build, err := e.GetBuild() 46 | if err != nil { 47 | msg := fmt.Errorf("unable to read build: %w", err).Error() 48 | 49 | c.AbortWithStatusJSON(http.StatusInternalServerError, api.Error{Message: &msg}) 50 | 51 | return 52 | } 53 | 54 | c.JSON(http.StatusOK, build.GetRepo()) 55 | } 56 | -------------------------------------------------------------------------------- /api/shutdown.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package api 4 | 5 | import ( 6 | "net/http" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // swagger:operation POST /api/v1/shutdown system Shutdown 12 | // 13 | // Perform a soft shutdown of the worker 14 | // 15 | // --- 16 | // produces: 17 | // - application/json 18 | // security: 19 | // - ApiKeyAuth: [] 20 | // responses: 21 | // '501': 22 | // description: Endpoint is not yet implemented 23 | // schema: 24 | // type: string 25 | 26 | // Shutdown represents the API handler to shutdown a 27 | // executors currently running on an worker. 28 | // 29 | // This function performs a soft shut down of a worker. 30 | // Any build running during this time will safely complete, then 31 | // the worker will safely shut itself down. 32 | func Shutdown(c *gin.Context) { 33 | c.JSON(http.StatusNotImplemented, "This endpoint is not yet implemented") 34 | } 35 | -------------------------------------------------------------------------------- /api/version.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package api 4 | 5 | import ( 6 | "net/http" 7 | 8 | "github.com/gin-gonic/gin" 9 | 10 | "github.com/go-vela/worker/version" 11 | ) 12 | 13 | // swagger:operation GET /version router Version 14 | // 15 | // Get the version of the Vela API 16 | // 17 | // --- 18 | // produces: 19 | // - application/json 20 | // parameters: 21 | // responses: 22 | // '200': 23 | // description: Successfully retrieved the Vela API version 24 | // schema: 25 | // type: string 26 | 27 | // Version represents the API handler to 28 | // report the version information for Vela. 29 | func Version(c *gin.Context) { 30 | c.JSON(http.StatusOK, version.New()) 31 | } 32 | -------------------------------------------------------------------------------- /cmd/vela-worker/client.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/sirupsen/logrus" 7 | 8 | "github.com/go-vela/sdk-go/vela" 9 | ) 10 | 11 | // helper function to setup the queue from the CLI arguments. 12 | func setupClient(s *Server, token string) (*vela.Client, error) { 13 | logrus.Debug("creating vela client from worker configuration") 14 | 15 | // create a new Vela client from the server configuration 16 | // 17 | // https://pkg.go.dev/github.com/go-vela/sdk-go/vela#NewClient 18 | vela, err := vela.NewClient(s.Address, "", nil) 19 | if err != nil { 20 | return nil, err 21 | } 22 | // set token for authentication with the server 23 | // 24 | // https://pkg.go.dev/github.com/go-vela/sdk-go/vela#AuthenticationService.SetTokenAuth 25 | vela.Authentication.SetTokenAuth(token) 26 | 27 | return vela, nil 28 | } 29 | -------------------------------------------------------------------------------- /cmd/vela-worker/main.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "encoding/json" 8 | "fmt" 9 | "os" 10 | 11 | "github.com/sirupsen/logrus" 12 | "github.com/urfave/cli/v3" 13 | 14 | _ "github.com/joho/godotenv/autoload" 15 | 16 | "github.com/go-vela/worker/version" 17 | ) 18 | 19 | // hostname stores the worker host name reported by the kernel. 20 | var hostname string 21 | 22 | // create an init function to set the hostname for the worker. 23 | // 24 | // https://golang.org/doc/effective_go.html#init 25 | func init() { 26 | // attempt to capture the hostname for the worker 27 | hostname, _ = os.Hostname() 28 | // check if a hostname is set 29 | if len(hostname) == 0 { 30 | // default the hostname to localhost 31 | hostname = "localhost" 32 | } 33 | } 34 | 35 | func main() { 36 | // capture application version information 37 | v := version.New() 38 | 39 | // serialize the version information as pretty JSON 40 | bytes, err := json.MarshalIndent(v, "", " ") 41 | if err != nil { 42 | logrus.Fatal(err) 43 | } 44 | 45 | // output the version information to stdout 46 | fmt.Fprintf(os.Stdout, "%s\n", string(bytes)) 47 | 48 | cmd := cli.Command{ 49 | Name: "vela-worker", 50 | Version: v.Semantic(), 51 | Action: run, 52 | Usage: "Vela build daemon designed for executing pipelines", 53 | } 54 | 55 | // Worker Flags 56 | 57 | cmd.Flags = flags() 58 | 59 | // Worker Start 60 | 61 | if err = cmd.Run(context.Background(), os.Args); err != nil { 62 | logrus.Fatal(err) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /cmd/vela-worker/server.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/tls" 7 | "net/http" 8 | "os" 9 | "strings" 10 | "time" 11 | 12 | "github.com/sirupsen/logrus" 13 | 14 | "github.com/go-vela/worker/router" 15 | "github.com/go-vela/worker/router/middleware" 16 | ) 17 | 18 | // server is a helper function to listen and serve 19 | // traffic for web and API requests for the Worker. 20 | func (w *Worker) server() (http.Handler, *tls.Config) { 21 | // log a message indicating the setup of the server handlers 22 | // 23 | // https://pkg.go.dev/github.com/sirupsen/logrus#Trace 24 | logrus.Trace("loading router with server handlers") 25 | 26 | // create the worker router to listen and serve traffic 27 | // 28 | // https://pkg.go.dev/github.com/go-vela/worker/router#Load 29 | _server := router.Load( 30 | middleware.RequestVersion, 31 | middleware.ServerAddress(w.Config.Server.Address), 32 | middleware.WorkerHostname(w.Config.API.Address.Hostname()), 33 | middleware.Executors(w.Executors), 34 | middleware.Logger(logrus.StandardLogger(), time.RFC3339, true), 35 | middleware.RegisterToken(w.RegisterToken), 36 | ) 37 | 38 | // log a message indicating the start of serving traffic 39 | // 40 | // https://pkg.go.dev/github.com/sirupsen/logrus#Tracef 41 | logrus.Tracef("serving traffic on %s", w.Config.API.Address.Port()) 42 | 43 | // if running with HTTPS, check certs are provided and run with TLS. 44 | if strings.EqualFold(w.Config.API.Address.Scheme, "https") { 45 | if len(w.Config.Certificate.Cert) > 0 && len(w.Config.Certificate.Key) > 0 { 46 | // check that the certificate and key are both populated 47 | _, err := os.Stat(w.Config.Certificate.Cert) 48 | if err != nil { 49 | logrus.Fatalf("expecting certificate file at %s, got %v", w.Config.Certificate.Cert, err) 50 | } 51 | 52 | _, err = os.Stat(w.Config.Certificate.Key) 53 | if err != nil { 54 | logrus.Fatalf("expecting certificate key at %s, got %v", w.Config.Certificate.Key, err) 55 | } 56 | } else { 57 | logrus.Fatal("unable to run with TLS: No certificate provided") 58 | } 59 | 60 | // define TLS config struct for server start up 61 | tlsCfg := new(tls.Config) 62 | 63 | // if a TLS minimum version is supplied, set that in the config 64 | if len(w.Config.TLSMinVersion) > 0 { 65 | var tlsVersion uint16 66 | 67 | switch w.Config.TLSMinVersion { 68 | case "1.0": 69 | tlsVersion = tls.VersionTLS10 70 | case "1.1": 71 | tlsVersion = tls.VersionTLS11 72 | case "1.2": 73 | tlsVersion = tls.VersionTLS12 74 | case "1.3": 75 | tlsVersion = tls.VersionTLS13 76 | default: 77 | logrus.Fatal("invalid TLS minimum version supplied") 78 | } 79 | 80 | tlsCfg.MinVersion = tlsVersion 81 | } 82 | 83 | return _server, tlsCfg 84 | } 85 | 86 | // else serve over http 87 | // https://pkg.go.dev/github.com/gin-gonic/gin#Engine.Run 88 | return _server, nil 89 | } 90 | -------------------------------------------------------------------------------- /cmd/vela-worker/validate.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "net/url" 8 | "strings" 9 | 10 | "github.com/sirupsen/logrus" 11 | ) 12 | 13 | // Validate verifies the Worker is properly configured. 14 | func (w *Worker) Validate() error { 15 | // log a message indicating the configuration verification 16 | // 17 | // https://pkg.go.dev/github.com/sirupsen/logrus#Info 18 | logrus.Info("validating worker configuration") 19 | 20 | // check that hostname was properly populated 21 | if len(w.Config.API.Address.Hostname()) == 0 { 22 | switch strings.ToLower(w.Config.API.Address.Scheme) { 23 | case "http", "https": 24 | retErr := "worker server address invalid: %s" 25 | return fmt.Errorf(retErr, w.Config.API.Address.String()) 26 | default: 27 | // hostname will be empty if a scheme is not provided 28 | retErr := "worker server address invalid, no scheme: %s" 29 | return fmt.Errorf(retErr, w.Config.API.Address.String()) 30 | } 31 | } 32 | 33 | // verify a build limit was provided 34 | if w.Config.Build.Limit <= 0 { 35 | return fmt.Errorf("no worker build limit provided") 36 | } 37 | 38 | // verify a build timeout was provided 39 | if w.Config.Build.Timeout <= 0 { 40 | return fmt.Errorf("no worker build timeout provided") 41 | } 42 | 43 | // verify a worker address was provided 44 | if *w.Config.API.Address == (url.URL{}) { 45 | return fmt.Errorf("no worker address provided") 46 | } 47 | 48 | // verify a server address was provided 49 | if len(w.Config.Server.Address) == 0 { 50 | return fmt.Errorf("no worker server address provided") 51 | } 52 | 53 | // verify an executor driver was provided 54 | if len(w.Config.Executor.Driver) == 0 { 55 | return fmt.Errorf("no worker executor driver provided") 56 | } 57 | 58 | // verify the runtime configuration 59 | // 60 | // https://pkg.go.dev/github.com/go-vela/worker/runtime#Setup.Validate 61 | err := w.Config.Runtime.Validate() 62 | if err != nil { 63 | return err 64 | } 65 | 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /cmd/vela-worker/worker.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package main 4 | 5 | import ( 6 | "net/url" 7 | "sync" 8 | "time" 9 | 10 | "github.com/go-vela/sdk-go/vela" 11 | api "github.com/go-vela/server/api/types" 12 | "github.com/go-vela/server/queue" 13 | "github.com/go-vela/worker/executor" 14 | "github.com/go-vela/worker/runtime" 15 | ) 16 | 17 | type ( 18 | // API represents the worker configuration for API information. 19 | API struct { 20 | Address *url.URL 21 | } 22 | 23 | // Build represents the worker configuration for build information. 24 | Build struct { 25 | Limit int 26 | Timeout time.Duration 27 | } 28 | 29 | // Logger represents the worker configuration for logger information. 30 | Logger struct { 31 | Format string 32 | Level string 33 | } 34 | 35 | // Server represents the worker configuration for server information. 36 | Server struct { 37 | Address string 38 | Secret string 39 | } 40 | 41 | // Certificate represents the optional cert and key to enable TLS. 42 | Certificate struct { 43 | Cert string 44 | Key string 45 | } 46 | 47 | // Config represents the worker configuration. 48 | Config struct { 49 | Mock bool // Mock should only be true for tests 50 | API *API 51 | Build *Build 52 | CheckIn time.Duration 53 | Executor *executor.Setup 54 | Logger *Logger 55 | Queue *queue.Setup 56 | Runtime *runtime.Setup 57 | Server *Server 58 | Certificate *Certificate 59 | TLSMinVersion string 60 | } 61 | 62 | // Worker represents all configuration and 63 | // system processes for the worker. 64 | Worker struct { 65 | Config *Config 66 | Executors map[int]executor.Engine 67 | Queue queue.Service 68 | Runtime runtime.Engine 69 | VelaClient *vela.Client 70 | RegisterToken chan string 71 | CheckedIn bool 72 | RunningBuilds []*api.Build 73 | QueueCheckedIn bool 74 | RunningBuildsMutex sync.Mutex 75 | } 76 | ) 77 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # The is the default codecov.io yaml configuration all projects use 2 | # that do not have their own codecov.yml already in the project. 3 | # 4 | # https://docs.codecov.io/docs/codecov-yaml#section-default-yaml 5 | 6 | # This section provides the generic configuration for codecov. 7 | # 8 | # https://docs.codecov.io/docs/codecovyml-reference#section-codecov 9 | codecov: 10 | 11 | require_ci_to_pass: yes 12 | 13 | # This section provides the configuration for the 14 | # coverage report codecov analyzes for results. 15 | # 16 | # https://docs.codecov.io/docs/codecovyml-reference#section-coverage 17 | coverage: 18 | 19 | precision: 2 20 | round: down 21 | range: "70...100" 22 | 23 | # https://docs.codecov.io/docs/commit-status 24 | status: 25 | # set rules for the project status report 26 | project: 27 | default: 28 | target: 90% 29 | threshold: 0% 30 | # disable the patch status report 31 | patch: off 32 | # disable the changes status report 33 | changes: off 34 | 35 | # This section provides the configuration that tells 36 | # codecov to ignore files matching these files or patterns. 37 | # 38 | # https://docs.codecov.io/docs/codecovyml-reference#section-ignore 39 | ignore: 40 | # this is only for tests, so reporting coverage is misleading. 41 | - runtime/kubernetes/mock.go 42 | 43 | # This section provides the configuration for the 44 | # parsers codecov uses for the coverage report. 45 | # 46 | # https://docs.codecov.io/docs/codecovyml-reference#section-parsers 47 | parsers: 48 | 49 | gcov: 50 | 51 | branch_detection: 52 | conditional: yes 53 | loop: yes 54 | method: no 55 | macro: no 56 | 57 | # This section provides the configuration for the 58 | # comments codecov makes to open pull requests. 59 | # 60 | # https://docs.codecov.io/docs/codecovyml-reference#section-comment 61 | comment: 62 | 63 | layout: "reach, diff, flags, files" 64 | behavior: default 65 | require_changes: no 66 | -------------------------------------------------------------------------------- /executor/context.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package executor 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // key defines the key type for storing 12 | // the executor Engine in the context. 13 | const key = "executor" 14 | 15 | // FromContext retrieves the executor Engine from the context.Context. 16 | func FromContext(c context.Context) Engine { 17 | // get executor value from context.Context 18 | v := c.Value(key) 19 | if v == nil { 20 | return nil 21 | } 22 | 23 | // cast executor value to expected Engine type 24 | e, ok := v.(Engine) 25 | if !ok { 26 | return nil 27 | } 28 | 29 | return e 30 | } 31 | 32 | // FromGinContext retrieves the executor Engine from the gin.Context. 33 | func FromGinContext(c *gin.Context) Engine { 34 | // get executor value from gin.Context 35 | // 36 | // https://pkg.go.dev/github.com/gin-gonic/gin#Context.Get 37 | v, ok := c.Get(key) 38 | if !ok { 39 | return nil 40 | } 41 | 42 | // cast executor value to expected Engine type 43 | e, ok := v.(Engine) 44 | if !ok { 45 | return nil 46 | } 47 | 48 | return e 49 | } 50 | 51 | // WithContext inserts the executor Engine into the context.Context. 52 | func WithContext(c context.Context, e Engine) context.Context { 53 | // set the executor Engine in the context.Context 54 | // 55 | //nolint:revive,staticcheck // ignore using string with context value 56 | return context.WithValue(c, key, e) 57 | } 58 | 59 | // WithGinContext inserts the executor Engine into the gin.Context. 60 | func WithGinContext(c *gin.Context, e Engine) { 61 | // set the executor Engine in the gin.Context 62 | // 63 | // https://pkg.go.dev/github.com/gin-gonic/gin#Context.Set 64 | c.Set(key, e) 65 | } 66 | -------------------------------------------------------------------------------- /executor/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package executor provides the ability for Vela to 4 | // integrate with different supported operating 5 | // systems. 6 | // 7 | // Usage: 8 | // 9 | // import "github.com/go-vela/worker/executor" 10 | package executor 11 | -------------------------------------------------------------------------------- /executor/executor.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package executor 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/sirupsen/logrus" 9 | 10 | "github.com/go-vela/server/constants" 11 | ) 12 | 13 | // New creates and returns a Vela engine capable of 14 | // integrating with the configured executor. 15 | // 16 | // Currently the following executors are supported: 17 | // 18 | // * linux 19 | // * local 20 | // . 21 | func New(s *Setup) (Engine, error) { 22 | // validate the setup being provided 23 | // 24 | // https://pkg.go.dev/github.com/go-vela/worker/executor#Setup.Validate 25 | err := s.Validate() 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | logrus.Debug("creating executor engine from setup") 31 | // process the executor driver being provided 32 | switch s.Driver { 33 | case constants.DriverDarwin: 34 | // handle the Darwin executor driver being provided 35 | // 36 | // https://pkg.go.dev/github.com/go-vela/worker/executor#Setup.Darwin 37 | return s.Darwin() 38 | case constants.DriverLinux: 39 | // handle the Linux executor driver being provided 40 | // 41 | // https://pkg.go.dev/github.com/go-vela/worker/executor#Setup.Linux 42 | return s.Linux() 43 | case constants.DriverLocal: 44 | // handle the Local executor driver being provided 45 | // 46 | // https://pkg.go.dev/github.com/go-vela/worker/executor#Setup.Local 47 | return s.Local() 48 | case constants.DriverWindows: 49 | // handle the Windows executor driver being provided 50 | // 51 | // https://pkg.go.dev/github.com/go-vela/worker/executor#Setup.Windows 52 | return s.Windows() 53 | default: 54 | // handle an invalid executor driver being provided 55 | return nil, fmt.Errorf("invalid executor driver provided: %s", s.Driver) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /executor/flags.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package executor 4 | 5 | import ( 6 | "time" 7 | 8 | "github.com/urfave/cli/v3" 9 | 10 | "github.com/go-vela/server/constants" 11 | ) 12 | 13 | // Flags represents all supported command line 14 | // interface (CLI) flags for the executor. 15 | // 16 | // https://pkg.go.dev/github.com/urfave/cli#Flag 17 | var Flags = []cli.Flag{ 18 | // Executor Flags 19 | 20 | &cli.StringFlag{ 21 | Name: "executor.driver", 22 | Usage: "driver to be used for the executor", 23 | Sources: cli.NewValueSourceChain( 24 | cli.EnvVar("VELA_EXECUTOR_DRIVER"), 25 | cli.EnvVar("EXECUTOR_DRIVER"), 26 | cli.File("/vela/executor/driver"), 27 | ), 28 | Value: constants.DriverLinux, 29 | }, 30 | &cli.UintFlag{ 31 | Name: "executor.max_log_size", 32 | Usage: "maximum log size (in bytes)", 33 | Sources: cli.NewValueSourceChain( 34 | cli.EnvVar("VELA_EXECUTOR_MAX_LOG_SIZE"), 35 | cli.EnvVar("EXECUTOR_MAX_LOG_SIZE"), 36 | cli.File("/vela/executor/max_log_size"), 37 | ), 38 | }, 39 | &cli.DurationFlag{ 40 | Name: "executor.log_streaming_timeout", 41 | Usage: "maximum amount of time to wait for log streaming after build completes", 42 | Sources: cli.EnvVars("WORKER_LOG_STREAMING_TIMEOUT", "VELA_LOG_STREAMING_TIMEOUT", "LOG_STREAMING_TIMEOUT"), 43 | Value: 5 * time.Minute, 44 | }, 45 | &cli.BoolFlag{ 46 | Name: "executor.enforce-trusted-repos", 47 | Usage: "enforce trusted repo restrictions for privileged images", 48 | Sources: cli.NewValueSourceChain( 49 | cli.EnvVar("VELA_EXECUTOR_ENFORCE_TRUSTED_REPOS"), 50 | cli.EnvVar("EXECUTOR_ENFORCE_TRUSTED_REPOS"), 51 | cli.File("/vela/executor/enforce_trusted_repos"), 52 | ), 53 | Value: true, 54 | }, 55 | &cli.StringFlag{ 56 | Name: "executor.outputs-image", 57 | Usage: "image used for the outputs container sidecar", 58 | Sources: cli.NewValueSourceChain( 59 | cli.EnvVar("VELA_EXECUTOR_OUTPUTS_IMAGE"), 60 | cli.EnvVar("EXECUTOR_OUTPUTS_IMAGE"), 61 | cli.File("/vela/executor/outputs_image"), 62 | ), 63 | }, 64 | } 65 | -------------------------------------------------------------------------------- /executor/linux/api_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package linux 4 | 5 | import ( 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/go-vela/server/constants" 10 | ) 11 | 12 | func TestLinux_GetBuild(t *testing.T) { 13 | // setup types 14 | _build := testBuild() 15 | 16 | _engine, err := New( 17 | WithBuild(_build), 18 | ) 19 | if err != nil { 20 | t.Errorf("unable to create executor engine: %v", err) 21 | } 22 | 23 | // setup tests 24 | tests := []struct { 25 | name string 26 | failure bool 27 | engine *client 28 | }{ 29 | { 30 | name: "with build", 31 | failure: false, 32 | engine: _engine, 33 | }, 34 | { 35 | name: "missing build", 36 | failure: true, 37 | engine: new(client), 38 | }, 39 | } 40 | 41 | // run tests 42 | for _, test := range tests { 43 | t.Run(test.name, func(t *testing.T) { 44 | got, err := test.engine.GetBuild() 45 | 46 | if test.failure { 47 | if err == nil { 48 | t.Errorf("GetBuild should have returned err") 49 | } 50 | 51 | return // continue to next test 52 | } 53 | 54 | if err != nil { 55 | t.Errorf("GetBuild returned err: %v", err) 56 | } 57 | 58 | if !reflect.DeepEqual(got, _build) { 59 | t.Errorf("GetBuild is %v, want %v", got, _build) 60 | } 61 | }) 62 | } 63 | } 64 | 65 | func TestLinux_GetPipeline(t *testing.T) { 66 | // setup types 67 | _steps := testSteps(constants.DriverDocker) 68 | 69 | _engine, err := New( 70 | WithPipeline(_steps), 71 | ) 72 | if err != nil { 73 | t.Errorf("unable to create executor engine: %v", err) 74 | } 75 | 76 | // setup tests 77 | tests := []struct { 78 | name string 79 | failure bool 80 | engine *client 81 | }{ 82 | { 83 | name: "with pipeline", 84 | failure: false, 85 | engine: _engine, 86 | }, 87 | { 88 | name: "missing pipeline", 89 | failure: true, 90 | engine: new(client), 91 | }, 92 | } 93 | 94 | // run tests 95 | for _, test := range tests { 96 | t.Run(test.name, func(t *testing.T) { 97 | got, err := test.engine.GetPipeline() 98 | 99 | if test.failure { 100 | if err == nil { 101 | t.Errorf("GetPipeline should have returned err") 102 | } 103 | 104 | return // continue to next test 105 | } 106 | 107 | if err != nil { 108 | t.Errorf("GetPipeline returned err: %v", err) 109 | } 110 | 111 | if !reflect.DeepEqual(got, _steps) { 112 | t.Errorf("GetPipeline is %v, want %v", got, _steps) 113 | } 114 | }) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /executor/linux/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package linux provides the ability for Vela to 4 | // integrate with a Linux as an operating system. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/executor/linux" 9 | package linux 10 | -------------------------------------------------------------------------------- /executor/linux/driver.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package linux 4 | 5 | import "github.com/go-vela/server/constants" 6 | 7 | // Driver outputs the configured executor driver. 8 | func (c *client) Driver() string { 9 | return constants.DriverLinux 10 | } 11 | -------------------------------------------------------------------------------- /executor/linux/driver_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package linux 4 | 5 | import ( 6 | "net/http/httptest" 7 | "reflect" 8 | "testing" 9 | 10 | "github.com/gin-gonic/gin" 11 | 12 | "github.com/go-vela/sdk-go/vela" 13 | "github.com/go-vela/server/constants" 14 | "github.com/go-vela/server/mock/server" 15 | "github.com/go-vela/worker/runtime/docker" 16 | ) 17 | 18 | func TestLinux_Driver(t *testing.T) { 19 | // setup types 20 | gin.SetMode(gin.TestMode) 21 | 22 | s := httptest.NewServer(server.FakeHandler()) 23 | 24 | _client, err := vela.NewClient(s.URL, "", nil) 25 | if err != nil { 26 | t.Errorf("unable to create Vela API client: %v", err) 27 | } 28 | 29 | _runtime, err := docker.NewMock() 30 | if err != nil { 31 | t.Errorf("unable to create runtime engine: %v", err) 32 | } 33 | 34 | want := constants.DriverLinux 35 | 36 | _engine, err := New( 37 | WithBuild(testBuild()), 38 | WithHostname("localhost"), 39 | WithPipeline(testSteps(constants.DriverDocker)), 40 | WithRuntime(_runtime), 41 | WithVelaClient(_client), 42 | ) 43 | if err != nil { 44 | t.Errorf("unable to create executor engine: %v", err) 45 | } 46 | 47 | // run tes 48 | got := _engine.Driver() 49 | 50 | if !reflect.DeepEqual(got, want) { 51 | t.Errorf("Driver is %v, want %v", got, want) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/empty.yml: -------------------------------------------------------------------------------- 1 | --- -------------------------------------------------------------------------------- /executor/linux/testdata/build/secrets/basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | 12 | secrets: 13 | - name: foob 14 | origin: 15 | name: vault 16 | environment: 17 | FOO: bar 18 | image: vault:latest 19 | parameters: 20 | foo: bar 21 | pull: true 22 | 23 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/secrets/img_ignorenotfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | 12 | secrets: 13 | - name: foob 14 | origin: 15 | name: vault 16 | environment: 17 | FOO: bar 18 | image: vault:ignorenotfound 19 | parameters: 20 | foo: bar 21 | pull: true 22 | 23 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/secrets/img_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | 12 | secrets: 13 | - name: foob 14 | origin: 15 | name: vault 16 | environment: 17 | FOO: bar 18 | image: vault:notfound 19 | parameters: 20 | foo: bar 21 | pull: true 22 | 23 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/secrets/name_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | 12 | secrets: 13 | - name: foob 14 | origin: 15 | name: notfound 16 | environment: 17 | FOO: bar 18 | image: vault:latest 19 | parameters: 20 | foo: bar 21 | pull: true 22 | 23 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/services/basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | services: 4 | - name: postgres 5 | environment: 6 | FOO: bar 7 | image: postgres:latest 8 | pull: true 9 | 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: alpine:latest 17 | pull: true 18 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/services/img_environmentdynamic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | 4 | environment: 5 | DYNAMIC_IMAGE: "postgres" 6 | DYNAMIC_TAG: "latest" 7 | 8 | services: 9 | - name: test 10 | environment: 11 | FOO: bar 12 | image: "${DYNAMIC_IMAGE}:${DYNAMIC_TAG}" 13 | pull: on_start 14 | 15 | steps: 16 | - name: test 17 | commands: 18 | - echo ${FOO} 19 | environment: 20 | FOO: bar 21 | image: alpine:latest 22 | pull: true -------------------------------------------------------------------------------- /executor/linux/testdata/build/services/img_ignorenotfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | services: 4 | - name: postgres 5 | environment: 6 | FOO: bar 7 | image: postgres:ignorenotfound 8 | pull: true 9 | 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: alpine:latest 17 | pull: true -------------------------------------------------------------------------------- /executor/linux/testdata/build/services/img_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | services: 4 | - name: postgres 5 | environment: 6 | FOO: bar 7 | image: postgres:notfound 8 | pull: true 9 | 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: alpine:latest 17 | pull: true -------------------------------------------------------------------------------- /executor/linux/testdata/build/services/name_init.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | services: 4 | - name: init 5 | environment: 6 | FOO: bar 7 | image: postgres:latest 8 | pull: true 9 | 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: alpine:latest 17 | pull: true -------------------------------------------------------------------------------- /executor/linux/testdata/build/services/name_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | services: 4 | - name: notfound 5 | environment: 6 | FOO: bar 7 | image: postgres:latest 8 | pull: true 9 | 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: alpine:latest 17 | pull: true -------------------------------------------------------------------------------- /executor/linux/testdata/build/stages/basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | stages: 4 | test: 5 | steps: 6 | - name: test 7 | commands: 8 | - echo ${FOO} 9 | environment: 10 | FOO: bar 11 | image: alpine:latest 12 | pull: true 13 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/stages/img_environmentdynamic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | 4 | environment: 5 | DYNAMIC_IMAGE: "${VELA_BUILD_MESSAGE}" 6 | DYNAMIC_TAG: "${VELA_BUILD_NUMBER}" 7 | 8 | stages: 9 | test: 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: "${DYNAMIC_IMAGE}:${DYNAMIC_TAG}" 17 | pull: on_start 18 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/stages/img_ignorenotfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | stages: 4 | test: 5 | steps: 6 | - name: test 7 | commands: 8 | - echo ${FOO} 9 | environment: 10 | FOO: bar 11 | image: alpine:ignorenotfound 12 | pull: true 13 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/stages/img_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | stages: 4 | test: 5 | steps: 6 | - name: test 7 | commands: 8 | - echo ${FOO} 9 | environment: 10 | FOO: bar 11 | image: alpine:notfound 12 | pull: true 13 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/stages/name_init.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | stages: 4 | test: 5 | steps: 6 | - name: init 7 | commands: 8 | - echo ${FOO} 9 | environment: 10 | FOO: bar 11 | image: alpine:latest 12 | pull: true 13 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/stages/name_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | stages: 4 | test: 5 | steps: 6 | - name: notfound 7 | commands: 8 | - echo ${FOO} 9 | environment: 10 | FOO: bar 11 | image: alpine:latest 12 | pull: true 13 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/steps/basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/steps/basic_secrets.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | secrets: [ lazy, regular ] 12 | 13 | secrets: 14 | - name: lazy 15 | key: github/octocat/lazy 16 | engine: native 17 | type: repo 18 | pull: step_start 19 | - name: regular 20 | key: github/octocat/regular 21 | engine: native 22 | type: repo -------------------------------------------------------------------------------- /executor/linux/testdata/build/steps/img_environmentdynamic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | 4 | environment: 5 | DYNAMIC_IMAGE: "${VELA_BUILD_MESSAGE}" 6 | DYNAMIC_TAG: "${VELA_BUILD_NUMBER}" 7 | 8 | steps: 9 | - name: test 10 | commands: 11 | - echo ${FOO} 12 | environment: 13 | FOO: bar 14 | image: "${DYNAMIC_IMAGE}:${DYNAMIC_TAG}" 15 | pull: on_start 16 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/steps/img_ignorenotfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:ignorenotfound 10 | pull: true 11 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/steps/img_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:notfound 10 | pull: true 11 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/steps/name_init.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: init 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | -------------------------------------------------------------------------------- /executor/linux/testdata/build/steps/name_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: notfound 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | -------------------------------------------------------------------------------- /executor/linux/testdata/secret/basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | 12 | secrets: 13 | - name: foo 14 | 15 | - name: foob 16 | origin: 17 | name: vault 18 | environment: 19 | FOO: bar 20 | image: vault:latest 21 | parameters: 22 | addr: vault.company.com 23 | pull: true 24 | 25 | -------------------------------------------------------------------------------- /executor/linux/testdata/secret/name_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | 12 | secrets: 13 | - name: foo 14 | 15 | - name: foob 16 | origin: 17 | name: notfound 18 | environment: 19 | FOO: bar 20 | image: vault:latest 21 | parameters: 22 | foo: bar 23 | pull: true 24 | 25 | -------------------------------------------------------------------------------- /executor/linux/testdata/step/secret_text.txt: -------------------------------------------------------------------------------- 1 | this 2 | is 3 | a 4 | secret -------------------------------------------------------------------------------- /executor/local/api_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package local 4 | 5 | import ( 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestLocal_GetBuild(t *testing.T) { 11 | // setup types 12 | _build := testBuild() 13 | 14 | _engine, err := New( 15 | WithBuild(_build), 16 | ) 17 | if err != nil { 18 | t.Errorf("unable to create executor engine: %v", err) 19 | } 20 | 21 | // setup tests 22 | tests := []struct { 23 | name string 24 | failure bool 25 | engine *client 26 | }{ 27 | { 28 | name: "with build", 29 | failure: false, 30 | engine: _engine, 31 | }, 32 | { 33 | name: "missing build", 34 | failure: true, 35 | engine: new(client), 36 | }, 37 | } 38 | 39 | // run tests 40 | for _, test := range tests { 41 | t.Run(test.name, func(t *testing.T) { 42 | got, err := test.engine.GetBuild() 43 | 44 | if test.failure { 45 | if err == nil { 46 | t.Errorf("GetBuild should have returned err") 47 | } 48 | 49 | return // continue to next test 50 | } 51 | 52 | if err != nil { 53 | t.Errorf("GetBuild returned err: %v", err) 54 | } 55 | 56 | if !reflect.DeepEqual(got, _build) { 57 | t.Errorf("GetBuild is %v, want %v", got, _build) 58 | } 59 | }) 60 | } 61 | } 62 | 63 | func TestLocal_GetPipeline(t *testing.T) { 64 | // setup types 65 | _steps := testSteps() 66 | 67 | _engine, err := New( 68 | WithPipeline(_steps), 69 | ) 70 | if err != nil { 71 | t.Errorf("unable to create executor engine: %v", err) 72 | } 73 | 74 | // setup tests 75 | tests := []struct { 76 | name string 77 | failure bool 78 | engine *client 79 | }{ 80 | { 81 | name: "with pipeline", 82 | failure: false, 83 | engine: _engine, 84 | }, 85 | { 86 | name: "missing pipeline", 87 | failure: true, 88 | engine: new(client), 89 | }, 90 | } 91 | 92 | // run tests 93 | for _, test := range tests { 94 | t.Run(test.name, func(t *testing.T) { 95 | got, err := test.engine.GetPipeline() 96 | 97 | if test.failure { 98 | if err == nil { 99 | t.Errorf("GetPipeline should have returned err") 100 | } 101 | 102 | return // continue to next test 103 | } 104 | 105 | if err != nil { 106 | t.Errorf("GetPipeline returned err: %v", err) 107 | } 108 | 109 | if !reflect.DeepEqual(got, _steps) { 110 | t.Errorf("GetPipeline is %v, want %v", got, _steps) 111 | } 112 | }) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /executor/local/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package local provides the ability for Vela to 4 | // integrate with the local system. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/executor/local" 9 | package local 10 | -------------------------------------------------------------------------------- /executor/local/driver.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package local 4 | 5 | import "github.com/go-vela/server/constants" 6 | 7 | // Driver outputs the configured executor driver. 8 | func (c *client) Driver() string { 9 | return constants.DriverLocal 10 | } 11 | -------------------------------------------------------------------------------- /executor/local/driver_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package local 4 | 5 | import ( 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/go-vela/server/constants" 10 | "github.com/go-vela/worker/runtime/docker" 11 | ) 12 | 13 | func TestLocal_Driver(t *testing.T) { 14 | // setup types 15 | want := constants.DriverLocal 16 | 17 | _runtime, err := docker.NewMock() 18 | if err != nil { 19 | t.Errorf("unable to create runtime engine: %v", err) 20 | } 21 | 22 | _engine, err := New( 23 | WithBuild(testBuild()), 24 | WithHostname("localhost"), 25 | WithPipeline(testSteps()), 26 | WithRuntime(_runtime), 27 | ) 28 | if err != nil { 29 | t.Errorf("unable to create executor engine: %v", err) 30 | } 31 | 32 | // run tes 33 | got := _engine.Driver() 34 | 35 | if !reflect.DeepEqual(got, want) { 36 | t.Errorf("Driver is %v, want %v", got, want) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /executor/local/local.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package local 4 | 5 | import ( 6 | "os" 7 | "reflect" 8 | "sync" 9 | 10 | "github.com/go-vela/sdk-go/vela" 11 | api "github.com/go-vela/server/api/types" 12 | "github.com/go-vela/server/compiler/types/pipeline" 13 | "github.com/go-vela/worker/internal/message" 14 | "github.com/go-vela/worker/runtime" 15 | ) 16 | 17 | type ( 18 | // client manages communication with the pipeline resources. 19 | client struct { 20 | Vela *vela.Client 21 | Runtime runtime.Engine 22 | Hostname string 23 | Version string 24 | OutputCtn *pipeline.Container 25 | 26 | // private fields 27 | init *pipeline.Container 28 | build *api.Build 29 | pipeline *pipeline.Build 30 | services sync.Map 31 | steps sync.Map 32 | err error 33 | streamRequests chan message.StreamRequest 34 | 35 | outputs *outputSvc 36 | 37 | // internal field partially exported for tests 38 | stdout *os.File 39 | mockStdoutReader *os.File 40 | } 41 | 42 | svc struct { 43 | client *client 44 | } 45 | 46 | // MockedClient is for internal use to facilitate testing the local executor. 47 | MockedClient interface { 48 | MockStdout() *os.File 49 | } 50 | ) 51 | 52 | // MockStdout is for internal use to facilitate testing the local executor. 53 | // MockStdout returns a reader over a mocked Stdout. 54 | func (c *client) MockStdout() *os.File { 55 | return c.mockStdoutReader 56 | } 57 | 58 | // equal returns true if the other client is the equivalent. 59 | func Equal(a, b *client) bool { 60 | // handle any nil comparisons 61 | if a == nil || b == nil { 62 | return a == nil && b == nil 63 | } 64 | 65 | return reflect.DeepEqual(a.Vela, b.Vela) && 66 | reflect.DeepEqual(a.Runtime, b.Runtime) && 67 | a.Hostname == b.Hostname && 68 | a.Version == b.Version && 69 | reflect.DeepEqual(a.init, b.init) && 70 | reflect.DeepEqual(a.build, b.build) && 71 | reflect.DeepEqual(a.pipeline, b.pipeline) && 72 | reflect.DeepEqual(&a.services, &b.services) && 73 | reflect.DeepEqual(&a.steps, &b.steps) && 74 | reflect.DeepEqual(a.err, b.err) 75 | } 76 | 77 | // New returns an Executor implementation that integrates with the local system. 78 | // 79 | //nolint:revive // ignore unexported type as it is intentional 80 | func New(opts ...Opt) (*client, error) { 81 | // create new local client 82 | c := new(client) 83 | 84 | // Add stdout by default 85 | c.stdout = os.Stdout 86 | 87 | c.outputs = &outputSvc{client: c} 88 | 89 | // instantiate streamRequests channel (which may be overridden using withStreamRequests()). 90 | c.streamRequests = make(chan message.StreamRequest) 91 | 92 | // apply all provided configuration options 93 | for _, opt := range opts { 94 | err := opt(c) 95 | if err != nil { 96 | return nil, err 97 | } 98 | } 99 | 100 | return c, nil 101 | } 102 | -------------------------------------------------------------------------------- /executor/local/testdata/build/empty.yml: -------------------------------------------------------------------------------- 1 | --- -------------------------------------------------------------------------------- /executor/local/testdata/build/services/basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | services: 4 | - name: postgres 5 | environment: 6 | FOO: bar 7 | image: postgres:latest 8 | pull: true 9 | 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: alpine:latest 17 | pull: true 18 | -------------------------------------------------------------------------------- /executor/local/testdata/build/services/img_ignorenotfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | services: 4 | - name: postgres 5 | environment: 6 | FOO: bar 7 | image: postgres:ignorenotfound 8 | pull: true 9 | 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: alpine:latest 17 | pull: true -------------------------------------------------------------------------------- /executor/local/testdata/build/services/img_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | services: 4 | - name: postgres 5 | environment: 6 | FOO: bar 7 | image: postgres:notfound 8 | pull: true 9 | 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: alpine:latest 17 | pull: true -------------------------------------------------------------------------------- /executor/local/testdata/build/services/name_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | services: 4 | - name: notfound 5 | environment: 6 | FOO: bar 7 | image: postgres:latest 8 | pull: true 9 | 10 | steps: 11 | - name: test 12 | commands: 13 | - echo ${FOO} 14 | environment: 15 | FOO: bar 16 | image: alpine:latest 17 | pull: true -------------------------------------------------------------------------------- /executor/local/testdata/build/stages/basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | stages: 4 | test: 5 | steps: 6 | - name: test 7 | commands: 8 | - echo ${FOO} 9 | environment: 10 | FOO: bar 11 | image: alpine:latest 12 | pull: true 13 | -------------------------------------------------------------------------------- /executor/local/testdata/build/stages/img_ignorenotfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | stages: 4 | test: 5 | steps: 6 | - name: test 7 | commands: 8 | - echo ${FOO} 9 | environment: 10 | FOO: bar 11 | image: alpine:ignorenotfound 12 | pull: true 13 | -------------------------------------------------------------------------------- /executor/local/testdata/build/stages/img_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | stages: 4 | test: 5 | steps: 6 | - name: test 7 | commands: 8 | - echo ${FOO} 9 | environment: 10 | FOO: bar 11 | image: alpine:notfound 12 | pull: true 13 | -------------------------------------------------------------------------------- /executor/local/testdata/build/stages/name_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | stages: 4 | test: 5 | steps: 6 | - name: notfound 7 | commands: 8 | - echo ${FOO} 9 | environment: 10 | FOO: bar 11 | image: alpine:latest 12 | pull: true 13 | -------------------------------------------------------------------------------- /executor/local/testdata/build/steps/basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | -------------------------------------------------------------------------------- /executor/local/testdata/build/steps/img_ignorenotfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:ignorenotfound 10 | pull: true 11 | -------------------------------------------------------------------------------- /executor/local/testdata/build/steps/img_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: test 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:notfound 10 | pull: true 11 | -------------------------------------------------------------------------------- /executor/local/testdata/build/steps/name_notfound.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | steps: 4 | - name: notfound 5 | commands: 6 | - echo ${FOO} 7 | environment: 8 | FOO: bar 9 | image: alpine:latest 10 | pull: true 11 | -------------------------------------------------------------------------------- /internal/build/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package build provides the ability for Vela to 4 | // manipulate and manage a build from a pipeline. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/internal/build" 9 | package build 10 | -------------------------------------------------------------------------------- /internal/build/snapshot.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package build 4 | 5 | import ( 6 | "strings" 7 | "time" 8 | 9 | "github.com/sirupsen/logrus" 10 | 11 | "github.com/go-vela/sdk-go/vela" 12 | api "github.com/go-vela/server/api/types" 13 | "github.com/go-vela/server/constants" 14 | ) 15 | 16 | // Snapshot creates a moment in time record of the build 17 | // and attempts to upload it to the server. 18 | func Snapshot(b *api.Build, c *vela.Client, e error, l *logrus.Entry) { 19 | // check if the build is not in a canceled status 20 | if !strings.EqualFold(b.GetStatus(), constants.StatusCanceled) { 21 | // check if the error provided is empty 22 | if e != nil { 23 | // populate build fields with error based values 24 | b.SetError(e.Error()) 25 | b.SetStatus(constants.StatusError) 26 | b.SetFinished(time.Now().UTC().Unix()) 27 | } 28 | } 29 | 30 | // check if the logger provided is empty 31 | if l == nil { 32 | // create new logger 33 | // 34 | // https://pkg.go.dev/github.com/sirupsen/logrus#NewEntry 35 | l = logrus.NewEntry(logrus.StandardLogger()) 36 | } 37 | 38 | // check if the Vela client provided is empty 39 | if c != nil { 40 | l.Debug("uploading build snapshot") 41 | 42 | // send API call to update the build 43 | // 44 | // https://pkg.go.dev/github.com/go-vela/sdk-go/vela#BuildService.Update 45 | _, _, err := c.Build.Update(b) 46 | if err != nil { 47 | l.Errorf("unable to upload build snapshot: %v", err) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/build/upload.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package build 4 | 5 | import ( 6 | "strings" 7 | "time" 8 | 9 | "github.com/sirupsen/logrus" 10 | 11 | "github.com/go-vela/sdk-go/vela" 12 | api "github.com/go-vela/server/api/types" 13 | "github.com/go-vela/server/constants" 14 | ) 15 | 16 | // Upload tracks the final state of the build 17 | // and attempts to upload it to the server. 18 | func Upload(b *api.Build, c *vela.Client, e error, l *logrus.Entry) { 19 | // handle the build based off the status provided 20 | switch b.GetStatus() { 21 | // build is in a canceled state 22 | case constants.StatusCanceled: 23 | fallthrough 24 | // build is in a error state 25 | case constants.StatusError: 26 | fallthrough 27 | // build is in a failure state 28 | case constants.StatusFailure: 29 | // if the build is in a canceled, error 30 | // or failure state we DO NOT want to 31 | // update the state to be success 32 | break 33 | // build is in a pending state 34 | case constants.StatusPending: 35 | // if the build is in a pending state 36 | // then something must have gone 37 | // drastically wrong because this 38 | // SHOULD NOT happen 39 | b.SetStatus(constants.StatusKilled) 40 | default: 41 | // update the build with a success state 42 | b.SetStatus(constants.StatusSuccess) 43 | } 44 | 45 | // check if the build is not in a canceled status 46 | if !strings.EqualFold(b.GetStatus(), constants.StatusCanceled) { 47 | // check if the error provided is empty 48 | if e != nil { 49 | // update the build with error based values 50 | b.SetError(e.Error()) 51 | b.SetStatus(constants.StatusError) 52 | } 53 | } 54 | 55 | // update the build with the finished timestamp 56 | b.SetFinished(time.Now().UTC().Unix()) 57 | 58 | // check if the logger provided is empty 59 | if l == nil { 60 | // create new logger 61 | // 62 | // https://pkg.go.dev/github.com/sirupsen/logrus#NewEntry 63 | l = logrus.NewEntry(logrus.StandardLogger()) 64 | } 65 | 66 | // check if the Vela client provided is empty 67 | if c != nil { 68 | l.Debug("uploading final build state") 69 | 70 | // send API call to update the build 71 | // 72 | // https://pkg.go.dev/github.com/go-vela/sdk-go/vela#BuildService.Update 73 | _, _, err := c.Build.Update(b) 74 | if err != nil { 75 | l.Errorf("unable to upload final build state: %v", err) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /internal/context/context.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package context 4 | 5 | import ( 6 | "context" 7 | "time" 8 | 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | func WithDelayedCancelPropagation(parent context.Context, timeout time.Duration, name string, logger *logrus.Entry) (context.Context, context.CancelFunc) { 13 | ctx, cancel := context.WithCancel(context.Background()) 14 | 15 | go func() { 16 | var timer *time.Timer 17 | 18 | // start the timer once the parent context is canceled 19 | select { 20 | case <-parent.Done(): 21 | logger.Tracef("parent context is done, starting %s timer for %s", name, timeout) 22 | timer = time.NewTimer(timeout) 23 | 24 | break 25 | case <-ctx.Done(): 26 | logger.Tracef("%s finished before the parent context", name) 27 | 28 | return 29 | } 30 | 31 | // wait for the timer to elapse or the context to naturally finish. 32 | select { 33 | case <-timer.C: 34 | logger.Tracef("%s timed out, propagating cancel to %s context", name, name) 35 | cancel() 36 | 37 | return 38 | case <-ctx.Done(): 39 | logger.Tracef("%s finished, stopping timeout timer", name) 40 | timer.Stop() 41 | 42 | return 43 | } 44 | }() 45 | 46 | return ctx, cancel 47 | } 48 | -------------------------------------------------------------------------------- /internal/context/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package context provides context utilities. 4 | // 5 | // Usage: 6 | // 7 | // import "github.com/go-vela/worker/internal/context" 8 | package context 9 | -------------------------------------------------------------------------------- /internal/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package internal provides a collection of internal 4 | // packages used for the Vela executor systems. 5 | // 6 | // More information: 7 | // 8 | // - https://golang.org/doc/go1.4#internalpackages 9 | // - https://docs.google.com/document/d/1e8kOo3r51b2BWtTs_1uADIA5djfXhPT36s6eHVRIvaU/edit 10 | // 11 | // Usage: 12 | // 13 | // import "github.com/go-vela/worker/internal" 14 | package internal 15 | -------------------------------------------------------------------------------- /internal/image/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package image provides the ability for Vela to manage 4 | // and manipulate an image provided for a container. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/internal/image" 9 | package image 10 | -------------------------------------------------------------------------------- /internal/image/image.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package image 4 | 5 | import ( 6 | "github.com/distribution/reference" 7 | ) 8 | 9 | // Parse digests the provided image into a fully 10 | // qualified canonical reference. If an error 11 | // occurs, it will return the provided image. 12 | func Parse(_image string) string { 13 | // parse the image provided into a fully qualified canonical reference 14 | // 15 | // https://pkg.go.dev/github.com/go-vela/worker/runtime/internal/image#ParseWithError 16 | _canonical, err := ParseWithError(_image) 17 | if err != nil { 18 | return _image 19 | } 20 | 21 | return _canonical 22 | } 23 | 24 | // ParseWithError digests the provided image into a 25 | // fully qualified canonical reference. If an error 26 | // occurs, it will return the last digested form of 27 | // the image. 28 | func ParseWithError(_image string) (string, error) { 29 | // parse the image provided into a 30 | // named, fully qualified reference 31 | // 32 | // https://pkg.go.dev/github.com/distribution/reference#ParseAnyReference 33 | _reference, err := reference.ParseAnyReference(_image) 34 | if err != nil { 35 | return _image, err 36 | } 37 | 38 | // ensure we have the canonical form of the named reference 39 | // 40 | // https://pkg.go.dev/github.com/distribution/reference#ParseNamed 41 | _canonical, err := reference.ParseNamed(_reference.String()) 42 | if err != nil { 43 | return _reference.String(), err 44 | } 45 | 46 | // ensure the canonical reference has a tag 47 | // 48 | // https://pkg.go.dev/github.com/distribution/reference#TagNameOnly 49 | return reference.TagNameOnly(_canonical).String(), nil 50 | } 51 | 52 | // IsPrivilegedImage digests the provided image with a 53 | // privileged pattern to see if the image meets the criteria 54 | // needed to allow a Docker Socket mount. 55 | func IsPrivilegedImage(image string, privilegedSet []string) (bool, error) { 56 | for _, pattern := range privilegedSet { 57 | // parse the image provided into a 58 | // named, fully qualified reference 59 | // 60 | // https://pkg.go.dev/github.com/distribution/reference#ParseAnyReference 61 | _refImg, err := reference.ParseAnyReference(image) 62 | if err != nil { 63 | return false, err 64 | } 65 | 66 | // ensure we have the canonical form of the named reference 67 | // 68 | // https://pkg.go.dev/github.com/distribution/reference#ParseNamed 69 | _canonical, err := reference.ParseNamed(_refImg.String()) 70 | if err != nil { 71 | return false, err 72 | } 73 | 74 | // add default tag "latest" when tag does not exist 75 | _refImg = reference.TagNameOnly(_canonical) 76 | 77 | // check if the image matches the privileged pattern 78 | // 79 | // https://pkg.go.dev/github.com/distribution/reference#FamiliarMatch 80 | match, err := reference.FamiliarMatch(pattern, _refImg) 81 | if err != nil { 82 | return false, err 83 | } 84 | 85 | if match { 86 | return match, nil 87 | } 88 | } 89 | 90 | return false, nil 91 | } 92 | -------------------------------------------------------------------------------- /internal/internal.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package internal 4 | 5 | // For more information: 6 | // 7 | // * https://golang.org/doc/go1.4#internalpackages 8 | // * https://docs.google.com/document/d/1e8kOo3r51b2BWtTs_1uADIA5djfXhPT36s6eHVRIvaU/edit 9 | -------------------------------------------------------------------------------- /internal/message/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package message provides message types used in the executor. 4 | // These types have to be in a separate package to prevent circular imports. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/internal/message" 9 | package message 10 | -------------------------------------------------------------------------------- /internal/message/stream.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package message 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/go-vela/server/compiler/types/pipeline" 9 | ) 10 | 11 | // StreamFunc is either StreamService or StreamStep in executor.Engine. 12 | type StreamFunc = func(context.Context, *pipeline.Container) error 13 | 14 | // StreamRequest is the message used to begin streaming for a container 15 | // (requests goes from ExecService / ExecStep to StreamBuild in executor). 16 | type StreamRequest struct { 17 | // Key is either "service" or "step". 18 | Key string 19 | // Stream is either Engine.StreamService or Engine.StreamStep. 20 | Stream StreamFunc 21 | // Container is the container for the service or step to stream logs for. 22 | Container *pipeline.Container 23 | } 24 | 25 | // MockStreamRequestsWithCancel discards all requests until you call the cancel function. 26 | func MockStreamRequestsWithCancel(ctx context.Context) (chan StreamRequest, context.CancelFunc) { 27 | cancelCtx, done := context.WithCancel(ctx) 28 | streamRequests := make(chan StreamRequest) 29 | 30 | // discard all stream requests 31 | go func() { 32 | for { 33 | select { 34 | case <-streamRequests: 35 | case <-cancelCtx.Done(): 36 | return 37 | } 38 | } 39 | }() 40 | 41 | return streamRequests, done 42 | } 43 | -------------------------------------------------------------------------------- /internal/outputs/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package outputs provides the ability for Vela to 4 | // manipulate and manage a the outputs map during a build. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/internal/outputs" 9 | package outputs 10 | -------------------------------------------------------------------------------- /internal/outputs/sanitize.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package outputs 4 | 5 | import ( 6 | "github.com/go-vela/server/compiler/types/pipeline" 7 | ) 8 | 9 | // Skip creates the ruledata from the build and repository 10 | // information and returns true if the data does not match 11 | // the ruleset for the given container. 12 | func Sanitize(c *pipeline.Container, outputs map[string]string) map[string]string { 13 | // check if the container provided is empty 14 | if c == nil { 15 | return nil 16 | } 17 | 18 | if len(outputs) == 0 { 19 | return nil 20 | } 21 | 22 | // initialize sanitized outputs max length of outputs 23 | result := make(map[string]string, len(outputs)) 24 | 25 | for k, v := range outputs { 26 | // do not allow overwrites to compiled starter env for step 27 | if _, ok := c.Environment[k]; ok { 28 | continue 29 | } 30 | 31 | result[k] = v 32 | } 33 | 34 | return result 35 | } 36 | -------------------------------------------------------------------------------- /internal/service/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package service provides the ability for Vela to 4 | // manipulate and manage a service from a pipeline. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/internal/service" 9 | package service 10 | -------------------------------------------------------------------------------- /internal/service/environment.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package service 4 | 5 | import ( 6 | "fmt" 7 | 8 | api "github.com/go-vela/server/api/types" 9 | "github.com/go-vela/server/compiler/types/pipeline" 10 | "github.com/go-vela/server/constants" 11 | ) 12 | 13 | // Environment attempts to update the environment variables 14 | // for the container based off the library resources. 15 | func Environment(c *pipeline.Container, b *api.Build, s *api.Service, version string) error { 16 | // check if container or container environment are empty 17 | if c == nil || c.Environment == nil { 18 | return fmt.Errorf("empty container provided for environment") 19 | } 20 | 21 | // check if the build provided is empty 22 | if b != nil { 23 | // check if the channel exists in the environment 24 | channel, ok := c.Environment["VELA_CHANNEL"] 25 | if !ok { 26 | // set default for channel 27 | channel = constants.DefaultRoute 28 | } 29 | 30 | // check if the workspace exists in the environment 31 | workspace, ok := c.Environment["VELA_WORKSPACE"] 32 | if !ok { 33 | // set default for workspace 34 | workspace = constants.WorkspaceDefault 35 | } 36 | 37 | // update environment variables 38 | c.Environment["VELA_DISTRIBUTION"] = b.GetDistribution() 39 | c.Environment["VELA_HOST"] = b.GetHost() 40 | c.Environment["VELA_RUNTIME"] = b.GetRuntime() 41 | c.Environment["VELA_VERSION"] = version 42 | 43 | // populate environment variables from build library 44 | err := c.MergeEnv(b.Environment(workspace, channel)) 45 | if err != nil { 46 | return err 47 | } 48 | } 49 | 50 | // populate environment variables from repo library 51 | err := c.MergeEnv(b.GetRepo().Environment()) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | // check if the service provided is empty 57 | if s != nil { 58 | // populate environment variables from service library 59 | err := c.MergeEnv(s.Environment()) 60 | if err != nil { 61 | return err 62 | } 63 | } 64 | 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /internal/service/load.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package service 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | 9 | api "github.com/go-vela/server/api/types" 10 | "github.com/go-vela/server/compiler/types/pipeline" 11 | ) 12 | 13 | // Load attempts to capture the library service 14 | // representing the container from the map. 15 | func Load(c *pipeline.Container, m *sync.Map) (*api.Service, error) { 16 | // check if the container provided is empty 17 | if c == nil { 18 | return nil, fmt.Errorf("empty container provided") 19 | } 20 | 21 | // load the container ID as the service key from the map 22 | result, ok := m.Load(c.ID) 23 | if !ok { 24 | return nil, fmt.Errorf("unable to load service %s", c.ID) 25 | } 26 | 27 | // cast the value from the service key to the expected type 28 | s, ok := result.(*api.Service) 29 | if !ok { 30 | return nil, fmt.Errorf("unable to cast value for service %s", c.ID) 31 | } 32 | 33 | return s, nil 34 | } 35 | 36 | // LoadLogs attempts to capture the library service logs 37 | // representing the container from the map. 38 | func LoadLogs(c *pipeline.Container, m *sync.Map) (*api.Log, error) { 39 | // check if the container provided is empty 40 | if c == nil { 41 | return nil, fmt.Errorf("empty container provided") 42 | } 43 | 44 | // load the container ID as the service log key from the map 45 | result, ok := m.Load(c.ID) 46 | if !ok { 47 | return nil, fmt.Errorf("unable to load logs for service %s", c.ID) 48 | } 49 | 50 | // cast the value from the service log key to the expected type 51 | l, ok := result.(*api.Log) 52 | if !ok { 53 | return nil, fmt.Errorf("unable to cast value to logs for service %s", c.ID) 54 | } 55 | 56 | return l, nil 57 | } 58 | -------------------------------------------------------------------------------- /internal/service/snapshot.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package service 4 | 5 | import ( 6 | "strings" 7 | "time" 8 | 9 | "github.com/sirupsen/logrus" 10 | 11 | "github.com/go-vela/sdk-go/vela" 12 | api "github.com/go-vela/server/api/types" 13 | "github.com/go-vela/server/compiler/types/pipeline" 14 | "github.com/go-vela/server/constants" 15 | ) 16 | 17 | // Snapshot creates a moment in time record of the 18 | // service and attempts to upload it to the server. 19 | func Snapshot(ctn *pipeline.Container, b *api.Build, c *vela.Client, l *logrus.Entry, s *api.Service) { 20 | // check if the build is not in a canceled status 21 | if !strings.EqualFold(s.GetStatus(), constants.StatusCanceled) { 22 | // check if the container is running in headless mode 23 | if !ctn.Detach { 24 | // update the service fields to indicate a success 25 | s.SetStatus(constants.StatusSuccess) 26 | s.SetFinished(time.Now().UTC().Unix()) 27 | } 28 | 29 | // check if the container has an unsuccessful exit code 30 | if ctn.ExitCode != 0 { 31 | // check if container failures should be ignored 32 | if !ctn.Ruleset.Continue { 33 | // set build status to failure 34 | b.SetStatus(constants.StatusFailure) 35 | } 36 | 37 | // update the service fields to indicate a failure 38 | s.SetExitCode(ctn.ExitCode) 39 | s.SetStatus(constants.StatusFailure) 40 | } 41 | } 42 | 43 | // check if the logger provided is empty 44 | if l == nil { 45 | // create new logger 46 | // 47 | // https://pkg.go.dev/github.com/sirupsen/logrus#NewEntry 48 | l = logrus.NewEntry(logrus.StandardLogger()) 49 | } 50 | 51 | // check if the Vela client provided is empty 52 | if c != nil { 53 | l.Debug("uploading service snapshot") 54 | 55 | // send API call to update the service 56 | // 57 | // https://pkg.go.dev/github.com/go-vela/sdk-go/vela#SvcService.Update 58 | _, _, err := c.Svc.Update(b.GetRepo().GetOrg(), b.GetRepo().GetName(), b.GetNumber(), s) 59 | if err != nil { 60 | l.Errorf("unable to upload service snapshot: %v", err) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /internal/service/upload.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package service 4 | 5 | import ( 6 | "time" 7 | 8 | "github.com/sirupsen/logrus" 9 | 10 | "github.com/go-vela/sdk-go/vela" 11 | api "github.com/go-vela/server/api/types" 12 | "github.com/go-vela/server/compiler/types/pipeline" 13 | "github.com/go-vela/server/constants" 14 | ) 15 | 16 | // Upload tracks the final state of the service 17 | // and attempts to upload it to the server. 18 | func Upload(ctn *pipeline.Container, b *api.Build, c *vela.Client, l *logrus.Entry, s *api.Service) { 19 | // handle the service based off the status provided 20 | switch s.GetStatus() { 21 | // service is in a canceled state 22 | case constants.StatusCanceled: 23 | fallthrough 24 | // service is in a error state 25 | case constants.StatusError: 26 | fallthrough 27 | // service is in a failure state 28 | case constants.StatusFailure: 29 | // if the service is in a canceled, error 30 | // or failure state we DO NOT want to 31 | // update the state to be success 32 | break 33 | // service is in a pending state 34 | case constants.StatusPending: 35 | // if the service is in a pending state 36 | // then something must have gone 37 | // drastically wrong because this 38 | // SHOULD NOT happen 39 | // 40 | // TODO: consider making this a constant 41 | s.SetExitCode(137) 42 | s.SetFinished(time.Now().UTC().Unix()) 43 | s.SetStatus(constants.StatusKilled) 44 | 45 | // check if the service was not started 46 | if s.GetStarted() == 0 { 47 | // set the started time to the finished time 48 | s.SetStarted(s.GetFinished()) 49 | } 50 | default: 51 | // update the service with a success state 52 | s.SetStatus(constants.StatusSuccess) 53 | } 54 | 55 | // check if the service finished 56 | if s.GetFinished() == 0 { 57 | // update the service with the finished timestamp 58 | s.SetFinished(time.Now().UTC().Unix()) 59 | 60 | // check the container for an unsuccessful exit code 61 | if ctn.ExitCode != 0 { 62 | // update the service fields to indicate a failure 63 | s.SetExitCode(ctn.ExitCode) 64 | s.SetStatus(constants.StatusFailure) 65 | } 66 | } 67 | 68 | // check if the logger provided is empty 69 | if l == nil { 70 | // create new logger 71 | // 72 | // https://pkg.go.dev/github.com/sirupsen/logrus#NewEntry 73 | l = logrus.NewEntry(logrus.StandardLogger()) 74 | } 75 | 76 | // check if the Vela client provided is empty 77 | if c != nil { 78 | l.Debug("uploading service snapshot") 79 | 80 | // send API call to update the service 81 | // 82 | // https://pkg.go.dev/github.com/go-vela/sdk-go/vela#SvcService.Update 83 | _, _, err := c.Svc.Update(b.GetRepo().GetOrg(), b.GetRepo().GetName(), b.GetNumber(), s) 84 | if err != nil { 85 | l.Errorf("unable to upload service snapshot: %v", err) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /internal/step/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package step provides the ability for Vela to 4 | // manipulate and manage a step from a pipeline. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/internal/step" 9 | package step 10 | -------------------------------------------------------------------------------- /internal/step/environment.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package step 4 | 5 | import ( 6 | "fmt" 7 | 8 | api "github.com/go-vela/server/api/types" 9 | "github.com/go-vela/server/compiler/types/pipeline" 10 | "github.com/go-vela/server/constants" 11 | ) 12 | 13 | // Environment attempts to update the environment variables 14 | // for the container based off the library resources. 15 | func Environment(c *pipeline.Container, b *api.Build, s *api.Step, version, reqToken string) error { 16 | // check if container or container environment are empty 17 | if c == nil || c.Environment == nil { 18 | return fmt.Errorf("empty container provided for environment") 19 | } 20 | 21 | // check if the build provided is empty 22 | if b != nil { 23 | // check if the channel exists in the environment 24 | channel, ok := c.Environment["VELA_CHANNEL"] 25 | if !ok { 26 | // set default for channel 27 | channel = constants.DefaultRoute 28 | } 29 | 30 | // check if the workspace exists in the environment 31 | workspace, ok := c.Environment["VELA_WORKSPACE"] 32 | if !ok { 33 | // set default for workspace 34 | workspace = constants.WorkspaceDefault 35 | } 36 | 37 | // update environment variables 38 | c.Environment["VELA_DISTRIBUTION"] = b.GetDistribution() 39 | c.Environment["VELA_HOST"] = b.GetHost() 40 | c.Environment["VELA_RUNTIME"] = b.GetRuntime() 41 | c.Environment["VELA_VERSION"] = version 42 | c.Environment["VELA_ID_TOKEN_REQUEST_TOKEN"] = reqToken 43 | c.Environment["VELA_OUTPUTS"] = "/vela/outputs/.env" 44 | c.Environment["VELA_MASKED_OUTPUTS"] = "/vela/outputs/masked.env" 45 | 46 | // populate environment variables from build library 47 | err := c.MergeEnv(b.Environment(workspace, channel)) 48 | if err != nil { 49 | return err 50 | } 51 | } 52 | 53 | // populate environment variables from build library 54 | err := c.MergeEnv(b.GetRepo().Environment()) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | // check if the step provided is empty 60 | if s != nil { 61 | // populate environment variables from step library 62 | err := c.MergeEnv(s.Environment()) 63 | if err != nil { 64 | return err 65 | } 66 | } 67 | 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /internal/step/load.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package step 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | 9 | api "github.com/go-vela/server/api/types" 10 | "github.com/go-vela/server/compiler/types/pipeline" 11 | ) 12 | 13 | // Load attempts to capture the library step 14 | // representing the container from the map. 15 | func Load(c *pipeline.Container, m *sync.Map) (*api.Step, error) { 16 | // check if the container provided is empty 17 | if c == nil { 18 | return nil, fmt.Errorf("empty container provided") 19 | } 20 | 21 | // load the container ID as the step key from the map 22 | result, ok := m.Load(c.ID) 23 | if !ok { 24 | return nil, fmt.Errorf("unable to load step %s", c.ID) 25 | } 26 | 27 | // cast the value from the step key to the expected type 28 | s, ok := result.(*api.Step) 29 | if !ok { 30 | return nil, fmt.Errorf("unable to cast value for step %s", c.ID) 31 | } 32 | 33 | return s, nil 34 | } 35 | 36 | // LoadInit attempts to capture the container representing 37 | // the init process from the pipeline. 38 | func LoadInit(p *pipeline.Build) (*pipeline.Container, error) { 39 | // check if the pipeline provided is empty 40 | if p == nil { 41 | return nil, fmt.Errorf("empty pipeline provided") 42 | } 43 | 44 | // create new container for the init step 45 | c := new(pipeline.Container) 46 | 47 | // check if there are steps in the pipeline 48 | if len(p.Steps) > 0 { 49 | // update the container for the init process 50 | c = p.Steps[0] 51 | } 52 | 53 | // check if there are stages in the pipeline 54 | if len(p.Stages) > 0 { 55 | // update the container for the init process 56 | c = p.Stages[0].Steps[0] 57 | } 58 | 59 | return c, nil 60 | } 61 | 62 | // LoadLogs attempts to capture the library step logs 63 | // representing the container from the map. 64 | func LoadLogs(c *pipeline.Container, m *sync.Map) (*api.Log, error) { 65 | // check if the container provided is empty 66 | if c == nil { 67 | return nil, fmt.Errorf("empty container provided") 68 | } 69 | 70 | // load the container ID as the step log key from the map 71 | result, ok := m.Load(c.ID) 72 | if !ok { 73 | return nil, fmt.Errorf("unable to load logs for step %s", c.ID) 74 | } 75 | 76 | // cast the value from the step log key to the expected type 77 | l, ok := result.(*api.Log) 78 | if !ok { 79 | return nil, fmt.Errorf("unable to cast value to logs for step %s", c.ID) 80 | } 81 | 82 | return l, nil 83 | } 84 | -------------------------------------------------------------------------------- /internal/step/skip.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package step 4 | 5 | import ( 6 | "strings" 7 | 8 | api "github.com/go-vela/server/api/types" 9 | "github.com/go-vela/server/compiler/types/pipeline" 10 | "github.com/go-vela/server/constants" 11 | ) 12 | 13 | // Skip creates the ruledata from the build and repository 14 | // information and returns true if the data does not match 15 | // the ruleset for the given container. 16 | func Skip(c *pipeline.Container, b *api.Build, status string) (bool, error) { 17 | // check if the container provided is empty 18 | if c == nil { 19 | return true, nil 20 | } 21 | 22 | event := b.GetEvent() 23 | action := b.GetEventAction() 24 | 25 | // if the build has an event action, concatenate event and event action for matching 26 | if !strings.EqualFold(action, "") { 27 | event = event + ":" + action 28 | } 29 | 30 | // create ruledata from build and repository information 31 | ruledata := &pipeline.RuleData{ 32 | Branch: b.GetBranch(), 33 | Event: event, 34 | Repo: b.GetRepo().GetFullName(), 35 | Status: status, 36 | Env: c.Environment, 37 | } 38 | 39 | // check if the build event is tag 40 | if strings.EqualFold(b.GetEvent(), constants.EventTag) { 41 | // add tag information to ruledata with refs/tags prefix removed 42 | ruledata.Tag = strings.TrimPrefix(b.GetRef(), "refs/tags/") 43 | } 44 | 45 | // check if the build event is deployment 46 | if strings.EqualFold(b.GetEvent(), constants.EventDeploy) { 47 | // handle when deployment event is for a tag 48 | if strings.HasPrefix(b.GetRef(), "refs/tags/") { 49 | // add tag information to ruledata with refs/tags prefix removed 50 | ruledata.Tag = strings.TrimPrefix(b.GetRef(), "refs/tags/") 51 | } 52 | // add deployment target information to ruledata 53 | ruledata.Target = b.GetDeploy() 54 | } 55 | 56 | // check if the build event is schedule 57 | if strings.EqualFold(b.GetEvent(), constants.EventSchedule) { 58 | // add schedule target information to ruledata 59 | ruledata.Target = b.GetDeploy() 60 | } 61 | 62 | // return the inverse of container execute 63 | exec, err := c.Execute(ruledata) 64 | 65 | return !exec, err 66 | } 67 | -------------------------------------------------------------------------------- /internal/step/upload.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package step 4 | 5 | import ( 6 | "time" 7 | 8 | "github.com/sirupsen/logrus" 9 | 10 | "github.com/go-vela/sdk-go/vela" 11 | api "github.com/go-vela/server/api/types" 12 | "github.com/go-vela/server/compiler/types/pipeline" 13 | "github.com/go-vela/server/constants" 14 | ) 15 | 16 | // Upload tracks the final state of the step 17 | // and attempts to upload it to the server. 18 | func Upload(ctn *pipeline.Container, b *api.Build, c *vela.Client, l *logrus.Entry, s *api.Step) { 19 | // handle the step based off the status provided 20 | switch s.GetStatus() { 21 | // step is in a canceled state 22 | case constants.StatusCanceled: 23 | fallthrough 24 | // step is in a error state 25 | case constants.StatusError: 26 | fallthrough 27 | // step is in a failure state 28 | case constants.StatusFailure: 29 | // if the step is in a canceled, error 30 | // or failure state we DO NOT want to 31 | // update the state to be success 32 | break 33 | // step is in a pending state 34 | case constants.StatusPending: 35 | // if the step is in a pending state 36 | // then something must have gone 37 | // drastically wrong because this 38 | // SHOULD NOT happen 39 | // 40 | // TODO: consider making this a constant 41 | s.SetExitCode(137) 42 | s.SetFinished(time.Now().UTC().Unix()) 43 | s.SetStatus(constants.StatusKilled) 44 | 45 | // check if the step was not started 46 | if s.GetStarted() == 0 { 47 | // set the started time to the finished time 48 | s.SetStarted(s.GetFinished()) 49 | } 50 | default: 51 | // update the step with a success state 52 | s.SetStatus(constants.StatusSuccess) 53 | } 54 | 55 | // check if the step finished 56 | if s.GetFinished() == 0 { 57 | // update the step with the finished timestamp 58 | s.SetFinished(time.Now().UTC().Unix()) 59 | 60 | // check the container for an unsuccessful exit code 61 | if ctn.ExitCode != 0 { 62 | // update the step fields to indicate a failure 63 | s.SetExitCode(ctn.ExitCode) 64 | s.SetStatus(constants.StatusFailure) 65 | } 66 | } 67 | 68 | // check if the logger provided is empty 69 | if l == nil { 70 | // create new logger 71 | // 72 | // https://pkg.go.dev/github.com/sirupsen/logrus#NewEntry 73 | l = logrus.NewEntry(logrus.StandardLogger()) 74 | } 75 | 76 | // check if the Vela client provided is empty 77 | if c != nil { 78 | l.Debug("uploading final step state") 79 | 80 | // send API call to update the step 81 | // 82 | // https://pkg.go.dev/github.com/go-vela/sdk-go/vela#StepService.Update 83 | _, _, err := c.Step.Update(b.GetRepo().GetOrg(), b.GetRepo().GetName(), b.GetNumber(), s) 84 | if err != nil { 85 | l.Errorf("unable to upload final step state: %v", err) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /internal/volume/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package volume provides the ability for Vela to manage 4 | // and manipulate a volume provided for a container. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/internal/volume" 9 | package volume 10 | -------------------------------------------------------------------------------- /internal/volume/volume.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package volume 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | // Volume represents the volume definition used 11 | // to create volumes for a container. 12 | type Volume struct { 13 | Source string `json:"source,omitempty"` 14 | Destination string `json:"destination,omitempty"` 15 | AccessMode string `json:"access_mode,omitempty"` 16 | } 17 | 18 | // Parse digests the provided volume into a fully 19 | // qualified volume reference. If an error 20 | // occurs, it will return a nil volume. 21 | func Parse(_volume string) *Volume { 22 | // parse the image provided into a fully qualified canonical reference 23 | // 24 | // https://pkg.go.dev/github.com/go-vela/worker/runtime/internal/image#ParseWithError 25 | v, err := ParseWithError(_volume) 26 | if err != nil { 27 | return nil 28 | } 29 | 30 | return v 31 | } 32 | 33 | // ParseWithError digests the provided volume into a 34 | // fully qualified volume reference. If an error 35 | // occurs, it will return a nil volume and the 36 | // produced error. 37 | func ParseWithError(_volume string) (*Volume, error) { 38 | // split each slice element into source, destination and access mode 39 | parts := strings.Split(_volume, ":") 40 | 41 | switch len(parts) { 42 | case 1: 43 | // return the read-only volume with the same source and destination 44 | return &Volume{ 45 | Source: parts[0], 46 | Destination: parts[0], 47 | AccessMode: "ro", 48 | }, nil 49 | case 2: 50 | // return the read-only volume with different source and destination 51 | return &Volume{ 52 | Source: parts[0], 53 | Destination: parts[1], 54 | AccessMode: "ro", 55 | }, nil 56 | case 3: 57 | // return the full volume with source, destination and access mode 58 | return &Volume{ 59 | Source: parts[0], 60 | Destination: parts[1], 61 | AccessMode: parts[2], 62 | }, nil 63 | default: 64 | return nil, fmt.Errorf("volume %s requires at least 1, but no more than 2, `:`", _volume) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /mock/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package mock provides a collection of mock 4 | // packages used for a Vela worker. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/mock" 9 | package mock 10 | -------------------------------------------------------------------------------- /mock/docker/config.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //nolint:dupl // ignore similar code 4 | package docker 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/docker/docker/api/types" 10 | "github.com/docker/docker/api/types/swarm" 11 | "github.com/docker/docker/client" 12 | ) 13 | 14 | // ConfigService implements all the config 15 | // related functions for the Docker mock. 16 | type ConfigService struct{} 17 | 18 | // ConfigCreate is a helper function to simulate 19 | // a mocked call to create a config for a 20 | // Docker swarm cluster. 21 | func (c *ConfigService) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error) { 22 | return types.ConfigCreateResponse{}, nil 23 | } 24 | 25 | // ConfigInspectWithRaw is a helper function to simulate 26 | // a mocked call to inspect a config for a Docker swarm 27 | // cluster and return the raw body received from the API. 28 | func (c *ConfigService) ConfigInspectWithRaw(ctx context.Context, name string) (swarm.Config, []byte, error) { 29 | return swarm.Config{}, nil, nil 30 | } 31 | 32 | // ConfigList is a helper function to simulate 33 | // a mocked call to list the configs for a 34 | // Docker swarm cluster. 35 | func (c *ConfigService) ConfigList(ctx context.Context, options types.ConfigListOptions) ([]swarm.Config, error) { 36 | return nil, nil 37 | } 38 | 39 | // ConfigRemove is a helper function to simulate 40 | // a mocked call to remove a config for a 41 | // Docker swarm cluster. 42 | func (c *ConfigService) ConfigRemove(ctx context.Context, id string) error { return nil } 43 | 44 | // ConfigUpdate is a helper function to simulate 45 | // a mocked call to update a config for a 46 | // Docker swarm cluster. 47 | func (c *ConfigService) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error { 48 | return nil 49 | } 50 | 51 | // WARNING: DO NOT REMOVE THIS UNDER ANY CIRCUMSTANCES 52 | // 53 | // This line serves as a quick and efficient way to ensure that our 54 | // ImageService satisfies the ImageAPIClient interface that 55 | // the Docker client expects. 56 | // 57 | // https://pkg.go.dev/github.com/docker/docker/client#ConfigAPIClient 58 | var _ client.ConfigAPIClient = (*ConfigService)(nil) 59 | -------------------------------------------------------------------------------- /mock/docker/distribution.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/docker/docker/api/types/registry" 9 | "github.com/docker/docker/client" 10 | ) 11 | 12 | // DistributionService implements all the distribution 13 | // related functions for the Docker mock. 14 | type DistributionService struct{} 15 | 16 | // DistributionInspect is a helper function to simulate 17 | // a mocked call to inspect a Docker image and return 18 | // the digest and manifest. 19 | func (d *DistributionService) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registry.DistributionInspect, error) { 20 | return registry.DistributionInspect{}, nil 21 | } 22 | 23 | // WARNING: DO NOT REMOVE THIS UNDER ANY CIRCUMSTANCES 24 | // 25 | // This line serves as a quick and efficient way to ensure that our 26 | // DistributionService satisfies the DistributionAPIClient interface that 27 | // the Docker client expects. 28 | var _ client.DistributionAPIClient = (*DistributionService)(nil) 29 | -------------------------------------------------------------------------------- /mock/docker/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package docker provides a mock for using the Docker API. 4 | // 5 | // https://pkg.go.dev/github.com/docker/docker/client 6 | // 7 | // Usage: 8 | // 9 | // import "github.com/go-vela/worker/mock/docker" 10 | package docker 11 | -------------------------------------------------------------------------------- /mock/docker/docker.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | // Version represents the supported Docker API version for the mock. 6 | // 7 | // The Docker API version is pinned to ensure compatibility between the 8 | // Docker API and client. The goal is to maintain n-1 compatibility. 9 | // 10 | // The maximum supported Docker API version for the client is here: 11 | // 12 | // https://docs.docker.com/engine/api/#api-version-matrix 13 | // 14 | // For example (use the compatibility matrix above for reference): 15 | // 16 | // * the Docker version of v26.0 has a maximum API version of v1.45 17 | // * to maintain n-1, the API version is pinned to v1.44 18 | // . 19 | const Version = "v1.44" 20 | 21 | // New returns a client that is capable of handling 22 | // Docker client calls and returning stub responses. 23 | // 24 | //nolint:revive // ignore unexported type as it is intentional 25 | func New() (*mock, error) { 26 | return &mock{ 27 | ConfigService: ConfigService{}, 28 | ContainerService: ContainerService{}, 29 | DistributionService: DistributionService{}, 30 | ImageService: ImageService{}, 31 | NetworkService: NetworkService{}, 32 | NodeService: NodeService{}, 33 | PluginService: PluginService{}, 34 | SecretService: SecretService{}, 35 | ServiceService: ServiceService{}, 36 | SystemService: SystemService{}, 37 | SwarmService: SwarmService{}, 38 | VolumeService: VolumeService{}, 39 | Version: Version, 40 | }, nil 41 | } 42 | -------------------------------------------------------------------------------- /mock/docker/node.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/docker/docker/api/types" 9 | "github.com/docker/docker/api/types/swarm" 10 | "github.com/docker/docker/client" 11 | ) 12 | 13 | // NodeService implements all the node 14 | // related functions for the Docker mock. 15 | type NodeService struct{} 16 | 17 | // NodeInspectWithRaw is a helper function to simulate 18 | // a mocked call to inspect a node for a Docker swarm 19 | // cluster and return the raw body received from the API. 20 | // 21 | // https://pkg.go.dev/github.com/docker/docker/client#Client.NodeInspectWithRaw 22 | func (n *NodeService) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) { 23 | return swarm.Node{}, nil, nil 24 | } 25 | 26 | // NodeList is a helper function to simulate 27 | // a mocked call to list the nodes for a 28 | // Docker swarm cluster. 29 | // 30 | // https://pkg.go.dev/github.com/docker/docker/client#Client.NodeList 31 | func (n *NodeService) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) { 32 | return nil, nil 33 | } 34 | 35 | // NodeRemove is a helper function to simulate 36 | // a mocked call to remove a node from a 37 | // Docker swarm cluster. 38 | // 39 | // https://pkg.go.dev/github.com/docker/docker/client#Client.NodeRemove 40 | func (n *NodeService) NodeRemove(ctx context.Context, nodeID string, options types.NodeRemoveOptions) error { 41 | return nil 42 | } 43 | 44 | // NodeUpdate is a helper function to simulate 45 | // a mocked call to update a node for a 46 | // Docker swarm cluster. 47 | // 48 | // https://pkg.go.dev/github.com/docker/docker/client#Client.NodeUpdate 49 | func (n *NodeService) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error { 50 | return nil 51 | } 52 | 53 | // WARNING: DO NOT REMOVE THIS UNDER ANY CIRCUMSTANCES 54 | // 55 | // This line serves as a quick and efficient way to ensure that our 56 | // NodeService satisfies the NodeAPIClient interface that 57 | // the Docker client expects. 58 | // 59 | // https://pkg.go.dev/github.com/docker/docker/client#NodeAPIClient 60 | var _ client.NodeAPIClient = (*NodeService)(nil) 61 | -------------------------------------------------------------------------------- /mock/docker/secret.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //nolint:dupl // ignore similar code 4 | package docker 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/docker/docker/api/types" 10 | "github.com/docker/docker/api/types/swarm" 11 | "github.com/docker/docker/client" 12 | ) 13 | 14 | // SecretService implements all the secret 15 | // related functions for the Docker mock. 16 | type SecretService struct{} 17 | 18 | // SecretCreate is a helper function to simulate 19 | // a mocked call to create a secret for a 20 | // Docker swarm cluster. 21 | // 22 | // https://pkg.go.dev/github.com/docker/docker/client#Client.SecretCreate 23 | func (s *SecretService) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) { 24 | return types.SecretCreateResponse{}, nil 25 | } 26 | 27 | // SecretInspectWithRaw is a helper function to simulate 28 | // a mocked call to inspect a Docker secret and return 29 | // the raw body received from the API. 30 | // 31 | // https://pkg.go.dev/github.com/docker/docker/client#Client.SecretInspectWithRaw 32 | func (s *SecretService) SecretInspectWithRaw(ctx context.Context, name string) (swarm.Secret, []byte, error) { 33 | return swarm.Secret{}, nil, nil 34 | } 35 | 36 | // SecretList is a helper function to simulate 37 | // a mocked call to list secrets for a 38 | // Docker swarm cluster. 39 | // 40 | // https://pkg.go.dev/github.com/docker/docker/client#Client.SecretList 41 | func (s *SecretService) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) { 42 | return nil, nil 43 | } 44 | 45 | // SecretRemove is a helper function to simulate 46 | // a mocked call to remove a secret for a 47 | // Docker swarm cluster. 48 | // 49 | // https://pkg.go.dev/github.com/docker/docker/client#Client.SecretRemove 50 | func (s *SecretService) SecretRemove(ctx context.Context, id string) error { 51 | return nil 52 | } 53 | 54 | // SecretUpdate is a helper function to simulate 55 | // a mocked call to update a secret for a 56 | // Docker swarm cluster. 57 | // 58 | // https://pkg.go.dev/github.com/docker/docker/client#Client.SecretUpdate 59 | func (s *SecretService) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error { 60 | return nil 61 | } 62 | 63 | // WARNING: DO NOT REMOVE THIS UNDER ANY CIRCUMSTANCES 64 | // 65 | // This line serves as a quick and efficient way to ensure that our 66 | // SecretService satisfies the SecretAPIClient interface that 67 | // the Docker client expects. 68 | // 69 | // https://pkg.go.dev/github.com/docker/docker/client#SecretAPIClient 70 | var _ client.SecretAPIClient = (*SecretService)(nil) 71 | -------------------------------------------------------------------------------- /mock/docker/system.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/docker/docker/api/types" 9 | "github.com/docker/docker/api/types/events" 10 | "github.com/docker/docker/api/types/registry" 11 | "github.com/docker/docker/api/types/system" 12 | "github.com/docker/docker/client" 13 | ) 14 | 15 | // SystemService implements all the system 16 | // related functions for the Docker mock. 17 | type SystemService struct{} 18 | 19 | // DiskUsage is a helper function to simulate 20 | // a mocked call to capture the data usage 21 | // from the Docker daemon. 22 | // 23 | // https://pkg.go.dev/github.com/docker/docker/client#Client.DiskUsage 24 | func (s *SystemService) DiskUsage(ctx context.Context, opts types.DiskUsageOptions) (types.DiskUsage, error) { 25 | return types.DiskUsage{}, nil 26 | } 27 | 28 | // Events is a helper function to simulate 29 | // a mocked call to capture the events 30 | // from the Docker daemon. 31 | // 32 | // https://pkg.go.dev/github.com/docker/docker/client#Client.Events 33 | func (s *SystemService) Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error) { 34 | return nil, nil 35 | } 36 | 37 | // Info is a helper function to simulate 38 | // a mocked call to capture the system 39 | // information from the Docker daemon. 40 | // 41 | // https://pkg.go.dev/github.com/docker/docker/client#Client.Info 42 | func (s *SystemService) Info(ctx context.Context) (system.Info, error) { 43 | return system.Info{}, nil 44 | } 45 | 46 | // Ping is a helper function to simulate 47 | // a mocked call to ping the Docker 48 | // daemon and return version information. 49 | // 50 | // https://pkg.go.dev/github.com/docker/docker/client#Client.Ping 51 | func (s *SystemService) Ping(ctx context.Context) (types.Ping, error) { 52 | return types.Ping{}, nil 53 | } 54 | 55 | // RegistryLogin is a helper function to simulate 56 | // a mocked call to authenticate the Docker 57 | // daemon against a Docker registry. 58 | // 59 | // https://pkg.go.dev/github.com/docker/docker/client#Client.RegistryLogin 60 | func (s *SystemService) RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) { 61 | return registry.AuthenticateOKBody{}, nil 62 | } 63 | 64 | // WARNING: DO NOT REMOVE THIS UNDER ANY CIRCUMSTANCES 65 | // 66 | // This line serves as a quick and efficient way to ensure that our 67 | // SystemService satisfies the SystemAPIClient interface that 68 | // the Docker client expects. 69 | // 70 | // hhttps://pkg.go.dev/github.com/docker/docker/client#SystemAPIClient 71 | var _ client.NetworkAPIClient = (*NetworkService)(nil) 72 | -------------------------------------------------------------------------------- /mock/mock.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package mock 4 | -------------------------------------------------------------------------------- /mock/worker/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package worker provides a mock for using the Worker API. 4 | // 5 | // Usage: 6 | // 7 | // import "github.com/go-vela/worker/mock/worker" 8 | package worker 9 | -------------------------------------------------------------------------------- /mock/worker/executor.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package worker 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "net/http" 9 | "strings" 10 | 11 | "github.com/gin-gonic/gin" 12 | 13 | api "github.com/go-vela/server/api/types" 14 | ) 15 | 16 | const ( 17 | // ExecutorResp represents a JSON return for a single worker. 18 | ExecutorResp = ` 19 | { 20 | "id": 1, 21 | "host": "worker_1", 22 | "runtime": "docker", 23 | "distribution": "linux", 24 | "build": ` + BuildResp + `, 25 | "pipeline": ` + PipelineResp + `, 26 | "repo": ` + RepoResp + ` 27 | }` 28 | 29 | // ExecutorsResp represents a JSON return for one to many workers. 30 | ExecutorsResp = `[ ` + ExecutorResp + `,` + ExecutorResp + `]` 31 | ) 32 | 33 | // getExecutors returns mock JSON for a http GET. 34 | func getExecutors(c *gin.Context) { 35 | data := []byte(ExecutorsResp) 36 | 37 | var body []api.Executor 38 | _ = json.Unmarshal(data, &body) 39 | 40 | c.JSON(http.StatusOK, body) 41 | } 42 | 43 | // getExecutor has a param :executor returns mock JSON for a http GET. 44 | func getExecutor(c *gin.Context) { 45 | w := c.Param("executor") 46 | 47 | if strings.EqualFold(w, "0") { 48 | msg := fmt.Sprintf("Executor %s does not exist", w) 49 | 50 | c.AbortWithStatusJSON(http.StatusNotFound, api.Error{Message: &msg}) 51 | 52 | return 53 | } 54 | 55 | data := []byte(ExecutorResp) 56 | 57 | var body api.Executor 58 | _ = json.Unmarshal(data, &body) 59 | 60 | c.JSON(http.StatusOK, body) 61 | } 62 | -------------------------------------------------------------------------------- /mock/worker/pipeline.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package worker 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "net/http" 9 | "strings" 10 | 11 | "github.com/gin-gonic/gin" 12 | 13 | api "github.com/go-vela/server/api/types" 14 | ) 15 | 16 | const ( 17 | // PipelineResp represents a JSON return for a single pipeline. 18 | PipelineResp = `{ 19 | "id": 1, 20 | "repo_id": 1, 21 | "commit": "48afb5bdc41ad69bf22588491333f7cf71135163", 22 | "flavor": "", 23 | "platform": "", 24 | "ref": "refs/heads/main", 25 | "type": "yaml", 26 | "version": "1", 27 | "external_secrets": false, 28 | "internal_secrets": false, 29 | "services": false, 30 | "stages": false, 31 | "steps": true, 32 | "templates": false, 33 | "data": "LS0tCnZlcnNpb246ICIxIgoKc3RlcHM6CiAgLSBuYW1lOiBlY2hvCiAgICBpbWFnZTogYWxwaW5lOmxhdGVzdAogICAgY29tbWFuZHM6IFtlY2hvIGZvb10=" 34 | }` 35 | ) 36 | 37 | // getPipeline has a param :pipeline returns mock YAML for a http GET. 38 | // 39 | // Pass "0" to :pipeline to test receiving a http 404 response. 40 | func getPipeline(c *gin.Context) { 41 | p := c.Param("pipeline") 42 | 43 | if strings.EqualFold(p, "0") { 44 | msg := fmt.Sprintf("Pipeline %s does not exist", p) 45 | 46 | c.AbortWithStatusJSON(http.StatusNotFound, api.Error{Message: &msg}) 47 | 48 | return 49 | } 50 | 51 | data := []byte(PipelineResp) 52 | 53 | var body api.Pipeline 54 | _ = json.Unmarshal(data, &body) 55 | 56 | c.JSON(http.StatusOK, body) 57 | } 58 | -------------------------------------------------------------------------------- /mock/worker/register.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package worker 4 | 5 | import ( 6 | "net/http" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // postRegister returns mock JSON for a http POST. 12 | // 13 | // Do not pass an auth token to fail the request. 14 | func postRegister(c *gin.Context) { 15 | token := c.Request.Header.Get("Authorization") 16 | if len(token) == 0 { 17 | c.JSON(http.StatusUnauthorized, "no token provided in Authorization header") 18 | } 19 | 20 | c.JSON(http.StatusOK, "successfully passed token to worker") 21 | } 22 | -------------------------------------------------------------------------------- /mock/worker/repo.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package worker 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "net/http" 9 | "strings" 10 | 11 | "github.com/gin-gonic/gin" 12 | 13 | api "github.com/go-vela/server/api/types" 14 | ) 15 | 16 | const ( 17 | // RepoResp represents a JSON return for a single repo. 18 | RepoResp = `{ 19 | "id": 1, 20 | "user_id": 1, 21 | "org": "github", 22 | "name": "octocat", 23 | "full_name": "github/octocat", 24 | "link": "https://github.com/github/octocat", 25 | "clone": "https://github.com/github/octocat", 26 | "branch": "main", 27 | "build_limit": 10, 28 | "timeout": 60, 29 | "visibility": "public", 30 | "private": false, 31 | "trusted": true, 32 | "active": true, 33 | "allow_pr": false, 34 | "allow_push": true, 35 | "allow_deploy": false, 36 | "allow_tag": false 37 | }` 38 | ) 39 | 40 | // getRepo has a param :repo returns mock JSON for a http GET. 41 | // 42 | // Pass "not-found" to :repo to test receiving a http 404 response. 43 | func getRepo(c *gin.Context) { 44 | r := c.Param("repo") 45 | 46 | if strings.Contains(r, "not-found") { 47 | msg := fmt.Sprintf("Repo %s does not exist", r) 48 | 49 | c.AbortWithStatusJSON(http.StatusNotFound, api.Error{Message: &msg}) 50 | 51 | return 52 | } 53 | 54 | data := []byte(RepoResp) 55 | 56 | var body api.Repo 57 | _ = json.Unmarshal(data, &body) 58 | 59 | c.JSON(http.StatusOK, body) 60 | } 61 | -------------------------------------------------------------------------------- /mock/worker/server.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package worker 4 | 5 | import ( 6 | "net/http" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // FakeHandler returns an http.Handler that is capable of handling 12 | // Vela API requests and returning mock responses. 13 | func FakeHandler() http.Handler { 14 | gin.SetMode(gin.TestMode) 15 | 16 | e := gin.New() 17 | 18 | // mock endpoints for executor calls 19 | e.GET("/api/v1/executors", getExecutors) 20 | e.GET("/api/v1/executors/:executor", getExecutor) 21 | 22 | // mock endpoints for build calls 23 | e.GET("/api/v1/executors/:executor/build", getBuild) 24 | e.DELETE("/api/v1/executors/:executor/build/cancel", cancelBuild) 25 | 26 | // mock endpoints for pipeline calls 27 | e.GET("/api/v1/executors/:executor/pipeline", getPipeline) 28 | 29 | // mock endpoints for repo calls 30 | e.GET("/api/v1/executors/:executor/repo", getRepo) 31 | 32 | // mock endpoint for register call 33 | e.POST("/register", postRegister) 34 | 35 | return e 36 | } 37 | -------------------------------------------------------------------------------- /router/build.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package router 4 | 5 | import ( 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/go-vela/worker/api" 9 | ) 10 | 11 | // BuildHandlers extends the provided base router group 12 | // by adding a collection of endpoints for handling 13 | // build related requests. 14 | // 15 | // GET /api/v1/executors/:executor/build 16 | // DELETE /api/v1/executors/:executor/build/cancel 17 | // . 18 | func BuildHandlers(base *gin.RouterGroup) { 19 | // add a collection of endpoints for handling build related requests 20 | // 21 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.Group 22 | build := base.Group("/build") 23 | { 24 | // add an endpoint for capturing the build 25 | // 26 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.GET 27 | build.GET("", api.GetBuild) 28 | 29 | // add an endpoint for canceling the build 30 | // 31 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.DELETE 32 | build.DELETE("/cancel", api.CancelBuild) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /router/build_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package router 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/gin-gonic/gin" 9 | 10 | "github.com/go-vela/worker/api" 11 | ) 12 | 13 | func TestRouter_BuildHandlers(t *testing.T) { 14 | // setup types 15 | gin.SetMode(gin.TestMode) 16 | 17 | _engine := gin.New() 18 | 19 | want := gin.RoutesInfo{ 20 | { 21 | Method: "GET", 22 | Path: "/build", 23 | Handler: "github.com/go-vela/worker/api.GetBuild", 24 | HandlerFunc: api.GetBuild, 25 | }, 26 | { 27 | Method: "DELETE", 28 | Path: "/build/cancel", 29 | Handler: "github.com/go-vela/worker/api.CancelBuild", 30 | HandlerFunc: api.CancelBuild, 31 | }, 32 | } 33 | 34 | // run test 35 | BuildHandlers(&_engine.RouterGroup) 36 | 37 | got := _engine.Routes() 38 | 39 | if len(got) != len(want) { 40 | t.Errorf("BuildHandlers is %v, want %v", got, want) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /router/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package router provides the routing engine for Vela 4 | // to serve and process API requests. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/router" 9 | package router 10 | -------------------------------------------------------------------------------- /router/executor.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package router 4 | 5 | import ( 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/go-vela/worker/api" 9 | "github.com/go-vela/worker/router/middleware/executor" 10 | ) 11 | 12 | // ExecutorHandlers extends the provided base router group 13 | // by adding a collection of endpoints for handling 14 | // executor related requests. 15 | // 16 | // GET /api/v1/executors 17 | // GET /api/v1/executors/:executor 18 | // GET /api/v1/executors/:executor/build 19 | // DELETE /api/v1/executors/:executor/build/cancel 20 | // GET /api/v1/executors/:executor/pipeline 21 | // GET /api/v1/executors/:executor/repo 22 | // . 23 | func ExecutorHandlers(base *gin.RouterGroup) { 24 | // add a collection of endpoints for handling executors related requests 25 | // 26 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.Group 27 | executors := base.Group("/executors") 28 | { 29 | // add an endpoint for capturing the executors 30 | // 31 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.GET 32 | executors.GET("", api.GetExecutors) 33 | 34 | // add a collection of endpoints for handling executor related requests 35 | // 36 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.Group 37 | executor := executors.Group("/:executor", executor.Establish()) 38 | { 39 | // add an endpoint for capturing a executor 40 | // 41 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.GET 42 | executor.GET("", api.GetExecutor) 43 | 44 | // add a collection of endpoints for handling build related requests 45 | // 46 | // https://pkg.go.dev/github.com/go-vela/worker/router#BuildHandlers 47 | BuildHandlers(executor) 48 | 49 | // add a collection of endpoints for handling pipeline related requests 50 | // 51 | // https://pkg.go.dev/github.com/go-vela/worker/router#PipelineHandlers 52 | PipelineHandlers(executor) 53 | 54 | // add a collection of endpoints for handling repo related requests 55 | // 56 | // https://pkg.go.dev/github.com/go-vela/worker/router#RepoHandlers 57 | RepoHandlers(executor) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /router/executor_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package router 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/gin-gonic/gin" 9 | 10 | "github.com/go-vela/worker/api" 11 | ) 12 | 13 | func TestRouter_ExecutorHandlers(t *testing.T) { 14 | // setup types 15 | gin.SetMode(gin.TestMode) 16 | 17 | _engine := gin.New() 18 | 19 | want := gin.RoutesInfo{ 20 | { 21 | Method: "GET", 22 | Path: "/executors", 23 | Handler: "github.com/go-vela/worker/api.GetExecutors", 24 | HandlerFunc: api.GetExecutors, 25 | }, 26 | { 27 | Method: "GET", 28 | Path: "/executors/:executor", 29 | Handler: "github.com/go-vela/worker/api.GetExecutor", 30 | HandlerFunc: api.GetExecutor, 31 | }, 32 | { 33 | Method: "GET", 34 | Path: "/executors/:executor/build", 35 | Handler: "github.com/go-vela/worker/api.GetBuild", 36 | HandlerFunc: api.GetBuild, 37 | }, 38 | { 39 | Method: "DELETE", 40 | Path: "/executors/:executor/build/cancel", 41 | Handler: "github.com/go-vela/worker/api.CancelBuild", 42 | HandlerFunc: api.CancelBuild, 43 | }, 44 | { 45 | Method: "GET", 46 | Path: "/executors/:executor/pipeline", 47 | Handler: "github.com/go-vela/worker/api.GetPipeline", 48 | HandlerFunc: api.GetPipeline, 49 | }, 50 | { 51 | Method: "GET", 52 | Path: "/executors/:executor/repo", 53 | Handler: "github.com/go-vela/worker/api.GetRepo", 54 | HandlerFunc: api.GetRepo, 55 | }, 56 | } 57 | 58 | // run test 59 | ExecutorHandlers(&_engine.RouterGroup) 60 | 61 | got := _engine.Routes() 62 | 63 | if len(got) != len(want) { 64 | t.Errorf("ExecutorHandlers is %v, want %v", got, want) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /router/middleware/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package middleware provides the ability for injecting Vela 4 | // resources into the middleware chain for the API. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/router/middleware" 9 | package middleware 10 | -------------------------------------------------------------------------------- /router/middleware/executor.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/go-vela/worker/executor" 9 | ) 10 | 11 | // Executors is a middleware function that attaches the 12 | // executors to the context of every http.Request. 13 | func Executors(e map[int]executor.Engine) gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | c.Set("executors", e) 16 | c.Next() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /router/middleware/executor/executor.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package executor 4 | 5 | import ( 6 | "fmt" 7 | "net/http" 8 | "strconv" 9 | 10 | "github.com/gin-gonic/gin" 11 | "github.com/sirupsen/logrus" 12 | 13 | api "github.com/go-vela/server/api/types" 14 | "github.com/go-vela/worker/executor" 15 | ) 16 | 17 | // Retrieve gets the repo in the given context. 18 | func Retrieve(c *gin.Context) executor.Engine { 19 | return executor.FromGinContext(c) 20 | } 21 | 22 | // Establish sets the executor in the given context. 23 | func Establish() gin.HandlerFunc { 24 | return func(c *gin.Context) { 25 | param := c.Param("executor") 26 | if len(param) == 0 { 27 | msg := "No executor parameter provided" 28 | 29 | c.AbortWithStatusJSON(http.StatusBadRequest, api.Error{Message: &msg}) 30 | 31 | return 32 | } 33 | 34 | number, err := strconv.Atoi(param) 35 | if err != nil { 36 | msg := fmt.Sprintf("invalid executor parameter provided: %s", param) 37 | 38 | c.AbortWithStatusJSON(http.StatusBadRequest, api.Error{Message: &msg}) 39 | 40 | return 41 | } 42 | 43 | // capture executors value from context 44 | value := c.Value("executors") 45 | if value == nil { 46 | msg := "no running executors found" 47 | 48 | c.AbortWithStatusJSON(http.StatusInternalServerError, api.Error{Message: &msg}) 49 | 50 | return 51 | } 52 | 53 | // cast executors value to expected type 54 | executors, ok := value.(map[int]executor.Engine) 55 | if !ok { 56 | msg := "unable to get executors" 57 | 58 | c.AbortWithStatusJSON(http.StatusInternalServerError, api.Error{Message: &msg}) 59 | 60 | return 61 | } 62 | 63 | logrus.Debugf("Reading executor %s", param) 64 | 65 | e, ok := executors[number] 66 | if !ok { 67 | msg := fmt.Sprintf("unable to get executor %s", param) 68 | 69 | c.AbortWithStatusJSON(http.StatusBadRequest, api.Error{Message: &msg}) 70 | 71 | return 72 | } 73 | 74 | executor.WithGinContext(c, e) 75 | c.Next() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /router/middleware/executor_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "net/http" 7 | "net/http/httptest" 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/gin-gonic/gin" 12 | 13 | "github.com/go-vela/worker/executor" 14 | ) 15 | 16 | func TestMiddleware_Executors(t *testing.T) { 17 | // setup types 18 | got := map[int]executor.Engine{} 19 | want := make(map[int]executor.Engine) 20 | 21 | // setup context 22 | gin.SetMode(gin.TestMode) 23 | 24 | resp := httptest.NewRecorder() 25 | context, engine := gin.CreateTestContext(resp) 26 | context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) 27 | 28 | // setup mock server 29 | engine.Use(Executors(want)) 30 | engine.GET("/health", func(c *gin.Context) { 31 | got = c.Value("executors").(map[int]executor.Engine) 32 | 33 | c.Status(http.StatusOK) 34 | }) 35 | 36 | // run test 37 | engine.ServeHTTP(context.Writer, context.Request) 38 | 39 | if resp.Code != http.StatusOK { 40 | t.Errorf("Executors returned %v, want %v", resp.Code, http.StatusOK) 41 | } 42 | 43 | if !reflect.DeepEqual(got, want) { 44 | t.Errorf("Executors is %v, want %v", got, want) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /router/middleware/header.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "net/http" 7 | "time" 8 | 9 | "github.com/gin-gonic/gin" 10 | 11 | "github.com/go-vela/worker/version" 12 | ) 13 | 14 | // NoCache is a middleware function that appends headers 15 | // to prevent the client from caching the HTTP response. 16 | func NoCache(c *gin.Context) { 17 | c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value") 18 | c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT") 19 | c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat)) 20 | c.Next() 21 | } 22 | 23 | // Options is a middleware function that appends headers 24 | // for options requests and aborts then exits the middleware 25 | // chain and ends the request. 26 | func Options(c *gin.Context) { 27 | if c.Request.Method != "OPTIONS" { 28 | c.Next() 29 | } else { 30 | c.Header("Access-Control-Allow-Origin", "*") 31 | c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS") 32 | c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept") 33 | c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS") 34 | c.Header("Content-Type", "application/json") 35 | c.AbortWithStatus(http.StatusOK) 36 | } 37 | } 38 | 39 | // Secure is a middleware function that appends security 40 | // and resource access headers. 41 | func Secure(c *gin.Context) { 42 | // Also consider adding Content-Security-Policy headers 43 | // c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com") 44 | c.Header("Access-Control-Allow-Origin", "*") 45 | c.Header("X-Frame-Options", "DENY") 46 | c.Header("X-Content-Type-Options", "nosniff") 47 | c.Header("X-XSS-Protection", "1; mode=block") 48 | 49 | if c.Request.TLS != nil { 50 | c.Header("Strict-Transport-Security", "max-age=31536000") 51 | } 52 | } 53 | 54 | // RequestVersion is a middleware function that injects the Vela API version 55 | // information into the request so it will be logged. This is 56 | // intended for debugging and troubleshooting. 57 | func RequestVersion(c *gin.Context) { 58 | v := version.New() 59 | 60 | if gin.Mode() == "debug" { 61 | c.Request.Header.Set("X-Vela-Version", v.Semantic()) 62 | } else { // in prod we don't want the build number metadata 63 | c.Request.Header.Set("X-Vela-Version", v.Semantic()) 64 | } 65 | } 66 | 67 | // ResponseVersion is a middleware function that injects the Vela API version 68 | // information into the response so it will be logged. This is 69 | // intended for debugging and troubleshooting. 70 | func ResponseVersion(c *gin.Context) { 71 | v := version.New() 72 | 73 | if gin.Mode() == "debug" { 74 | c.Header("X-Vela-Version", v.Semantic()) 75 | } else { // in prod we don't want the build number metadata 76 | c.Header("X-Vela-Version", v.Semantic()) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /router/middleware/logger.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "time" 7 | 8 | "github.com/gin-gonic/gin" 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | // Logger returns a gin.HandlerFunc (middleware) that logs requests using logrus. 13 | // 14 | // Requests with errors are logged using logrus.Error(). 15 | // Requests without errors are logged using logrus.Info(). 16 | // 17 | // It receives: 18 | // 1. A time package format string (e.g. time.RFC3339). 19 | // 2. A boolean stating whether to use UTC time zone or local. 20 | func Logger(logger *logrus.Logger, timeFormat string, utc bool) gin.HandlerFunc { 21 | return func(c *gin.Context) { 22 | start := time.Now() 23 | // some evil middlewares modify this values 24 | path := c.Request.URL.Path 25 | 26 | c.Next() 27 | 28 | end := time.Now() 29 | latency := end.Sub(start) 30 | 31 | if utc { 32 | end = end.UTC() 33 | } 34 | 35 | // prevent us from logging the health endpoint 36 | if c.Request.URL.Path != "/health" { 37 | fields := logrus.Fields{ 38 | "api-version": c.GetHeader("X-Vela-Version"), 39 | "status": c.Writer.Status(), 40 | "method": c.Request.Method, 41 | "path": path, 42 | "ip": c.ClientIP(), 43 | "latency": latency, 44 | "user-worker": c.Request.UserAgent(), 45 | "time": end.Format(timeFormat), 46 | } 47 | 48 | body := c.Value("payload") 49 | if body != nil { 50 | fields["body"] = body 51 | } 52 | 53 | entry := logger.WithFields(fields) 54 | 55 | if len(c.Errors) > 0 { 56 | // Append error field if this is an erroneous request. 57 | entry.Error(c.Errors.String()) 58 | } else { 59 | entry.Info() 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /router/middleware/logger_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "bytes" 7 | "encoding/json" 8 | "fmt" 9 | "net/http" 10 | "net/http/httptest" 11 | "reflect" 12 | "testing" 13 | "time" 14 | 15 | "github.com/gin-gonic/gin" 16 | "github.com/sirupsen/logrus" 17 | "github.com/sirupsen/logrus/hooks/test" 18 | ) 19 | 20 | func TestMiddleware_Logger(t *testing.T) { 21 | // setup types 22 | payload, _ := json.Marshal(`{"foo": "bar"}`) 23 | wantLevel := logrus.InfoLevel 24 | wantMessage := "" 25 | 26 | logger, hook := test.NewNullLogger() 27 | defer hook.Reset() 28 | 29 | // setup context 30 | gin.SetMode(gin.TestMode) 31 | 32 | resp := httptest.NewRecorder() 33 | context, engine := gin.CreateTestContext(resp) 34 | context.Request, _ = http.NewRequest(http.MethodPost, "/foobar", bytes.NewBuffer(payload)) 35 | 36 | // setup mock server 37 | engine.Use(Payload()) 38 | engine.Use(Logger(logger, time.RFC3339, true)) 39 | engine.POST("/foobar", func(c *gin.Context) { 40 | c.Status(http.StatusOK) 41 | }) 42 | 43 | // run test 44 | engine.ServeHTTP(context.Writer, context.Request) 45 | 46 | gotLevel := hook.LastEntry().Level 47 | gotMessage := hook.LastEntry().Message 48 | 49 | if resp.Code != http.StatusOK { 50 | t.Errorf("Logger returned %v, want %v", resp.Code, http.StatusOK) 51 | } 52 | 53 | if !reflect.DeepEqual(gotLevel, wantLevel) { 54 | t.Errorf("Logger Level is %v, want %v", gotLevel, wantLevel) 55 | } 56 | 57 | if !reflect.DeepEqual(gotMessage, wantMessage) { 58 | t.Errorf("Logger Message is %v, want %v", gotMessage, wantMessage) 59 | } 60 | } 61 | 62 | func TestMiddleware_Logger_Error(t *testing.T) { 63 | // setup types 64 | wantLevel := logrus.ErrorLevel 65 | wantMessage := "Error #01: test error\n" 66 | 67 | logger, hook := test.NewNullLogger() 68 | defer hook.Reset() 69 | 70 | // setup context 71 | gin.SetMode(gin.TestMode) 72 | 73 | resp := httptest.NewRecorder() 74 | context, engine := gin.CreateTestContext(resp) 75 | context.Request, _ = http.NewRequest(http.MethodGet, "/foobar", nil) 76 | 77 | // setup mock server 78 | engine.Use(Logger(logger, time.RFC3339, true)) 79 | engine.GET("/foobar", func(c *gin.Context) { 80 | //nolint:errcheck // ignore checking error 81 | c.Error(fmt.Errorf("test error")) 82 | c.Status(http.StatusOK) 83 | }) 84 | 85 | // run test 86 | engine.ServeHTTP(context.Writer, context.Request) 87 | 88 | gotLevel := hook.LastEntry().Level 89 | gotMessage := hook.LastEntry().Message 90 | 91 | if resp.Code != http.StatusOK { 92 | t.Errorf("Logger returned %v, want %v", resp.Code, http.StatusOK) 93 | } 94 | 95 | if !reflect.DeepEqual(gotLevel, wantLevel) { 96 | t.Errorf("Logger Level is %v, want %v", gotLevel, wantLevel) 97 | } 98 | 99 | if !reflect.DeepEqual(gotMessage, wantMessage) { 100 | t.Errorf("Logger Message is %v, want %v", gotMessage, wantMessage) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /router/middleware/payload.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "bytes" 7 | "encoding/json" 8 | "io" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | // Payload is a middleware function that captures the user provided json body 14 | // and attaches it to the context of every http.Request to be logged. 15 | func Payload() gin.HandlerFunc { 16 | return func(c *gin.Context) { 17 | // bind JSON payload from request to be added to context 18 | var payload interface{} 19 | _ = c.BindJSON(&payload) 20 | 21 | body, _ := json.Marshal(&payload) 22 | 23 | c.Set("payload", payload) 24 | 25 | c.Request.Body = io.NopCloser(bytes.NewBuffer(body)) 26 | 27 | c.Next() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /router/middleware/payload_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "bytes" 7 | "encoding/json" 8 | "net/http" 9 | "net/http/httptest" 10 | "reflect" 11 | "testing" 12 | 13 | "github.com/gin-gonic/gin" 14 | ) 15 | 16 | func TestMiddleware_Payload(t *testing.T) { 17 | // setup types 18 | var got interface{} 19 | 20 | want := `{"foo": "bar"}` 21 | jsonBody, _ := json.Marshal(want) 22 | 23 | // setup context 24 | gin.SetMode(gin.TestMode) 25 | 26 | resp := httptest.NewRecorder() 27 | context, engine := gin.CreateTestContext(resp) 28 | context.Request, _ = http.NewRequest(http.MethodPost, "/health", bytes.NewBuffer(jsonBody)) 29 | 30 | // setup mock server 31 | engine.Use(Payload()) 32 | engine.POST("/health", func(c *gin.Context) { 33 | got = c.Value("payload") 34 | 35 | c.Status(http.StatusOK) 36 | }) 37 | 38 | // run test 39 | engine.ServeHTTP(context.Writer, context.Request) 40 | 41 | if resp.Code != http.StatusOK { 42 | t.Errorf("Payload returned %v, want %v", resp.Code, http.StatusOK) 43 | } 44 | 45 | if !reflect.DeepEqual(got, want) { 46 | t.Errorf("Payload is %v, want %v", got, want) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /router/middleware/perm/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package perm provides the ability for inserting 4 | // Vela user permissions resources into or extracting Vela user permissions 5 | // resources from the middleware chain for the API. 6 | // 7 | // Usage: 8 | // 9 | // import "github.com/go-vela/worker/router/middleware/perm" 10 | package perm 11 | -------------------------------------------------------------------------------- /router/middleware/perm/perm.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package perm 4 | 5 | import ( 6 | "fmt" 7 | "net/http" 8 | "strings" 9 | 10 | "github.com/gin-gonic/gin" 11 | "github.com/sirupsen/logrus" 12 | 13 | "github.com/go-vela/sdk-go/vela" 14 | api "github.com/go-vela/server/api/types" 15 | "github.com/go-vela/worker/router/middleware/token" 16 | ) 17 | 18 | // MustServer ensures the caller is the vela server. 19 | func MustServer() gin.HandlerFunc { 20 | return func(c *gin.Context) { 21 | // retrieve the callers token from the request headers 22 | tkn, err := token.Retrieve(c.Request) 23 | if err != nil { 24 | msg := fmt.Sprintf("error parsing token: %v", err) 25 | 26 | logrus.Error(msg) 27 | 28 | c.AbortWithStatusJSON(http.StatusBadRequest, api.Error{Message: &msg}) 29 | 30 | return 31 | } 32 | 33 | // retrieve the configured server address from the context 34 | addr := c.MustGet("server-address").(string) 35 | 36 | // create a temporary client to validate the incoming request 37 | vela, err := vela.NewClient(addr, "vela-worker", nil) 38 | if err != nil { 39 | msg := fmt.Sprintf("error creating vela client: %s", err) 40 | 41 | logrus.Error(msg) 42 | 43 | c.AbortWithStatusJSON(http.StatusInternalServerError, api.Error{Message: &msg}) 44 | 45 | return 46 | } 47 | 48 | // validate a token was provided 49 | if strings.EqualFold(tkn, "") { 50 | msg := "missing token" 51 | 52 | logrus.Error(msg) 53 | 54 | c.AbortWithStatusJSON(http.StatusBadRequest, api.Error{Message: &msg}) 55 | 56 | return 57 | } 58 | 59 | // set the token auth provided in the callers request header 60 | vela.Authentication.SetTokenAuth(tkn) 61 | 62 | // validate the token with the configured vela server 63 | resp, err := vela.Authentication.ValidateToken() 64 | if err != nil { 65 | msg := fmt.Sprintf("error validating token: %s", err) 66 | 67 | logrus.Error(msg) 68 | 69 | c.AbortWithStatusJSON(http.StatusInternalServerError, api.Error{Message: &msg}) 70 | 71 | return 72 | } 73 | 74 | // if ValidateToken returned anything other than 200 consider the token invalid 75 | if resp.StatusCode != http.StatusOK { 76 | msg := "unable to validate token" 77 | 78 | logrus.Error(msg) 79 | 80 | c.AbortWithStatusJSON(http.StatusUnauthorized, api.Error{Message: &msg}) 81 | 82 | return 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /router/middleware/register_token.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | // RegisterToken is a middleware function that attaches the 10 | // auth-token channel to the context of every http.Request. 11 | func RegisterToken(r chan string) gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | c.Set("register-token", r) 14 | c.Next() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /router/middleware/register_token_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "net/http" 7 | "net/http/httptest" 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func TestMiddleware_RegisterToken(t *testing.T) { 15 | // setup types 16 | want := make(chan string, 1) 17 | got := make(chan string, 1) 18 | 19 | want <- "foo" 20 | 21 | // setup context 22 | gin.SetMode(gin.TestMode) 23 | 24 | resp := httptest.NewRecorder() 25 | context, engine := gin.CreateTestContext(resp) 26 | context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) 27 | 28 | // setup mock server 29 | engine.Use(RegisterToken(want)) 30 | engine.GET("/health", func(c *gin.Context) { 31 | got = c.Value("register-token").(chan string) 32 | 33 | c.Status(http.StatusOK) 34 | }) 35 | 36 | // run test 37 | engine.ServeHTTP(context.Writer, context.Request) 38 | 39 | if resp.Code != http.StatusOK { 40 | t.Errorf("RegisterToken returned %v, want %v", resp.Code, http.StatusOK) 41 | } 42 | 43 | if !reflect.DeepEqual(got, want) { 44 | t.Errorf("RegisterToken is %v, want foo", got) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /router/middleware/server.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | // ServerAddress is a middleware function that attaches the 10 | // server address to the context of every http.Request. 11 | func ServerAddress(addr string) gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | c.Set("server-address", addr) 14 | c.Next() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /router/middleware/server_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "net/http" 7 | "net/http/httptest" 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func TestMiddleware_ServerAddress(t *testing.T) { 15 | // setup types 16 | got := "" 17 | want := "foobar" 18 | 19 | // setup context 20 | gin.SetMode(gin.TestMode) 21 | 22 | resp := httptest.NewRecorder() 23 | context, engine := gin.CreateTestContext(resp) 24 | context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) 25 | 26 | // setup mock server 27 | engine.Use(ServerAddress(want)) 28 | engine.GET("/health", func(c *gin.Context) { 29 | got = c.Value("server-address").(string) 30 | 31 | c.Status(http.StatusOK) 32 | }) 33 | 34 | // run test 35 | engine.ServeHTTP(context.Writer, context.Request) 36 | 37 | if resp.Code != http.StatusOK { 38 | t.Errorf("ServerAddress returned %v, want %v", resp.Code, http.StatusOK) 39 | } 40 | 41 | if !reflect.DeepEqual(got, want) { 42 | t.Errorf("ServerAddress is %v, want %v", got, want) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /router/middleware/token/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package token provides the ability for inserting 4 | // Vela token resources into or extracting Vela token 5 | // resources from the middleware chain for the API. 6 | // 7 | // Usage: 8 | // 9 | // import "github.com/go-vela/worker/router/middleware/token" 10 | package token 11 | -------------------------------------------------------------------------------- /router/middleware/token/token.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package token 4 | 5 | import ( 6 | "fmt" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | // Retrieve gets the token from the provided request http.Request 12 | // to be parsed and validated. This is called on every request 13 | // to enable capturing the user making the request and validating 14 | // they have the proper access. The following methods of providing 15 | // authentication to Vela are supported: 16 | // 17 | // * Bearer token in `Authorization` header 18 | // . 19 | func Retrieve(r *http.Request) (string, error) { 20 | // get the token from the `Authorization` header 21 | token := r.Header.Get("Authorization") 22 | if len(token) > 0 { 23 | if strings.Contains(token, "Bearer") { 24 | return strings.Split(token, "Bearer ")[1], nil 25 | } 26 | } 27 | 28 | return "", fmt.Errorf("no token provided in Authorization header") 29 | } 30 | -------------------------------------------------------------------------------- /router/middleware/token/token_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package token 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "net/http" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func TestToken_Retrieve(t *testing.T) { 14 | // setup types 15 | want := "foobar" 16 | 17 | header := fmt.Sprintf("Bearer %s", want) 18 | request, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/test", nil) 19 | request.Header.Set("Authorization", header) 20 | 21 | // run test 22 | got, err := Retrieve(request) 23 | if err != nil { 24 | t.Errorf("Retrieve returned err: %v", err) 25 | } 26 | 27 | if !strings.EqualFold(got, want) { 28 | t.Errorf("Retrieve is %v, want %v", got, want) 29 | } 30 | } 31 | 32 | func TestToken_Retrieve_Error(t *testing.T) { 33 | // setup types 34 | request, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/test", nil) 35 | 36 | // run test 37 | got, err := Retrieve(request) 38 | if err == nil { 39 | t.Errorf("Retrieve should have returned err") 40 | } 41 | 42 | if len(got) > 0 { 43 | t.Errorf("Retrieve is %v, want \"\"", got) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /router/middleware/worker.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | // WorkerHostname is a middleware function that attaches the 10 | // worker hostname to the context of every http.Request. 11 | func WorkerHostname(name string) gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | c.Set("worker-hostname", name) 14 | c.Next() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /router/middleware/worker_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package middleware 4 | 5 | import ( 6 | "net/http" 7 | "net/http/httptest" 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func TestMiddleware_WorkerHostname(t *testing.T) { 15 | // setup types 16 | got := "" 17 | want := "foobar" 18 | 19 | // setup context 20 | gin.SetMode(gin.TestMode) 21 | 22 | resp := httptest.NewRecorder() 23 | context, engine := gin.CreateTestContext(resp) 24 | context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) 25 | 26 | // setup mock server 27 | engine.Use(WorkerHostname(want)) 28 | engine.GET("/health", func(c *gin.Context) { 29 | got = c.Value("worker-hostname").(string) 30 | 31 | c.Status(http.StatusOK) 32 | }) 33 | 34 | // run test 35 | engine.ServeHTTP(context.Writer, context.Request) 36 | 37 | if resp.Code != http.StatusOK { 38 | t.Errorf("WorkerHostname returned %v, want %v", resp.Code, http.StatusOK) 39 | } 40 | 41 | if !reflect.DeepEqual(got, want) { 42 | t.Errorf("WorkerHostname is %v, want %v", got, want) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /router/pipeline.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package router 4 | 5 | import ( 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/go-vela/worker/api" 9 | ) 10 | 11 | // PipelineHandlers extends the provided base router group 12 | // by adding a collection of endpoints for handling 13 | // pipeline related requests. 14 | // 15 | // GET /api/v1/executors/:executor/pipeline 16 | // . 17 | func PipelineHandlers(base *gin.RouterGroup) { 18 | // add a collection of endpoints for handling pipeline related requests 19 | // 20 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.Group 21 | pipeline := base.Group("/pipeline") 22 | { 23 | // add an endpoint for capturing the pipeline 24 | // 25 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.GET 26 | pipeline.GET("", api.GetPipeline) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /router/pipeline_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package router 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/gin-gonic/gin" 9 | 10 | "github.com/go-vela/worker/api" 11 | ) 12 | 13 | func TestRouter_PipelineHandlers(t *testing.T) { 14 | // setup types 15 | gin.SetMode(gin.TestMode) 16 | 17 | _engine := gin.New() 18 | 19 | want := gin.RoutesInfo{ 20 | { 21 | Method: "GET", 22 | Path: "/pipeline", 23 | Handler: "github.com/go-vela/worker/api.GetPipeline", 24 | HandlerFunc: api.GetPipeline, 25 | }, 26 | } 27 | 28 | // run test 29 | PipelineHandlers(&_engine.RouterGroup) 30 | 31 | got := _engine.Routes() 32 | 33 | if len(got) != len(want) { 34 | t.Errorf("PipelineHandlers is %v, want %v", got, want) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /router/repo.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package router 4 | 5 | import ( 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/go-vela/worker/api" 9 | ) 10 | 11 | // RepoHandlers extends the provided base router group 12 | // by adding a collection of endpoints for handling 13 | // repo related requests. 14 | // 15 | // GET /api/v1/executors/:executor/repo 16 | // . 17 | func RepoHandlers(base *gin.RouterGroup) { 18 | // add a collection of endpoints for handling repo related requests 19 | // 20 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.Group 21 | repo := base.Group("/repo") 22 | { 23 | // add an endpoint for capturing the repo 24 | // 25 | // https://pkg.go.dev/github.com/gin-gonic/gin#RouterGroup.GET 26 | repo.GET("", api.GetRepo) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /router/repo_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package router 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/gin-gonic/gin" 9 | 10 | "github.com/go-vela/worker/api" 11 | ) 12 | 13 | func TestRouter_RepoHandlers(t *testing.T) { 14 | // setup types 15 | gin.SetMode(gin.TestMode) 16 | 17 | _engine := gin.New() 18 | 19 | want := gin.RoutesInfo{ 20 | { 21 | Method: "GET", 22 | Path: "/repo", 23 | Handler: "github.com/go-vela/worker/api.GetRepo", 24 | HandlerFunc: api.GetRepo, 25 | }, 26 | } 27 | 28 | // run test 29 | RepoHandlers(&_engine.RouterGroup) 30 | 31 | got := _engine.Routes() 32 | 33 | if len(got) != len(want) { 34 | t.Errorf("RepoHandlers is %v, want %v", got, want) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /router/router_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package router 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/gin-gonic/gin" 9 | 10 | "github.com/go-vela/worker/api" 11 | ) 12 | 13 | func TestRouter_Load(t *testing.T) { 14 | // setup types 15 | gin.SetMode(gin.TestMode) 16 | 17 | want := gin.RoutesInfo{ 18 | { 19 | Method: "GET", 20 | Path: "/health", 21 | Handler: "github.com/go-vela/worker/api.Health", 22 | HandlerFunc: api.Health, 23 | }, 24 | { 25 | Method: "GET", 26 | Path: "/metrics", 27 | Handler: "github.com/go-vela/worker/api.Metrics", 28 | HandlerFunc: gin.WrapH(api.Metrics()), 29 | }, 30 | { 31 | Method: "GET", 32 | Path: "/version", 33 | Handler: "github.com/go-vela/worker/api.Version", 34 | HandlerFunc: api.Version, 35 | }, 36 | { 37 | Method: "POST", 38 | Path: "/api/v1/shutdown", 39 | Handler: "github.com/go-vela/worker/api.Shutdown", 40 | HandlerFunc: api.Shutdown, 41 | }, 42 | { 43 | Method: "POST", 44 | Path: "/api/v1/register", 45 | Handler: "github.com/go-vela/worker/api.Register", 46 | HandlerFunc: api.Register, 47 | }, 48 | { 49 | Method: "GET", 50 | Path: "/api/v1/executors", 51 | Handler: "github.com/go-vela/worker/api.GetExecutors", 52 | HandlerFunc: api.GetExecutors, 53 | }, 54 | { 55 | Method: "GET", 56 | Path: "/api/v1/executors/:executor", 57 | Handler: "github.com/go-vela/worker/api.GetExecutor", 58 | HandlerFunc: api.GetExecutor, 59 | }, 60 | { 61 | Method: "GET", 62 | Path: "/api/v1/executors/:executor/build", 63 | Handler: "github.com/go-vela/worker/api.GetBuild", 64 | HandlerFunc: api.GetBuild, 65 | }, 66 | { 67 | Method: "DELETE", 68 | Path: "/api/v1/executors/:executor/build/cancel", 69 | Handler: "github.com/go-vela/worker/api.CancelBuild", 70 | HandlerFunc: api.CancelBuild, 71 | }, 72 | { 73 | Method: "GET", 74 | Path: "/api/v1/executors/:executor/pipeline", 75 | Handler: "github.com/go-vela/worker/api.GetPipeline", 76 | HandlerFunc: api.GetPipeline, 77 | }, 78 | { 79 | Method: "GET", 80 | Path: "/api/v1/executors/:executor/repo", 81 | Handler: "github.com/go-vela/worker/api.GetRepo", 82 | HandlerFunc: api.GetRepo, 83 | }, 84 | } 85 | 86 | // run test 87 | got := Load() 88 | 89 | if len(got.Routes()) != len(want) { 90 | t.Errorf("Load is %v, want %v", got.Routes(), want) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /runtime/context.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package runtime 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // key defines the key type for storing 12 | // the runtime Engine in the context. 13 | const key = "runtime" 14 | 15 | // FromContext retrieves the runtime Engine from the context.Context. 16 | func FromContext(c context.Context) Engine { 17 | // get runtime value from context.Context 18 | v := c.Value(key) 19 | if v == nil { 20 | return nil 21 | } 22 | 23 | // cast runtime value to expected Engine type 24 | e, ok := v.(Engine) 25 | if !ok { 26 | return nil 27 | } 28 | 29 | return e 30 | } 31 | 32 | // FromGinContext retrieves the runtime Engine from the gin.Context. 33 | func FromGinContext(c *gin.Context) Engine { 34 | // get runtime value from gin.Context 35 | // 36 | // https://pkg.go.dev/github.com/gin-gonic/gin#Context.Get 37 | v, ok := c.Get(key) 38 | if !ok { 39 | return nil 40 | } 41 | 42 | // cast runtime value to expected Engine type 43 | e, ok := v.(Engine) 44 | if !ok { 45 | return nil 46 | } 47 | 48 | return e 49 | } 50 | 51 | // WithContext inserts the runtime Engine into the context.Context. 52 | func WithContext(c context.Context, e Engine) context.Context { 53 | // set the runtime Engine in the context.Context 54 | // 55 | //nolint:revive,staticcheck // ignore using string with context value 56 | return context.WithValue(c, key, e) 57 | } 58 | 59 | // WithGinContext inserts the runtime Engine into the gin.Context. 60 | func WithGinContext(c *gin.Context, e Engine) { 61 | // set the runtime Engine in the gin.Context 62 | // 63 | // https://pkg.go.dev/github.com/gin-gonic/gin#Context.Set 64 | c.Set(key, e) 65 | } 66 | -------------------------------------------------------------------------------- /runtime/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package runtime provides the ability for Vela to 4 | // integrate with different supported Runtime 5 | // environments. 6 | // 7 | // Currently the following runtimes are supported: 8 | // 9 | // * Docker - https://docker.io/ 10 | // * Kubernetes - https://kubernetes.io/ 11 | // 12 | // Usage: 13 | // 14 | // import "github.com/go-vela/worker/runtime" 15 | package runtime 16 | -------------------------------------------------------------------------------- /runtime/docker/build.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/go-vela/server/compiler/types/pipeline" 9 | ) 10 | 11 | // InspectBuild displays details about the pod for the init step. 12 | // This is a no-op for docker. 13 | func (c *client) InspectBuild(ctx context.Context, b *pipeline.Build) ([]byte, error) { 14 | c.Logger.Tracef("no-op: inspecting build for pipeline %s", b.ID) 15 | 16 | return []byte{}, nil 17 | } 18 | 19 | // SetupBuild prepares the pipeline build. 20 | // This is a no-op for docker. 21 | func (c *client) SetupBuild(ctx context.Context, b *pipeline.Build) error { 22 | c.Logger.Tracef("no-op: setting up for build %s", b.ID) 23 | 24 | return nil 25 | } 26 | 27 | // StreamBuild initializes log/event streaming for build. 28 | // This is a no-op for docker. 29 | func (c *client) StreamBuild(ctx context.Context, b *pipeline.Build) error { 30 | c.Logger.Tracef("no-op: streaming build %s", b.ID) 31 | 32 | return nil 33 | } 34 | 35 | // AssembleBuild finalizes pipeline build setup. 36 | // This is a no-op for docker. 37 | func (c *client) AssembleBuild(ctx context.Context, b *pipeline.Build) error { 38 | c.Logger.Tracef("no-op: assembling build %s", b.ID) 39 | 40 | return nil 41 | } 42 | 43 | // RemoveBuild deletes (kill, remove) the pipeline build metadata. 44 | // This is a no-op for docker. 45 | func (c *client) RemoveBuild(ctx context.Context, b *pipeline.Build) error { 46 | c.Logger.Tracef("no-op: removing build %s", b.ID) 47 | 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /runtime/docker/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package docker provides the ability for Vela to 4 | // integrate with Docker as a runtime environment. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/runtime/docker" 9 | package docker 10 | -------------------------------------------------------------------------------- /runtime/docker/driver.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | import "github.com/go-vela/server/constants" 6 | 7 | // Driver outputs the configured runtime driver. 8 | func (c *client) Driver() string { 9 | return constants.DriverDocker 10 | } 11 | -------------------------------------------------------------------------------- /runtime/docker/driver_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | import ( 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/go-vela/server/constants" 10 | ) 11 | 12 | func TestDocker_Driver(t *testing.T) { 13 | // setup types 14 | want := constants.DriverDocker 15 | 16 | _engine, err := NewMock() 17 | if err != nil { 18 | t.Errorf("unable to create runtime engine: %v", err) 19 | } 20 | 21 | // run tes 22 | got := _engine.Driver() 23 | 24 | if !reflect.DeepEqual(got, want) { 25 | t.Errorf("Driver is %v, want %v", got, want) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /runtime/docker/image.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "io" 9 | "os" 10 | "strings" 11 | 12 | dockerImageTypes "github.com/docker/docker/api/types/image" 13 | "github.com/sirupsen/logrus" 14 | 15 | "github.com/go-vela/server/compiler/types/pipeline" 16 | "github.com/go-vela/server/constants" 17 | "github.com/go-vela/worker/internal/image" 18 | ) 19 | 20 | // CreateImage creates the pipeline container image. 21 | func (c *client) CreateImage(ctx context.Context, ctn *pipeline.Container) error { 22 | c.Logger.Tracef("creating image for container %s", ctn.ID) 23 | 24 | // parse image from container 25 | // 26 | // https://pkg.go.dev/github.com/go-vela/worker/internal/image#ParseWithError 27 | _image, err := image.ParseWithError(ctn.Image) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | // create options for pulling image 33 | // 34 | // https://pkg.go.dev/github.com/docker/docker/api/types/image#PullOptions 35 | opts := dockerImageTypes.PullOptions{} 36 | 37 | // send API call to pull the image for the container 38 | // 39 | // https://pkg.go.dev/github.com/docker/docker/client#Client.ImagePull 40 | reader, err := c.Docker.ImagePull(ctx, _image, opts) 41 | if err != nil { 42 | return err 43 | } 44 | defer reader.Close() 45 | 46 | // check if logrus is set up with trace level 47 | if logrus.GetLevel() == logrus.TraceLevel { 48 | // copy output from image pull to standard output 49 | _, err = io.Copy(os.Stdout, reader) 50 | if err != nil { 51 | return err 52 | } 53 | } else { 54 | // discard output from image pull 55 | _, err = io.Copy(io.Discard, reader) 56 | if err != nil { 57 | return err 58 | } 59 | } 60 | 61 | return nil 62 | } 63 | 64 | // InspectImage inspects the pipeline container image. 65 | func (c *client) InspectImage(ctx context.Context, ctn *pipeline.Container) ([]byte, error) { 66 | c.Logger.Tracef("inspecting image for container %s", ctn.ID) 67 | 68 | // create output for inspecting image 69 | output := []byte( 70 | fmt.Sprintf("$ docker image inspect %s\n", ctn.Image), 71 | ) 72 | 73 | // check if the container pull policy is on start 74 | if strings.EqualFold(ctn.Pull, constants.PullOnStart) || strings.EqualFold(ctn.Pull, constants.PullNever) { 75 | return []byte( 76 | fmt.Sprintf("skipped for container %s due to pull policy %s\n", ctn.ID, ctn.Pull), 77 | ), nil 78 | } 79 | 80 | // parse image from container 81 | // 82 | // https://pkg.go.dev/github.com/go-vela/worker/internal/image#ParseWithError 83 | _image, err := image.ParseWithError(ctn.Image) 84 | if err != nil { 85 | return output, err 86 | } 87 | 88 | // send API call to inspect the image 89 | // 90 | // https://pkg.go.dev/github.com/docker/docker/client#Client.ImageInspectWithRaw 91 | i, _, err := c.Docker.ImageInspectWithRaw(ctx, _image) 92 | if err != nil { 93 | return output, err 94 | } 95 | 96 | // add new line to end of bytes 97 | return append(output, []byte(i.ID+"\n")...), nil 98 | } 99 | -------------------------------------------------------------------------------- /runtime/docker/image_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | import ( 6 | "context" 7 | "testing" 8 | 9 | "github.com/go-vela/server/compiler/types/pipeline" 10 | ) 11 | 12 | func TestDocker_InspectImage(t *testing.T) { 13 | // setup types 14 | _engine, err := NewMock() 15 | if err != nil { 16 | t.Errorf("unable to create runtime engine: %v", err) 17 | } 18 | 19 | // setup tests 20 | tests := []struct { 21 | name string 22 | failure bool 23 | container *pipeline.Container 24 | }{ 25 | { 26 | name: "tag exists", 27 | failure: false, 28 | container: _container, 29 | }, 30 | { 31 | name: "empty build container", 32 | failure: true, 33 | container: new(pipeline.Container), 34 | }, 35 | { 36 | name: "tag notfound", 37 | failure: true, 38 | container: &pipeline.Container{ 39 | ID: "step_github_octocat_1_clone", 40 | Directory: "/vela/src/github.com/octocat/helloworld", 41 | Environment: map[string]string{"FOO": "bar"}, 42 | Image: "target/vela-git:notfound", 43 | Name: "clone", 44 | Number: 2, 45 | Pull: "always", 46 | }, 47 | }, 48 | } 49 | 50 | // run tests 51 | for _, test := range tests { 52 | t.Run(test.name, func(t *testing.T) { 53 | _, err = _engine.InspectImage(context.Background(), test.container) 54 | 55 | if test.failure { 56 | if err == nil { 57 | t.Errorf("InspectImage should have returned err") 58 | } 59 | 60 | return // continue to next test 61 | } 62 | 63 | if err != nil { 64 | t.Errorf("InspectImage returned err: %v", err) 65 | } 66 | }) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /runtime/docker/opts.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package docker 4 | 5 | import ( 6 | "github.com/sirupsen/logrus" 7 | ) 8 | 9 | // ClientOpt represents a configuration option to initialize the runtime client for Docker. 10 | type ClientOpt func(*client) error 11 | 12 | // WithHostVolumes sets the host volumes in the runtime client for Docker. 13 | func WithHostVolumes(volumes []string) ClientOpt { 14 | return func(c *client) error { 15 | c.Logger.Trace("configuring host volumes in docker runtime client") 16 | 17 | // set the runtime host volumes in the docker client 18 | c.config.Volumes = volumes 19 | 20 | return nil 21 | } 22 | } 23 | 24 | // WithLogger sets the logger in the runtime client for Docker. 25 | func WithLogger(logger *logrus.Entry) ClientOpt { 26 | return func(c *client) error { 27 | c.Logger.Trace("configuring logger in docker runtime client") 28 | 29 | // check if the logger provided is empty 30 | if logger != nil { 31 | // set the runtime logger in the docker client 32 | c.Logger = logger 33 | } 34 | 35 | return nil 36 | } 37 | } 38 | 39 | // WithPrivilegedImages sets the privileged images in the runtime client for Docker. 40 | func WithPrivilegedImages(images []string) ClientOpt { 41 | return func(c *client) error { 42 | c.Logger.Trace("configuring privileged images in docker runtime client") 43 | 44 | // set the runtime privileged images in the docker client 45 | c.config.Images = images 46 | 47 | return nil 48 | } 49 | } 50 | 51 | // WithDropCapabilities sets the kernel capabilities to drop from each container in the runtime client for Docker. 52 | func WithDropCapabilities(caps []string) ClientOpt { 53 | return func(c *client) error { 54 | c.Logger.Trace("configuring dropped capabilities in docker runtime client") 55 | 56 | // set the runtime dropped kernel capabilities in the docker client 57 | c.config.DropCapabilities = caps 58 | 59 | return nil 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /runtime/kubernetes/apis/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package apis defines the worker-config CRD and related utilities. 4 | // 5 | // Usage: 6 | // 7 | // import "github.com/go-vela/worker/runtime/kubernetes/apis" 8 | package apis 9 | -------------------------------------------------------------------------------- /runtime/kubernetes/apis/vela/register.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package vela 4 | 5 | const ( 6 | // GroupName is the group name used in this package. 7 | GroupName = "go-vela.github.io" 8 | ) 9 | -------------------------------------------------------------------------------- /runtime/kubernetes/apis/vela/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // +kubebuilder:object:generate=true 4 | // +kubebuilder:validation:Optional 5 | // +groupName=go-vela.github.io 6 | // +groupGoName=Vela 7 | 8 | // Package v1alpha1 defines version 1alpha1 of the worker-config CRD. 9 | // 10 | // Usage: 11 | // 12 | // import "github.com/go-vela/worker/runtime/kubernetes/apis/v1alpha1" 13 | package v1alpha1 14 | -------------------------------------------------------------------------------- /runtime/kubernetes/apis/vela/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package v1alpha1 4 | 5 | import ( 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "k8s.io/apimachinery/pkg/runtime" 8 | "k8s.io/apimachinery/pkg/runtime/schema" 9 | 10 | "github.com/go-vela/worker/runtime/kubernetes/apis/vela" 11 | ) 12 | 13 | // SchemeGroupVersion is group version used to register these objects. 14 | var SchemeGroupVersion = schema.GroupVersion{Group: vela.GroupName, Version: "v1alpha1"} 15 | 16 | // Kind takes an unqualified kind and returns a Group qualified GroupKind. 17 | func Kind(kind string) schema.GroupKind { 18 | return SchemeGroupVersion.WithKind(kind).GroupKind() 19 | } 20 | 21 | // Resource takes an unqualified resource and returns a Group qualified GroupResource. 22 | func Resource(resource string) schema.GroupResource { 23 | return SchemeGroupVersion.WithResource(resource).GroupResource() 24 | } 25 | 26 | var ( 27 | // SchemeBuilder initializes a scheme builder. 28 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 29 | // AddToScheme is a global function that registers this API group & version to a scheme. 30 | AddToScheme = SchemeBuilder.AddToScheme 31 | ) 32 | 33 | // addKnownTypes adds the list of known types to Scheme. 34 | func addKnownTypes(scheme *runtime.Scheme) error { 35 | scheme.AddKnownTypes(SchemeGroupVersion, 36 | &PipelinePodsTemplate{}, 37 | &PipelinePodsTemplateList{}, 38 | ) 39 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 40 | 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /runtime/kubernetes/codegen/header.go.txt: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | -------------------------------------------------------------------------------- /runtime/kubernetes/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Package kubernetes provides the ability for Vela to 4 | // integrate with Kubernetes as a runtime environment. 5 | // 6 | // Usage: 7 | // 8 | // import "github.com/go-vela/worker/runtime/kubernetes" 9 | package kubernetes 10 | -------------------------------------------------------------------------------- /runtime/kubernetes/driver.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package kubernetes 4 | 5 | import "github.com/go-vela/server/constants" 6 | 7 | // Driver outputs the configured runtime driver. 8 | func (c *client) Driver() string { 9 | return constants.DriverKubernetes 10 | } 11 | -------------------------------------------------------------------------------- /runtime/kubernetes/driver_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package kubernetes 4 | 5 | import ( 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/go-vela/server/constants" 10 | ) 11 | 12 | func TestKubernetes_Driver(t *testing.T) { 13 | // setup types 14 | want := constants.DriverKubernetes 15 | 16 | _engine, err := NewMock(_pod) 17 | if err != nil { 18 | t.Errorf("unable to create runtime engine: %v", err) 19 | } 20 | 21 | // run tes 22 | got := _engine.Driver() 23 | 24 | if !reflect.DeepEqual(got, want) { 25 | t.Errorf("Driver is %v, want %v", got, want) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | // This package has the automatically generated clientset. 6 | package versioned 7 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | package fake 6 | 7 | import ( 8 | "k8s.io/apimachinery/pkg/runtime" 9 | "k8s.io/apimachinery/pkg/watch" 10 | "k8s.io/client-go/discovery" 11 | fakediscovery "k8s.io/client-go/discovery/fake" 12 | "k8s.io/client-go/testing" 13 | 14 | clientset "github.com/go-vela/worker/runtime/kubernetes/generated/clientset/versioned" 15 | velav1alpha1 "github.com/go-vela/worker/runtime/kubernetes/generated/clientset/versioned/typed/vela/v1alpha1" 16 | fakevelav1alpha1 "github.com/go-vela/worker/runtime/kubernetes/generated/clientset/versioned/typed/vela/v1alpha1/fake" 17 | ) 18 | 19 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 20 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 21 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 22 | // for a real clientset and is mostly useful in simple unit tests. 23 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 24 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 25 | for _, obj := range objects { 26 | if err := o.Add(obj); err != nil { 27 | panic(err) 28 | } 29 | } 30 | 31 | cs := &Clientset{tracker: o} 32 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 33 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 34 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 35 | gvr := action.GetResource() 36 | ns := action.GetNamespace() 37 | watch, err := o.Watch(gvr, ns) 38 | if err != nil { 39 | return false, nil, err 40 | } 41 | return true, watch, nil 42 | }) 43 | 44 | return cs 45 | } 46 | 47 | // Clientset implements clientset.Interface. Meant to be embedded into a 48 | // struct to get a default implementation. This makes faking out just the method 49 | // you want to test easier. 50 | type Clientset struct { 51 | testing.Fake 52 | discovery *fakediscovery.FakeDiscovery 53 | tracker testing.ObjectTracker 54 | } 55 | 56 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 57 | return c.discovery 58 | } 59 | 60 | func (c *Clientset) Tracker() testing.ObjectTracker { 61 | return c.tracker 62 | } 63 | 64 | var ( 65 | _ clientset.Interface = &Clientset{} 66 | _ testing.FakeClient = &Clientset{} 67 | ) 68 | 69 | // VelaV1alpha1 retrieves the VelaV1alpha1Client 70 | func (c *Clientset) VelaV1alpha1() velav1alpha1.VelaV1alpha1Interface { 71 | return &fakevelav1alpha1.FakeVelaV1alpha1{Fake: &c.Fake} 72 | } 73 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | // This package has the automatically generated fake clientset. 6 | package fake 7 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | package fake 6 | 7 | import ( 8 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | runtime "k8s.io/apimachinery/pkg/runtime" 10 | schema "k8s.io/apimachinery/pkg/runtime/schema" 11 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 12 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 13 | 14 | velav1alpha1 "github.com/go-vela/worker/runtime/kubernetes/apis/vela/v1alpha1" 15 | ) 16 | 17 | var scheme = runtime.NewScheme() 18 | var codecs = serializer.NewCodecFactory(scheme) 19 | 20 | var localSchemeBuilder = runtime.SchemeBuilder{ 21 | velav1alpha1.AddToScheme, 22 | } 23 | 24 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 25 | // of clientsets, like in: 26 | // 27 | // import ( 28 | // "k8s.io/client-go/kubernetes" 29 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 30 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 31 | // ) 32 | // 33 | // kclientset, _ := kubernetes.NewForConfig(c) 34 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 35 | // 36 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 37 | // correctly. 38 | var AddToScheme = localSchemeBuilder.AddToScheme 39 | 40 | func init() { 41 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 42 | utilruntime.Must(AddToScheme(scheme)) 43 | } 44 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | // This package contains the scheme of the automatically generated clientset. 6 | package scheme 7 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | package scheme 6 | 7 | import ( 8 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | runtime "k8s.io/apimachinery/pkg/runtime" 10 | schema "k8s.io/apimachinery/pkg/runtime/schema" 11 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 12 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 13 | 14 | velav1alpha1 "github.com/go-vela/worker/runtime/kubernetes/apis/vela/v1alpha1" 15 | ) 16 | 17 | var Scheme = runtime.NewScheme() 18 | var Codecs = serializer.NewCodecFactory(Scheme) 19 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 20 | var localSchemeBuilder = runtime.SchemeBuilder{ 21 | velav1alpha1.AddToScheme, 22 | } 23 | 24 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 25 | // of clientsets, like in: 26 | // 27 | // import ( 28 | // "k8s.io/client-go/kubernetes" 29 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 30 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 31 | // ) 32 | // 33 | // kclientset, _ := kubernetes.NewForConfig(c) 34 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 35 | // 36 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 37 | // correctly. 38 | var AddToScheme = localSchemeBuilder.AddToScheme 39 | 40 | func init() { 41 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 42 | utilruntime.Must(AddToScheme(Scheme)) 43 | } 44 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/typed/vela/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | // This package has the automatically generated typed clients. 6 | package v1alpha1 7 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/typed/vela/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | // Package fake has the automatically generated clients. 6 | package fake 7 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/typed/vela/v1alpha1/fake/fake_vela_client.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | package fake 6 | 7 | import ( 8 | rest "k8s.io/client-go/rest" 9 | testing "k8s.io/client-go/testing" 10 | 11 | v1alpha1 "github.com/go-vela/worker/runtime/kubernetes/generated/clientset/versioned/typed/vela/v1alpha1" 12 | ) 13 | 14 | type FakeVelaV1alpha1 struct { 15 | *testing.Fake 16 | } 17 | 18 | func (c *FakeVelaV1alpha1) PipelinePodsTemplates(namespace string) v1alpha1.PipelinePodsTemplateInterface { 19 | return &FakePipelinePodsTemplates{c, namespace} 20 | } 21 | 22 | // RESTClient returns a RESTClient that is used to communicate 23 | // with API server by this client implementation. 24 | func (c *FakeVelaV1alpha1) RESTClient() rest.Interface { 25 | var ret *rest.RESTClient 26 | return ret 27 | } 28 | -------------------------------------------------------------------------------- /runtime/kubernetes/generated/clientset/versioned/typed/vela/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Code generated by client-gen. DO NOT EDIT. 4 | 5 | package v1alpha1 6 | 7 | type PipelinePodsTemplateExpansion interface{} 8 | -------------------------------------------------------------------------------- /runtime/kubernetes/image.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package kubernetes 4 | 5 | import ( 6 | "context" 7 | "encoding/json" 8 | "fmt" 9 | "strings" 10 | 11 | "github.com/go-vela/server/compiler/types/pipeline" 12 | "github.com/go-vela/server/constants" 13 | ) 14 | 15 | const ( 16 | pauseImage = "kubernetes/pause:latest" 17 | imagePatch = ` 18 | { 19 | "spec": { 20 | "containers": [ 21 | { 22 | "name": "%s", 23 | "image": "%s" 24 | } 25 | ] 26 | } 27 | } 28 | ` 29 | ) 30 | 31 | // CreateImage creates the pipeline container image. 32 | func (c *client) CreateImage(ctx context.Context, ctn *pipeline.Container) error { 33 | c.Logger.Tracef("no-op: creating image for container %s", ctn.ID) 34 | 35 | return nil 36 | } 37 | 38 | // InspectImage inspects the pipeline container image. 39 | func (c *client) InspectImage(ctx context.Context, ctn *pipeline.Container) ([]byte, error) { 40 | c.Logger.Tracef("inspecting image for container %s", ctn.ID) 41 | 42 | // TODO: consider updating this command 43 | // 44 | // create output for inspecting image 45 | output := []byte( 46 | fmt.Sprintf("$ kubectl get pod -o=jsonpath='{.spec.containers[%d].image}' %s\n", ctn.Number, ctn.ID), 47 | ) 48 | 49 | // check if the container pull policy is on start 50 | if strings.EqualFold(ctn.Pull, constants.PullOnStart) { 51 | return []byte( 52 | fmt.Sprintf("skipped for container %s due to pull policy %s\n", ctn.ID, ctn.Pull), 53 | ), nil 54 | } 55 | 56 | // marshal the image information from the container 57 | image, err := json.MarshalIndent( 58 | c.Pod.Spec.Containers[c.containersLookup[ctn.ID]].Image, "", " ", 59 | ) 60 | if err != nil { 61 | return output, err 62 | } 63 | 64 | // add new line to end of bytes 65 | return append(output, append(image, "\n"...)...), nil 66 | } 67 | -------------------------------------------------------------------------------- /runtime/kubernetes/image_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package kubernetes 4 | 5 | import ( 6 | "context" 7 | "testing" 8 | 9 | "github.com/go-vela/server/compiler/types/pipeline" 10 | ) 11 | 12 | func TestKubernetes_InspectImage(t *testing.T) { 13 | // setup types 14 | _engine, err := NewMock(_pod) 15 | if err != nil { 16 | t.Errorf("unable to create runtime engine: %v", err) 17 | } 18 | 19 | // setup tests 20 | tests := []struct { 21 | name string 22 | failure bool 23 | container *pipeline.Container 24 | }{ 25 | { 26 | name: "valid image", 27 | failure: false, 28 | container: _container, 29 | }, 30 | } 31 | 32 | // run tests 33 | for _, test := range tests { 34 | t.Run(test.name, func(t *testing.T) { 35 | _, err = _engine.InspectImage(context.Background(), test.container) 36 | 37 | if test.failure { 38 | if err == nil { 39 | t.Errorf("InspectImage should have returned err") 40 | } 41 | 42 | return // continue to next test 43 | } 44 | 45 | if err != nil { 46 | t.Errorf("InspectImage returned err: %v", err) 47 | } 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /runtime/kubernetes/testdata/config: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | clusters: 3 | - cluster: 4 | server: https://localhost:443 5 | name: foo 6 | contexts: 7 | - context: 8 | cluster: foo 9 | namespace: test 10 | user: foo 11 | name: foo 12 | current-context: foo 13 | kind: Config 14 | preferences: {} 15 | users: 16 | - name: foo 17 | user: 18 | token: somerandomstringqwerty -------------------------------------------------------------------------------- /runtime/kubernetes/testdata/config_empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/go-vela/worker/3738750c678552fa013df545fda18d1e1f51bcba/runtime/kubernetes/testdata/config_empty -------------------------------------------------------------------------------- /runtime/kubernetes/testdata/pipeline-pods-template-dns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "go-vela.github.io/v1alpha1" 2 | kind: PipelinePodsTemplate 3 | metadata: 4 | name: pipeline-pods-template 5 | spec: 6 | template: 7 | spec: 8 | # https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config 9 | dnsPolicy: "None" 10 | dnsConfig: 11 | nameservers: 12 | - 1.2.3.4 13 | searches: 14 | - ns1.svc.cluster-domain.example 15 | - my.dns.search.suffix 16 | options: 17 | - name: ndots 18 | value: "2" 19 | - name: edns0 20 | -------------------------------------------------------------------------------- /runtime/kubernetes/testdata/pipeline-pods-template-empty.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "go-vela.github.io/v1alpha1" 2 | kind: PipelinePodsTemplate 3 | metadata: 4 | name: pipeline-pods-template-empty 5 | spec: 6 | template: {} 7 | -------------------------------------------------------------------------------- /runtime/kubernetes/testdata/pipeline-pods-template-malformed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "go-vela.github.io/v1alpha1" 2 | kind: PipelinePodsTemplate 3 | metadata: 4 | name: pipeline-pods-template 5 | spec: 6 | template: 7 | metadata: 8 | annotations: 9 | # annotations is a map[string]string, so this is malformed. 10 | - name: annotation/foo 11 | value: bar 12 | -------------------------------------------------------------------------------- /runtime/kubernetes/testdata/pipeline-pods-template-node-selection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "go-vela.github.io/v1alpha1" 2 | kind: PipelinePodsTemplate 3 | metadata: 4 | name: pipeline-pods-template 5 | spec: 6 | template: 7 | spec: 8 | # nodeName is not supported. Using nodeSelector or affinity should be sufficient. 9 | # nodeName: foo-node 10 | 11 | # https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-your-chosen-node 12 | nodeSelector: 13 | disktype: ssd 14 | 15 | affinity: 16 | # https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity 17 | nodeAffinity: 18 | requiredDuringSchedulingIgnoredDuringExecution: 19 | nodeSelectorTerms: 20 | - matchExpressions: 21 | - key: kubernetes.io/os 22 | operator: In 23 | values: 24 | - linux 25 | preferredDuringSchedulingIgnoredDuringExecution: 26 | - weight: 1 27 | preference: 28 | matchExpressions: 29 | - key: another-node-label-key 30 | operator: In 31 | values: 32 | - another-node-label-value 33 | # https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity 34 | podAffinity: 35 | requiredDuringSchedulingIgnoredDuringExecution: 36 | - labelSelector: 37 | matchExpressions: 38 | - key: security 39 | operator: In 40 | values: 41 | - S1 42 | topologyKey: topology.kubernetes.io/zone 43 | podAntiAffinity: 44 | preferredDuringSchedulingIgnoredDuringExecution: 45 | - weight: 100 46 | podAffinityTerm: 47 | labelSelector: 48 | matchExpressions: 49 | - key: security 50 | operator: In 51 | values: 52 | - S2 53 | topologyKey: topology.kubernetes.io/zone 54 | 55 | # https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ 56 | tolerations: 57 | - key: "key1" 58 | operator: "Equal" 59 | value: "value1" 60 | effect: "NoSchedule" 61 | - key: "key1" 62 | operator: "Equal" 63 | value: "value1" 64 | effect: "NoExecute" 65 | -------------------------------------------------------------------------------- /runtime/kubernetes/testdata/pipeline-pods-template-security-context.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "go-vela.github.io/v1alpha1" 2 | kind: PipelinePodsTemplate 3 | metadata: 4 | name: pipeline-pods-template 5 | spec: 6 | template: 7 | spec: 8 | securityContext: 9 | runAsNonRoot: true 10 | sysctls: 11 | # https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ 12 | - name: kernel.shm_rmid_forced 13 | value: "0" 14 | - name: net.core.somaxconn 15 | value: "1024" 16 | - name: kernel.msgmax 17 | value: "65536" 18 | container: 19 | securityContext: 20 | capabilities: 21 | # https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capabilities-for-a-container 22 | drop: ["ALL"] 23 | add: ["NET_ADMIN","SYS_TIME"] 24 | -------------------------------------------------------------------------------- /runtime/kubernetes/testdata/pipeline-pods-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "go-vela.github.io/v1alpha1" 2 | kind: PipelinePodsTemplate 3 | metadata: 4 | name: pipeline-pods-template 5 | spec: 6 | template: 7 | metadata: 8 | annotations: 9 | annotation/foo: bar 10 | labels: 11 | foo: bar 12 | # attempting to override worker-provided labels will be ignored. 13 | pipeline: this-is-ignored 14 | -------------------------------------------------------------------------------- /runtime/runtime.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package runtime 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/sirupsen/logrus" 9 | 10 | "github.com/go-vela/server/constants" 11 | ) 12 | 13 | // New creates and returns a Vela engine capable of 14 | // integrating with the configured runtime. 15 | // 16 | // Currently the following runtimes are supported: 17 | // 18 | // * docker 19 | // * kubernetes 20 | // . 21 | func New(s *Setup) (Engine, error) { 22 | // validate the setup being provided 23 | // 24 | // https://pkg.go.dev/github.com/go-vela/worker/runtime#Setup.Validate 25 | err := s.Validate() 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | logrus.Debug("creating runtime engine from setup") 31 | // process the runtime driver being provided 32 | switch s.Driver { 33 | case constants.DriverDocker: 34 | // handle the Docker runtime driver being provided 35 | // 36 | // https://pkg.go.dev/github.com/go-vela/worker/runtime#Setup.Docker 37 | return s.Docker() 38 | case constants.DriverKubernetes: 39 | // handle the Kubernetes runtime driver being provided 40 | // 41 | // https://pkg.go.dev/github.com/go-vela/worker/runtime#Setup.Kubernetes 42 | return s.Kubernetes() 43 | default: 44 | // handle an invalid runtime driver being provided 45 | return nil, fmt.Errorf("invalid runtime driver provided: %s", s.Driver) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /runtime/runtime_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package runtime 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/go-vela/server/constants" 9 | ) 10 | 11 | func TestRuntime_New(t *testing.T) { 12 | // setup tests 13 | tests := []struct { 14 | name string 15 | failure bool 16 | setup *Setup 17 | }{ 18 | { 19 | name: "docker driver", 20 | failure: false, 21 | setup: &Setup{ 22 | Driver: constants.DriverDocker, 23 | }, 24 | }, 25 | { 26 | name: "kubernetes driver", 27 | failure: false, 28 | setup: &Setup{ 29 | Driver: constants.DriverKubernetes, 30 | Namespace: "docker", 31 | ConfigFile: "testdata/config", 32 | }, 33 | }, 34 | { 35 | name: "invalid driver fails", 36 | failure: true, 37 | setup: &Setup{ 38 | Driver: "invalid", 39 | }, 40 | }, 41 | { 42 | name: "empty driver fails", 43 | failure: true, 44 | setup: &Setup{ 45 | Driver: "", 46 | }, 47 | }, 48 | } 49 | 50 | // run tests 51 | for _, test := range tests { 52 | t.Run(test.name, func(t *testing.T) { 53 | _, err := New(test.setup) 54 | 55 | if test.failure { 56 | if err == nil { 57 | t.Errorf("New should have returned err") 58 | } 59 | 60 | return // continue to next test 61 | } 62 | 63 | if err != nil { 64 | t.Errorf("New returned err: %v", err) 65 | } 66 | }) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /runtime/setup_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package runtime 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/go-vela/server/constants" 9 | ) 10 | 11 | func TestRuntime_Setup_Docker(t *testing.T) { 12 | tests := []struct { 13 | name string 14 | mock bool 15 | }{ 16 | {name: "standard", mock: false}, 17 | {name: "mocked", mock: true}, 18 | } 19 | 20 | // run tests 21 | for _, test := range tests { 22 | t.Run(test.name, func(t *testing.T) { 23 | // setup types 24 | _setup := &Setup{ 25 | Mock: test.mock, 26 | Driver: constants.DriverDocker, 27 | } 28 | 29 | _, err := _setup.Docker() 30 | if err != nil { 31 | t.Errorf("Docker returned err: %v", err) 32 | } 33 | }) 34 | } 35 | } 36 | 37 | func TestRuntime_Setup_Kubernetes(t *testing.T) { 38 | tests := []struct { 39 | name string 40 | mock bool 41 | }{ 42 | {name: "standard", mock: false}, 43 | {name: "mocked", mock: true}, 44 | } 45 | 46 | // run tests 47 | for _, test := range tests { 48 | t.Run(test.name, func(t *testing.T) { 49 | // setup types 50 | _setup := &Setup{ 51 | Mock: test.mock, 52 | Driver: constants.DriverKubernetes, 53 | ConfigFile: "testdata/config", 54 | Namespace: "docker", 55 | } 56 | 57 | _, err := _setup.Kubernetes() 58 | if err != nil { 59 | t.Errorf("Kubernetes returned err: %v", err) 60 | } 61 | }) 62 | } 63 | } 64 | 65 | func TestRuntime_Validate(t *testing.T) { 66 | // setup types 67 | tests := []struct { 68 | name string 69 | failure bool 70 | setup *Setup 71 | want error 72 | }{ 73 | { 74 | name: "docker driver", 75 | failure: false, 76 | setup: &Setup{ 77 | Driver: constants.DriverDocker, 78 | DropCapabilities: []string{"CAP_DAC_OVERRIDE"}, 79 | }, 80 | }, 81 | { 82 | name: "docker driver bad cap", 83 | failure: true, 84 | setup: &Setup{ 85 | Driver: constants.DriverDocker, 86 | DropCapabilities: []string{"BAD"}, 87 | }, 88 | }, 89 | { 90 | name: "kubernetes driver", 91 | failure: false, 92 | setup: &Setup{ 93 | Driver: constants.DriverKubernetes, 94 | Namespace: "docker", 95 | }, 96 | }, 97 | { 98 | name: "empty driver", 99 | failure: true, 100 | setup: &Setup{ 101 | Driver: "", 102 | }, 103 | }, 104 | { 105 | name: "kubernetes driver-missing namespace", 106 | failure: true, 107 | setup: &Setup{ 108 | Driver: constants.DriverKubernetes, 109 | }, 110 | }, 111 | } 112 | 113 | // run tests 114 | for _, test := range tests { 115 | t.Run(test.name, func(t *testing.T) { 116 | err := test.setup.Validate() 117 | 118 | if test.failure { 119 | if err == nil { 120 | t.Errorf("Validate should have returned err") 121 | } 122 | 123 | return // continue to next test 124 | } 125 | 126 | if err != nil { 127 | t.Errorf("Validate returned err: %v", err) 128 | } 129 | }) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /runtime/testdata/config: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | clusters: 3 | - cluster: 4 | server: https://localhost:443 5 | name: foo 6 | contexts: 7 | - context: 8 | cluster: foo 9 | namespace: test 10 | user: foo 11 | name: foo 12 | current-context: foo 13 | kind: Config 14 | preferences: {} 15 | users: 16 | - name: foo 17 | user: 18 | token: somerandomstringqwerty -------------------------------------------------------------------------------- /runtime/testdata/stages.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | 4 | stages: 5 | test: 6 | steps: 7 | - name: test 8 | commands: 9 | - echo ${FOO} 10 | environment: 11 | FOO: bar 12 | image: alpine:latest 13 | pull: true 14 | -------------------------------------------------------------------------------- /runtime/testdata/steps.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "1" 3 | 4 | steps: 5 | - name: git-test 6 | image: target/vela-git:latest 7 | pull: true 8 | parameters: 9 | path: hello-world 10 | ref: refs/heads/main 11 | remote: https://github.com/octocat/hello-world.git 12 | sha: 7fd1a60b01f91b314f59955a4e4d4e80d8edf11d 13 | 14 | # sleep testing waiting step 15 | - name: sleep 16 | commands: | 17 | secs=30 18 | while [ $secs -gt 0 ]; do 19 | echo "$secs" 20 | sleep 1 21 | : $((secs--)) 22 | done 23 | image: alpine:latest 24 | pull: true 25 | 26 | # exit testing inspect step 27 | - name: exit 28 | commands: 29 | - exit 1 30 | image: alpine:latest 31 | pull: true 32 | 33 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package version 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | 9 | "github.com/Masterminds/semver/v3" 10 | "github.com/sirupsen/logrus" 11 | 12 | "github.com/go-vela/server/version" 13 | ) 14 | 15 | var ( 16 | // Arch represents the architecture information for the package. 17 | Arch = runtime.GOARCH 18 | // Commit represents the git commit information for the package. 19 | Commit string 20 | // Compiler represents the compiler information for the package. 21 | Compiler = runtime.Compiler 22 | // Date represents the build date information for the package. 23 | Date string 24 | // Go represents the golang version information for the package. 25 | Go = runtime.Version() 26 | // OS represents the operating system information for the package. 27 | OS = runtime.GOOS 28 | // Tag represents the git tag information for the package. 29 | Tag string 30 | ) 31 | 32 | // New creates a new version object for Vela that is used throughout the application. 33 | func New() *version.Version { 34 | // check if a semantic tag was provided 35 | if len(Tag) == 0 { 36 | logrus.Warning("no semantic tag provided - defaulting to v0.0.0") 37 | 38 | // set a fallback default for the tag 39 | Tag = "v0.0.0" 40 | } 41 | 42 | v, err := semver.NewVersion(Tag) 43 | if err != nil { 44 | fmt.Println(fmt.Errorf("unable to parse semantic version for %s: %w", Tag, err)) 45 | } 46 | 47 | return &version.Version{ 48 | Canonical: Tag, 49 | Major: v.Major(), 50 | Minor: v.Minor(), 51 | Patch: v.Patch(), 52 | PreRelease: v.Prerelease(), 53 | Metadata: version.Metadata{ 54 | Architecture: Arch, 55 | BuildDate: Date, 56 | Compiler: Compiler, 57 | GitCommit: Commit, 58 | GoVersion: Go, 59 | OperatingSystem: OS, 60 | }, 61 | } 62 | } 63 | --------------------------------------------------------------------------------