diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 858594f58a..0000000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!release/* diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 50a28859bc..0000000000 --- a/.drone.yml +++ /dev/null @@ -1,107 +0,0 @@ ---- -kind: pipeline -type: docker -name: linux-amd64 - -platform: - arch: amd64 - os: linux - -steps: -- name: test - image: golang:1.14.15 - commands: - - go test -race ./... - - go build -o /dev/null github.com/drone/drone/cmd/drone-server - - go build -o /dev/null -tags "oss nolimit" github.com/drone/drone/cmd/drone-server - -- name: build - image: golang:1.14.15 - commands: - - sh scripts/build.sh - environment: - GOARCH: amd64 - GOOS: linux - -- name: publish - image: plugins/docker:18 - settings: - auto_tag: true - auto_tag_suffix: linux-amd64 - dockerfile: docker/Dockerfile.server.linux.amd64 - repo: drone/drone - username: - from_secret: docker_username - password: - from_secret: docker_password - when: - event: - - push - - tag - ---- -kind: pipeline -type: vm -name: linux-arm64 - -pool: - use: ubuntu_arm64 - -platform: - arch: arm64 - os: linux - -steps: -- name: build - image: golang:1.14.15 - commands: - - sh scripts/build.sh - environment: - GOARCH: arm64 - GOOS: linux - -- name: publish - image: plugins/docker:18 - settings: - auto_tag: true - auto_tag_suffix: linux-arm64 - dockerfile: docker/Dockerfile.server.linux.arm64 - repo: drone/drone - username: - from_secret: docker_username - password: - from_secret: docker_password - -trigger: - event: - - push - - tag - -depends_on: -- linux-amd64 - ---- -kind: pipeline -type: docker -name: manifest - -steps: -- name: publish - image: plugins/manifest:1.2 - settings: - auto_tag: true - ignore_missing: true - spec: docker/manifest.server.tmpl - username: - from_secret: docker_username - password: - from_secret: docker_password - -trigger: - event: - - push - - tag - -depends_on: -- linux-arm64 - diff --git a/.github/code_of_conduct.md b/.github/code_of_conduct.md deleted file mode 100644 index 63481f408b..0000000000 --- a/.github/code_of_conduct.md +++ /dev/null @@ -1,3 +0,0 @@ -## Drone Community Code of Conduct - -Drone follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). diff --git a/.github/contributing.md b/.github/contributing.md deleted file mode 100644 index 63f857a003..0000000000 --- a/.github/contributing.md +++ /dev/null @@ -1,35 +0,0 @@ -# Contributing - -Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved. - -Following these guidelines helps to communicate that you respect the time of the developers maintaining this project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features. - -## Support Requests - -The [mailing list](https://discourse.drone.io) is the preferred channel for support requests. Please do not use the issue tracker for personal support requests. - -## Feature Requests - -Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of this project. It is up to you to make a strong case to convince the developers of the merits of this feature. Please provide as much detail and context as possible. - -## Compatibility - -This project maintains a strong commitment to backward compatibility. Changing the runtime behavior or configuration in a manner that breaks the public contract should always be avoided. - -## Dependencies - -This project does not attempt to track the latest version for each dependency. Please only apply the minimal dependency changes required for your patch to work. _If it ain't broke, don't fix it._ - -## Pull Requests - -Please discuss on our [mailing list](https://discourse.drone.io) before embarking on any significant pull request, otherwise you risk spending time working on something the developers might not want to merge into the project. - -Pull requests should remain focused in scope and avoid containing unrelated commits. For example, a pull request could add a feature, fix a bug, or format code; but not a mixture. - -When you are ready to submit a pull request you can use the below checklist to to increase the likelihood of your pull request being accepted in a timely manner: - -- Run the unit tests. -- Format the code. -- Include entry in [CHANGELOG.md](../CHANGELOG.md) that describes the change. -- Include unit tests when you contribute a new feature. -- Include unit tests when you contribute a bug fix to prevent regressions. diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index fa9398c321..0000000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index d920ce44de..0000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,27 +0,0 @@ -## Commit Checklist - -Thank you for creating a pull request! To help us review / merge this can you make sure that your PR adheres as much as possible to the following. - -### The Basics - -- Commit is a single logical unit of work, only use multiple commits if doing different tasks -- Commit does not include commented out code or unneeded files -- rebase of main branch - -### The Content - -- Must include testing for bug or feature -- Must include appropriate documentation changes if it is introducing a new feature or changing existing functionality -- Must pass existing test suites - -### The Commit Message - -- Short meaningful description (ex: remove deprecated steps) -- Uses the imperative, present tense: "change", not "changed" or "changes" -- Includes motivation for the change, and contrasts its implementation with the previous behavior - -### The Pull Request - -- What is the reason for this change -- Example usage of the failure for a bug, or configuration and expected output for a feature -- Steps to test the change diff --git a/.github/readme.md b/.github/readme.md deleted file mode 100644 index ae25629bbf..0000000000 --- a/.github/readme.md +++ /dev/null @@ -1,7 +0,0 @@ -Gitness is an Open Source developer platform with Source Control management, Continuous Integration and Continuous Delivery. Gitness is the successor to Drone and will be merging with the Drone project over the coming months. - -Documentation and Other Links: - -* Setup Documentation [docs.gitness.com/getting-started](https://docs.gitness.com/) -* Usage Documentation for repositories [docs.drone.io/getting-started](https://docs.gitness.com/repositories/overview) -* Join the [Gitness Slack Channel](https://join.slack.com/t/harnesscommunity/shared_invite/zt-y4hdqh7p-RVuEQyIl5Hcx4Ck8VCvzBw) diff --git a/.github/security.md b/.github/security.md deleted file mode 100644 index 8069277379..0000000000 --- a/.github/security.md +++ /dev/null @@ -1,36 +0,0 @@ -# Security Policies and Procedures - -This document outlines security procedures and general policies for this project. - - * [Reporting a Bug](#reporting-a-bug) - * [Disclosure Policy](#disclosure-policy) - * [Comments on this Policy](#comments-on-this-policy) - -## Reporting a Bug - -Report security bugs by emailing the lead maintainer at security@drone.io. - -The lead maintainer will acknowledge your email within 48 hours, and will send a -more detailed response within 48 hours indicating the next steps in handling -your report. After the initial reply to your report, the security team will -endeavor to keep you informed of the progress towards a fix and full -announcement, and may ask for additional information or guidance. - -Report security bugs in third-party software to the person or team maintaining -that software. - -## Disclosure Policy - -When the security team receives a security bug report, they will assign it to a -primary handler. This person will coordinate the fix and release process, -involving the following steps: - - * Confirm the problem and determine the affected versions. - * Audit code to find any potential similar problems. - * Prepare fixes for all releases still under maintenance. These fixes will be - released as fast as possible to DockerHub. - -## Comments on this Policy - -If you have suggestions on how this process could be improved please submit a -pull request. diff --git a/.github_changelog_generator b/.github_changelog_generator deleted file mode 100644 index 7cd67cacd7..0000000000 --- a/.github_changelog_generator +++ /dev/null @@ -1,3 +0,0 @@ -since-tag=v2.0.4 -issues=false - diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 95b2417d8c..0000000000 --- a/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -.vscode -__debug_bin -*.sqlite -*.txt -*.out -*.key -.env -.env.* -release/ -scripts/*.go -docker/**/data -TODO* -.idea diff --git a/BUILDING b/BUILDING deleted file mode 100644 index fe4ee3168a..0000000000 --- a/BUILDING +++ /dev/null @@ -1,11 +0,0 @@ -1. Clone the repository -2. Install go 1.11 or later with Go modules enabled -3. Install binaries to $GOPATH/bin - - go install github.com/drone/drone/cmd/drone-server - -4. Start the server at localhost:8080 - - export DRONE_GITHUB_CLIENT_ID=... - export DRONE_GITHUB_CLIENT_SECRET=... - drone-server diff --git a/BUILDING_OSS b/BUILDING_OSS deleted file mode 100644 index b7ed226feb..0000000000 --- a/BUILDING_OSS +++ /dev/null @@ -1,11 +0,0 @@ -1. Clone the repository -2. Install go 1.11 or later with Go modules enabled -3. Install binaries to $GOPATH/bin - - go install -tags "oss nolimit" github.com/drone/drone/cmd/drone-server - -4. Start the server at localhost:8080 - - export DRONE_GITHUB_CLIENT_ID=... - export DRONE_GITHUB_CLIENT_SECRET=... - drone-server diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 1cad0841f5..0000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,797 +0,0 @@ -# Changelog - -## [v2.20.0](https://github.com/harness/drone/tree/v2.20.0) (2023-08-21) - -[Full Changelog](https://github.com/harness/drone/compare/v2.19.0...v2.20.0) - -**Implemented enhancements:** - -- + sync gitea redirecturl config from gitee for customize login redireā€¦ [\#3319](https://github.com/harness/drone/pull/3319) ([fireinice](https://github.com/fireinice)) - -**Fixed bugs:** - -- \(CI-8780\) set approved stages to waiting, if they have stage dependeā€¦ [\#3355](https://github.com/harness/drone/pull/3355) ([tphoney](https://github.com/tphoney)) - -## [v2.19.0](https://github.com/harness/drone/tree/v2.19.0) (2023-08-15) - -[Full Changelog](https://github.com/harness/drone/compare/scheduler_experiment...v2.19.0) - -**Implemented enhancements:** - -- Support arbitrary action value from parameter in query string [\#3341](https://github.com/harness/drone/pull/3341) ([filippopisano](https://github.com/filippopisano)) - -**Fixed bugs:** - -- bump drone-ui to 2.11.5 [\#3350](https://github.com/harness/drone/pull/3350) ([d1wilko](https://github.com/d1wilko)) -- bump drone-ui to 2.11.4 [\#3349](https://github.com/harness/drone/pull/3349) ([d1wilko](https://github.com/d1wilko)) -- \(fix\) prevent scheduler deadlock [\#3344](https://github.com/harness/drone/pull/3344) ([tphoney](https://github.com/tphoney)) -- bump drone-ui to 2.11.3 [\#3337](https://github.com/harness/drone/pull/3337) ([d1wilko](https://github.com/d1wilko)) - -**Merged pull requests:** - -- \(maint\) prep for v2.19.0 [\#3352](https://github.com/harness/drone/pull/3352) ([tphoney](https://github.com/tphoney)) -- remove repetitive words [\#3342](https://github.com/harness/drone/pull/3342) ([cuishuang](https://github.com/cuishuang)) -- Revert "fix scheduler queue deadlock" [\#3331](https://github.com/harness/drone/pull/3331) ([tphoney](https://github.com/tphoney)) - -## [scheduler_experiment](https://github.com/harness/drone/tree/scheduler_experiment) (2023-07-05) - -[Full Changelog](https://github.com/harness/drone/compare/v2.18.0...scheduler_experiment) - -**Fixed bugs:** - -- fix scheduler queue deadlock [\#3330](https://github.com/harness/drone/pull/3330) ([tphoney](https://github.com/tphoney)) - -## [v2.18.0](https://github.com/harness/drone/tree/v2.18.0) (2023-07-04) - -[Full Changelog](https://github.com/harness/drone/compare/v2.17.0...v2.18.0) - -**Implemented enhancements:** - -- support custom pipeline message [\#3294](https://github.com/harness/drone/pull/3294) ([zc2638](https://github.com/zc2638)) - -**Fixed bugs:** - -- bump drone-ui to 2.11.2 [\#3327](https://github.com/harness/drone/pull/3327) ([d1wilko](https://github.com/d1wilko)) -- Fix comment errors [\#3302](https://github.com/harness/drone/pull/3302) ([weidongkl](https://github.com/weidongkl)) - -**Merged pull requests:** - -- v2.18.0 release prep [\#3328](https://github.com/harness/drone/pull/3328) ([tphoney](https://github.com/tphoney)) - -## [v2.17.0](https://github.com/harness/drone/tree/v2.17.0) (2023-04-25) - -[Full Changelog](https://github.com/harness/drone/compare/v2.16.0...v2.17.0) - -**Implemented enhancements:** - -- Add `authtype` to logging middleware [\#3310](https://github.com/harness/drone/pull/3310) ([colinhoglund](https://github.com/colinhoglund)) -- Add config for the buffer [\#3308](https://github.com/harness/drone/pull/3308) ([TheJokersThief](https://github.com/TheJokersThief)) - -**Fixed bugs:** - -- store/card: fix dropped error [\#3300](https://github.com/harness/drone/pull/3300) ([alrs](https://github.com/alrs)) -- bump drone-ui to 2.9.1 [\#3298](https://github.com/harness/drone/pull/3298) ([d1wilko](https://github.com/d1wilko)) -- Starlark: Update `go.starlark.net` dependency [\#3284](https://github.com/harness/drone/pull/3284) ([dsotirakis](https://github.com/dsotirakis)) - -**Merged pull requests:** - -- release prep for v2.17.0 [\#3316](https://github.com/harness/drone/pull/3316) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- bump drone-ui to 2.11.1 [\#3315](https://github.com/harness/drone/pull/3315) ([d1wilko](https://github.com/d1wilko)) -- bump drone-ui to 2.11.0 [\#3313](https://github.com/harness/drone/pull/3313) ([d1wilko](https://github.com/d1wilko)) -- bump drone-ui to 2.10.0 [\#3311](https://github.com/harness/drone/pull/3311) ([d1wilko](https://github.com/d1wilko)) -- \(maint\) move to use the arm64 pool [\#3296](https://github.com/harness/drone/pull/3296) ([tphoney](https://github.com/tphoney)) - -## [v2.16.0](https://github.com/harness/drone/tree/v2.16.0) (2022-12-15) - -[Full Changelog](https://github.com/harness/drone/compare/v2.15.0...v2.16.0) - -**Implemented enhancements:** - -- Make Starlark file size limit configurable [\#3291](https://github.com/harness/drone/pull/3291) ([andrii-kasparevych](https://github.com/andrii-kasparevych)) -- Enhance status check label for promotions [\#3263](https://github.com/harness/drone/pull/3263) ([michelangelomo](https://github.com/michelangelomo)) - -**Fixed bugs:** - -- \(bugfix\) bump go-scm to v1.28.0 [\#3290](https://github.com/harness/drone/pull/3290) ([tphoney](https://github.com/tphoney)) - -**Merged pull requests:** - -- \(maint\) 2.16.0 release prep [\#3295](https://github.com/harness/drone/pull/3295) ([tphoney](https://github.com/tphoney)) - -## [v2.15.0](https://github.com/harness/drone/tree/v2.15.0) (2022-10-28) - -[Full Changelog](https://github.com/harness/drone/compare/v2.14.0...v2.15.0) - -**Implemented enhancements:** - -- bump ui version [\#3279](https://github.com/harness/drone/pull/3279) ([d1wilko](https://github.com/d1wilko)) -- Add endpoint for allowing admins to force rotate a user's token [\#3272](https://github.com/harness/drone/pull/3272) ([ShiftedMr](https://github.com/ShiftedMr)) - -**Merged pull requests:** - -- release prep v2.15.0 [\#3281](https://github.com/harness/drone/pull/3281) ([d1wilko](https://github.com/d1wilko)) - -## [v2.14.0](https://github.com/harness/drone/tree/v2.14.0) (2022-10-18) - -[Full Changelog](https://github.com/harness/drone/compare/v2.13.0...v2.14.0) - -**Implemented enhancements:** - -- \(DRON-418\) send webhook and set status for failed builds [\#3266](https://github.com/harness/drone/pull/3266) ([tphoney](https://github.com/tphoney)) - -**Merged pull requests:** - -- v2.14.0 release prep [\#3275](https://github.com/harness/drone/pull/3275) ([d1wilko](https://github.com/d1wilko)) - -## [v2.13.0](https://github.com/harness/drone/tree/v2.13.0) (2022-09-21) - -[Full Changelog](https://github.com/harness/drone/compare/v2.12.1...v2.13.0) - -**Implemented enhancements:** - -- feat: update drone-yaml module [\#3249](https://github.com/harness/drone/pull/3249) ([jimsheldon](https://github.com/jimsheldon)) -- support time zone [\#3241](https://github.com/harness/drone/pull/3241) ([zc2638](https://github.com/zc2638)) - -**Fixed bugs:** - -- update discourse.drone.io to community.harness.io [\#3261](https://github.com/harness/drone/pull/3261) ([kit101](https://github.com/kit101)) -- \(DRON-392\) cascade deletes on purge [\#3243](https://github.com/harness/drone/pull/3243) ([tphoney](https://github.com/tphoney)) -- Template converter, don't skip .yaml extension. [\#3242](https://github.com/harness/drone/pull/3242) ([staffanselander](https://github.com/staffanselander)) - -**Merged pull requests:** - -- v2.13.0 release prep [\#3268](https://github.com/harness/drone/pull/3268) ([tphoney](https://github.com/tphoney)) -- \(maint\) disable arm builds [\#3262](https://github.com/harness/drone/pull/3262) ([tphoney](https://github.com/tphoney)) -- Update links to discourse in issue template [\#3233](https://github.com/harness/drone/pull/3233) ([alikhil](https://github.com/alikhil)) - -## [v2.12.1](https://github.com/harness/drone/tree/v2.12.1) (2022-06-15) - -[Full Changelog](https://github.com/harness/drone/compare/v2.12.0...v2.12.1) - -**Fixed bugs:** - -- \(bug\) - fix original template scripts & remove amend scripts [\#3229](https://github.com/harness/drone/pull/3229) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- \(bug\) - remove unique index on template name [\#3226](https://github.com/harness/drone/pull/3226) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- Added OAuth2 token refresher for Gitlab [\#3215](https://github.com/harness/drone/pull/3215) ([EndymionWight](https://github.com/EndymionWight)) - -**Merged pull requests:** - -- release prep for v2.12.1 [\#3232](https://github.com/harness/drone/pull/3232) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- \(maint\) fix starlark test on windows [\#3230](https://github.com/harness/drone/pull/3230) ([tphoney](https://github.com/tphoney)) -- \(maint\) fix unit tests so they pass on windows [\#3228](https://github.com/harness/drone/pull/3228) ([tphoney](https://github.com/tphoney)) -- Update Readme to Fix Typo [\#3223](https://github.com/harness/drone/pull/3223) ([hrittikhere](https://github.com/hrittikhere)) -- \(bug\) add unit test for comments in template file [\#3221](https://github.com/harness/drone/pull/3221) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- Bump scm version to v1.24.0 [\#3219](https://github.com/harness/drone/pull/3219) ([kit101](https://github.com/kit101)) - -## [v2.12.0](https://github.com/harness/drone/tree/v2.12.0) (2022-05-16) - -[Full Changelog](https://github.com/harness/drone/compare/v2.11.1...v2.12.0) - -**Implemented enhancements:** - -- bump SCM version to v1.21.1 [\#3204](https://github.com/harness/drone/pull/3204) ([d1wilko](https://github.com/d1wilko)) -- bump ui version [\#3202](https://github.com/harness/drone/pull/3202) ([d1wilko](https://github.com/d1wilko)) - -**Fixed bugs:** - -- \(fix\) update drone ui to 2.8.2 [\#3211](https://github.com/harness/drone/pull/3211) ([tphoney](https://github.com/tphoney)) -- \(dron-267\) correctly set parent for promotion retry [\#3210](https://github.com/harness/drone/pull/3210) ([tphoney](https://github.com/tphoney)) - -**Merged pull requests:** - -- release prep v2.12.0 [\#3214](https://github.com/harness/drone/pull/3214) ([tphoney](https://github.com/tphoney)) -- fixing URL [\#3208](https://github.com/harness/drone/pull/3208) ([dnielsen](https://github.com/dnielsen)) -- update community information with updated links [\#3199](https://github.com/harness/drone/pull/3199) ([mrsantons](https://github.com/mrsantons)) - -## [v2.11.1](https://github.com/harness/drone/tree/v2.11.1) (2022-03-15) - -[Full Changelog](https://github.com/harness/drone/compare/v2.11.0...v2.11.1) - -**Fixed bugs:** - -- ignore nil repos in list and add better debugging [\#3196](https://github.com/harness/drone/pull/3196) ([d1wilko](https://github.com/d1wilko)) - -**Merged pull requests:** - -- \(maint\) release prep for 2.11.1 [\#3197](https://github.com/harness/drone/pull/3197) ([d1wilko](https://github.com/d1wilko)) - -## [v2.11.0](https://github.com/harness/drone/tree/v2.11.0) (2022-03-08) - -[Full Changelog](https://github.com/harness/drone/compare/v2.10.0...v2.11.0) - -**Implemented enhancements:** - -- bump UI and SCM versions [\#3193](https://github.com/harness/drone/pull/3193) ([d1wilko](https://github.com/d1wilko)) - -**Merged pull requests:** - -- \(maint\) release prep for 2.11.0 [\#3194](https://github.com/harness/drone/pull/3194) ([d1wilko](https://github.com/d1wilko)) - -## [v2.10.0](https://github.com/harness/drone/tree/v2.10.0) (2022-03-03) - -[Full Changelog](https://github.com/harness/drone/compare/v2.9.1...v2.10.0) - -**Implemented enhancements:** - -- bump UI version to v2.7.0 [\#3190](https://github.com/harness/drone/pull/3190) ([d1wilko](https://github.com/d1wilko)) -- bump UI version to v2.6.2 [\#3188](https://github.com/harness/drone/pull/3188) ([d1wilko](https://github.com/d1wilko)) - -**Merged pull requests:** - -- \(maint\) release prep for 2.10.0 [\#3191](https://github.com/harness/drone/pull/3191) ([d1wilko](https://github.com/d1wilko)) - -## [v2.9.1](https://github.com/harness/drone/tree/v2.9.1) (2022-01-27) - -[Full Changelog](https://github.com/harness/drone/compare/v2.9.0...v2.9.1) - -**Fixed bugs:** - -- bump ui version 2.6.1 [\#3185](https://github.com/harness/drone/pull/3185) ([d1wilko](https://github.com/d1wilko)) - -**Merged pull requests:** - -- \(maint\) release prep for 2.9.1 [\#3186](https://github.com/harness/drone/pull/3186) ([tphoney](https://github.com/tphoney)) - -## [v2.9.0](https://github.com/harness/drone/tree/v2.9.0) (2022-01-26) - -[Full Changelog](https://github.com/harness/drone/compare/v2.8.0...v2.9.0) - -**Implemented enhancements:** - -- bump ui to v2.6.0 [\#3183](https://github.com/harness/drone/pull/3183) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -**Merged pull requests:** - -- release prep for v2.9.0 [\#3184](https://github.com/harness/drone/pull/3184) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -## [v2.8.0](https://github.com/harness/drone/tree/v2.8.0) (2022-01-11) - -[Full Changelog](https://github.com/harness/drone/compare/v2.7.3...v2.8.0) - -**Implemented enhancements:** - -- bump UI to v2.5.0 [\#3180](https://github.com/harness/drone/pull/3180) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- \(feat\) ignore archive repos on sync [\#3178](https://github.com/harness/drone/pull/3178) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- Datadog add the tag of 'remote:gitee' [\#3174](https://github.com/harness/drone/pull/3174) ([kit101](https://github.com/kit101)) -- Add tag filter when call build list endpoint [\#3173](https://github.com/harness/drone/pull/3173) ([michelangelomo](https://github.com/michelangelomo)) - -**Fixed bugs:** - -- \(maint\) add warning around typo for stage\_id in step struct [\#3179](https://github.com/harness/drone/pull/3179) ([tphoney](https://github.com/tphoney)) - -**Merged pull requests:** - -- release prep v2.8.0 [\#3181](https://github.com/harness/drone/pull/3181) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -## [v2.7.3](https://github.com/harness/drone/tree/v2.7.3) (2021-12-30) - -[Full Changelog](https://github.com/harness/drone/compare/v2.7.2...v2.7.3) - -**Fixed bugs:** - -- bump go-scm to v1.16.3 [\#3175](https://github.com/harness/drone/pull/3175) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -**Merged pull requests:** - -- release prep v2.7.3 [\#3176](https://github.com/harness/drone/pull/3176) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -## [v2.7.2](https://github.com/harness/drone/tree/v2.7.2) (2021-12-19) - -[Full Changelog](https://github.com/harness/drone/compare/v2.7.1...v2.7.2) - -**Implemented enhancements:** - -- bump go-scm to v1.16.2 [\#3169](https://github.com/harness/drone/pull/3169) ([kit101](https://github.com/kit101)) - -**Fixed bugs:** - -- fixbug gitee provide refresher [\#3168](https://github.com/harness/drone/pull/3168) ([kit101](https://github.com/kit101)) - -**Merged pull requests:** - -- release prep 2.7.2 [\#3172](https://github.com/harness/drone/pull/3172) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -## [v2.7.1](https://github.com/harness/drone/tree/v2.7.1) (2021-12-17) - -[Full Changelog](https://github.com/harness/drone/compare/v2.7.0...v2.7.1) - -**Fixed bugs:** - -- fixes issue with redirects on double slashes in url [\#3170](https://github.com/harness/drone/pull/3170) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -**Merged pull requests:** - -- release prep v2.7.1 [\#3171](https://github.com/harness/drone/pull/3171) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -## [v2.7.0](https://github.com/harness/drone/tree/v2.7.0) (2021-12-15) - -[Full Changelog](https://github.com/harness/drone/compare/v2.6.0...v2.7.0) - -**Implemented enhancements:** - -- bump UI to v2.4.1 [\#3167](https://github.com/harness/drone/pull/3167) ([d1wilko](https://github.com/d1wilko)) - -**Fixed bugs:** - -- \(DRON-157\) use deploy string in deployment [\#3165](https://github.com/harness/drone/pull/3165) ([tphoney](https://github.com/tphoney)) - -**Merged pull requests:** - -- release v2.7.0 [\#3166](https://github.com/harness/drone/pull/3166) ([d1wilko](https://github.com/d1wilko)) - -## [v2.6.0](https://github.com/harness/drone/tree/v2.6.0) (2021-11-30) - -[Full Changelog](https://github.com/harness/drone/compare/v2.5.0...v2.6.0) - -**Implemented enhancements:** - -- Feat: implemented gitee client [\#3156](https://github.com/harness/drone/pull/3156) ([kit101](https://github.com/kit101)) - -**Merged pull requests:** - -- release prep for v2.6.0 [\#3163](https://github.com/harness/drone/pull/3163) ([tphoney](https://github.com/tphoney)) - -## [v2.5.0](https://github.com/harness/drone/tree/v2.5.0) (2021-11-17) - -[Full Changelog](https://github.com/harness/drone/compare/v2.4.0...v2.5.0) - -**Implemented enhancements:** - -- bump ui to v2.4.0 [\#3160](https://github.com/harness/drone/pull/3160) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- add new endpoint for uploading cards [\#3159](https://github.com/harness/drone/pull/3159) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- refactor create / find / delete end points for cards [\#3158](https://github.com/harness/drone/pull/3158) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- bump ui to v2.3.1 [\#3155](https://github.com/harness/drone/pull/3155) ([d1wilko](https://github.com/d1wilko)) -- provide ability to create/read/store card data in drone server [\#3149](https://github.com/harness/drone/pull/3149) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- \(DRON-124\) adding new status endpoint [\#3143](https://github.com/harness/drone/pull/3143) ([tphoney](https://github.com/tphoney)) - -**Fixed bugs:** - -- fix a typo in readme [\#3150](https://github.com/harness/drone/pull/3150) ([nothatDinger](https://github.com/nothatDinger)) - -**Merged pull requests:** - -- release prep for v2.5.0 [\#3161](https://github.com/harness/drone/pull/3161) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -## [v2.4.0](https://github.com/harness/drone/tree/v2.4.0) (2021-09-23) - -[Full Changelog](https://github.com/harness/drone/compare/v2.3.1...v2.4.0) - -**Implemented enhancements:** - -- bump ui version to v2.3.0 [\#3146](https://github.com/harness/drone/pull/3146) ([d1wilko](https://github.com/d1wilko)) -- verify if the application is buildable [\#3144](https://github.com/harness/drone/pull/3144) ([marko-gacesa](https://github.com/marko-gacesa)) - -**Fixed bugs:** - -- fixes build issue with bitbucket cloud [\#3147](https://github.com/harness/drone/pull/3147) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- Fix stepLimit param in Starlark and Template OSS code [\#3141](https://github.com/harness/drone/pull/3141) ([phil-davis](https://github.com/phil-davis)) -- fix a broken link in readme [\#3140](https://github.com/harness/drone/pull/3140) ([empire](https://github.com/empire)) - -**Merged pull requests:** - -- \(maint\)-release 2.4.0 [\#3148](https://github.com/harness/drone/pull/3148) ([d1wilko](https://github.com/d1wilko)) -- Allow jsonnet imports in pipeline configuration [\#3105](https://github.com/harness/drone/pull/3105) ([hhamalai](https://github.com/hhamalai)) - -## [v2.3.1](https://github.com/harness/drone/tree/v2.3.1) (2021-09-09) - -[Full Changelog](https://github.com/harness/drone/compare/v2.3.0...v2.3.1) - -**Implemented enhancements:** - -- bump ui to v2.2.1 - https://github.com/drone/drone-ui/blob/main/CHANGELOG.md [\#3138](https://github.com/harness/drone/pull/3138) ([d1wilko](https://github.com/d1wilko)) - -**Merged pull requests:** - -- \(maint\)-release 2.3.1 [\#3139](https://github.com/harness/drone/pull/3139) ([d1wilko](https://github.com/d1wilko)) - -## [v2.3.0](https://github.com/harness/drone/tree/v2.3.0) (2021-09-09) - -[Full Changelog](https://github.com/harness/drone/compare/v2.2.0...v2.3.0) - -**Implemented enhancements:** - -- bump ui to v2.2.0 - https://github.com/drone/drone-ui/blob/main/CHANGELOG.md [\#3137](https://github.com/harness/drone/pull/3137) ([d1wilko](https://github.com/d1wilko)) -- Make starlark step limit configurable [\#3134](https://github.com/harness/drone/pull/3134) ([phil-davis](https://github.com/phil-davis)) -- \(feat\) drone h/a: wrapped scheduler's signal func with redis mutex [\#3130](https://github.com/harness/drone/pull/3130) ([marko-gacesa](https://github.com/marko-gacesa)) - -**Fixed bugs:** - -- \(fix\) trim http/s prefixes from config hostnames [\#3136](https://github.com/harness/drone/pull/3136) ([tphoney](https://github.com/tphoney)) -- \(fix\) remove unused jwt-go library [\#3129](https://github.com/harness/drone/pull/3129) ([tphoney](https://github.com/tphoney)) - -## [v2.2.0](https://github.com/harness/drone/tree/v2.2.0) (2021-09-01) - -[Full Changelog](https://github.com/harness/drone/compare/v2.1.0...v2.2.0) - -**Implemented enhancements:** - -- \(maint\) ui version v2.1.0 - https://github.com/drone/drone-ui/blob/main/CHANGELOG.md [\#3132](https://github.com/harness/drone/pull/3132) ([d1wilko](https://github.com/d1wilko)) -- Ability to cancel running builds, if a new commit is pushed [\#3126](https://github.com/harness/drone/pull/3126) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -**Fixed bugs:** - -- fix templating reg expression to match if .drone.yml contains --- characters [\#3131](https://github.com/harness/drone/pull/3131) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- add check on template extension type - throw error if invalid [\#3128](https://github.com/harness/drone/pull/3128) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -**Merged pull requests:** - -- \(maint\)-release 2.2.0 [\#3133](https://github.com/harness/drone/pull/3133) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- Rename files with camelCase name to use snake\_case convention [\#3127](https://github.com/harness/drone/pull/3127) ([marko-gacesa](https://github.com/marko-gacesa)) -- event-stream supports timeout [\#3125](https://github.com/harness/drone/pull/3125) ([zc2638](https://github.com/zc2638)) -- \(maint\) Readme update Add Contributor Section [\#3111](https://github.com/harness/drone/pull/3111) ([mrsantons](https://github.com/mrsantons)) - -## [v2.1.0](https://github.com/harness/drone/tree/v2.1.0) (2021-08-24) - -[Full Changelog](https://github.com/harness/drone/compare/v2.0.6...v2.1.0) - -**Implemented enhancements:** - -- \(maint\) ui version v2.0.1. - https://github.com/drone/drone-ui/blob/main/CHANGELOG.md [\#3123](https://github.com/harness/drone/pull/3123) ([d1wilko](https://github.com/d1wilko)) -- add support for yaml templates [\#3120](https://github.com/harness/drone/pull/3120) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -**Fixed bugs:** - -- Update error message to forbidden if user membership doesn't exist on repo [\#3122](https://github.com/harness/drone/pull/3122) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- update create template path to have namespace, instead of inside the payload [\#3121](https://github.com/harness/drone/pull/3121) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- update dependency drone/go-scm to 1.15.2 to fix gitea build problem [\#3118](https://github.com/harness/drone/pull/3118) ([sesky4](https://github.com/sesky4)) - -**Merged pull requests:** - -- \(maint\) v2.1.0 release prep [\#3124](https://github.com/harness/drone/pull/3124) ([d1wilko](https://github.com/d1wilko)) - -## [v2.0.6](https://github.com/harness/drone/tree/v2.0.6) (2021-08-17) - -[Full Changelog](https://github.com/harness/drone/compare/v2.0.5...v2.0.6) - -**Merged pull requests:** - -- \(maint\) v2.0.6 release prep [\#3119](https://github.com/harness/drone/pull/3119) ([tphoney](https://github.com/tphoney)) - -## [v2.0.5](https://github.com/harness/drone/tree/v2.0.5) (2021-08-17) - -[Full Changelog](https://github.com/harness/drone/compare/v2.0.4...v2.0.5) - -**Implemented enhancements:** - -- bump ui version [\#3115](https://github.com/harness/drone/pull/3115) ([d1wilko](https://github.com/d1wilko)) -- bump ui version [\#3114](https://github.com/harness/drone/pull/3114) ([d1wilko](https://github.com/d1wilko)) -- Add support for nested data objects within templates [\#3110](https://github.com/harness/drone/pull/3110) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- \(feat\) redis implementation for pub-sub, log streaming and canceller [\#3108](https://github.com/harness/drone/pull/3108) ([marko-gacesa](https://github.com/marko-gacesa)) - -**Fixed bugs:** - -- fix issue where map changes order therefore test randomly fails [\#3112](https://github.com/harness/drone/pull/3112) ([eoinmcafee00](https://github.com/eoinmcafee00)) - -**Merged pull requests:** - -- release 2.0.5 [\#3117](https://github.com/harness/drone/pull/3117) ([eoinmcafee00](https://github.com/eoinmcafee00)) -- Update pull\_request\_template.md [\#3107](https://github.com/harness/drone/pull/3107) ([tphoney](https://github.com/tphoney)) - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## Unreleased - -## [2.0.4] -### Fixed -- DRON-97 remove use of request animation frame to prevent high CPU on tab refocus events. - -## [2.0.3] -### Fixed -- DONE-91 handle extra slashes in url. [#3009](https://github.com/drone/drone/pull/3099). - -## [2.0.2] -### Added -- Merge remote-tracking branch 'origin/master' -- prevent repository list short circuit in UI -- remove deprecated steps from building file [#3097](https://github.com/drone/drone/pull/3097) -- adding depends_on, image and detached fields to step [#3072](https://github.com/drone/drone/pull/3072) -- Add ctx.build.debug boolean [#3082](https://github.com/drone/drone/pull/3082) -- Bump github.com/google/go-jsonnet to v0.17.0 [#3084](https://github.com/drone/drone/pull/3084) -- bump go-scm v1.15.1 [#3096](https://github.com/drone/drone/pull/3096) -- bitbucket server build issue [#3092](https://github.com/drone/drone/pull/3092) -- update scm version [#3091](https://github.com/drone/drone/pull/3091) -- Limit graceful shutdown duration [#3093](https://github.com/drone/drone/pull/3093) -- bump user interface -- bump ui version -- ignore skip directive for promote and rollback events -- new feature: maximum open DB connections is configurable[#3089](https://github.com/drone/drone/pull/3089) -- jsonnet additional parameters [#3087](https://github.com/drone/drone/pull/3087) -- hide login button if user already authenticated -- new feature: configuration templates [#3081](https://github.com/drone/drone/pull/3081) - -### Fixed -- various typos [#3088](https://github.com/drone/drone/pull/3088) -- handle error properly if template doesn't exist [#3095](https://github.com/drone/drone/pull/3093) -- oss build issue [#3086](https://github.com/drone/drone/pull/3086) -- graceful shutdown [#3083](https://github.com/drone/drone/pull/3083) - -## [2.0.1] -### Added -- support for configuring the internal yaml cache size. - -## [2.0.0] -### Added -- feature flags for mixed-mode database encryption. - -### Changed -- user-interface re-design - -### Breaking -- removed deprecated kubernetes integration in favor of official kubernetes runner. -- removed deprecated nomad integration in favor of official nomad runner. - -## [1.10.1] -### Added -- support for repository-level concurrency limits. -- support for gitlab and github internal visibility on initial sync. - -### Fixed -- create machine user with a custom API token. - -## [1.10.0] -### Added -- support for starlark scripts in core. -- support for executing pipelines in debug mode. - -## [1.9.2] -### Added -- update go-scm dependency to fix - -## [1.9.1] -### Added -- support for increasing the http request timeout for extensions. [#2998](https://github.com/drone/drone/pull/2998). -- support for skipping a pipeline if the validation extension returns an ErrSkip. -- support for blocking a pipeline if the validation extension returns an ErrBlock. - -### Fixed -- rollback endpoint should be available to users with write permission. -- retrying a build should re-use custom build parameters from parent build. - -## [1.9.0] - 2020-07-12 -### Added -- ui support for deployment list and summary. -- ui support for promoting and rolling back builds. -- feature flag to use static secret when signing webhooks, from @chiraggadasc. - -### Fixed -- ui branch list improperly capped. - -### Changed -- upgrade drone/envsubst dependency -- upgrade drone/go-scm dependency - -## [1.8.1] - 2020-06-23 -### Fixed -- support for gitea api pagination, repository sync hanging. - -## [1.8.0] - 2020-06-10 -### Added -- re-assigned repository ownership when deactivating a user. -- re-assigned repository ownership when deleting a user. -- de-activate a repository when deleting a user if re-assignment fails. -- de-activate a repository when deactivating a user if re-assignment fails. -- routine to cleanup builds stuck in a pending state. -- routine to cleanup builds stuck in a running state. -- private mode setting requires authentication to view public repositories. - -### Fixed -- canceling a build emits a sql.ErrNoRows error. -- custom token is ignored when creating a user account via the API. -- machine accounts with sufficient permissions can create builds via the API. - -### Changed -- upgraded Go toolchain to version 1.14.4. - -## [1.7.0] - 2020-03-27 -### Added -- endpoint to display the latest build by branch. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to display the latest build by pull request. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to display the latest build by environment. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to delete a branch from the index. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to delete a pull request from the index. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to delete an environment from the index. [#2940](https://github.com/drone/drone/pull/2940). -- page to view the latest build per branch. - -### Fixed -- sync routine not executing asynchronously, being cancelled by http context. -- sync routine should ignore gitlab subrepositories -- convert deploy events in 0.8 yaml to promote events. -- do not execute cron job for disabled repositories. [#2931](https://github.com/drone/drone/issues/2931). -- remove trailing slash from gitea url to prevent oauth2 token refresh errors, by [@cmj0121](https://github.com/cmj0121). [#2920](https://github.com/drone/drone/issues/2920). -- disable font ligatures in build log output. [drone/drone-ui#322](https://github.com/drone/drone-ui/pull/322). -- missing am/pm in timestamps - -## [1.6.5] - 2020-01-29 -### Changed -- update version of go-scm -- update alpine version in docker images -- use ticker for cron jobs for more accurate timing - -## [1.6.4] - 2019-12-30 -### Added -- optionally enable pprof endpoints for profiling, by [@bradrydzewski](https://github.com/bradrydzewski). - -## [1.6.3] - 2019-12-10 -### Fixed -- disable caching generated yaml files by commit sha, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Added -- support for bitbucket skipverify, by [@toni-moreno](https://github.com/toni-moreno). -- support for gitea skipverify, by [@toni-moreno](https://github.com/toni-moreno). - -## [1.6.2] - 2019-11-08 -### Added -- support for loading license contents from env, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Fixed -- regression not converting legacy pipeline when using new runners, by [@bradrydzewski](https://github.com/bradrydzewski). - -## [1.6.1] - 2019-10-17 -### Added -- updated autocert library in support of acme v2 protocol, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Fixed -- fixed nil pointer when manually adding user from api, by [@bradrydzewski](https://github.com/bradrydzewski). - -## [1.6.0] - 2019-10-04 -### Added -- added nsswitch to docker images -- option to auto-cancel pending builds when newer build enqueued, by [@bradrydzewski](https://github.com/bradrydzewski). [#1980](https://github.com/drone/drone/issues/1980). -- endpoint to list all repositories in the database, by [@bradrydzewski](https://github.com/bradrydzewski). [#2785](https://github.com/drone/drone/issues/2785). - -### Fixed -- improve sync to handle duplicate repository names with different unique identifiers, by [@bradrydzewski](https://github.com/bradrydzewski). [#2658](https://github.com/drone/drone/issues/2658). _You can revert to the previous sync logic with DRONE_DATABASE_LEGACY_BATCH=true_. - -## [1.5.1] - 2019-09-30 -### Added -- allow organization admins access to organization secret endpoints, by [@bradrydzewski](https://github.com/bradrydzewski). [#2838](https://github.com/drone/drone/issues/2838). - -### Fixed -- fix invalid deep links in UI for github enterprise, by [@bradrydzewski](https://github.com/bradrydzewski). -- ensure correct casing when manually adding user, by [@bradrydzewski](https://github.com/bradrydzewski). [#2766](https://github.com/drone/drone/issues/2766). - -## [1.5.0] - 2019-09-28 -### Added -- endpoint to execute a cron pipeline on-demand, by [@bradrydzewski](https://github.com/bradrydzewski). [#2781](https://github.com/drone/drone/issues/2781). -- endpoint to list builds by branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#1495](https://github.com/drone/drone/issues/1495). -- ignore skip comments when cron event, by [@bradrydzewski](https://github.com/bradrydzewski). [#2835](https://github.com/drone/drone/issues/2835). -- support for admission extensions, by [@bradrydzewski](https://github.com/bradrydzewski). [#2043](https://github.com/drone/drone/issues/2043). -- endpoint to provide link to git resources, by [@bradrydzewski](https://github.com/bradrydzewski). [#2843](https://github.com/drone/drone/issues/2843). -- improve bitbucket status display text on new pull request screen, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Fixed -- missing cron job name in user interface, by [@bradrydzewski](https://github.com/bradrydzewski). -- log lines not properly wrapping in user interface, by [@bradrydzewski](https://github.com/bradrydzewski). -[#309](https://github.com/drone/drone-ui/issues/309). - -### Breaking -- the server now runs in multi-machine mode by default. In order to run the server in single-machine mode (agents disabled) you must set DRONE_AGENTS_DISABLED=true. - -## [1.4.0] - 2019-09-12 -### Added -- upgrade to Go 1.13 to resolve arm segfault, by [@KN4CK3R](https://github.com/KN4CK3R). [#2823](https://github.com/drone/drone/issues/2823). -- configure default visibility, by [@JordanSussman](https://github.com/JordanSussman). [#2824](https://github.com/drone/drone/issues/2824). -- configure default trusted flag, by [@vyckou](https://github.com/vyckou). -- support for validation plugins, by [@bradrydzewski](https://github.com/bradrydzewski). [#2266](https://github.com/drone/drone/issues/2266). -- support for conversion plugins, by [@bradrydzewski](https://github.com/bradrydzewski). -- support for cron event type, by [@bradrydzewski](https://github.com/bradrydzewski). [#2705](https://github.com/drone/drone/issues/2705). -- support for rollback event, by [@bradrydzewski](https://github.com/bradrydzewski). [#2695](https://github.com/drone/drone/issues/2695). -- support for lets encrypt email, by [@bradrydzewski](https://github.com/bradrydzewski). [#2505](https://github.com/drone/drone/issues/2505). - -### Removed -- Support for basic auth as an option for Gitea, by [@techknowlogick](https://giteahub.com/techknowlogick). [#2721](https://github.com/drone/drone/issues/2721) - -### Fixed -- copy cron job name when restarting a cron job, by [@bradrydzewski](https://github.com/bradrydzewski). [#2760](https://github.com/drone/drone/issues/2760). - -## [1.3.1] - 2019-08-26 -### Added -- support for the GitHub deployment status API, by [@bradrydzewski](https://github.com/bradrydzewski). - -## [1.3.0] - 2019-08-20 -### Added -- support for storing logs in Azure Cloud Storage, by [@Lucretius](https://github.com/Lucretius). [#2788](https://github.com/drone/drone/pull/2788) -- support for windows server 1903, by [@bradrydzewski](https://github.com/bradrydzewski). -- button to view the full log file, by [@dramich](https://github.com/dramich). [drone/drone-ui#287](https://github.com/drone/drone-ui/pull/287). - -### Fixed -- read gogs sha from webhook, by [@marcotuna](https://github.com/marcotuna). -- create bind volume on host if not exists, by [@bradrydzewski](https://github.com/bradrydzewski). [#2725](https://github.com/drone/drone/issues/2725). -- preserve whitespace in build logs, by [@geek1011](https://github.com/geek1011). [drone/drone-ui#294](https://github.com/drone/drone-ui/pull/294). -- enable log file download on firefox, by [@bobmanary](https://github.com/bobmanary). [drone/drone-ui#303](https://github.com/drone/drone-ui/pull/303) - -### Security -- upgraded to Go 1.12.9 due to CVE-2019-9512 and CVE-2019-9514 - -## [1.2.3] - 2019-07-30 -### Added - -- disable github status for cron jobs -- support for action in conditionals, by [@bradrydzewski](https://github.com/bradrydzewski). [#2685](https://github.com/drone/drone/issues/2685). - -### Fixed - -- improve cancel logic for dangling stages, by [@bradrydzewski](https://github.com/bradrydzewski). -- improve error when kubernetes malforms the port configuration, by [@bradrydzewski](https://github.com/bradrydzewski). [#2742](https://github.com/drone/drone/issues/2742). -- copy parameters from parent build when promoting, by [@bradrydzewski](https://github.com/bradrydzewski). [#2748](https://github.com/drone/drone/issues/2748). - -## [1.2.2] - 2019-07-29 -### Added - -- support for legacy environment variables -- support for legacy workspace based on repository name -- support for github deployment hooks -- provide base sha for github pull requests -- option to filter webhooks by event and type -- upgrade drone-yaml to v1.2.2 -- upgrade drone-runtime to v1.0.7 - -### Fixed - -- error when manually creating an empty user, by [@bradrydzewski](https://github.com/bradrydzewski). [#2738](https://github.com/drone/drone/issues/2738). - -## [1.2.1] - 2019-06-11 -### Added - -- support for legacy tokens to ease upgrade path, by [@bradrydzewski](https://github.com/bradrydzewski). [#2713](https://github.com/drone/drone/issues/2713). -- include repository name and id in batch update error message, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Fixed - -- fix inconsistent base64 encoding and decoding of encrypted secrets, by [@bradrydzewski](https://github.com/bradrydzewski). -- update drone-yaml to version 1.1.2 for improved 0.8 to 1.0 yaml marshal escaping. -- update drone-yaml to version 1.1.3 for improved 0.8 to 1.0 workspace conversion. - -## [1.2.0] - 2019-05-30 -### Added - -- endpoint to trigger new build for default branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679). -- endpoint to trigger new build for branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679). -- endpoint to trigger new build for branch and sha, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679). -- enable optional prometheus metrics guest access, by [@janberktold](https://github.com/janberktold) -- fallback to database when logs not found in s3, by [@bradrydzewski](https://github.com/bradrydzewski). [#2689](https://github.com/drone/drone/issues/2689). -- support for custom stage definitions and runners, by [@bradrydzewski](https://github.com/bradrydzewski). [#2680](https://github.com/drone/drone/issues/2680). -- update drone-yaml to version 1.1.0 - -### Fixed - -- retrieve latest build by branch, by [@tboerger](https://github.com/tboerger). -- copy the fork value when restarting a build, by [@bradrydzewski](https://github.com/bradrydzewski). [#2708](https://github.com/drone/drone/issues/2708). -- make healthz available without redirect, by [@bradrydzewski](https://github.com/bradrydzewski). [#2706](https://github.com/drone/drone/issues/2706). - -## [1.1.0] - 2019-04-23 -### Added - -- specify a user for the pipeline step, by [@bradrydzewski](https://github.com/bradrydzewski). [#2651](https://github.com/drone/drone/issues/2651). -- support for Gitea oauth2, by [@techknowlogick](https://github.com/techknowlogick). [#2622](https://github.com/drone/drone/pull/2622). -- ping the docker daemon before starting the agent, by [@bradrydzewski](https://github.com/bradrydzewski). [#2495](https://github.com/drone/drone/issues/2495). -- support for Cron job name in Yaml trigger block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628). -- support for Cron job name in Yaml when block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628). -- sqlite username column changed to case-insensitive, by [@bradrydzewski](https://github.com/bradrydzewski). -- endpoint to purge repository from database, by [@bradrydzewski](https://github.com/bradrydzewski). -- support for per-organization secrets, by [@bradrydzewski](https://github.com/bradrydzewski). -- include system metadata in global webhooks, by [@bradrydzewski](https://github.com/bradrydzewski). -- ability to customize cookie secure flag, by [@bradrydzewski](https://github.com/bradrydzewski). -- update drone-yaml from version 1.0.6 to 1.0.8. -- update drone-runtime from version 1.0.4 to 1.0.6. -- update go-scm from version 1.0.3 to 1.0.4. - -### Fixed - -- fixed error in mysql table creation syntax, from [@xuyang2](https://github.com/xuyang2). [#2677](https://github.com/drone/drone/pull/2677). -- fixed stuck builds when upstream dependency is skipped, from [@bradrydzewski](https://github.com/bradrydzewski). [#2634](https://github.com/drone/drone/issues/2634). -- fixed issue running steps with dependencies on failure, from [@bradrydzewski](https://github.com/bradrydzewski). [#2667](https://github.com/drone/drone/issues/2667). - -## [1.0.1] - 2019-04-10 -### Added - -- pass stage environment variables to pipeline steps, by [@bradrydzewski](https://github.com/bradrydzewski). -- update go-scm to version 1.3.0, by [@bradrydzewski](https://github.com/bradrydzewski). -- update drone-runtime to version to 1.0.4, by [@bradrydzewski](https://github.com/bradrydzewski). -- ping docker daemon before agent starts to ensure connectivity, by [@bradrydzewski](https://github.com/bradrydzewski). - - -\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/HISTORY.md b/HISTORY.md deleted file mode 100644 index 8f2bf2af83..0000000000 --- a/HISTORY.md +++ /dev/null @@ -1,326 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## Unreleased - -## [2.0.4] -### Fixed -- DRON-97 remove use of request animation frame to prevent high CPU on tab refocus events. - -## [2.0.3] -### Fixed -- DONE-91 handle extra slashes in url. [#3009](https://github.com/drone/drone/pull/3099). - -## [2.0.2] -### Added -- Merge remote-tracking branch 'origin/master' -- prevent repository list short circuit in UI -- remove deprecated steps from building file [#3097](https://github.com/drone/drone/pull/3097) -- adding depends_on, image and detached fields to step [#3072](https://github.com/drone/drone/pull/3072) -- Add ctx.build.debug boolean [#3082](https://github.com/drone/drone/pull/3082) -- Bump github.com/google/go-jsonnet to v0.17.0 [#3084](https://github.com/drone/drone/pull/3084) -- bump go-scm v1.15.1 [#3096](https://github.com/drone/drone/pull/3096) -- bitbucket server build issue [#3092](https://github.com/drone/drone/pull/3092) -- update scm version [#3091](https://github.com/drone/drone/pull/3091) -- Limit graceful shutdown duration [#3093](https://github.com/drone/drone/pull/3093) -- bump user interface -- bump ui version -- ignore skip directive for promote and rollback events -- new feature: maximum open DB connections is configurable[#3089](https://github.com/drone/drone/pull/3089) -- jsonnet additional parameters [#3087](https://github.com/drone/drone/pull/3087) -- hide login button if user already authenticated -- new feature: configuration templates [#3081](https://github.com/drone/drone/pull/3081) - -### Fixed -- various typos [#3088](https://github.com/drone/drone/pull/3088) -- handle error properly if template doesn't exist [#3095](https://github.com/drone/drone/pull/3093) -- oss build issue [#3086](https://github.com/drone/drone/pull/3086) -- graceful shutdown [#3083](https://github.com/drone/drone/pull/3083) - -## [2.0.1] -### Added -- support for configuring the internal yaml cache size. - -## [2.0.0] -### Added -- feature flags for mixed-mode database encryption. - -### Changed -- user-interface re-design - -### Breaking -- removed deprecated kubernetes integration in favor of official kubernetes runner. -- removed deprecated nomad integration in favor of official nomad runner. - -## [1.10.1] -### Added -- support for repository-level concurrency limits. -- support for gitlab and github internal visibility on initial sync. - -### Fixed -- create machine user with a custom API token. - -## [1.10.0] -### Added -- support for starlark scripts in core. -- support for executing pipelines in debug mode. - -## [1.9.2] -### Added -- update go-scm dependency to fix - -## [1.9.1] -### Added -- support for increasing the http request timeout for extensions. [#2998](https://github.com/drone/drone/pull/2998). -- support for skipping a pipeline if the validation extension returns an ErrSkip. -- support for blocking a pipeline if the validation extension returns an ErrBlock. - -### Fixed -- rollback endpoint should be available to users with write permission. -- retrying a build should re-use custom build parameters from parent build. - -## [1.9.0] - 2020-07-12 -### Added -- ui support for deployment list and summary. -- ui support for promoting and rolling back builds. -- feature flag to use static secret when signing webhooks, from @chiraggadasc. - -### Fixed -- ui branch list improperly capped. - -### Changed -- upgrade drone/envsubst dependency -- upgrade drone/go-scm dependency - -## [1.8.1] - 2020-06-23 -### Fixed -- support for gitea api pagination, repository sync hanging. - -## [1.8.0] - 2020-06-10 -### Added -- re-assigned repository ownership when deactivating a user. -- re-assigned repository ownership when deleting a user. -- de-activate a repository when deleting a user if re-assignment fails. -- de-activate a repository when deactivating a user if re-assignment fails. -- routine to cleanup builds stuck in a pending state. -- routine to cleanup builds stuck in a running state. -- private mode setting requires authentication to view public repositories. - -### Fixed -- canceling a build emits a sql.ErrNoRows error. -- custom token is ignored when creating a user account via the API. -- machine accounts with sufficient permissions can create builds via the API. - -### Changed -- upgraded Go toolchain to version 1.14.4. - -## [1.7.0] - 2020-03-27 -### Added -- endpoint to display the latest build by branch. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to display the latest build by pull request. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to display the latest build by environment. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to delete a branch from the index. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to delete a pull request from the index. [#2940](https://github.com/drone/drone/pull/2940). -- endpoint to delete an environment from the index. [#2940](https://github.com/drone/drone/pull/2940). -- page to view the latest build per branch. - -### Fixed -- sync routine not executing asynchronously, being cancelled by http context. -- sync routine should ignore gitlab subrepositories -- convert deploy events in 0.8 yaml to promote events. -- do not execute cron job for disabled repositories. [#2931](https://github.com/drone/drone/issues/2931). -- remove trailing slash from gitea url to prevent oauth2 token refresh errors, by [@cmj0121](https://github.com/cmj0121). [#2920](https://github.com/drone/drone/issues/2920). -- disable font ligatures in build log output. [drone/drone-ui#322](https://github.com/drone/drone-ui/pull/322). -- missing am/pm in timestamps - -## [1.6.5] - 2020-01-29 -### Changed -- update version of go-scm -- update alpine version in docker images -- use ticker for cron jobs for more accurate timing - -## [1.6.4] - 2019-12-30 -### Added -- optionally enable pprof endpoints for profiling, by [@bradrydzewski](https://github.com/bradrydzewski). - -## [1.6.3] - 2019-12-10 -### Fixed -- disable caching generated yaml files by commit sha, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Added -- support for bitbucket skipverify, by [@toni-moreno](https://github.com/toni-moreno). -- support for gitea skipverify, by [@toni-moreno](https://github.com/toni-moreno). - -## [1.6.2] - 2019-11-08 -### Added -- support for loading license contents from env, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Fixed -- regression not converting legacy pipeline when using new runners, by [@bradrydzewski](https://github.com/bradrydzewski). - -## [1.6.1] - 2019-10-17 -### Added -- updated autocert library in support of acme v2 protocol, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Fixed -- fixed nil pointer when manually adding user from api, by [@bradrydzewski](https://github.com/bradrydzewski). - -## [1.6.0] - 2019-10-04 -### Added -- added nsswitch to docker images -- option to auto-cancel pending builds when newer build enqueued, by [@bradrydzewski](https://github.com/bradrydzewski). [#1980](https://github.com/drone/drone/issues/1980). -- endpoint to list all repositories in the database, by [@bradrydzewski](https://github.com/bradrydzewski). [#2785](https://github.com/drone/drone/issues/2785). - -### Fixed -- improve sync to handle duplicate repository names with different unique identifiers, by [@bradrydzewski](https://github.com/bradrydzewski). [#2658](https://github.com/drone/drone/issues/2658). _You can revert to the previous sync logic with DRONE_DATABASE_LEGACY_BATCH=true_. - -## [1.5.1] - 2019-09-30 -### Added -- allow organization admins access to organization secret endpoints, by [@bradrydzewski](https://github.com/bradrydzewski). [#2838](https://github.com/drone/drone/issues/2838). - -### Fixed -- fix invalid deep links in UI for github enterprise, by [@bradrydzewski](https://github.com/bradrydzewski). -- ensure correct casing when manually adding user, by [@bradrydzewski](https://github.com/bradrydzewski). [#2766](https://github.com/drone/drone/issues/2766). - -## [1.5.0] - 2019-09-28 -### Added -- endpoint to execute a cron pipeline on-demand, by [@bradrydzewski](https://github.com/bradrydzewski). [#2781](https://github.com/drone/drone/issues/2781). -- endpoint to list builds by branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#1495](https://github.com/drone/drone/issues/1495). -- ignore skip comments when cron event, by [@bradrydzewski](https://github.com/bradrydzewski). [#2835](https://github.com/drone/drone/issues/2835). -- support for admission extensions, by [@bradrydzewski](https://github.com/bradrydzewski). [#2043](https://github.com/drone/drone/issues/2043). -- endpoint to provide link to git resources, by [@bradrydzewski](https://github.com/bradrydzewski). [#2843](https://github.com/drone/drone/issues/2843). -- improve bitbucket status display text on new pull request screen, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Fixed -- missing cron job name in user interface, by [@bradrydzewski](https://github.com/bradrydzewski). -- log lines not properly wrapping in user interface, by [@bradrydzewski](https://github.com/bradrydzewski). -[#309](https://github.com/drone/drone-ui/issues/309). - -### Breaking -- the server now runs in multi-machine mode by default. In order to run the server in single-machine mode (agents disabled) you must set DRONE_AGENTS_DISABLED=true. - -## [1.4.0] - 2019-09-12 -### Added -- upgrade to Go 1.13 to resolve arm segfault, by [@KN4CK3R](https://github.com/KN4CK3R). [#2823](https://github.com/drone/drone/issues/2823). -- configure default visibility, by [@JordanSussman](https://github.com/JordanSussman). [#2824](https://github.com/drone/drone/issues/2824). -- configure default trusted flag, by [@vyckou](https://github.com/vyckou). -- support for validation plugins, by [@bradrydzewski](https://github.com/bradrydzewski). [#2266](https://github.com/drone/drone/issues/2266). -- support for conversion plugins, by [@bradrydzewski](https://github.com/bradrydzewski). -- support for cron event type, by [@bradrydzewski](https://github.com/bradrydzewski). [#2705](https://github.com/drone/drone/issues/2705). -- support for rollback event, by [@bradrydzewski](https://github.com/bradrydzewski). [#2695](https://github.com/drone/drone/issues/2695). -- support for lets encrypt email, by [@bradrydzewski](https://github.com/bradrydzewski). [#2505](https://github.com/drone/drone/issues/2505). - -### Removed -- Support for basic auth as an option for Gitea, by [@techknowlogick](https://giteahub.com/techknowlogick). [#2721](https://github.com/drone/drone/issues/2721) - -### Fixed -- copy cron job name when restarting a cron job, by [@bradrydzewski](https://github.com/bradrydzewski). [#2760](https://github.com/drone/drone/issues/2760). - -## [1.3.1] - 2019-08-26 -### Added -- support for the GitHub deployment status API, by [@bradrydzewski](https://github.com/bradrydzewski). - -## [1.3.0] - 2019-08-20 -### Added -- support for storing logs in Azure Cloud Storage, by [@Lucretius](https://github.com/Lucretius). [#2788](https://github.com/drone/drone/pull/2788) -- support for windows server 1903, by [@bradrydzewski](https://github.com/bradrydzewski). -- button to view the full log file, by [@dramich](https://github.com/dramich). [drone/drone-ui#287](https://github.com/drone/drone-ui/pull/287). - -### Fixed -- read gogs sha from webhook, by [@marcotuna](https://github.com/marcotuna). -- create bind volume on host if not exists, by [@bradrydzewski](https://github.com/bradrydzewski). [#2725](https://github.com/drone/drone/issues/2725). -- preserve whitespace in build logs, by [@geek1011](https://github.com/geek1011). [drone/drone-ui#294](https://github.com/drone/drone-ui/pull/294). -- enable log file download on firefox, by [@bobmanary](https://github.com/bobmanary). [drone/drone-ui#303](https://github.com/drone/drone-ui/pull/303) - -### Security -- upgraded to Go 1.12.9 due to CVE-2019-9512 and CVE-2019-9514 - -## [1.2.3] - 2019-07-30 -### Added - -- disable github status for cron jobs -- support for action in conditionals, by [@bradrydzewski](https://github.com/bradrydzewski). [#2685](https://github.com/drone/drone/issues/2685). - -### Fixed - -- improve cancel logic for dangling stages, by [@bradrydzewski](https://github.com/bradrydzewski). -- improve error when kubernetes malforms the port configuration, by [@bradrydzewski](https://github.com/bradrydzewski). [#2742](https://github.com/drone/drone/issues/2742). -- copy parameters from parent build when promoting, by [@bradrydzewski](https://github.com/bradrydzewski). [#2748](https://github.com/drone/drone/issues/2748). - -## [1.2.2] - 2019-07-29 -### Added - -- support for legacy environment variables -- support for legacy workspace based on repository name -- support for github deployment hooks -- provide base sha for github pull requests -- option to filter webhooks by event and type -- upgrade drone-yaml to v1.2.2 -- upgrade drone-runtime to v1.0.7 - -### Fixed - -- error when manually creating an empty user, by [@bradrydzewski](https://github.com/bradrydzewski). [#2738](https://github.com/drone/drone/issues/2738). - -## [1.2.1] - 2019-06-11 -### Added - -- support for legacy tokens to ease upgrade path, by [@bradrydzewski](https://github.com/bradrydzewski). [#2713](https://github.com/drone/drone/issues/2713). -- include repository name and id in batch update error message, by [@bradrydzewski](https://github.com/bradrydzewski). - -### Fixed - -- fix inconsistent base64 encoding and decoding of encrypted secrets, by [@bradrydzewski](https://github.com/bradrydzewski). -- update drone-yaml to version 1.1.2 for improved 0.8 to 1.0 yaml marshal escaping. -- update drone-yaml to version 1.1.3 for improved 0.8 to 1.0 workspace conversion. - -## [1.2.0] - 2019-05-30 -### Added - -- endpoint to trigger new build for default branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679). -- endpoint to trigger new build for branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679). -- endpoint to trigger new build for branch and sha, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679). -- enable optional prometheus metrics guest access, by [@janberktold](https://github.com/janberktold) -- fallback to database when logs not found in s3, by [@bradrydzewski](https://github.com/bradrydzewski). [#2689](https://github.com/drone/drone/issues/2689). -- support for custom stage definitions and runners, by [@bradrydzewski](https://github.com/bradrydzewski). [#2680](https://github.com/drone/drone/issues/2680). -- update drone-yaml to version 1.1.0 - -### Fixed - -- retrieve latest build by branch, by [@tboerger](https://github.com/tboerger). -- copy the fork value when restarting a build, by [@bradrydzewski](https://github.com/bradrydzewski). [#2708](https://github.com/drone/drone/issues/2708). -- make healthz available without redirect, by [@bradrydzewski](https://github.com/bradrydzewski). [#2706](https://github.com/drone/drone/issues/2706). - -## [1.1.0] - 2019-04-23 -### Added - -- specify a user for the pipeline step, by [@bradrydzewski](https://github.com/bradrydzewski). [#2651](https://github.com/drone/drone/issues/2651). -- support for Gitea oauth2, by [@techknowlogick](https://github.com/techknowlogick). [#2622](https://github.com/drone/drone/pull/2622). -- ping the docker daemon before starting the agent, by [@bradrydzewski](https://github.com/bradrydzewski). [#2495](https://github.com/drone/drone/issues/2495). -- support for Cron job name in Yaml trigger block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628). -- support for Cron job name in Yaml when block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628). -- sqlite username column changed to case-insensitive, by [@bradrydzewski](https://github.com/bradrydzewski). -- endpoint to purge repository from database, by [@bradrydzewski](https://github.com/bradrydzewski). -- support for per-organization secrets, by [@bradrydzewski](https://github.com/bradrydzewski). -- include system metadata in global webhooks, by [@bradrydzewski](https://github.com/bradrydzewski). -- ability to customize cookie secure flag, by [@bradrydzewski](https://github.com/bradrydzewski). -- update drone-yaml from version 1.0.6 to 1.0.8. -- update drone-runtime from version 1.0.4 to 1.0.6. -- update go-scm from version 1.0.3 to 1.0.4. - -### Fixed - -- fixed error in mysql table creation syntax, from [@xuyang2](https://github.com/xuyang2). [#2677](https://github.com/drone/drone/pull/2677). -- fixed stuck builds when upstream dependency is skipped, from [@bradrydzewski](https://github.com/bradrydzewski). [#2634](https://github.com/drone/drone/issues/2634). -- fixed issue running steps with dependencies on failure, from [@bradrydzewski](https://github.com/bradrydzewski). [#2667](https://github.com/drone/drone/issues/2667). - -## [1.0.1] - 2019-04-10 -### Added - -- pass stage environment variables to pipeline steps, by [@bradrydzewski](https://github.com/bradrydzewski). -- update go-scm to version 1.3.0, by [@bradrydzewski](https://github.com/bradrydzewski). -- update drone-runtime to version to 1.0.4, by [@bradrydzewski](https://github.com/bradrydzewski). -- ping docker daemon before agent starts to ensure connectivity, by [@bradrydzewski](https://github.com/bradrydzewski). diff --git a/LICENSE b/LICENSE deleted file mode 100644 index ce08ee8a65..0000000000 --- a/LICENSE +++ /dev/null @@ -1,89 +0,0 @@ -Copyright 2019 Drone.IO, Inc. - -The Drone Community Edition is licensed under the Apache License, -Version 2.0 (the "Apache License"). You may obtain a copy of the -Apache License at - - http://www.apache.org/licenses/LICENSE-2.0 - -The Drone Enterprise Edition is licensed under the Drone -Non-Commercial License (the "Non-Commercial License"). A copy of -the Non-Commercial License is provided below. - -The source files in this repository have a header indicating -which license they are under. The BUILDING_OSS file provides -instructions for creating the Community Edition distribution -subject to the terms of the Apache License. - ------------------------------------------------------------------ - -Drone Non-Commercial License - -Contributor: Drone.IO, Inc. - -Source Code: https://github.com/harness/drone - -This license lets you use and share this software for free, -with a trial-length time limit on commercial use. Specifically: - -If you follow the rules below, you may do everything with this -software that would otherwise infringe either the contributor's -copyright in it, any patent claim the contributor can license -that covers this software as of the contributor's latest -contribution, or both. - -1. You must limit use of this software in any manner primarily - intended for or directed toward commercial advantage or - private monetary compensation to a trial period of 32 - consecutive calendar days. This limit does not apply to use in - developing feedback, modifications, or extensions that you - contribute back to those giving this license. - -2. Ensure everyone who gets a copy of this software from you, in - source code or any other form, gets the text of this license - and the contributor and source code lines above. - -3. Do not make any legal claim against anyone for infringing any - patent claim they would infringe by using this software alone, - accusing this software, with or without changes, alone or as - part of a larger application. - -You are excused for unknowingly breaking rule 1 if you stop -doing anything requiring this license within 30 days of -learning you broke the rule. - -**This software comes as is, without any warranty at all. As far -as the law allows, the contributor will not be liable for any -damages related to this software or this license, for any kind of -legal claim.** - ------------------------------------------------------------------ - -Waiver: Individual and Small Business - -Contributor waives the terms of rule 1 for companies meeting all -the following criteria, counting all subsidiaries and affiliated -entities as one: - -1. worldwide annual gross revenue under $5 million US dollars, - per generally accepted accounting principles - -2. less than $5 million US dollars in all-time aggregate debt and - equity financing - -Contributor will not revoke this waiver, but may change terms for -future versions of the software. - ------------------------------------------------------------------ - -Waiver: Low Usage - -Contributor waives the terms of rule 1 for companies meeting all -the following criteria, counting all subsidiaries and affiliated -entities as one: - -1. less than 5,000 total pipelines executed using this software - in the immediately preceding, year-long period - -Contributor will not revoke this waiver, but may change terms for -future versions of the software. diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 748c8350ce..0000000000 --- a/NOTICE +++ /dev/null @@ -1,14 +0,0 @@ -Drone -Copyright 2019 Drone.IO, Inc - -This product includes software developed at Drone.IO, Inc. -(http://drone.io/). - -This product includes software developed by Docker, Inc. -(https://www.docker.com/). - -This product includes software developed by Canonical Ltd. -(https://www.canonical.com/). - -This product includes software developed at CoreOS, Inc. -(http://www.coreos.com/). diff --git a/README.md b/README.md new file mode 100644 index 0000000000..c9616c3e4c --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# What is Gitness and where is Drone +Gitness is our new open source development platform packed with the power of code hosting and automated CI pipelines. + +Gitness represents a massive investment in the next generation of Drone, with the goal to bring code management and pipelines closer together.\ +The idea is for Gitness to eventually be at full parity with Drone in terms of pipeline capabilities, allowing users to seemlessly migrate from drone to gitness. +But, we expect this to take some time, which is why we took a snapshot of Drone as a feature branch [drone](https://github.com/harness/gitness/tree/drone) ([README](https://github.com/harness/gitness/blob/drone/.github/readme.md)) so it can continue development. As for Gitness, the development is taking place on the [main](https://github.com/harness/gitness/tree/main) branch. + +For more information on Gitness, please visit [gitness.com](https://gitness.com/)\ +For more information on Drone, please visit [drone.io](https://www.drone.io/) + +## As a Drone customer, do I have to migrate to gitness? +No. You can continue to use Drone. Gitness is still in its early stages, but long term it will have all the Drone pipeline features. + +Gitness will also continue to integrate with GitHub, GitLab, Gitea etc. Nobody will be forced to use Gitness code hosting to use the Pipeline portion of the software. diff --git a/Taskfile.yml b/Taskfile.yml deleted file mode 100644 index 858f034c3c..0000000000 --- a/Taskfile.yml +++ /dev/null @@ -1,129 +0,0 @@ -# https://taskfile.org - -version: '2' - -tasks: - install: - dir: cmd/drone-server - cmds: [ go install -v ] - env: - GO111MODULE: on - - build: - cmds: - - task: build-base - vars: { name: server } - - build-base: - env: - GOOS: linux - GOARCH: amd64 - CGO_ENABLED: '0' - GO111MODULE: 'on' - cmds: - - cmd: > - go build -o release/linux/amd64/drone-{{.name}} - github.com/drone/drone/cmd/drone-{{.name}} - - cleanup: - cmds: - - rm -rf release - - docker: - cmds: - - task: docker-base - vars: { name: server, image: drone/drone } - - docker-base: - vars: - GIT_BRANCH: - sh: git rev-parse --abbrev-ref HEAD - cmds: - - cmd: docker rmi {{.image}} - ignore_error: true - - cmd: docker rmi {{.image}}:{{.GIT_BRANCH}} - ignore_error: true - - cmd: > - docker build --rm - -f docker/Dockerfile.{{.name}}.linux.amd64 - -t {{.image}} . - - cmd: > - docker tag {{.image}} {{.image}}:{{.GIT_BRANCH}} - - test: - cmds: - - go test ./... - env: - GO111MODULE: 'on' - - test-mysql: - env: - DRONE_DATABASE_DRIVER: mysql - DRONE_DATABASE_DATASOURCE: root@tcp(localhost:3306)/test?parseTime=true - GO111MODULE: 'on' - cmds: - - cmd: docker kill mysql - silent: true - ignore_error: true - - cmd: > - docker run - -p 3306:3306 - --env MYSQL_DATABASE=test - --env MYSQL_ALLOW_EMPTY_PASSWORD=yes - --name mysql - --detach - --rm - mysql:5.7 - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - - cmd: go test -count=1 github.com/drone/drone/store/batch - - cmd: go test -count=1 github.com/drone/drone/store/batch2 - - cmd: go test -count=1 github.com/drone/drone/store/build - - cmd: go test -count=1 github.com/drone/drone/store/card - - cmd: go test -count=1 github.com/drone/drone/store/cron - - cmd: go test -count=1 github.com/drone/drone/store/logs - - cmd: go test -count=1 github.com/drone/drone/store/perm - - cmd: go test -count=1 github.com/drone/drone/store/repos - - cmd: go test -count=1 github.com/drone/drone/store/secret - - cmd: go test -count=1 github.com/drone/drone/store/secret/global - - cmd: go test -count=1 github.com/drone/drone/store/stage - - cmd: go test -count=1 github.com/drone/drone/store/step - - cmd: go test -count=1 github.com/drone/drone/store/template - - cmd: go test -count=1 github.com/drone/drone/store/user - - cmd: docker kill mysql - - test-postgres: - env: - DRONE_DATABASE_DRIVER: postgres - DRONE_DATABASE_DATASOURCE: host=localhost user=postgres password=postgres dbname=postgres sslmode=disable - GO111MODULE: 'on' - cmds: - - cmd: docker kill postgres - ignore_error: true - silent: false - - silent: false - cmd: > - docker run - -p 5432:5432 - --env POSTGRES_PASSWORD=postgres - --env POSTGRES_USER=postgres - --name postgres - --detach - --rm - postgres:9-alpine - - cmd: go test -count=1 github.com/drone/drone/store/batch - - cmd: go test -count=1 github.com/drone/drone/store/batch2 - - cmd: go test -count=1 github.com/drone/drone/store/build - - cmd: go test -count=1 github.com/drone/drone/store/card - - cmd: go test -count=1 github.com/drone/drone/store/cron - - cmd: go test -count=1 github.com/drone/drone/store/logs - - cmd: go test -count=1 github.com/drone/drone/store/perm - - cmd: go test -count=1 github.com/drone/drone/store/repos - - cmd: go test -count=1 github.com/drone/drone/store/secret - - cmd: go test -count=1 github.com/drone/drone/store/secret/global - - cmd: go test -count=1 github.com/drone/drone/store/stage - - cmd: go test -count=1 github.com/drone/drone/store/step - - cmd: go test -count=1 github.com/drone/drone/store/template - - cmd: go test -count=1 github.com/drone/drone/store/user - - cmd: docker kill postgres - silent: true diff --git a/cmd/drone-server/bootstrap/bootstrap.go b/cmd/drone-server/bootstrap/bootstrap.go deleted file mode 100644 index bfcdc3bcef..0000000000 --- a/cmd/drone-server/bootstrap/bootstrap.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bootstrap - -import ( - "context" - "errors" - "time" - - "github.com/dchest/uniuri" - "github.com/drone/drone/core" - "github.com/drone/drone/logger" - - "github.com/sirupsen/logrus" -) - -var errMissingToken = errors.New("You must provide the machine account token") - -// New returns a new account bootstrapper. -func New(users core.UserStore) *Bootstrapper { - return &Bootstrapper{ - users: users, - } -} - -// Bootstrapper bootstraps the system with the initial account. -type Bootstrapper struct { - users core.UserStore -} - -// Bootstrap creates the user account. If the account already exists, -// no account is created, and a nil error is returned. -func (b *Bootstrapper) Bootstrap(ctx context.Context, user *core.User) error { - if user.Login == "" { - return nil - } - - log := logrus.WithFields( - logrus.Fields{ - "login": user.Login, - "admin": user.Admin, - "machine": user.Machine, - "token": user.Hash, - }, - ) - - log.Debugln("bootstrap: create account") - - existingUser, err := b.users.FindLogin(ctx, user.Login) - if err == nil { - ctx = logger.WithContext(ctx, log) - return b.update(ctx, user, existingUser) - } - - if user.Machine && user.Hash == "" { - log.Errorln("bootstrap: cannot create account, missing token") - return errMissingToken - } - - user.Active = true - user.Created = time.Now().Unix() - user.Updated = time.Now().Unix() - if user.Hash == "" { - user.Hash = uniuri.NewLen(32) - } - - err = b.users.Create(ctx, user) - if err != nil { - log = log.WithError(err) - log.Errorln("bootstrap: cannot create account") - return err - } - - log = log.WithField("token", user.Hash) - log.Infoln("bootstrap: account created") - return nil -} - -func (b *Bootstrapper) update(ctx context.Context, src, dst *core.User) error { - log := logger.FromContext(ctx) - log.Debugln("bootstrap: updating account") - var updated bool - if src.Hash != dst.Hash && src.Hash != "" { - log.Infoln("bootstrap: found updated user token") - dst.Hash = src.Hash - updated = true - } - if src.Machine != dst.Machine { - log.Infoln("bootstrap: found updated machine flag") - dst.Machine = src.Machine - updated = true - } - if src.Admin != dst.Admin { - log.Infoln("bootstrap: found updated admin flag") - dst.Admin = src.Admin - updated = true - } - if !updated { - log.Debugln("bootstrap: account already up-to-date") - return nil - } - dst.Updated = time.Now().Unix() - err := b.users.Update(ctx, dst) - if err != nil { - log = log.WithError(err) - log.Errorln("bootstrap: cannot update account") - return err - } - log.Infoln("bootstrap: account successfully updated") - return nil -} diff --git a/cmd/drone-server/bootstrap/bootstrap_test.go b/cmd/drone-server/bootstrap/bootstrap_test.go deleted file mode 100644 index 02dfb7b685..0000000000 --- a/cmd/drone-server/bootstrap/bootstrap_test.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package bootstrap - -import ( - "context" - "database/sql" - "io/ioutil" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/dchest/uniuri" - "github.com/golang/mock/gomock" - "github.com/sirupsen/logrus" -) - -var noContext = context.TODO() - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -func TestBootstrap(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - Machine: true, - Admin: true, - Hash: uniuri.NewLen(32), - } - - store := mock.NewMockUserStore(controller) - store.EXPECT().FindLogin(gomock.Any(), dummyUser.Login).Return(nil, sql.ErrNoRows) - store.EXPECT().Create(gomock.Any(), dummyUser).Return(nil) - - err := New(store).Bootstrap(noContext, dummyUser) - if err != nil { - t.Error(err) - } -} - -func TestBootstrap_GenerateHash(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - Machine: false, - Admin: true, - Hash: "", - } - - store := mock.NewMockUserStore(controller) - store.EXPECT().FindLogin(gomock.Any(), dummyUser.Login).Return(nil, sql.ErrNoRows) - store.EXPECT().Create(gomock.Any(), dummyUser).Return(nil) - - err := New(store).Bootstrap(noContext, dummyUser) - if err != nil { - t.Error(err) - } - if got, want := len(dummyUser.Hash), 32; got != want { - t.Errorf("Want generated hash length %d, got %d", want, got) - } -} - -func TestBootstrap_Empty(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "", - } - - store := mock.NewMockUserStore(controller) - err := New(store).Bootstrap(noContext, dummyUser) - if err != nil { - t.Error(err) - } -} - -func TestBootstrap_Exists_WithoutUpdates(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - Machine: true, - Admin: true, - Hash: uniuri.NewLen(32), - } - - store := mock.NewMockUserStore(controller) - store.EXPECT().FindLogin(gomock.Any(), dummyUser.Login).Return(dummyUser, nil) - err := New(store).Bootstrap(noContext, dummyUser) - if err != nil { - t.Error(err) - } -} - -func TestBootstrap_Exists_WithUpdates(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - Machine: true, - Admin: true, - Hash: uniuri.NewLen(32), - } - existingUser := &core.User{ - Login: "octocat", - Machine: false, - Admin: false, - Hash: uniuri.NewLen(32), - } - - store := mock.NewMockUserStore(controller) - store.EXPECT().FindLogin(gomock.Any(), dummyUser.Login).Return(existingUser, nil) - store.EXPECT().Update(gomock.Any(), existingUser).Return(nil) - err := New(store).Bootstrap(noContext, dummyUser) - if err != nil { - t.Error(err) - } -} - -func TestBootstrap_MissingTokenError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - Machine: true, - Admin: true, - } - - store := mock.NewMockUserStore(controller) - store.EXPECT().FindLogin(gomock.Any(), dummyUser.Login).Return(nil, sql.ErrNoRows) - - err := New(store).Bootstrap(noContext, dummyUser) - if err != errMissingToken { - t.Errorf("Expect missing token error") - } -} - -func TestBootstrap_CreateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - Machine: true, - Admin: true, - Hash: uniuri.NewLen(32), - } - - store := mock.NewMockUserStore(controller) - store.EXPECT().FindLogin(gomock.Any(), dummyUser.Login).Return(nil, sql.ErrNoRows) - store.EXPECT().Create(gomock.Any(), dummyUser).Return(sql.ErrConnDone) - - err := New(store).Bootstrap(noContext, dummyUser) - if err != sql.ErrConnDone { - t.Errorf("Expect error creating user") - } -} diff --git a/cmd/drone-server/config/config.go b/cmd/drone-server/config/config.go deleted file mode 100644 index 36b978a645..0000000000 --- a/cmd/drone-server/config/config.go +++ /dev/null @@ -1,644 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "errors" - "fmt" - "os" - "strings" - "time" - - "github.com/dchest/uniuri" - "github.com/dustin/go-humanize" - "github.com/kelseyhightower/envconfig" - "gopkg.in/yaml.v2" -) - -// IMPORTANT please do not add new configuration parameters unless it has -// been discussed on the mailing list. We are attempting to reduce the -// number of configuration parameters, and may reject pull requests that -// introduce new parameters. (mailing list https://community.harness.io) - -// default runner hostname. -var hostname string - -func init() { - hostname, _ = os.Hostname() - if hostname == "" { - hostname = "localhost" - } -} - -type ( - // Config provides the system configuration. - Config struct { - License string `envconfig:"DRONE_LICENSE"` - - Authn Authentication - Agent Agent - AzureBlob AzureBlob - Convert Convert - Cleanup Cleanup - Cron Cron - Cloning Cloning - Database Database - Datadog Datadog - Docker Docker - HTTP HTTP - Jsonnet Jsonnet - Starlark Starlark - Logging Logging - Prometheus Prometheus - Proxy Proxy - Redis Redis - Registration Registration - Registries Registries - Repository Repository - Runner Runner - RPC RPC - S3 S3 - Secrets Secrets - Server Server - Session Session - Status Status - Users Users - Validate Validate - Webhook Webhook - Yaml Yaml - - // Remote configurations - Bitbucket Bitbucket - Gitea Gitea - Github Github - GitLab GitLab - Gogs Gogs - Stash Stash - Gitee Gitee - } - - // Cloning provides the cloning configuration. - Cloning struct { - AlwaysAuth bool `envconfig:"DRONE_GIT_ALWAYS_AUTH"` - Username string `envconfig:"DRONE_GIT_USERNAME"` - Password string `envconfig:"DRONE_GIT_PASSWORD"` - Image string `envconfig:"DRONE_GIT_IMAGE"` - Pull string `envconfig:"DRONE_GIT_IMAGE_PULL" default:"IfNotExists"` - } - - Cleanup struct { - Disabled bool `envconfig:"DRONE_CLEANUP_DISABLED"` - Interval time.Duration `envconfig:"DRONE_CLEANUP_INTERVAL" default:"24h"` - Running time.Duration `envconfig:"DRONE_CLEANUP_DEADLINE_RUNNING" default:"24h"` - Pending time.Duration `envconfig:"DRONE_CLEANUP_DEADLINE_PENDING" default:"24h"` - Buffer time.Duration `envconfig:"DRONE_CLEANUP_BUFFER" default:"30m"` - } - - // Cron provides the cron configuration. - Cron struct { - Disabled bool `envconfig:"DRONE_CRON_DISABLED"` - Interval time.Duration `envconfig:"DRONE_CRON_INTERVAL" default:"30m"` - } - - // Database provides the database configuration. - Database struct { - Driver string `envconfig:"DRONE_DATABASE_DRIVER" default:"sqlite3"` - Datasource string `envconfig:"DRONE_DATABASE_DATASOURCE" default:"core.sqlite"` - Secret string `envconfig:"DRONE_DATABASE_SECRET"` - MaxConnections int `envconfig:"DRONE_DATABASE_MAX_CONNECTIONS" default:"0"` - - // Feature flag - LegacyBatch bool `envconfig:"DRONE_DATABASE_LEGACY_BATCH"` - - // Feature flag - EncryptUserTable bool `envconfig:"DRONE_DATABASE_ENCRYPT_USER_TABLE"` - EncryptMixedContent bool `envconfig:"DRONE_DATABASE_ENCRYPT_MIXED_MODE"` - } - - // Docker provides docker configuration - Docker struct { - Config string `envconfig:"DRONE_DOCKER_CONFIG"` - } - - // Datadog provides datadog configuration - Datadog struct { - Enabled bool `envconfig:"DRONE_DATADOG_ENABLED"` - Endpoint string `envconfig:"DRONE_DATADOG_ENDPOINT"` - Token string `envconfig:"DRONE_DATADOG_TOKEN"` - } - - // Jsonnet configures the jsonnet plugin - Jsonnet struct { - Enabled bool `envconfig:"DRONE_JSONNET_ENABLED"` - ImportLimit int `envconfig:"DRONE_JSONNET_IMPORT_LIMIT" default:"0"` - } - - // Starlark configures the starlark plugin - Starlark struct { - Enabled bool `envconfig:"DRONE_STARLARK_ENABLED"` - StepLimit uint64 `envconfig:"DRONE_STARLARK_STEP_LIMIT"` - SizeLimit uint64 `envconfig:"DRONE_STARLARK_SIZE_LIMIT" default:"0"` - } - - // License provides license configuration - License struct { - Key string `envconfig:"DRONE_LICENSE"` - Endpoint string `envconfig:"DRONE_LICENSE_ENDPOINT"` - } - - // Logging provides the logging configuration. - Logging struct { - Debug bool `envconfig:"DRONE_LOGS_DEBUG"` - Trace bool `envconfig:"DRONE_LOGS_TRACE"` - Color bool `envconfig:"DRONE_LOGS_COLOR"` - Pretty bool `envconfig:"DRONE_LOGS_PRETTY"` - Text bool `envconfig:"DRONE_LOGS_TEXT"` - } - - // Prometheus provides the prometheus configuration. - Prometheus struct { - EnableAnonymousAccess bool `envconfig:"DRONE_PROMETHEUS_ANONYMOUS_ACCESS" default:"false"` - } - - // Redis provides the redis configuration. - Redis struct { - ConnectionString string `envconfig:"DRONE_REDIS_CONNECTION"` - Addr string `envconfig:"DRONE_REDIS_ADDR"` - Password string `envconfig:"DRONE_REDIS_PASSWORD"` - DB int `envconfig:"DRONE_REDIS_DB"` - } - - // Repository provides the repository configuration. - Repository struct { - Filter []string `envconfig:"DRONE_REPOSITORY_FILTER"` - Visibility string `envconfig:"DRONE_REPOSITORY_VISIBILITY"` - Trusted bool `envconfig:"DRONE_REPOSITORY_TRUSTED"` - - // THIS SETTING IS INTERNAL USE ONLY AND SHOULD - // NOT BE USED OR RELIED UPON IN PRODUCTION. - Ignore []string `envconfig:"DRONE_REPOSITORY_IGNORE"` - } - - // Registries provides the registry configuration. - Registries struct { - Endpoint string `envconfig:"DRONE_REGISTRY_ENDPOINT"` - Password string `envconfig:"DRONE_REGISTRY_SECRET"` - SkipVerify bool `envconfig:"DRONE_REGISTRY_SKIP_VERIFY"` - } - - // Secrets provides the secret configuration. - Secrets struct { - Endpoint string `envconfig:"DRONE_SECRET_ENDPOINT"` - Password string `envconfig:"DRONE_SECRET_SECRET"` - SkipVerify bool `envconfig:"DRONE_SECRET_SKIP_VERIFY"` - } - - // RPC provides the rpc configuration. - RPC struct { - Server string `envconfig:"DRONE_RPC_SERVER"` - Secret string `envconfig:"DRONE_RPC_SECRET"` - Debug bool `envconfig:"DRONE_RPC_DEBUG"` - Host string `envconfig:"DRONE_RPC_HOST"` - Proto string `envconfig:"DRONE_RPC_PROTO"` - // Hosts map[string]string `envconfig:"DRONE_RPC_EXTRA_HOSTS"` - } - - Agent struct { - Disabled bool `envconfig:"DRONE_AGENTS_DISABLED"` - } - - // Runner provides the runner configuration. - Runner struct { - Local bool `envconfig:"DRONE_RUNNER_LOCAL"` - Image string `envconfig:"DRONE_RUNNER_IMAGE" default:"drone/controller:1"` - Platform string `envconfig:"DRONE_RUNNER_PLATFORM" default:"linux/amd64"` - OS string `envconfig:"DRONE_RUNNER_OS"` - Arch string `envconfig:"DRONE_RUNNER_ARCH"` - Kernel string `envconfig:"DRONE_RUNNER_KERNEL"` - Variant string `envconfig:"DRONE_RUNNER_VARIANT"` - Machine string `envconfig:"DRONE_RUNNER_NAME"` - Capacity int `envconfig:"DRONE_RUNNER_CAPACITY" default:"2"` - Labels map[string]string `envconfig:"DRONE_RUNNER_LABELS"` - Volumes []string `envconfig:"DRONE_RUNNER_VOLUMES"` - Networks []string `envconfig:"DRONE_RUNNER_NETWORKS"` - Devices []string `envconfig:"DRONE_RUNNER_DEVICES"` - Privileged []string `envconfig:"DRONE_RUNNER_PRIVILEGED_IMAGES"` - Environ map[string]string `envconfig:"DRONE_RUNNER_ENVIRON"` - Limits struct { - MemSwapLimit Bytes `envconfig:"DRONE_LIMIT_MEM_SWAP"` - MemLimit Bytes `envconfig:"DRONE_LIMIT_MEM"` - ShmSize Bytes `envconfig:"DRONE_LIMIT_SHM_SIZE"` - CPUQuota int64 `envconfig:"DRONE_LIMIT_CPU_QUOTA"` - CPUShares int64 `envconfig:"DRONE_LIMIT_CPU_SHARES"` - CPUSet string `envconfig:"DRONE_LIMIT_CPU_SET"` - } - } - - // Server provides the server configuration. - Server struct { - Addr string `envconfig:"-"` - Host string `envconfig:"DRONE_SERVER_HOST" default:"localhost:8080"` - Port string `envconfig:"DRONE_SERVER_PORT" default:":8080"` - Proto string `envconfig:"DRONE_SERVER_PROTO" default:"http"` - Pprof bool `envconfig:"DRONE_PPROF_ENABLED"` - Acme bool `envconfig:"DRONE_TLS_AUTOCERT"` - Email string `envconfig:"DRONE_TLS_EMAIL"` - Cert string `envconfig:"DRONE_TLS_CERT"` - Key string `envconfig:"DRONE_TLS_KEY"` - } - - // Proxy provides proxy server configuration. - Proxy struct { - Addr string `envconfig:"-"` - Host string `envconfig:"DRONE_SERVER_PROXY_HOST"` - Proto string `envconfig:"DRONE_SERVER_PROXY_PROTO"` - } - - // Registration configuration. - Registration struct { - Closed bool `envconfig:"DRONE_REGISTRATION_CLOSED"` - } - - // Authentication Controller configuration - Authentication struct { - Endpoint string `envconfig:"DRONE_ADMISSION_PLUGIN_ENDPOINT"` - Secret string `envconfig:"DRONE_ADMISSION_PLUGIN_SECRET"` - SkipVerify bool `envconfig:"DRONE_ADMISSION_PLUGIN_SKIP_VERIFY"` - } - - // Session provides the session configuration. - Session struct { - Timeout time.Duration `envconfig:"DRONE_COOKIE_TIMEOUT" default:"720h"` - Secret string `envconfig:"DRONE_COOKIE_SECRET"` - Secure bool `envconfig:"DRONE_COOKIE_SECURE"` - } - - // Status provides status configurations. - Status struct { - Disabled bool `envconfig:"DRONE_STATUS_DISABLED"` - Name string `envconfig:"DRONE_STATUS_NAME"` - } - - // Users provides the user configuration. - Users struct { - Create UserCreate `envconfig:"DRONE_USER_CREATE"` - Filter []string `envconfig:"DRONE_USER_FILTER"` - MinAge time.Duration `envconfig:"DRONE_MIN_AGE"` - } - - // Webhook provides the webhook configuration. - Webhook struct { - Events []string `envconfig:"DRONE_WEBHOOK_EVENTS"` - Endpoint []string `envconfig:"DRONE_WEBHOOK_ENDPOINT"` - Secret string `envconfig:"DRONE_WEBHOOK_SECRET"` - SkipVerify bool `envconfig:"DRONE_WEBHOOK_SKIP_VERIFY"` - } - - // Yaml provides the yaml webhook configuration. - Yaml struct { - Endpoint string `envconfig:"DRONE_YAML_ENDPOINT"` - Secret string `envconfig:"DRONE_YAML_SECRET"` - SkipVerify bool `envconfig:"DRONE_YAML_SKIP_VERIFY"` - Timeout time.Duration `envconfig:"DRONE_YAML_TIMEOUT" default:"1m"` - } - - // Convert provides the converter webhook configuration. - Convert struct { - Extension string `envconfig:"DRONE_CONVERT_PLUGIN_EXTENSION"` - Endpoint string `envconfig:"DRONE_CONVERT_PLUGIN_ENDPOINT"` - Secret string `envconfig:"DRONE_CONVERT_PLUGIN_SECRET"` - SkipVerify bool `envconfig:"DRONE_CONVERT_PLUGIN_SKIP_VERIFY"` - CacheSize int `envconfig:"DRONE_CONVERT_PLUGIN_CACHE_SIZE" default:"10"` - Timeout time.Duration `envconfig:"DRONE_CONVERT_TIMEOUT" default:"1m"` - - // this flag can be removed once we solve for - // https://github.com/harness/drone/pull/2994#issuecomment-795955312 - Multi bool `envconfig:"DRONE_CONVERT_MULTI"` - } - - // Validate provides the validation webhook configuration. - Validate struct { - Endpoint string `envconfig:"DRONE_VALIDATE_PLUGIN_ENDPOINT"` - Secret string `envconfig:"DRONE_VALIDATE_PLUGIN_SECRET"` - SkipVerify bool `envconfig:"DRONE_VALIDATE_PLUGIN_SKIP_VERIFY"` - Timeout time.Duration `envconfig:"DRONE_VALIDATE_TIMEOUT" default:"1m"` - } - - // - // Source code management. - // - - // Bitbucket provides the bitbucket client configuration. - Bitbucket struct { - ClientID string `envconfig:"DRONE_BITBUCKET_CLIENT_ID"` - ClientSecret string `envconfig:"DRONE_BITBUCKET_CLIENT_SECRET"` - SkipVerify bool `envconfig:"DRONE_BITBUCKET_SKIP_VERIFY"` - Debug bool `envconfig:"DRONE_BITBUCKET_DEBUG"` - } - - // Gitea provides the gitea client configuration. - Gitea struct { - Server string `envconfig:"DRONE_GITEA_SERVER"` - ClientID string `envconfig:"DRONE_GITEA_CLIENT_ID"` - ClientSecret string `envconfig:"DRONE_GITEA_CLIENT_SECRET"` - RedirectURL string `envconfig:"DRONE_GITEA_REDIRECT_URL"` - SkipVerify bool `envconfig:"DRONE_GITEA_SKIP_VERIFY"` - Scope []string `envconfig:"DRONE_GITEA_SCOPE" default:"repo,repo:status,user:email,read:org"` - Debug bool `envconfig:"DRONE_GITEA_DEBUG"` - } - - // Github provides the github client configuration. - Github struct { - Server string `envconfig:"DRONE_GITHUB_SERVER" default:"https://github.com"` - APIServer string `envconfig:"DRONE_GITHUB_API_SERVER"` - ClientID string `envconfig:"DRONE_GITHUB_CLIENT_ID"` - ClientSecret string `envconfig:"DRONE_GITHUB_CLIENT_SECRET"` - SkipVerify bool `envconfig:"DRONE_GITHUB_SKIP_VERIFY"` - Scope []string `envconfig:"DRONE_GITHUB_SCOPE" default:"repo,repo:status,user:email,read:org"` - RateLimit int `envconfig:"DRONE_GITHUB_USER_RATELIMIT"` - Debug bool `envconfig:"DRONE_GITHUB_DEBUG"` - } - - // Gitee providers the gitee client configuration. - Gitee struct { - Server string `envconfig:"DRONE_GITEE_SERVER" default:"https://gitee.com"` - APIServer string `envconfig:"DRONE_GITEE_API_SERVER" default:"https://gitee.com/api/v5"` - ClientID string `envconfig:"DRONE_GITEE_CLIENT_ID"` - ClientSecret string `envconfig:"DRONE_GITEE_CLIENT_SECRET"` - RedirectURL string `envconfig:"DRONE_GITEE_REDIRECT_URL"` - SkipVerify bool `envconfig:"DRONE_GITEE_SKIP_VERIFY"` - Scope []string `envconfig:"DRONE_GITEE_SCOPE" default:"user_info,projects,pull_requests,hook"` - Debug bool `envconfig:"DRONE_GITEE_DEBUG"` - } - - // GitLab provides the gitlab client configuration. - GitLab struct { - Server string `envconfig:"DRONE_GITLAB_SERVER" default:"https://gitlab.com"` - ClientID string `envconfig:"DRONE_GITLAB_CLIENT_ID"` - ClientSecret string `envconfig:"DRONE_GITLAB_CLIENT_SECRET"` - SkipVerify bool `envconfig:"DRONE_GITLAB_SKIP_VERIFY"` - Debug bool `envconfig:"DRONE_GITLAB_DEBUG"` - } - - // Gogs provides the gogs client configuration. - Gogs struct { - Server string `envconfig:"DRONE_GOGS_SERVER"` - SkipVerify bool `envconfig:"DRONE_GOGS_SKIP_VERIFY"` - Debug bool `envconfig:"DRONE_GOGS_DEBUG"` - } - - // Stash provides the stash client configuration. - Stash struct { - Server string `envconfig:"DRONE_STASH_SERVER"` - ConsumerKey string `envconfig:"DRONE_STASH_CONSUMER_KEY"` - ConsumerSecret string `envconfig:"DRONE_STASH_CONSUMER_SECRET"` - PrivateKey string `envconfig:"DRONE_STASH_PRIVATE_KEY"` - SkipVerify bool `envconfig:"DRONE_STASH_SKIP_VERIFY"` - Debug bool `envconfig:"DRONE_STASH_DEBUG"` - } - - // S3 provides the storage configuration. - S3 struct { - Bucket string `envconfig:"DRONE_S3_BUCKET"` - Prefix string `envconfig:"DRONE_S3_PREFIX"` - Endpoint string `envconfig:"DRONE_S3_ENDPOINT"` - PathStyle bool `envconfig:"DRONE_S3_PATH_STYLE"` - } - - //AzureBlob providers the storage configuration. - AzureBlob struct { - ContainerName string `envconfig:"DRONE_AZURE_BLOB_CONTAINER_NAME"` - StorageAccountName string `envconfig:"DRONE_AZURE_STORAGE_ACCOUNT_NAME"` - StorageAccessKey string `envconfig:"DRONE_AZURE_STORAGE_ACCESS_KEY"` - } - - // HTTP provides http configuration. - HTTP struct { - AllowedHosts []string `envconfig:"DRONE_HTTP_ALLOWED_HOSTS"` - HostsProxyHeaders []string `envconfig:"DRONE_HTTP_PROXY_HEADERS"` - SSLRedirect bool `envconfig:"DRONE_HTTP_SSL_REDIRECT"` - SSLTemporaryRedirect bool `envconfig:"DRONE_HTTP_SSL_TEMPORARY_REDIRECT"` - SSLHost string `envconfig:"DRONE_HTTP_SSL_HOST"` - SSLProxyHeaders map[string]string `envconfig:"DRONE_HTTP_SSL_PROXY_HEADERS"` - STSSeconds int64 `envconfig:"DRONE_HTTP_STS_SECONDS"` - STSIncludeSubdomains bool `envconfig:"DRONE_HTTP_STS_INCLUDE_SUBDOMAINS"` - STSPreload bool `envconfig:"DRONE_HTTP_STS_PRELOAD"` - ForceSTSHeader bool `envconfig:"DRONE_HTTP_STS_FORCE_HEADER"` - BrowserXSSFilter bool `envconfig:"DRONE_HTTP_BROWSER_XSS_FILTER" default:"true"` - FrameDeny bool `envconfig:"DRONE_HTTP_FRAME_DENY" default:"true"` - ContentTypeNosniff bool `envconfig:"DRONE_HTTP_CONTENT_TYPE_NO_SNIFF"` - ContentSecurityPolicy string `envconfig:"DRONE_HTTP_CONTENT_SECURITY_POLICY"` - ReferrerPolicy string `envconfig:"DRONE_HTTP_REFERRER_POLICY"` - } -) - -// Environ returns the settings from the environment. -func Environ() (Config, error) { - cfg := Config{} - err := envconfig.Process("", &cfg) - defaultAddress(&cfg) - defaultProxy(&cfg) - defaultRunner(&cfg) - defaultSession(&cfg) - defaultCallback(&cfg) - configureGithub(&cfg) - if err := kubernetesServiceConflict(&cfg); err != nil { - return cfg, err - } - return cfg, err -} - -// String returns the configuration in string format. -func (c *Config) String() string { - out, _ := yaml.Marshal(c) - return string(out) -} - -// IsGitHub returns true if the GitHub integration -// is activated. -func (c *Config) IsGitHub() bool { - return c.Github.ClientID != "" -} - -// IsGitHubEnterprise returns true if the GitHub -// integration is activated. -func (c *Config) IsGitHubEnterprise() bool { - return c.IsGitHub() && !strings.HasPrefix(c.Github.Server, "https://github.com") -} - -// IsGitLab returns true if the GitLab integration -// is activated. -func (c *Config) IsGitLab() bool { - return c.GitLab.ClientID != "" -} - -// IsGogs returns true if the Gogs integration -// is activated. -func (c *Config) IsGogs() bool { - return c.Gogs.Server != "" -} - -// IsGitea returns true if the Gitea integration -// is activated. -func (c *Config) IsGitea() bool { - return c.Gitea.Server != "" -} - -// IsGitee returns true if the Gitee integration -// is activated. -func (c *Config) IsGitee() bool { - return c.Gitee.ClientID != "" -} - -// IsBitbucket returns true if the Bitbucket Cloud -// integration is activated. -func (c *Config) IsBitbucket() bool { - return c.Bitbucket.ClientID != "" -} - -// IsStash returns true if the Atlassian Stash -// integration is activated. -func (c *Config) IsStash() bool { - return c.Stash.Server != "" -} - -func cleanHostname(hostname string) string { - hostname = strings.ToLower(hostname) - hostname = strings.TrimPrefix(hostname, "http://") - hostname = strings.TrimPrefix(hostname, "https://") - - return hostname -} - -func defaultAddress(c *Config) { - if c.Server.Key != "" || c.Server.Cert != "" || c.Server.Acme { - c.Server.Port = ":443" - c.Server.Proto = "https" - } - c.Server.Host = cleanHostname(c.Server.Host) - c.Server.Addr = c.Server.Proto + "://" + c.Server.Host -} - -func defaultProxy(c *Config) { - if c.Proxy.Host == "" { - c.Proxy.Host = c.Server.Host - } else { - c.Proxy.Host = cleanHostname(c.Proxy.Host) - } - if c.Proxy.Proto == "" { - c.Proxy.Proto = c.Server.Proto - } - c.Proxy.Addr = c.Proxy.Proto + "://" + c.Proxy.Host -} - -func defaultCallback(c *Config) { - if c.RPC.Host == "" { - c.RPC.Host = c.Server.Host - } - if c.RPC.Proto == "" { - c.RPC.Proto = c.Server.Proto - } -} - -func defaultRunner(c *Config) { - if c.Runner.Machine == "" { - c.Runner.Machine = hostname - } - parts := strings.Split(c.Runner.Platform, "/") - if len(parts) == 2 && c.Runner.OS == "" { - c.Runner.OS = parts[0] - } - if len(parts) == 2 && c.Runner.Arch == "" { - c.Runner.Arch = parts[1] - } -} - -func defaultSession(c *Config) { - if c.Session.Secret == "" { - c.Session.Secret = uniuri.NewLen(32) - } -} - -func configureGithub(c *Config) { - if c.Github.APIServer != "" { - return - } - if c.Github.Server == "https://github.com" { - c.Github.APIServer = "https://api.github.com" - } else { - c.Github.APIServer = strings.TrimSuffix(c.Github.Server, "/") + "/api/v3" - } -} - -func kubernetesServiceConflict(c *Config) error { - if strings.HasPrefix(c.Server.Port, "tcp://") { - return errors.New("Invalid port configuration. See https://community.harness.io/t/drone-server-changing-ports-protocol/11400") - } - return nil -} - -// Bytes stores number bytes (e.g. megabytes) -type Bytes int64 - -// Decode implements a decoder that parses a string representation -// of bytes into the number of bytes it represents. -func (b *Bytes) Decode(value string) error { - v, err := humanize.ParseBytes(value) - *b = Bytes(v) - return err -} - -// Int64 returns the int64 value of the Byte. -func (b *Bytes) Int64() int64 { - return int64(*b) -} - -// String returns the string value of the Byte. -func (b *Bytes) String() string { - return fmt.Sprint(*b) -} - -// UserCreate stores account information used to bootstrap -// the admin user account when the system initializes. -type UserCreate struct { - Username string - Machine bool - Admin bool - Token string -} - -// Decode implements a decoder that extracts user information -// from the environment variable string. -func (u *UserCreate) Decode(value string) error { - for _, param := range strings.Split(value, ",") { - parts := strings.Split(param, ":") - if len(parts) != 2 { - continue - } - key := parts[0] - val := parts[1] - switch key { - case "username": - u.Username = val - case "token": - u.Token = val - case "machine": - u.Machine = val == "true" - case "admin": - u.Admin = val == "true" - } - } - return nil -} diff --git a/cmd/drone-server/config/config_test.go b/cmd/drone-server/config/config_test.go deleted file mode 100644 index 500620a795..0000000000 --- a/cmd/drone-server/config/config_test.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import "testing" - -func Test_cleanHostname(t *testing.T) { - tests := []struct { - name string - hostname string - want string - }{ - { - name: "no prefix", - hostname: "drone.io", - want: "drone.io", - }, - { - name: "http prefix", - hostname: "http://drone.io", - want: "drone.io", - }, - { - name: "https prefix", - hostname: "https://drone.io", - want: "drone.io", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := cleanHostname(tt.hostname); got != tt.want { - t.Errorf("cleanHostname() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/cmd/drone-server/inject_client.go b/cmd/drone-server/inject_client.go deleted file mode 100644 index 93fab947c1..0000000000 --- a/cmd/drone-server/inject_client.go +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "encoding/pem" - "io/ioutil" - "net/http" - "net/http/httputil" - "strings" - - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/go-scm/scm" - "github.com/drone/go-scm/scm/driver/bitbucket" - "github.com/drone/go-scm/scm/driver/gitea" - "github.com/drone/go-scm/scm/driver/gitee" - "github.com/drone/go-scm/scm/driver/github" - "github.com/drone/go-scm/scm/driver/gitlab" - "github.com/drone/go-scm/scm/driver/gogs" - "github.com/drone/go-scm/scm/driver/stash" - "github.com/drone/go-scm/scm/transport/oauth1" - "github.com/drone/go-scm/scm/transport/oauth2" - - "github.com/google/wire" - "github.com/sirupsen/logrus" -) - -// wire set for loading the scm client. -var clientSet = wire.NewSet( - provideClient, -) - -// provideBitbucketClient is a Wire provider function that -// returns a Source Control Management client based on the -// environment configuration. -func provideClient(config config.Config) *scm.Client { - switch { - case config.Bitbucket.ClientID != "": - return provideBitbucketClient(config) - case config.Github.ClientID != "": - return provideGithubClient(config) - case config.Gitee.ClientID != "": - return provideGiteeClient(config) - case config.Gitea.Server != "": - return provideGiteaClient(config) - case config.GitLab.ClientID != "": - return provideGitlabClient(config) - case config.Gogs.Server != "": - return provideGogsClient(config) - case config.Stash.ConsumerKey != "": - return provideStashClient(config) - } - logrus.Fatalln("main: source code management system not configured") - return nil -} - -// provideBitbucketClient is a Wire provider function that -// returns a Bitbucket Cloud client based on the environment -// configuration. -func provideBitbucketClient(config config.Config) *scm.Client { - client := bitbucket.NewDefault() - client.Client = &http.Client{ - Transport: &oauth2.Transport{ - Source: &oauth2.Refresher{ - ClientID: config.Bitbucket.ClientID, - ClientSecret: config.Bitbucket.ClientSecret, - Endpoint: "https://bitbucket.org/site/oauth2/access_token", - Source: oauth2.ContextTokenSource(), - }, - }, - } - if config.Bitbucket.Debug { - client.DumpResponse = httputil.DumpResponse - } - return client -} - -// provideGithubClient is a Wire provider function that returns -// a GitHub client based on the environment configuration. -func provideGithubClient(config config.Config) *scm.Client { - client, err := github.New(config.Github.APIServer) - if err != nil { - logrus.WithError(err). - Fatalln("main: cannot create the GitHub client") - } - if config.Github.Debug { - client.DumpResponse = httputil.DumpResponse - } - client.Client = &http.Client{ - Transport: &oauth2.Transport{ - Source: oauth2.ContextTokenSource(), - Base: defaultTransport(config.Github.SkipVerify), - }, - } - return client -} - -// provideGiteeClient is a Wire provider function that returns -// a Gitee client based on the environment configuration. -func provideGiteeClient(config config.Config) *scm.Client { - client, err := gitee.New(config.Gitee.APIServer) - if err != nil { - logrus.WithError(err). - Fatalln("main: cannot create the Gitee client") - } - if config.Gitee.Debug { - client.DumpResponse = httputil.DumpResponse - } - client.Client = &http.Client{ - Transport: &oauth2.Transport{ - Scheme: oauth2.SchemeBearer, - Source: &oauth2.Refresher{ - ClientID: config.Gitee.ClientID, - ClientSecret: config.Gitee.ClientSecret, - Endpoint: strings.TrimSuffix(config.Gitee.Server, "/") + "/oauth/token", - Source: oauth2.ContextTokenSource(), - }, - Base: defaultTransport(config.Gitee.SkipVerify), - }, - } - return client -} - -// provideGiteaClient is a Wire provider function that returns -// a Gitea client based on the environment configuration. -func provideGiteaClient(config config.Config) *scm.Client { - client, err := gitea.New(config.Gitea.Server) - if err != nil { - logrus.WithError(err). - Fatalln("main: cannot create the Gitea client") - } - if config.Gitea.Debug { - client.DumpResponse = httputil.DumpResponse - } - client.Client = &http.Client{ - Transport: &oauth2.Transport{ - Scheme: oauth2.SchemeBearer, - Source: &oauth2.Refresher{ - ClientID: config.Gitea.ClientID, - ClientSecret: config.Gitea.ClientSecret, - Endpoint: strings.TrimSuffix(config.Gitea.Server, "/") + "/login/oauth/access_token", - Source: oauth2.ContextTokenSource(), - }, - Base: defaultTransport(config.Gitea.SkipVerify), - }, - } - return client -} - -// provideGitlabClient is a Wire provider function that returns -// a GitLab client based on the environment configuration. -func provideGitlabClient(config config.Config) *scm.Client { - logrus.WithField("server", config.GitLab.Server). - WithField("client", config.GitLab.ClientID). - WithField("skip_verify", config.GitLab.SkipVerify). - Debugln("main: creating the GitLab client") - - client, err := gitlab.New(config.GitLab.Server) - if err != nil { - logrus.WithError(err). - Fatalln("main: cannot create the GitLab client") - } - if config.GitLab.Debug { - client.DumpResponse = httputil.DumpResponse - } - client.Client = &http.Client{ - Transport: &oauth2.Transport{ - Scheme: oauth2.SchemeBearer, - Source: &oauth2.Refresher{ - ClientID: config.GitLab.ClientID, - ClientSecret: config.GitLab.ClientSecret, - Endpoint: strings.TrimSuffix(config.GitLab.Server, "/") + "/oauth/token", - Source: oauth2.ContextTokenSource(), - }, - Base: defaultTransport(config.GitLab.SkipVerify), - }, - } - return client -} - -// provideGogsClient is a Wire provider function that returns -// a Gogs client based on the environment configuration. -func provideGogsClient(config config.Config) *scm.Client { - logrus.WithField("server", config.Gogs.Server). - WithField("skip_verify", config.Gogs.SkipVerify). - Debugln("main: creating the Gogs client") - - client, err := gogs.New(config.Gogs.Server) - if err != nil { - logrus.WithError(err). - Fatalln("main: cannot create the Gogs client") - } - if config.Gogs.Debug { - client.DumpResponse = httputil.DumpResponse - } - client.Client = &http.Client{ - Transport: &oauth2.Transport{ - Scheme: oauth2.SchemeToken, - Source: oauth2.ContextTokenSource(), - Base: defaultTransport(config.Gogs.SkipVerify), - }, - } - return client -} - -// provideStashClient is a Wire provider function that returns -// a Stash client based on the environment configuration. -func provideStashClient(config config.Config) *scm.Client { - logrus.WithField("server", config.Stash.Server). - WithField("skip_verify", config.Stash.SkipVerify). - Debugln("main: creating the Stash client") - - privateKey, err := parsePrivateKeyFile(config.Stash.PrivateKey) - if err != nil { - logrus.WithError(err). - Fatalln("main: cannot parse the Stash Private Key") - } - client, err := stash.New(config.Stash.Server) - if err != nil { - logrus.WithError(err). - Fatalln("main: cannot create the Stash client") - } - if config.Stash.Debug { - client.DumpResponse = httputil.DumpResponse - } - client.Client = &http.Client{ - Transport: &oauth1.Transport{ - ConsumerKey: config.Stash.ConsumerKey, - PrivateKey: privateKey, - Source: oauth1.ContextTokenSource(), - Base: defaultTransport(config.Stash.SkipVerify), - }, - } - return client -} - -// defaultClient provides a default http.Client. If skipverify -// is true, the default transport will skip ssl verification. -func defaultClient(skipverify bool) *http.Client { - client := &http.Client{} - client.Transport = defaultTransport(skipverify) - return client -} - -// defaultTransport provides a default http.Transport. If -// skipverify is true, the transport will skip ssl verification. -func defaultTransport(skipverify bool) http.RoundTripper { - return &http.Transport{ - Proxy: http.ProxyFromEnvironment, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: skipverify, - }, - } -} - -// parsePrivateKeyFile is a helper function that parses an -// RSA Private Key file encoded in PEM format. -func parsePrivateKeyFile(path string) (*rsa.PrivateKey, error) { - d, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - return parsePrivateKey(d) -} - -// parsePrivateKey is a helper function that parses an RSA -// Private Key encoded in PEM format. -func parsePrivateKey(data []byte) (*rsa.PrivateKey, error) { - p, _ := pem.Decode(data) - return x509.ParsePKCS1PrivateKey(p.Bytes) -} diff --git a/cmd/drone-server/inject_external.go b/cmd/drone-server/inject_external.go deleted file mode 100644 index d5a29a1e37..0000000000 --- a/cmd/drone-server/inject_external.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/drone/service/redisdb" - - "github.com/google/wire" -) - -// wire set for loading the external services. -var externalSet = wire.NewSet( - provideRedisClient, -) - -func provideRedisClient(config config.Config) (rdb redisdb.RedisDB, err error) { - return redisdb.New(config) -} diff --git a/cmd/drone-server/inject_license.go b/cmd/drone-server/inject_license.go deleted file mode 100644 index 4a4bc2c5be..0000000000 --- a/cmd/drone-server/inject_license.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/drone/core" - "github.com/drone/drone/service/license" - "github.com/drone/go-scm/scm" - - "github.com/google/wire" - "github.com/sirupsen/logrus" -) - -// wire set for loading the license. -var licenseSet = wire.NewSet( - provideLicense, - license.NewService, -) - -// provideLicense is a Wire provider function that returns a -// license loaded from a license file. -func provideLicense(client *scm.Client, config config.Config) *core.License { - l, err := license.Load(config.License) - if config.License == "" { - l = license.Trial(client.Driver.String()) - } else if err != nil { - logrus.WithError(err). - Fatalln("main: invalid or expired license") - } - logrus.WithFields( - logrus.Fields{ - "kind": l.Kind, - "expires": l.Expires, - "repo.limit": l.Repos, - "user.limit": l.Users, - "build.limit": l.Builds, - }, - ).Debugln("main: license loaded") - return l -} diff --git a/cmd/drone-server/inject_login.go b/cmd/drone-server/inject_login.go deleted file mode 100644 index a3f4fed39f..0000000000 --- a/cmd/drone-server/inject_login.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/go-login/login" - "github.com/drone/go-login/login/bitbucket" - "github.com/drone/go-login/login/gitea" - "github.com/drone/go-login/login/gitee" - "github.com/drone/go-login/login/github" - "github.com/drone/go-login/login/gitlab" - "github.com/drone/go-login/login/gogs" - "github.com/drone/go-login/login/stash" - "github.com/drone/go-scm/scm/transport/oauth2" - "strings" - - "github.com/google/wire" - "github.com/sirupsen/logrus" -) - -// wire set for loading the authenticator. -var loginSet = wire.NewSet( - provideLogin, - provideRefresher, -) - -// provideLogin is a Wire provider function that returns an -// authenticator based on the environment configuration. -func provideLogin(config config.Config) login.Middleware { - switch { - case config.Bitbucket.ClientID != "": - return provideBitbucketLogin(config) - case config.Github.ClientID != "": - return provideGithubLogin(config) - case config.Gitee.ClientID != "": - return provideGiteeLogin(config) - case config.Gitea.Server != "": - return provideGiteaLogin(config) - case config.GitLab.ClientID != "": - return provideGitlabLogin(config) - case config.Gogs.Server != "": - return provideGogsLogin(config) - case config.Stash.ConsumerKey != "": - return provideStashLogin(config) - } - logrus.Fatalln("main: source code management system not configured") - return nil -} - -// provideBitbucketLogin is a Wire provider function that -// returns a Bitbucket Cloud authenticator based on the -// environment configuration. -func provideBitbucketLogin(config config.Config) login.Middleware { - if config.Bitbucket.ClientID == "" { - return nil - } - return &bitbucket.Config{ - ClientID: config.Bitbucket.ClientID, - ClientSecret: config.Bitbucket.ClientSecret, - RedirectURL: config.Server.Addr + "/login", - } -} - -// provideGithubLogin is a Wire provider function that returns -// a GitHub authenticator based on the environment configuration. -func provideGithubLogin(config config.Config) login.Middleware { - if config.Github.ClientID == "" { - return nil - } - return &github.Config{ - ClientID: config.Github.ClientID, - ClientSecret: config.Github.ClientSecret, - Scope: config.Github.Scope, - Server: config.Github.Server, - Client: defaultClient(config.Github.SkipVerify), - Logger: logrus.StandardLogger(), - } -} - -// provideGiteeLogin is a Wire provider function that returns -// a Gitee authenticator based on the environment configuration. -func provideGiteeLogin(config config.Config) login.Middleware { - if config.Gitee.ClientID == "" { - return nil - } - redirectURL := config.Gitee.RedirectURL - if redirectURL == "" { - redirectURL = config.Server.Addr + "/login" - } - return &gitee.Config{ - ClientID: config.Gitee.ClientID, - ClientSecret: config.Gitee.ClientSecret, - RedirectURL: redirectURL, - Server: config.Gitee.Server, - Scope: config.Gitee.Scope, - Client: defaultClient(config.Gitee.SkipVerify), - } -} - -// provideGiteaLogin is a Wire provider function that returns -// a Gitea authenticator based on the environment configuration. -func provideGiteaLogin(config config.Config) login.Middleware { - if config.Gitea.Server == "" { - return nil - } - redirectURL := config.Gitea.RedirectURL - if redirectURL == "" { - redirectURL = config.Server.Addr + "/login" - } - return &gitea.Config{ - ClientID: config.Gitea.ClientID, - ClientSecret: config.Gitea.ClientSecret, - Server: config.Gitea.Server, - Client: defaultClient(config.Gitea.SkipVerify), - Logger: logrus.StandardLogger(), - RedirectURL: redirectURL, - Scope: config.Gitea.Scope, - } -} - -// provideGitlabLogin is a Wire provider function that returns -// a GitLab authenticator based on the environment configuration. -func provideGitlabLogin(config config.Config) login.Middleware { - if config.GitLab.ClientID == "" { - return nil - } - return &gitlab.Config{ - ClientID: config.GitLab.ClientID, - ClientSecret: config.GitLab.ClientSecret, - RedirectURL: config.Server.Addr + "/login", - Server: config.GitLab.Server, - Client: defaultClient(config.GitLab.SkipVerify), - } -} - -// provideGogsLogin is a Wire provider function that returns -// a Gogs authenticator based on the environment configuration. -func provideGogsLogin(config config.Config) login.Middleware { - if config.Gogs.Server == "" { - return nil - } - return &gogs.Config{ - Label: "drone", - Login: "/login/form", - Server: config.Gogs.Server, - Client: defaultClient(config.Gogs.SkipVerify), - } -} - -// provideStashLogin is a Wire provider function that returns -// a Stash authenticator based on the environment configuration. -func provideStashLogin(config config.Config) login.Middleware { - if config.Stash.ConsumerKey == "" { - return nil - } - privateKey, err := stash.ParsePrivateKeyFile(config.Stash.PrivateKey) - if err != nil { - logrus.WithError(err). - Fatalln("main: cannot parse Private Key file") - } - return &stash.Config{ - Address: config.Stash.Server, - ConsumerKey: config.Stash.ConsumerKey, - ConsumerSecret: config.Stash.ConsumerSecret, - PrivateKey: privateKey, - CallbackURL: config.Server.Addr + "/login", - Client: defaultClient(config.Stash.SkipVerify), - } -} - -// provideRefresher is a Wire provider function that returns -// an oauth token refresher for Bitbucket and Gitea -func provideRefresher(config config.Config) *oauth2.Refresher { - switch { - case config.Bitbucket.ClientID != "": - return &oauth2.Refresher{ - ClientID: config.Bitbucket.ClientID, - ClientSecret: config.Bitbucket.ClientSecret, - Endpoint: "https://bitbucket.org/site/oauth2/access_token", - Source: oauth2.ContextTokenSource(), - Client: defaultClient(config.Bitbucket.SkipVerify), - } - case config.Gitea.ClientID != "": - return &oauth2.Refresher{ - ClientID: config.Gitea.ClientID, - ClientSecret: config.Gitea.ClientSecret, - Endpoint: strings.TrimSuffix(config.Gitea.Server, "/") + "/login/oauth/access_token", - Source: oauth2.ContextTokenSource(), - Client: defaultClient(config.Gitea.SkipVerify), - } - case config.GitLab.ClientID != "": - return &oauth2.Refresher{ - ClientID: config.GitLab.ClientID, - ClientSecret: config.GitLab.ClientSecret, - Endpoint: strings.TrimSuffix(config.GitLab.Server, "/") + "/oauth/token", - Source: oauth2.ContextTokenSource(), - Client: defaultClient(config.GitLab.SkipVerify), - } - case config.Gitee.ClientID != "": - return &oauth2.Refresher{ - ClientID: config.Gitee.ClientID, - ClientSecret: config.Gitee.ClientSecret, - Endpoint: strings.TrimSuffix(config.Gitee.Server, "/") + "/oauth/token", - Source: oauth2.ContextTokenSource(), - Client: defaultClient(config.Gitee.SkipVerify), - } - - } - return nil -} diff --git a/cmd/drone-server/inject_plugin.go b/cmd/drone-server/inject_plugin.go deleted file mode 100644 index a97e8e8dae..0000000000 --- a/cmd/drone-server/inject_plugin.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - spec "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/drone/core" - "github.com/drone/drone/plugin/admission" - "github.com/drone/drone/plugin/config" - "github.com/drone/drone/plugin/converter" - "github.com/drone/drone/plugin/registry" - "github.com/drone/drone/plugin/secret" - "github.com/drone/drone/plugin/validator" - "github.com/drone/drone/plugin/webhook" - "github.com/drone/go-scm/scm" - - "github.com/google/wire" -) - -// wire set for loading plugins. -var pluginSet = wire.NewSet( - provideAdmissionPlugin, - provideConfigPlugin, - provideConvertPlugin, - provideRegistryPlugin, - provideSecretPlugin, - provideValidatePlugin, - provideWebhookPlugin, -) - -// provideAdmissionPlugin is a Wire provider function that -// returns an admission plugin based on the environment -// configuration. -func provideAdmissionPlugin(client *scm.Client, orgs core.OrganizationService, users core.UserService, config spec.Config) core.AdmissionService { - return admission.Combine( - admission.Membership(orgs, config.Users.Filter), - admission.Open(config.Registration.Closed), - admission.Nobot(users, config.Users.MinAge), - admission.External( - config.Authn.Endpoint, - config.Authn.Secret, - config.Authn.SkipVerify, - ), - ) -} - -// provideConfigPlugin is a Wire provider function that returns -// a yaml configuration plugin based on the environment -// configuration. -func provideConfigPlugin(client *scm.Client, contents core.FileService, conf spec.Config) core.ConfigService { - return config.Combine( - config.Memoize( - config.Global( - conf.Yaml.Endpoint, - conf.Yaml.Secret, - conf.Yaml.SkipVerify, - conf.Yaml.Timeout, - ), - ), - config.Repository(contents), - ) -} - -// provideConvertPlugin is a Wire provider function that returns -// a yaml conversion plugin based on the environment -// configuration. -func provideConvertPlugin(client *scm.Client, fileService core.FileService, conf spec.Config, templateStore core.TemplateStore) core.ConvertService { - return converter.Combine( - conf.Convert.Multi, - converter.Legacy(false), - converter.Starlark( - conf.Starlark.Enabled, - conf.Starlark.StepLimit, - conf.Starlark.SizeLimit, - ), - converter.Jsonnet( - conf.Jsonnet.Enabled, - conf.Jsonnet.ImportLimit, - fileService, - ), - converter.Template( - templateStore, - conf.Starlark.StepLimit, - conf.Starlark.SizeLimit, - ), - converter.Memoize( - converter.Remote( - conf.Convert.Endpoint, - conf.Convert.Secret, - conf.Convert.Extension, - conf.Convert.SkipVerify, - conf.Convert.Timeout, - ), - conf.Convert.CacheSize, - ), - ) -} - -// provideRegistryPlugin is a Wire provider function that -// returns a registry plugin based on the environment -// configuration. -func provideRegistryPlugin(config spec.Config) core.RegistryService { - return registry.Combine( - registry.External( - config.Secrets.Endpoint, - config.Secrets.Password, - config.Secrets.SkipVerify, - ), - registry.FileSource( - config.Docker.Config, - ), - registry.EndpointSource( - config.Registries.Endpoint, - config.Registries.Password, - config.Registries.SkipVerify, - ), - ) -} - -// provideSecretPlugin is a Wire provider function that returns -// a secret plugin based on the environment configuration. -func provideSecretPlugin(config spec.Config) core.SecretService { - return secret.External( - config.Secrets.Endpoint, - config.Secrets.Password, - config.Secrets.SkipVerify, - ) -} - -// provideValidatePlugin is a Wire provider function that -// returns a yaml validation plugin based on the environment -// configuration. -func provideValidatePlugin(conf spec.Config) core.ValidateService { - return validator.Combine( - validator.Remote( - conf.Validate.Endpoint, - conf.Validate.Secret, - conf.Validate.SkipVerify, - conf.Validate.Timeout, - ), - // THIS FEATURE IS INTERNAL USE ONLY AND SHOULD - // NOT BE USED OR RELIED UPON IN PRODUCTION. - validator.Filter( - nil, - conf.Repository.Ignore, - ), - ) -} - -// provideWebhookPlugin is a Wire provider function that returns -// a webhook plugin based on the environment configuration. -func provideWebhookPlugin(config spec.Config, system *core.System) core.WebhookSender { - return webhook.New(webhook.Config{ - Events: config.Webhook.Events, - Endpoint: config.Webhook.Endpoint, - Secret: config.Webhook.Secret, - System: system, - }) -} diff --git a/cmd/drone-server/inject_runner.go b/cmd/drone-server/inject_runner.go deleted file mode 100644 index 416b26f20b..0000000000 --- a/cmd/drone-server/inject_runner.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/drone/drone-runtime/engine/docker" - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/drone/core" - "github.com/drone/drone/operator/manager" - "github.com/drone/drone/operator/runner" - - "github.com/google/wire" - "github.com/sirupsen/logrus" -) - -// wire set for loading the server. -var runnerSet = wire.NewSet( - provideRunner, -) - -// provideRunner is a Wire provider function that returns a -// local build runner configured from the environment. -func provideRunner( - manager manager.BuildManager, - secrets core.SecretService, - registry core.RegistryService, - config config.Config, -) *runner.Runner { - // the local runner is only created when remote agents - // are disabled - if config.Agent.Disabled == false { - return nil - } - engine, err := docker.NewEnv() - if err != nil { - logrus.WithError(err). - Fatalln("cannot load the docker engine") - return nil - } - return &runner.Runner{ - Platform: config.Runner.Platform, - OS: config.Runner.OS, - Arch: config.Runner.Arch, - Kernel: config.Runner.Kernel, - Variant: config.Runner.Variant, - Engine: engine, - Manager: manager, - Secrets: secrets, - Registry: registry, - Volumes: config.Runner.Volumes, - Networks: config.Runner.Networks, - Devices: config.Runner.Devices, - Privileged: config.Runner.Privileged, - Machine: config.Runner.Machine, - Labels: config.Runner.Labels, - Environ: config.Runner.Environ, - Limits: runner.Limits{ - MemSwapLimit: int64(config.Runner.Limits.MemSwapLimit), - MemLimit: int64(config.Runner.Limits.MemLimit), - ShmSize: int64(config.Runner.Limits.ShmSize), - CPUQuota: config.Runner.Limits.CPUQuota, - CPUShares: config.Runner.Limits.CPUShares, - CPUSet: config.Runner.Limits.CPUSet, - }, - } -} diff --git a/cmd/drone-server/inject_scheduler.go b/cmd/drone-server/inject_scheduler.go deleted file mode 100644 index 42a6053c90..0000000000 --- a/cmd/drone-server/inject_scheduler.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/drone/drone/core" - "github.com/drone/drone/scheduler/queue" - "github.com/drone/drone/service/redisdb" - - "github.com/google/wire" -) - -// wire set for loading the scheduler. -var schedulerSet = wire.NewSet( - provideScheduler, -) - -// provideScheduler is a Wire provider function that returns a -// scheduler based on the environment configuration. -func provideScheduler(store core.StageStore, r redisdb.RedisDB) core.Scheduler { - return queue.New(store, r) -} diff --git a/cmd/drone-server/inject_server.go b/cmd/drone-server/inject_server.go deleted file mode 100644 index 07302bd5eb..0000000000 --- a/cmd/drone-server/inject_server.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "net/http" - - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api" - "github.com/drone/drone/handler/health" - "github.com/drone/drone/handler/web" - "github.com/drone/drone/metric" - "github.com/drone/drone/operator/manager" - "github.com/drone/drone/operator/manager/rpc" - "github.com/drone/drone/operator/manager/rpc2" - "github.com/drone/drone/server" - "github.com/google/wire" - - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" - "github.com/unrolled/secure" -) - -type ( - healthzHandler http.Handler - metricsHandler http.Handler - pprofHandler http.Handler - rpcHandlerV1 http.Handler - rpcHandlerV2 http.Handler -) - -// wire set for loading the server. -var serverSet = wire.NewSet( - manager.New, - api.New, - web.New, - provideHealthz, - provideMetric, - providePprof, - provideRouter, - provideRPC, - provideRPC2, - provideServer, - provideServerOptions, -) - -// provideRouter is a Wire provider function that returns a -// router that is serves the provided handlers. -func provideRouter(api api.Server, web web.Server, rpcv1 rpcHandlerV1, rpcv2 rpcHandlerV2, healthz healthzHandler, metrics *metric.Server, pprof pprofHandler) *chi.Mux { - r := chi.NewRouter() - r.Mount("/healthz", healthz) - r.Mount("/metrics", metrics) - r.Mount("/api", api.Handler()) - r.Mount("/rpc/v2", rpcv2) - r.Mount("/rpc", rpcv1) - r.Mount("/", web.Handler()) - r.Mount("/debug", pprof) - return r -} - -// provideMetric is a Wire provider function that returns the -// healthcheck server. -func provideHealthz() healthzHandler { - v := health.New() - return healthzHandler(v) -} - -// provideMetric is a Wire provider function that returns the -// metrics server exposing metrics in prometheus format. -func provideMetric(session core.Session, config config.Config) *metric.Server { - return metric.NewServer(session, config.Prometheus.EnableAnonymousAccess) -} - -// providePprof is a Wire provider function that returns the -// pprof server endpoints. -func providePprof(config config.Config) pprofHandler { - if config.Server.Pprof == false { - return pprofHandler( - http.NotFoundHandler(), - ) - } - return pprofHandler( - middleware.Profiler(), - ) -} - -// provideRPC is a Wire provider function that returns an rpc -// handler that exposes the build manager to a remote agent. -func provideRPC(m manager.BuildManager, config config.Config) rpcHandlerV1 { - v := rpc.NewServer(m, config.RPC.Secret) - return rpcHandlerV1(v) -} - -// provideRPC2 is a Wire provider function that returns an rpc -// handler that exposes the build manager to a remote agent. -func provideRPC2(m manager.BuildManager, config config.Config) rpcHandlerV2 { - v := rpc2.NewServer(m, config.RPC.Secret) - return rpcHandlerV2(v) -} - -// provideServer is a Wire provider function that returns an -// http server that is configured from the environment. -func provideServer(handler *chi.Mux, config config.Config) *server.Server { - return &server.Server{ - Acme: config.Server.Acme, - Addr: config.Server.Port, - Cert: config.Server.Cert, - Key: config.Server.Key, - Host: config.Server.Host, - Handler: handler, - } -} - -// provideServerOptions is a Wire provider function that returns -// the http web server security option from the environment. -func provideServerOptions(config config.Config) secure.Options { - return secure.Options{ - AllowedHosts: config.HTTP.AllowedHosts, - HostsProxyHeaders: config.HTTP.HostsProxyHeaders, - SSLRedirect: config.HTTP.SSLRedirect, - SSLTemporaryRedirect: config.HTTP.SSLTemporaryRedirect, - SSLHost: config.HTTP.SSLHost, - SSLProxyHeaders: config.HTTP.SSLProxyHeaders, - STSSeconds: config.HTTP.STSSeconds, - STSIncludeSubdomains: config.HTTP.STSIncludeSubdomains, - STSPreload: config.HTTP.STSPreload, - ForceSTSHeader: config.HTTP.ForceSTSHeader, - FrameDeny: config.HTTP.FrameDeny, - ContentTypeNosniff: config.HTTP.ContentTypeNosniff, - BrowserXssFilter: config.HTTP.BrowserXSSFilter, - ContentSecurityPolicy: config.HTTP.ContentSecurityPolicy, - ReferrerPolicy: config.HTTP.ReferrerPolicy, - } -} diff --git a/cmd/drone-server/inject_service.go b/cmd/drone-server/inject_service.go deleted file mode 100644 index 25c9b8eb88..0000000000 --- a/cmd/drone-server/inject_service.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "time" - - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/drone/core" - "github.com/drone/drone/livelog" - "github.com/drone/drone/metric/sink" - "github.com/drone/drone/pubsub" - "github.com/drone/drone/service/canceler" - "github.com/drone/drone/service/canceler/reaper" - "github.com/drone/drone/service/commit" - contents "github.com/drone/drone/service/content" - "github.com/drone/drone/service/content/cache" - "github.com/drone/drone/service/hook" - "github.com/drone/drone/service/hook/parser" - "github.com/drone/drone/service/linker" - "github.com/drone/drone/service/netrc" - orgs "github.com/drone/drone/service/org" - "github.com/drone/drone/service/repo" - "github.com/drone/drone/service/status" - "github.com/drone/drone/service/syncer" - "github.com/drone/drone/service/token" - "github.com/drone/drone/service/transfer" - "github.com/drone/drone/service/user" - "github.com/drone/drone/session" - "github.com/drone/drone/trigger" - "github.com/drone/drone/trigger/cron" - "github.com/drone/drone/version" - "github.com/drone/go-scm/scm" - - "github.com/google/wire" -) - -// wire set for loading the services. -var serviceSet = wire.NewSet( - canceler.New, - commit.New, - cron.New, - livelog.New, - linker.New, - parser.New, - pubsub.New, - token.Renewer, - transfer.New, - trigger.New, - user.New, - - provideRepositoryService, - provideContentService, - provideDatadog, - provideHookService, - provideNetrcService, - provideOrgService, - provideReaper, - provideSession, - provideStatusService, - provideSyncer, - provideSystem, -) - -// provideContentService is a Wire provider function that -// returns a contents service wrapped with a simple LRU cache. -func provideContentService(client *scm.Client, renewer core.Renewer) core.FileService { - return cache.Contents( - contents.New(client, renewer), - ) -} - -// provideHookService is a Wire provider function that returns a -// hook service based on the environment configuration. -func provideHookService(client *scm.Client, renewer core.Renewer, config config.Config) core.HookService { - return hook.New(client, config.Proxy.Addr, renewer) -} - -// provideNetrcService is a Wire provider function that returns -// a netrc service based on the environment configuration. -func provideNetrcService(client *scm.Client, renewer core.Renewer, config config.Config) core.NetrcService { - return netrc.New( - client, - renewer, - config.Cloning.AlwaysAuth, - config.Cloning.Username, - config.Cloning.Password, - ) -} - -// provideOrgService is a Wire provider function that -// returns an organization service wrapped with a simple cache. -func provideOrgService(client *scm.Client, renewer core.Renewer) core.OrganizationService { - return orgs.NewCache(orgs.New(client, renewer), 10, time.Minute*5) -} - -// provideRepo is a Wire provider function that returns -// a repo based on the environment configuration -func provideRepositoryService(client *scm.Client, renewer core.Renewer, config config.Config) core.RepositoryService { - return repo.New( - client, - renewer, - config.Repository.Visibility, - config.Repository.Trusted, - ) -} - -// provideSession is a Wire provider function that returns a -// user session based on the environment configuration. -func provideSession(store core.UserStore, config config.Config) (core.Session, error) { - return session.New(store, session.NewConfig( - config.Session.Secret, - config.Session.Timeout, - config.Session.Secure), - ), nil -} - -// provideUserService is a Wire provider function that returns a -// user service based on the environment configuration. -func provideStatusService(client *scm.Client, renewer core.Renewer, config config.Config) core.StatusService { - return status.New(client, renewer, status.Config{ - Base: config.Server.Addr, - Name: config.Status.Name, - Disabled: config.Status.Disabled, - }) -} - -// provideSyncer is a Wire provider function that returns a -// repository synchronizer. -func provideSyncer(repoz core.RepositoryService, - repos core.RepositoryStore, - users core.UserStore, - batch core.Batcher, - config config.Config) core.Syncer { - sync := syncer.New(repoz, repos, users, batch) - // the user can define a filter that limits which - // repositories can be synchronized and stored in the - // database. - if filter := config.Repository.Filter; len(filter) > 0 { - sync.SetFilter(syncer.NamespaceFilter(filter)) - } - return sync -} - -// provideSyncer is a Wire provider function that returns the -// system details structure. -func provideSystem(config config.Config) *core.System { - return &core.System{ - Proto: config.Server.Proto, - Host: config.Server.Host, - Link: config.Server.Addr, - Version: version.Version.String(), - } -} - -// provideReaper is a Wire provider function that returns the -// zombie build reaper. -func provideReaper( - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, - canceler core.Canceler, - config config.Config, -) *reaper.Reaper { - return reaper.New( - repos, - builds, - stages, - canceler, - config.Cleanup.Running, - config.Cleanup.Pending, - config.Cleanup.Buffer, - ) -} - -// provideDatadog is a Wire provider function that returns the -// datadog sink. -func provideDatadog( - users core.UserStore, - repos core.RepositoryStore, - builds core.BuildStore, - system *core.System, - license *core.License, - config config.Config, -) *sink.Datadog { - return sink.New( - users, - repos, - builds, - *system, - sink.Config{ - Endpoint: config.Datadog.Endpoint, - Token: config.Datadog.Token, - License: license.Kind, - Licensor: license.Licensor, - Subscription: license.Subscription, - EnableGithub: config.IsGitHub(), - EnableGithubEnt: config.IsGitHubEnterprise(), - EnableGitlab: config.IsGitLab(), - EnableBitbucket: config.IsBitbucket(), - EnableStash: config.IsStash(), - EnableGogs: config.IsGogs(), - EnableGitea: config.IsGitea(), - EnableGitee: config.IsGitee(), - EnableAgents: !config.Agent.Disabled, - }, - ) -} diff --git a/cmd/drone-server/inject_store.go b/cmd/drone-server/inject_store.go deleted file mode 100644 index 23a6dcf1fc..0000000000 --- a/cmd/drone-server/inject_store.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/drone/core" - "github.com/drone/drone/metric" - "github.com/drone/drone/store/batch" - "github.com/drone/drone/store/batch2" - "github.com/drone/drone/store/build" - "github.com/drone/drone/store/card" - "github.com/drone/drone/store/cron" - "github.com/drone/drone/store/logs" - "github.com/drone/drone/store/perm" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/secret" - "github.com/drone/drone/store/secret/global" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/encrypt" - "github.com/drone/drone/store/stage" - "github.com/drone/drone/store/step" - "github.com/drone/drone/store/template" - "github.com/drone/drone/store/user" - - "github.com/google/wire" - "github.com/sirupsen/logrus" -) - -// wire set for loading the stores. -var storeSet = wire.NewSet( - provideDatabase, - provideEncrypter, - provideBuildStore, - provideLogStore, - provideRepoStore, - provideStageStore, - provideUserStore, - provideBatchStore, - // batch.New, - cron.New, - card.New, - perm.New, - secret.New, - global.New, - step.New, - template.New, -) - -// provideDatabase is a Wire provider function that provides a -// database connection, configured from the environment. -func provideDatabase(config config.Config) (*db.DB, error) { - return db.Connect( - config.Database.Driver, - config.Database.Datasource, - config.Database.MaxConnections, - ) -} - -// provideEncrypter is a Wire provider function that provides a -// database encrypter, configured from the environment. -func provideEncrypter(config config.Config) (encrypt.Encrypter, error) { - enc, err := encrypt.New(config.Database.Secret) - // mixed-content mode should be set to true if the database - // originally had encryption disabled and therefore has - // plaintext entries. This prevents Drone from returning an - // error if decryption fails; on failure, the ciphertext is - // returned as-is and the error is ignored. - if aesgcm, ok := enc.(*encrypt.Aesgcm); ok { - logrus.Debugln("main: database encryption enabled") - if config.Database.EncryptMixedContent { - logrus.Debugln("main: database encryption mixed-mode enabled") - aesgcm.Compat = true - } - } - return enc, err -} - -// provideBuildStore is a Wire provider function that provides a -// build datastore, configured from the environment, with metrics -// enabled. -func provideBuildStore(db *db.DB) core.BuildStore { - builds := build.New(db) - metric.BuildCount(builds) - metric.PendingBuildCount(builds) - metric.RunningBuildCount(builds) - return builds -} - -// provideLogStore is a Wire provider function that provides a -// log datastore, configured from the environment. -func provideLogStore(db *db.DB, config config.Config) core.LogStore { - s := logs.New(db) - if config.S3.Bucket != "" { - p := logs.NewS3Env( - config.S3.Bucket, - config.S3.Prefix, - config.S3.Endpoint, - config.S3.PathStyle, - ) - return logs.NewCombined(p, s) - } - if config.AzureBlob.ContainerName != "" { - p := logs.NewAzureBlobEnv( - config.AzureBlob.ContainerName, - config.AzureBlob.StorageAccountName, - config.AzureBlob.StorageAccessKey, - ) - return logs.NewCombined(p, s) - } - return s -} - -// provideStageStore is a Wire provider function that provides a -// stage datastore, configured from the environment, with metrics -// enabled. -func provideStageStore(db *db.DB) core.StageStore { - stages := stage.New(db) - metric.PendingJobCount(stages) - metric.RunningJobCount(stages) - return stages -} - -// provideRepoStore is a Wire provider function that provides a -// user datastore, configured from the environment, with metrics -// enabled. -func provideRepoStore(db *db.DB) core.RepositoryStore { - repos := repos.New(db) - metric.RepoCount(repos) - return repos -} - -// provideBatchStore is a Wire provider function that provides a -// batcher. If the experimental batcher is enabled it is returned. -func provideBatchStore(db *db.DB, config config.Config) core.Batcher { - if config.Database.LegacyBatch { - return batch.New(db) - } - return batch2.New(db) -} - -// provideUserStore is a Wire provider function that provides a -// user datastore, configured from the environment, with metrics -// enabled. -func provideUserStore(db *db.DB, enc encrypt.Encrypter, config config.Config) core.UserStore { - // create the user store with encryption iff the user - // encryption feature flag is enabled. - // - // why not enable by default? because the user table is - // accessed on every http request and we are unsure what, - // if any performance implications user table encryption - // may have on the system. - // - // it is very possible there are zero material performance - // implications, however, if there is a performance regression - // we could look at implementing in-memory lru caching, which - // we already employ in other areas of the software. - if config.Database.EncryptUserTable { - logrus.Debugln("main: database encryption enabled for user table") - users := user.New(db, enc) - metric.UserCount(users) - return users - } - - noenc, _ := encrypt.New("") - users := user.New(db, noenc) - metric.UserCount(users) - return users -} diff --git a/cmd/drone-server/main.go b/cmd/drone-server/main.go deleted file mode 100644 index d03a57dc07..0000000000 --- a/cmd/drone-server/main.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "flag" - "fmt" - - "github.com/drone/drone/cmd/drone-server/bootstrap" - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/drone/core" - "github.com/drone/drone/metric/sink" - "github.com/drone/drone/operator/runner" - "github.com/drone/drone/service/canceler/reaper" - "github.com/drone/drone/server" - "github.com/drone/drone/trigger/cron" - "github.com/drone/signal" - - "github.com/joho/godotenv" - "github.com/sirupsen/logrus" - "golang.org/x/sync/errgroup" - - _ "github.com/go-sql-driver/mysql" - _ "github.com/lib/pq" - _ "github.com/mattn/go-sqlite3" -) - -func main() { - var envfile string - flag.StringVar(&envfile, "env-file", ".env", "Read in a file of environment variables") - flag.Parse() - - godotenv.Load(envfile) - config, err := config.Environ() - if err != nil { - logger := logrus.WithError(err) - logger.Fatalln("main: invalid configuration") - } - - initLogging(config) - ctx := signal.WithContext( - context.Background(), - ) - - // if trace level logging is enabled, output the - // configuration parameters. - if logrus.IsLevelEnabled(logrus.TraceLevel) { - fmt.Println(config.String()) - } - - app, err := InitializeApplication(config) - if err != nil { - logger := logrus.WithError(err) - logger.Fatalln("main: cannot initialize server") - } - - // optionally bootstrap the system with administrative or - // machine users configured in the environment. - err = bootstrap.New(app.users).Bootstrap(ctx, &core.User{ - Login: config.Users.Create.Username, - Machine: config.Users.Create.Machine, - Admin: config.Users.Create.Admin, - Hash: config.Users.Create.Token, - }) - if err != nil { - logger := logrus.WithError(err) - logger.Fatalln("cannot bootstrap user account") - } - - g := errgroup.Group{} - g.Go(func() error { - logrus.WithFields( - logrus.Fields{ - "proto": config.Server.Proto, - "host": config.Server.Host, - "port": config.Server.Port, - "url": config.Server.Addr, - "acme": config.Server.Acme, - }, - ).Infoln("starting the http server") - return app.server.ListenAndServe(ctx) - }) - - // launches the datadog sink in a goroutine. If the sink - // is disabled, the goroutine exits immediately without error. - g.Go(func() (err error) { - if !config.Datadog.Enabled { - return nil - } - return app.sink.Start(ctx) - }) - - // launches the cron runner in a goroutine. If the cron - // runner is disabled, the goroutine exits immediately - // without error. - g.Go(func() (err error) { - if config.Cron.Disabled { - return nil - } - logrus.WithField("interval", config.Cron.Interval.String()). - Infoln("starting the cron scheduler") - return app.cron.Start(ctx, config.Cron.Interval) - }) - - // launches the reaper in a goroutine. If the reaper - // is disabled, the goroutine exits immediately - // without error. - g.Go(func() (err error) { - if config.Cleanup.Disabled { - return nil - } - logrus.WithField("interval", config.Cleanup.Interval.String()). - Infoln("starting the zombie build reaper") - return app.reaper.Start(ctx, config.Cleanup.Interval) - }) - - // launches the build runner in a goroutine. If the local - // runner is disabled (because nomad or kubernetes is enabled) - // then the goroutine exits immediately without error. - g.Go(func() (err error) { - if app.runner == nil { - return nil - } - logrus.WithField("threads", config.Runner.Capacity). - Infoln("main: starting the local build runner") - return app.runner.Start(ctx, config.Runner.Capacity) - }) - - if err := g.Wait(); err != nil { - logrus.WithError(err).Fatalln("program terminated") - } -} - -// helper function configures the logging. -func initLogging(c config.Config) { - if c.Logging.Debug { - logrus.SetLevel(logrus.DebugLevel) - } - if c.Logging.Trace { - logrus.SetLevel(logrus.TraceLevel) - } - if c.Logging.Text { - logrus.SetFormatter(&logrus.TextFormatter{ - ForceColors: c.Logging.Color, - DisableColors: !c.Logging.Color, - }) - } else { - logrus.SetFormatter(&logrus.JSONFormatter{ - PrettyPrint: c.Logging.Pretty, - }) - } -} - -// application is the main struct for the Drone server. -type application struct { - cron *cron.Scheduler - reaper *reaper.Reaper - sink *sink.Datadog - runner *runner.Runner - server *server.Server - users core.UserStore -} - -// newApplication creates a new application struct. -func newApplication( - cron *cron.Scheduler, - reaper *reaper.Reaper, - sink *sink.Datadog, - runner *runner.Runner, - server *server.Server, - users core.UserStore) application { - return application{ - users: users, - cron: cron, - sink: sink, - server: server, - runner: runner, - reaper: reaper, - } -} diff --git a/cmd/drone-server/wire.go b/cmd/drone-server/wire.go deleted file mode 100644 index bc9376511e..0000000000 --- a/cmd/drone-server/wire.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//+build wireinject - -package main - -import ( - "github.com/drone/drone/cmd/drone-server/config" - "github.com/google/wire" -) - -func InitializeApplication(config config.Config) (application, error) { - wire.Build( - clientSet, - licenseSet, - loginSet, - pluginSet, - runnerSet, - schedulerSet, - serverSet, - serviceSet, - storeSet, - externalSet, - newApplication, - ) - return application{}, nil -} diff --git a/cmd/drone-server/wire_gen.go b/cmd/drone-server/wire_gen.go deleted file mode 100644 index 99e04df405..0000000000 --- a/cmd/drone-server/wire_gen.go +++ /dev/null @@ -1,120 +0,0 @@ -// Code generated by Wire. DO NOT EDIT. - -//go:generate wire -//+build !wireinject - -package main - -import ( - "github.com/drone/drone/cmd/drone-server/config" - "github.com/drone/drone/handler/api" - "github.com/drone/drone/handler/web" - "github.com/drone/drone/livelog" - "github.com/drone/drone/operator/manager" - "github.com/drone/drone/pubsub" - "github.com/drone/drone/service/canceler" - "github.com/drone/drone/service/commit" - "github.com/drone/drone/service/hook/parser" - "github.com/drone/drone/service/license" - "github.com/drone/drone/service/linker" - "github.com/drone/drone/service/token" - "github.com/drone/drone/service/transfer" - "github.com/drone/drone/service/user" - "github.com/drone/drone/store/card" - "github.com/drone/drone/store/cron" - "github.com/drone/drone/store/perm" - "github.com/drone/drone/store/secret" - "github.com/drone/drone/store/secret/global" - "github.com/drone/drone/store/step" - "github.com/drone/drone/store/template" - "github.com/drone/drone/trigger" - cron2 "github.com/drone/drone/trigger/cron" -) - -import ( - _ "github.com/go-sql-driver/mysql" - _ "github.com/lib/pq" - _ "github.com/mattn/go-sqlite3" -) - -// Injectors from wire.go: - -func InitializeApplication(config2 config.Config) (application, error) { - client := provideClient(config2) - refresher := provideRefresher(config2) - db, err := provideDatabase(config2) - if err != nil { - return application{}, err - } - encrypter, err := provideEncrypter(config2) - if err != nil { - return application{}, err - } - userStore := provideUserStore(db, encrypter, config2) - renewer := token.Renewer(refresher, userStore) - commitService := commit.New(client, renewer) - cronStore := cron.New(db) - repositoryStore := provideRepoStore(db) - buildStore := provideBuildStore(db) - redisDB, err := provideRedisClient(config2) - if err != nil { - return application{}, err - } - corePubsub := pubsub.New(redisDB) - stageStore := provideStageStore(db) - scheduler := provideScheduler(stageStore, redisDB) - statusService := provideStatusService(client, renewer, config2) - stepStore := step.New(db) - system := provideSystem(config2) - webhookSender := provideWebhookPlugin(config2, system) - coreCanceler := canceler.New(buildStore, corePubsub, repositoryStore, scheduler, stageStore, statusService, stepStore, userStore, webhookSender) - fileService := provideContentService(client, renewer) - configService := provideConfigPlugin(client, fileService, config2) - templateStore := template.New(db) - convertService := provideConvertPlugin(client, fileService, config2, templateStore) - validateService := provideValidatePlugin(config2) - triggerer := trigger.New(coreCanceler, configService, convertService, commitService, statusService, buildStore, scheduler, repositoryStore, userStore, validateService, webhookSender) - cronScheduler := cron2.New(commitService, cronStore, repositoryStore, userStore, triggerer) - reaper := provideReaper(repositoryStore, buildStore, stageStore, coreCanceler, config2) - coreLicense := provideLicense(client, config2) - datadog := provideDatadog(userStore, repositoryStore, buildStore, system, coreLicense, config2) - cardStore := card.New(db) - logStore := provideLogStore(db, config2) - logStream := livelog.New(redisDB) - netrcService := provideNetrcService(client, renewer, config2) - secretStore := secret.New(db, encrypter) - globalSecretStore := global.New(db, encrypter) - buildManager := manager.New(buildStore, cardStore, configService, convertService, corePubsub, logStore, logStream, netrcService, repositoryStore, scheduler, secretStore, globalSecretStore, statusService, stageStore, stepStore, system, userStore, webhookSender) - secretService := provideSecretPlugin(config2) - registryService := provideRegistryPlugin(config2) - runner := provideRunner(buildManager, secretService, registryService, config2) - hookService := provideHookService(client, renewer, config2) - licenseService := license.NewService(userStore, repositoryStore, buildStore, coreLicense) - organizationService := provideOrgService(client, renewer) - permStore := perm.New(db) - repositoryService := provideRepositoryService(client, renewer, config2) - session, err := provideSession(userStore, config2) - if err != nil { - return application{}, err - } - batcher := provideBatchStore(db, config2) - syncer := provideSyncer(repositoryService, repositoryStore, userStore, batcher, config2) - transferer := transfer.New(repositoryStore, permStore) - userService := user.New(client, renewer) - server := api.New(buildStore, commitService, cardStore, cronStore, corePubsub, globalSecretStore, hookService, logStore, coreLicense, licenseService, organizationService, permStore, repositoryStore, repositoryService, scheduler, secretStore, stageStore, stepStore, statusService, session, logStream, syncer, system, templateStore, transferer, triggerer, userStore, userService, webhookSender) - admissionService := provideAdmissionPlugin(client, organizationService, userService, config2) - hookParser := parser.New(client) - coreLinker := linker.New(client) - middleware := provideLogin(config2) - options := provideServerOptions(config2) - webServer := web.New(admissionService, buildStore, client, hookParser, coreLicense, licenseService, coreLinker, middleware, repositoryStore, session, syncer, triggerer, userStore, userService, webhookSender, options, system) - mainRpcHandlerV1 := provideRPC(buildManager, config2) - mainRpcHandlerV2 := provideRPC2(buildManager, config2) - mainHealthzHandler := provideHealthz() - metricServer := provideMetric(session, config2) - mainPprofHandler := providePprof(config2) - mux := provideRouter(server, webServer, mainRpcHandlerV1, mainRpcHandlerV2, mainHealthzHandler, metricServer, mainPprofHandler) - serverServer := provideServer(mux, config2) - mainApplication := newApplication(cronScheduler, reaper, datadog, runner, serverServer, userStore) - return mainApplication, nil -} diff --git a/core/admission.go b/core/admission.go deleted file mode 100644 index ad682b2f87..0000000000 --- a/core/admission.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// AdmissionService grants access to the system. The service can -// be used to restrict access to authorized users, such as -// members of an organization in your source control management -// system. -type AdmissionService interface { - Admit(context.Context, *User) error -} diff --git a/core/batch.go b/core/batch.go deleted file mode 100644 index 0303fa0fdd..0000000000 --- a/core/batch.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Batch represents a Batch request to synchronize the local -// repository and permission store for a user account. -type Batch struct { - Insert []*Repository `json:"insert"` - Update []*Repository `json:"update"` - Rename []*Repository `json:"rename"` - Revoke []*Repository `json:"revoke"` -} - -// Batcher batch updates the user account. -type Batcher interface { - Batch(context.Context, *User, *Batch) error -} diff --git a/core/build.go b/core/build.go deleted file mode 100644 index f70761869d..0000000000 --- a/core/build.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Build represents a build execution. -type Build struct { - ID int64 `db:"build_id" json:"id"` - RepoID int64 `db:"build_repo_id" json:"repo_id"` - Trigger string `db:"build_trigger" json:"trigger"` - Number int64 `db:"build_number" json:"number"` - Parent int64 `db:"build_parent" json:"parent,omitempty"` - Status string `db:"build_status" json:"status"` - Error string `db:"build_error" json:"error,omitempty"` - Event string `db:"build_event" json:"event"` - Action string `db:"build_action" json:"action"` - Link string `db:"build_link" json:"link"` - Timestamp int64 `db:"build_timestamp" json:"timestamp"` - Title string `db:"build_title" json:"title,omitempty"` - Message string `db:"build_message" json:"message"` - Before string `db:"build_before" json:"before"` - After string `db:"build_after" json:"after"` - Ref string `db:"build_ref" json:"ref"` - Fork string `db:"build_source_repo" json:"source_repo"` - Source string `db:"build_source" json:"source"` - Target string `db:"build_target" json:"target"` - Author string `db:"build_author" json:"author_login"` - AuthorName string `db:"build_author_name" json:"author_name"` - AuthorEmail string `db:"build_author_email" json:"author_email"` - AuthorAvatar string `db:"build_author_avatar" json:"author_avatar"` - Sender string `db:"build_sender" json:"sender"` - Params map[string]string `db:"build_params" json:"params,omitempty"` - Cron string `db:"build_cron" json:"cron,omitempty"` - Deploy string `db:"build_deploy" json:"deploy_to,omitempty"` - DeployID int64 `db:"build_deploy_id" json:"deploy_id,omitempty"` - Debug bool `db:"build_debug" json:"debug,omitempty"` - Started int64 `db:"build_started" json:"started"` - Finished int64 `db:"build_finished" json:"finished"` - Created int64 `db:"build_created" json:"created"` - Updated int64 `db:"build_updated" json:"updated"` - Version int64 `db:"build_version" json:"version"` - Stages []*Stage `db:"-" json:"stages,omitempty"` -} - -// BuildStore defines operations for working with builds. -type BuildStore interface { - // Find returns a build from the datastore. - Find(context.Context, int64) (*Build, error) - - // FindNumber returns a build from the datastore by build number. - FindNumber(context.Context, int64, int64) (*Build, error) - - // FindLast returns the last build from the datastore by ref. - FindRef(context.Context, int64, string) (*Build, error) - - // List returns a list of builds from the datastore by repository id. - List(context.Context, int64, int, int) ([]*Build, error) - - // ListRef returns a list of builds from the datastore by ref. - ListRef(context.Context, int64, string, int, int) ([]*Build, error) - - // LatestBranches returns the latest builds from the - // datastore by branch. - LatestBranches(context.Context, int64) ([]*Build, error) - - // LatestPulls returns the latest builds from the - // datastore by pull request. - LatestPulls(context.Context, int64) ([]*Build, error) - - // LatestDeploys returns the latest builds from the - // datastore by deployment target. - LatestDeploys(context.Context, int64) ([]*Build, error) - - // Pending returns a list of pending builds from the - // datastore by repository id (DEPRECATED). - Pending(context.Context) ([]*Build, error) - - // Running returns a list of running builds from the - // datastore by repository id (DEPRECATED). - Running(context.Context) ([]*Build, error) - - // Create persists a build to the datastore. - Create(context.Context, *Build, []*Stage) error - - // Update updates a build in the datastore. - Update(context.Context, *Build) error - - // Delete deletes a build from the datastore. - Delete(context.Context, *Build) error - - // DeletePull deletes a pull request index from the datastore. - DeletePull(context.Context, int64, int) error - - // DeleteBranch deletes a branch index from the datastore. - DeleteBranch(context.Context, int64, string) error - - // DeleteDeploy deletes a deploy index from the datastore. - DeleteDeploy(context.Context, int64, string) error - - // Purge deletes builds from the database where the build number is less than n. - Purge(context.Context, int64, int64) error - - // Count returns a count of builds. - Count(context.Context) (int64, error) -} - -// IsDone returns true if the build has a completed state. -func (b *Build) IsDone() bool { - switch b.Status { - case StatusWaiting, - StatusPending, - StatusRunning, - StatusBlocked: - return false - default: - return true - } -} - -// IsFailed returns true if the build has failed -func (b *Build) IsFailed() bool { - switch b.Status { - case StatusFailing, - StatusKilled, - StatusError: - return true - default: - return false - } -} diff --git a/core/build_test.go b/core/build_test.go deleted file mode 100644 index 0912528b2f..0000000000 --- a/core/build_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core diff --git a/core/cancel.go b/core/cancel.go deleted file mode 100644 index 00a4a5ecfc..0000000000 --- a/core/cancel.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Canceler cancels a build. -type Canceler interface { - // Cancel cancels the provided build. - Cancel(context.Context, *Repository, *Build) error - - // CancelPending cancels all pending builds of the same - // type of as the provided build. - CancelPending(context.Context, *Repository, *Build) error -} diff --git a/core/card.go b/core/card.go deleted file mode 100644 index 2da4dcc40a..0000000000 --- a/core/card.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - "encoding/json" - "io" -) - -type CardInput struct { - Schema string `json:"schema"` - Data json.RawMessage `json:"data"` -} - -// CardStore manages repository cards. -type CardStore interface { - // Find returns a card data stream from the datastore. - Find(ctx context.Context, step int64) (io.ReadCloser, error) - - // Create copies the card stream from Reader r to the datastore. - Create(ctx context.Context, step int64, r io.Reader) error - - // Update copies the card stream from Reader r to the datastore. - Update(ctx context.Context, step int64, r io.Reader) error - - // Delete purges the card data from the datastore. - Delete(ctx context.Context, step int64) error -} diff --git a/core/commit.go b/core/commit.go deleted file mode 100644 index f556b9d8e2..0000000000 --- a/core/commit.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -type ( - // Commit represents a git commit. - Commit struct { - Sha string - Ref string - Message string - Author *Committer - Committer *Committer - Link string - } - - // Committer represents the commit author. - Committer struct { - Name string - Email string - Date int64 - Login string - Avatar string - } - - // Change represents a file change in a commit. - Change struct { - Path string - Added bool - Renamed bool - Deleted bool - } - - // CommitService provides access to the commit history from - // the external source code management service (e.g. GitHub). - CommitService interface { - // Find returns the commit information by sha. - Find(ctx context.Context, user *User, repo, sha string) (*Commit, error) - - // FindRef returns the commit information by reference. - FindRef(ctx context.Context, user *User, repo, ref string) (*Commit, error) - - // ListChanges returns the files change by sha or reference. - ListChanges(ctx context.Context, user *User, repo, sha, ref string) ([]*Change, error) - } -) diff --git a/core/config.go b/core/config.go deleted file mode 100644 index d3c2acd2b5..0000000000 --- a/core/config.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -type ( - // Config represents a pipeline config file. - Config struct { - Data string `json:"data"` - Kind string `json:"kind"` - } - - // ConfigArgs represents a request for the pipeline - // configuration file (e.g. .drone.yml) - ConfigArgs struct { - User *User `json:"-"` - Repo *Repository `json:"repo,omitempty"` - Build *Build `json:"build,omitempty"` - Config *Config `json:"config,omitempty"` - } - - // ConfigService provides pipeline configuration from an - // external service. - ConfigService interface { - Find(context.Context, *ConfigArgs) (*Config, error) - } -) diff --git a/core/convert.go b/core/convert.go deleted file mode 100644 index 8ed8e02078..0000000000 --- a/core/convert.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -type ( - // ConvertArgs represents a request to the pipeline - // conversion service. - ConvertArgs struct { - User *User `json:"-"` - Repo *Repository `json:"repo,omitempty"` - Build *Build `json:"build,omitempty"` - Config *Config `json:"config,omitempty"` - } - - // ConvertService converts non-native pipeline - // configuration formats to native configuration - // formats (e.g. jsonnet to yaml). - ConvertService interface { - Convert(context.Context, *ConvertArgs) (*Config, error) - } -) diff --git a/core/cron.go b/core/cron.go deleted file mode 100644 index 5d64d2b817..0000000000 --- a/core/cron.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - "errors" - "time" - - "github.com/gosimple/slug" - "github.com/robfig/cron" -) - -var ( - errCronExprInvalid = errors.New("Invalid Cronjob Expression") - errCronNameInvalid = errors.New("Invalid Cronjob Name") - errCronBranchInvalid = errors.New("Invalid Cronjob Branch") -) - -type ( - // Cron defines a cron job. - Cron struct { - ID int64 `json:"id"` - RepoID int64 `json:"repo_id"` - Name string `json:"name"` - Expr string `json:"expr"` - Next int64 `json:"next"` - Prev int64 `json:"prev"` - Event string `json:"event"` - Branch string `json:"branch"` - Target string `json:"target,omitempty"` - Disabled bool `json:"disabled"` - Created int64 `json:"created"` - Updated int64 `json:"updated"` - Version int64 `json:"version"` - } - - // CronStore persists cron information to storage. - CronStore interface { - // List returns a cron list from the datastore. - List(context.Context, int64) ([]*Cron, error) - - // Ready returns a cron list from the datastore ready for execution. - Ready(context.Context, int64) ([]*Cron, error) - - // Find returns a cron job from the datastore. - Find(context.Context, int64) (*Cron, error) - - // FindName returns a cron job from the datastore. - FindName(context.Context, int64, string) (*Cron, error) - - // Create persists a new cron job to the datastore. - Create(context.Context, *Cron) error - - // Update persists an updated cron job to the datastore. - Update(context.Context, *Cron) error - - // Delete deletes a cron job from the datastore. - Delete(context.Context, *Cron) error - } -) - -// Validate validates the required fields and formats. -func (c *Cron) Validate() error { - _, err := cron.Parse(c.Expr) - if err != nil { - return errCronExprInvalid - } - switch { - case c.Name == "": - return errCronNameInvalid - case c.Name != slug.Make(c.Name): - return errCronNameInvalid - case c.Branch == "": - return errCronBranchInvalid - default: - return nil - } -} - -// SetExpr sets the cron expression name and updates -// the next execution date. -func (c *Cron) SetExpr(expr string) error { - _, err := cron.Parse(expr) - if err != nil { - return errCronExprInvalid - } - c.Expr = expr - return c.Update() -} - -// SetName sets the cronjob name. -func (c *Cron) SetName(name string) { - c.Name = slug.Make(name) -} - -// Update updates the next Cron execution date. -func (c *Cron) Update() error { - sched, err := cron.Parse(c.Expr) - if err != nil { - return err - } - c.Next = sched.Next(time.Now()).Unix() - return nil -} diff --git a/core/cron_test.go b/core/cron_test.go deleted file mode 100644 index 0912528b2f..0000000000 --- a/core/cron_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core diff --git a/core/event.go b/core/event.go deleted file mode 100644 index adffb5df20..0000000000 --- a/core/event.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -// Hook event constants. -const ( - EventCron = "cron" - EventCustom = "custom" - EventPush = "push" - EventPullRequest = "pull_request" - EventTag = "tag" - EventPromote = "promote" - EventRollback = "rollback" -) diff --git a/core/file.go b/core/file.go deleted file mode 100644 index 3f7a1ca947..0000000000 --- a/core/file.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -type ( - // File represents the raw file contents in the remote - // version control system. - File struct { - Data []byte - Hash []byte - } - - // FileArgs provides repository and commit details required - // to fetch the file from the remote source code management - // service. - FileArgs struct { - Commit string - Ref string - } - - // FileService provides access to contents of files in - // the remote source code management service (e.g. GitHub). - FileService interface { - Find(ctx context.Context, user *User, repo, commit, ref, path string) (*File, error) - } -) diff --git a/core/hook.go b/core/hook.go deleted file mode 100644 index 6b05095604..0000000000 --- a/core/hook.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - "net/http" -) - -// Hook action constants. -const ( - ActionOpen = "open" - ActionClose = "close" - ActionCreate = "create" - ActionDelete = "delete" - ActionSync = "sync" -) - -// Hook represents the payload of a post-commit hook. -type Hook struct { - Parent int64 `json:"parent"` - Trigger string `json:"trigger"` - Event string `json:"event"` - Action string `json:"action"` - Link string `json:"link"` - Timestamp int64 `json:"timestamp"` - Title string `json:"title"` - Message string `json:"message"` - Before string `json:"before"` - After string `json:"after"` - Ref string `json:"ref"` - Fork string `json:"hook"` - Source string `json:"source"` - Target string `json:"target"` - Author string `json:"author_login"` - AuthorName string `json:"author_name"` - AuthorEmail string `json:"author_email"` - AuthorAvatar string `json:"author_avatar"` - Deployment string `json:"deploy_to"` - DeploymentID int64 `json:"deploy_id"` - Debug bool `json:"debug"` - Cron string `json:"cron"` - Sender string `json:"sender"` - Params map[string]string `json:"params"` -} - -// HookService manages post-commit hooks in the external -// source code management service (e.g. GitHub). -type HookService interface { - Create(ctx context.Context, user *User, repo *Repository) error - Delete(ctx context.Context, user *User, repo *Repository) error -} - -// HookParser parses a post-commit hook from the source -// code management system, and returns normalized data. -type HookParser interface { - Parse(req *http.Request, secretFunc func(string) string) (*Hook, *Repository, error) -} diff --git a/core/hook_test.go b/core/hook_test.go deleted file mode 100644 index 0912528b2f..0000000000 --- a/core/hook_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core diff --git a/core/license.go b/core/license.go deleted file mode 100644 index 0927cb834f..0000000000 --- a/core/license.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - "errors" - "time" -) - -// License types. -const ( - LicenseFoss = "foss" - LicenseFree = "free" - LicensePersonal = "personal" - LicenseStandard = "standard" - LicenseTrial = "trial" -) - -// ErrUserLimit is returned when attempting to create a new -// user but the maximum number of allowed user accounts -// is exceeded. -var ErrUserLimit = errors.New("User limit exceeded") - -// ErrRepoLimit is returned when attempting to create a new -// repository but the maximum number of allowed repositories -// is exceeded. -var ErrRepoLimit = errors.New("Repository limit exceeded") - -// ErrBuildLimit is returned when attempting to create a new -// build but the maximum number of allowed builds is exceeded. -var ErrBuildLimit = errors.New("Build limit exceeded") - -type ( - // License defines software license parameters. - License struct { - Licensor string `json:"-"` - Subscription string `json:"-"` - Expires time.Time `json:"expires_at,omitempty"` - Kind string `json:"kind,omitempty"` - Repos int64 `json:"repos,omitempty"` - Users int64 `json:"users,omitempty"` - Builds int64 `json:"builds,omitempty"` - Nodes int64 `json:"nodes,omitempty"` - } - - // LicenseService provides access to the license - // service and can be used to check for violations - // and expirations. - LicenseService interface { - // Exceeded returns true if the system has exceeded - // its limits as defined in the license. - Exceeded(context.Context) (bool, error) - - // Expired returns true if the license is expired. - Expired(context.Context) bool - } -) - -// Expired returns true if the license is expired. -func (l *License) Expired() bool { - return l.Expires.IsZero() == false && time.Now().After(l.Expires) -} diff --git a/core/license_test.go b/core/license_test.go deleted file mode 100644 index 0912528b2f..0000000000 --- a/core/license_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core diff --git a/core/linker.go b/core/linker.go deleted file mode 100644 index 6cf5fdc215..0000000000 --- a/core/linker.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Linker provides a deep link to a git resource in the -// source control management system for a given build. -type Linker interface { - Link(ctx context.Context, repo, ref, sha string) (string, error) -} diff --git a/core/logs.go b/core/logs.go deleted file mode 100644 index 65a7074f76..0000000000 --- a/core/logs.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - "io" -) - -// Line represents a line in the logs. -type Line struct { - Number int `json:"pos"` - Message string `json:"out"` - Timestamp int64 `json:"time"` -} - -// LogStore persists build output to storage. -type LogStore interface { - // Find returns a log stream from the datastore. - Find(ctx context.Context, stage int64) (io.ReadCloser, error) - - // Create writes copies the log stream from Reader r to the datastore. - Create(ctx context.Context, stage int64, r io.Reader) error - - // Update writes copies the log stream from Reader r to the datastore. - Update(ctx context.Context, stage int64, r io.Reader) error - - // Delete purges the log stream from the datastore. - Delete(ctx context.Context, stage int64) error -} - -// LogStream manages a live stream of logs. -type LogStream interface { - // Create creates the log stream for the step ID. - Create(context.Context, int64) error - - // Delete deletes the log stream for the step ID. - Delete(context.Context, int64) error - - // Writes writes to the log stream. - Write(context.Context, int64, *Line) error - - // Tail tails the log stream. - Tail(context.Context, int64) (<-chan *Line, <-chan error) - - // Info returns internal stream information. - Info(context.Context) *LogStreamInfo -} - -// LogStreamInfo provides internal stream information. This can -// be used to monitor the number of registered streams and -// subscribers. -type LogStreamInfo struct { - // Streams is a key-value pair where the key is the step - // identifier, and the value is the count of subscribers - // streaming the logs. - Streams map[int64]int `json:"streams"` -} diff --git a/core/netrc.go b/core/netrc.go deleted file mode 100644 index 2e11dd2281..0000000000 --- a/core/netrc.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - "fmt" - "net/url" -) - -type ( - // Netrc contains login and initialization information used by - // an automated login process. - Netrc struct { - Machine string `json:"machine"` - Login string `json:"login"` - Password string `json:"password"` - } - - // NetrcService returns a valid netrc file that can be used - // to authenticate and clone a private repository. If - // authentication is not required or enabled, a nil Netrc - // file and nil error are returned. - NetrcService interface { - Create(context.Context, *User, *Repository) (*Netrc, error) - } -) - -// SetMachine sets the netrc machine from a URL value. -func (n *Netrc) SetMachine(address string) error { - url, err := url.Parse(address) - if err != nil { - return err - } - n.Machine = url.Hostname() - return nil -} - -// String returns the string representation of a netrc file. -func (n *Netrc) String() string { - return fmt.Sprintf("machine %s login %s password %s", - n.Machine, - n.Login, - n.Password, - ) -} diff --git a/core/netrc_test.go b/core/netrc_test.go deleted file mode 100644 index 0912528b2f..0000000000 --- a/core/netrc_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core diff --git a/core/org.go b/core/org.go deleted file mode 100644 index 4ea548e6e4..0000000000 --- a/core/org.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Organization represents an organization in the source -// code management system (e.g. GitHub). -type Organization struct { - Name string - Avatar string -} - -// OrganizationService provides access to organization and -// team access in the external source code management system -// (e.g. GitHub). -type OrganizationService interface { - // List returns a list of organization to which the - // user is a member. - List(context.Context, *User) ([]*Organization, error) - - // Membership returns true if the user is a member - // of the organization, and true if the user is an - // of the organization. - Membership(context.Context, *User, string) (bool, bool, error) -} diff --git a/core/perm.go b/core/perm.go deleted file mode 100644 index 4479ed365a..0000000000 --- a/core/perm.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -type ( - // Perm represents an individuals repository - // permission. - Perm struct { - UserID int64 `db:"perm_user_id" json:"-"` - RepoUID string `db:"perm_repo_uid" json:"-"` - Read bool `db:"perm_read" json:"read"` - Write bool `db:"perm_write" json:"write"` - Admin bool `db:"perm_admin" json:"admin"` - Synced int64 `db:"perm_synced" json:"-"` - Created int64 `db:"perm_created" json:"-"` - Updated int64 `db:"perm_updated" json:"-"` - } - - // Collaborator represents a project collaborator, - // and provides the account and repository permissions - // details. - Collaborator struct { - UserID int64 `db:"perm_user_id" json:"user_id"` - RepoUID string `db:"perm_repo_uid" json:"repo_id"` - Login string `db:"user_login" json:"login"` - Avatar string `db:"user_avatar" json:"avatar"` - Read bool `db:"perm_read" json:"read"` - Write bool `db:"perm_write" json:"write"` - Admin bool `db:"perm_admin" json:"admin"` - Synced int64 `db:"perm_synced" json:"synced"` - Created int64 `db:"perm_created" json:"created"` - Updated int64 `db:"perm_updated" json:"updated"` - } - - // PermStore defines operations for working with - // repository permissions. - PermStore interface { - // Find returns a project member from the - // datastore. - Find(ctx context.Context, repoUID string, userID int64) (*Perm, error) - - // List returns a list of project members from the - // datastore. - List(ctx context.Context, repoUID string) ([]*Collaborator, error) - - // Update persists an updated project member - // to the datastore. - Update(context.Context, *Perm) error - - // Delete deletes a project member from the - // datastore. - Delete(context.Context, *Perm) error - } -) diff --git a/core/pubsub.go b/core/pubsub.go deleted file mode 100644 index 59e4d97a7f..0000000000 --- a/core/pubsub.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Message defines a build change. -type Message struct { - Repository string - Visibility string - Data []byte -} - -// Pubsub provides publish subscriber capabilities, distributing -// messages from multiple publishers to multiple subscribers. -type Pubsub interface { - // Publish publishes the message to all subscribers. - Publish(context.Context, *Message) error - - // Subscribe subscribes to the message broker. - Subscribe(context.Context) (<-chan *Message, <-chan error) - - // Subscribers returns a count of subscribers. - Subscribers() (int, error) -} diff --git a/core/registry.go b/core/registry.go deleted file mode 100644 index b8cc3f0844..0000000000 --- a/core/registry.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - - "github.com/drone/drone-yaml/yaml" -) - -const ( - // RegistryPull policy allows pulling from a registry. - RegistryPull = "pull" - - // RegistryPush Policy allows pushing to a registry for - // all event types except pull requests. - RegistryPush = "push" - - // RegistryPushPullRequest Policy allows pushing to a - // registry for all event types, including pull requests. - RegistryPushPullRequest = "push-pull-request" -) - -type ( - // Registry represents a docker registry with credentials. - Registry struct { - Address string `json:"address"` - Username string `json:"username"` - Password string `json:"password"` - Policy string `json:"policy"` - } - - // RegistryArgs provides arguments for requesting - // registry credentials from the remote service. - RegistryArgs struct { - Repo *Repository `json:"repo,omitempty"` - Build *Build `json:"build,omitempty"` - Conf *yaml.Manifest `json:"-"` - Pipeline *yaml.Pipeline `json:"-"` - } - - // RegistryService provides registry credentials from an - // external service. - RegistryService interface { - // List returns registry credentials from the global - // remote registry plugin. - List(context.Context, *RegistryArgs) ([]*Registry, error) - } -) diff --git a/core/renewer.go b/core/renewer.go deleted file mode 100644 index 2f3fac66a7..0000000000 --- a/core/renewer.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Renewer renews the user account authorization. If -// successful, the user token and token expiry attributes -// are updated, and persisted to the datastore. -type Renewer interface { - Renew(ctx context.Context, user *User, force bool) error -} diff --git a/core/repo.go b/core/repo.go deleted file mode 100644 index 2220213abd..0000000000 --- a/core/repo.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Repository visibility. -const ( - VisibilityPublic = "public" - VisibilityPrivate = "private" - VisibilityInternal = "internal" -) - -// Version control systems. -const ( - VersionControlGit = "git" - VersionControlMercurial = "hg" -) - -type ( - // Repository represents a source code repository. - Repository struct { - ID int64 `json:"id"` - UID string `json:"uid"` - UserID int64 `json:"user_id"` - Namespace string `json:"namespace"` - Name string `json:"name"` - Slug string `json:"slug"` - SCM string `json:"scm"` - HTTPURL string `json:"git_http_url"` - SSHURL string `json:"git_ssh_url"` - Link string `json:"link"` - Branch string `json:"default_branch"` - Private bool `json:"private"` - Visibility string `json:"visibility"` - Active bool `json:"active"` - Config string `json:"config_path"` - Trusted bool `json:"trusted"` - Protected bool `json:"protected"` - IgnoreForks bool `json:"ignore_forks"` - IgnorePulls bool `json:"ignore_pull_requests"` - CancelPulls bool `json:"auto_cancel_pull_requests"` - CancelPush bool `json:"auto_cancel_pushes"` - CancelRunning bool `json:"auto_cancel_running"` - Timeout int64 `json:"timeout"` - Throttle int64 `json:"throttle,omitempty"` - Counter int64 `json:"counter"` - Synced int64 `json:"synced"` - Created int64 `json:"created"` - Updated int64 `json:"updated"` - Version int64 `json:"version"` - Signer string `json:"-"` - Secret string `json:"-"` - Build *Build `json:"build,omitempty"` - Perms *Perm `json:"permissions,omitempty"` - Archived bool `json:"archived"` - } - - RepoBuildStage struct { - RepoNamespace string `json:"repo_namespace"` - RepoName string `json:"repo_name"` - RepoSlug string `json:"repo_slug"` - BuildNumber int64 `json:"build_number"` - BuildAuthor string `json:"build_author"` - BuildAuthorName string `json:"build_author_name"` - BuildAuthorEmail string `json:"build_author_email"` - BuildAuthorAvatar string `json:"build_author_avatar"` - BuildSender string `json:"build_sender"` - BuildStarted int64 `json:"build_started"` - BuildFinished int64 `json:"build_finished"` - BuildCreated int64 `json:"build_created"` - BuildUpdated int64 `json:"build_updated"` - StageName string `json:"stage_name"` - StageKind string `json:"stage_kind"` - StageType string `json:"stage_type"` - StageStatus string `json:"stage_status"` - StageMachine string `json:"stage_machine"` - StageOS string `json:"stage_os"` - StageArch string `json:"stage_arch"` - StageVariant string `json:"stage_variant"` - StageKernel string `json:"stage_kernel"` - StageLimit string `json:"stage_limit"` - StageLimitRepo string `json:"stage_limit_repo"` - StageStarted int64 `json:"stage_started"` - StageStopped int64 `json:"stage_stopped"` - } - - // RepositoryStore defines operations for working with repositories. - RepositoryStore interface { - // List returns a repository list from the datastore. - List(context.Context, int64) ([]*Repository, error) - - // ListLatest returns a unique repository list form - // the datastore with the most recent build. - ListLatest(context.Context, int64) ([]*Repository, error) - - // ListRecent returns a non-unique repository list form - // the datastore with the most recent builds. - ListRecent(context.Context, int64) ([]*Repository, error) - - // ListIncomplete returns a non-unique repository list form - // the datastore with incomplete builds. - ListIncomplete(context.Context) ([]*Repository, error) - - // ListRunningStatus returns a list of build / repository /stage information for builds that are incomplete. - ListRunningStatus(context.Context) ([]*RepoBuildStage, error) - - // ListAll returns a paginated list of all repositories - // stored in the database, including disabled repositories. - ListAll(ctx context.Context, limit, offset int) ([]*Repository, error) - - // Find returns a repository from the datastore. - Find(context.Context, int64) (*Repository, error) - - // FindName returns a named repository from the datastore. - FindName(context.Context, string, string) (*Repository, error) - - // Create persists a new repository in the datastore. - Create(context.Context, *Repository) error - - // Activate persists the activated repository to the datastore. - Activate(context.Context, *Repository) error - - // Update persists repository changes to the datastore. - Update(context.Context, *Repository) error - - // Delete deletes a repository from the datastore. - Delete(context.Context, *Repository) error - - // Count returns a count of activated repositories. - Count(context.Context) (int64, error) - - // Increment returns an incremented build number - Increment(context.Context, *Repository) (*Repository, error) - } - - // RepositoryService provides access to repository information - // in the remote source code management system (e.g. GitHub). - RepositoryService interface { - // List returns a list of repositories. - List(ctx context.Context, user *User) ([]*Repository, error) - - // Find returns the named repository details. - Find(ctx context.Context, user *User, repo string) (*Repository, error) - - // FindPerm returns the named repository permissions. - FindPerm(ctx context.Context, user *User, repo string) (*Perm, error) - } -) diff --git a/core/sched.go b/core/sched.go deleted file mode 100644 index 58e2488f86..0000000000 --- a/core/sched.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Filter provides filter criteria to limit stages requested -// from the scheduler. -type Filter struct { - Kind string - Type string - OS string - Arch string - Kernel string - Variant string - Labels map[string]string -} - -// Scheduler schedules Build stages for execution. -type Scheduler interface { - // Schedule schedules the stage for execution. - Schedule(context.Context, *Stage) error - - // Request requests the next stage scheduled for execution. - Request(context.Context, Filter) (*Stage, error) - - // Cancel cancels scheduled or running jobs associated - // with the parent build ID. - Cancel(context.Context, int64) error - - // Cancelled blocks and listens for a cancellation event and - // returns true if the build has been cancelled. - Cancelled(context.Context, int64) (bool, error) - - // Pause pauses the scheduler and prevents new pipelines - // from being scheduled for execution. - Pause(context.Context) error - - // Resume unpauses the scheduler, allowing new pipelines - // to be scheduled for execution. - Resume(context.Context) error - - // Stats provides statistics for underlying scheduler. The - // data format is scheduler-specific. - Stats(context.Context) (interface{}, error) -} diff --git a/core/secret.go b/core/secret.go deleted file mode 100644 index 79776f7759..0000000000 --- a/core/secret.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - "errors" - "regexp" - - "github.com/drone/drone-yaml/yaml" -) - -var ( - errSecretNameInvalid = errors.New("Invalid Secret Name") - errSecretDataInvalid = errors.New("Invalid Secret Value") -) - -type ( - // Secret represents a secret variable, such as a password or token, - // that is provided to the build at runtime. - Secret struct { - ID int64 `json:"id,omitempty"` - RepoID int64 `json:"repo_id,omitempty"` - Namespace string `json:"namespace,omitempty"` - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - Data string `json:"data,omitempty"` - PullRequest bool `json:"pull_request,omitempty"` - PullRequestPush bool `json:"pull_request_push,omitempty"` - } - - // SecretArgs provides arguments for requesting secrets - // from the remote service. - SecretArgs struct { - Name string `json:"name"` - Repo *Repository `json:"repo,omitempty"` - Build *Build `json:"build,omitempty"` - Conf *yaml.Manifest `json:"-"` - } - - // SecretStore manages repository secrets. - SecretStore interface { - // List returns a secret list from the datastore. - List(context.Context, int64) ([]*Secret, error) - - // Find returns a secret from the datastore. - Find(context.Context, int64) (*Secret, error) - - // FindName returns a secret from the datastore. - FindName(context.Context, int64, string) (*Secret, error) - - // Create persists a new secret to the datastore. - Create(context.Context, *Secret) error - - // Update persists an updated secret to the datastore. - Update(context.Context, *Secret) error - - // Delete deletes a secret from the datastore. - Delete(context.Context, *Secret) error - } - - // GlobalSecretStore manages global secrets accessible to - // all repositories in the system. - GlobalSecretStore interface { - // List returns a secret list from the datastore. - List(ctx context.Context, namespace string) ([]*Secret, error) - - // ListAll returns a secret list from the datastore - // for all namespaces. - ListAll(ctx context.Context) ([]*Secret, error) - - // Find returns a secret from the datastore. - Find(ctx context.Context, id int64) (*Secret, error) - - // FindName returns a secret from the datastore. - FindName(ctx context.Context, namespace, name string) (*Secret, error) - - // Create persists a new secret to the datastore. - Create(ctx context.Context, secret *Secret) error - - // Update persists an updated secret to the datastore. - Update(ctx context.Context, secret *Secret) error - - // Delete deletes a secret from the datastore. - Delete(ctx context.Context, secret *Secret) error - } - - // SecretService provides secrets from an external service. - SecretService interface { - // Find returns a named secret from the global remote service. - Find(context.Context, *SecretArgs) (*Secret, error) - } -) - -// Validate validates the required fields and formats. -func (s *Secret) Validate() error { - switch { - case len(s.Name) == 0: - return errSecretNameInvalid - case len(s.Data) == 0: - return errSecretDataInvalid - case slugRE.MatchString(s.Name): - return errSecretNameInvalid - default: - return nil - } -} - -// Copy makes a copy of the secret without the value. -func (s *Secret) Copy() *Secret { - return &Secret{ - ID: s.ID, - RepoID: s.RepoID, - Namespace: s.Namespace, - Name: s.Name, - Type: s.Type, - PullRequest: s.PullRequest, - PullRequestPush: s.PullRequestPush, - } -} - -// slug regular expression -var slugRE = regexp.MustCompile("[^a-zA-Z0-9-_.]+") diff --git a/core/secret_test.go b/core/secret_test.go deleted file mode 100644 index 96df2b028e..0000000000 --- a/core/secret_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core - -import "testing" - -func TestSecretValidate(t *testing.T) { - tests := []struct { - secret *Secret - error error - }{ - { - secret: &Secret{Name: "password", Data: "correct-horse-battery-staple"}, - error: nil, - }, - { - secret: &Secret{Name: ".some_random-password", Data: "correct-horse-battery-staple"}, - error: nil, - }, - { - secret: &Secret{Name: "password", Data: ""}, - error: errSecretDataInvalid, - }, - { - secret: &Secret{Name: "", Data: "correct-horse-battery-staple"}, - error: errSecretNameInvalid, - }, - { - secret: &Secret{Name: "docker/password", Data: "correct-horse-battery-staple"}, - error: errSecretNameInvalid, - }, - } - for i, test := range tests { - got, want := test.secret.Validate(), test.error - if got != want { - t.Errorf("Want error %v, got %v at index %d", want, got, i) - } - } -} - -func TestSecretSafeCopy(t *testing.T) { - before := Secret{ - ID: 1, - RepoID: 2, - Name: "docker_password", - Namespace: "octocat", - Type: "", - Data: "correct-horse-battery-staple", - PullRequest: true, - PullRequestPush: true, - } - after := before.Copy() - if got, want := after.ID, before.ID; got != want { - t.Errorf("Want secret ID %d, got %d", want, got) - } - if got, want := after.RepoID, before.RepoID; got != want { - t.Errorf("Want secret RepoID %d, got %d", want, got) - } - if got, want := after.Name, before.Name; got != want { - t.Errorf("Want secret Name %s, got %s", want, got) - } - if got, want := after.Namespace, before.Namespace; got != want { - t.Errorf("Want secret Namespace %s, got %s", want, got) - } - if got, want := after.PullRequest, before.PullRequest; got != want { - t.Errorf("Want secret PullRequest %v, got %v", want, got) - } - if got, want := after.PullRequestPush, before.PullRequestPush; got != want { - t.Errorf("Want secret PullRequest %v, got %v", want, got) - } - if after.Data != "" { - t.Errorf("Expect secret is empty after copy") - } -} diff --git a/core/session.go b/core/session.go deleted file mode 100644 index 3200caba69..0000000000 --- a/core/session.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "net/http" - -// Session provides session management for -// authenticated users. -type Session interface { - // Create creates a new user session and writes the - // session to the http.Response. - Create(http.ResponseWriter, *User) error - - // Delete deletes the user session from the http.Response. - Delete(http.ResponseWriter) error - - // Get returns the session from the http.Request. If no - // session exists a nil user is returned. Returning an - // error is optional, for debugging purposes only. - Get(*http.Request) (*User, error) -} diff --git a/core/stage.go b/core/stage.go deleted file mode 100644 index c4ca9471a6..0000000000 --- a/core/stage.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -type ( - // Stage represents a stage of build execution. - Stage struct { - ID int64 `json:"id"` - RepoID int64 `json:"repo_id"` - BuildID int64 `json:"build_id"` - Number int `json:"number"` - Name string `json:"name"` - Kind string `json:"kind,omitempty"` - Type string `json:"type,omitempty"` - Status string `json:"status"` - Error string `json:"error,omitempty"` - ErrIgnore bool `json:"errignore"` - ExitCode int `json:"exit_code"` - Machine string `json:"machine,omitempty"` - OS string `json:"os"` - Arch string `json:"arch"` - Variant string `json:"variant,omitempty"` - Kernel string `json:"kernel,omitempty"` - Limit int `json:"limit,omitempty"` - LimitRepo int `json:"throttle,omitempty"` - Started int64 `json:"started"` - Stopped int64 `json:"stopped"` - Created int64 `json:"created"` - Updated int64 `json:"updated"` - Version int64 `json:"version"` - OnSuccess bool `json:"on_success"` - OnFailure bool `json:"on_failure"` - DependsOn []string `json:"depends_on,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Steps []*Step `json:"steps,omitempty"` - } - - // StageStore persists build stage information to storage. - StageStore interface { - // List returns a build stage list from the datastore. - List(context.Context, int64) ([]*Stage, error) - - // List returns a build stage list from the datastore - // where the stage is incomplete (pending or running). - ListIncomplete(ctx context.Context) ([]*Stage, error) - - // ListSteps returns a build stage list from the datastore, - // with the individual steps included. - ListSteps(context.Context, int64) ([]*Stage, error) - - // ListState returns a build stage list from the database - // across all repositories. - ListState(context.Context, string) ([]*Stage, error) - - // Find returns a build stage from the datastore by ID. - Find(context.Context, int64) (*Stage, error) - - // FindNumber returns a stage from the datastore by number. - FindNumber(context.Context, int64, int) (*Stage, error) - - // Create persists a new stage to the datastore. - Create(context.Context, *Stage) error - - // Update persists an updated stage to the datastore. - Update(context.Context, *Stage) error - } -) - -// IsDone returns true if the step has a completed state. -func (s *Stage) IsDone() bool { - switch s.Status { - case StatusWaiting, - StatusPending, - StatusRunning, - StatusBlocked: - return false - default: - return true - } -} - -// IsFailed returns true if the step has failed -func (s *Stage) IsFailed() bool { - switch s.Status { - case StatusFailing, - StatusKilled, - StatusError: - return true - default: - return false - } -} diff --git a/core/stage_test.go b/core/stage_test.go deleted file mode 100644 index b00c91c987..0000000000 --- a/core/stage_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core - -import "testing" - -var statusDone = []string{ - StatusDeclined, - StatusError, - StatusFailing, - StatusKilled, - StatusSkipped, - StatusPassing, -} - -var statusNotDone = []string{ - StatusWaiting, - StatusPending, - StatusRunning, - StatusBlocked, -} - -var statusFailed = []string{ - StatusError, - StatusFailing, - StatusKilled, -} - -var statusNotFailed = []string{ - StatusDeclined, - StatusSkipped, - StatusPassing, - StatusWaiting, - StatusPending, - StatusRunning, - StatusBlocked, -} - -func TestStageIsDone(t *testing.T) { - for _, status := range statusDone { - v := Stage{Status: status} - if v.IsDone() == false { - t.Errorf("Expect status %s is done", status) - } - } - - for _, status := range statusNotDone { - v := Stage{Status: status} - if v.IsDone() == true { - t.Errorf("Expect status %s is not done", status) - } - } -} - -func TestStageIsFailed(t *testing.T) { - for _, status := range statusFailed { - v := Stage{Status: status} - if v.IsFailed() == false { - t.Errorf("Expect status %s is failed", status) - } - } - - for _, status := range statusNotFailed { - v := Stage{Status: status} - if v.IsFailed() == true { - t.Errorf("Expect status %s is not failed", status) - } - } -} diff --git a/core/status.go b/core/status.go deleted file mode 100644 index d2e440dc6b..0000000000 --- a/core/status.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Status types. -const ( - StatusSkipped = "skipped" - StatusBlocked = "blocked" - StatusDeclined = "declined" - StatusWaiting = "waiting_on_dependencies" - StatusPending = "pending" - StatusRunning = "running" - StatusPassing = "success" - StatusFailing = "failure" - StatusKilled = "killed" - StatusError = "error" -) - -type ( - // Status represents a commit status. - Status struct { - State string - Label string - Desc string - Target string - } - - // StatusInput provides the necessary metadata to - // set the commit or deployment status. - StatusInput struct { - Repo *Repository - Build *Build - } - - // StatusService sends the commit status to an external - // source code management service (e.g. GitHub). - StatusService interface { - Send(ctx context.Context, user *User, req *StatusInput) error - } -) diff --git a/core/step.go b/core/step.go deleted file mode 100644 index 1339b15c23..0000000000 --- a/core/step.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -type ( - // Step represents an individual step in the stage. - Step struct { - ID int64 `json:"id"` - StageID int64 `json:"step_id"` // this is a typo, fixing it has far reaching ramifications. It should only be attempted in a major version change - Number int `json:"number"` - Name string `json:"name"` - Status string `json:"status"` - Error string `json:"error,omitempty"` - ErrIgnore bool `json:"errignore,omitempty"` - ExitCode int `json:"exit_code"` - Started int64 `json:"started,omitempty"` - Stopped int64 `json:"stopped,omitempty"` - Version int64 `json:"version"` - DependsOn []string `json:"depends_on,omitempty"` - Image string `json:"image,omitempty"` - Detached bool `json:"detached,omitempty"` - Schema string `json:"schema,omitempty"` - } - - // StepStore persists build step information to storage. - StepStore interface { - // List returns a build stage list from the datastore. - List(context.Context, int64) ([]*Step, error) - - // Find returns a build stage from the datastore by ID. - Find(context.Context, int64) (*Step, error) - - // FindNumber returns a stage from the datastore by number. - FindNumber(context.Context, int64, int) (*Step, error) - - // Create persists a new stage to the datastore. - Create(context.Context, *Step) error - - // Update persists an updated stage to the datastore. - Update(context.Context, *Step) error - } -) - -// IsDone returns true if the step has a completed state. -func (s *Step) IsDone() bool { - switch s.Status { - case StatusWaiting, - StatusPending, - StatusRunning, - StatusBlocked: - return false - default: - return true - } -} diff --git a/core/step_test.go b/core/step_test.go deleted file mode 100644 index b7d689a466..0000000000 --- a/core/step_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core - -import "testing" - -func TestStepIsDone(t *testing.T) { - for _, status := range statusDone { - v := Step{Status: status} - if v.IsDone() == false { - t.Errorf("Expect status %s is done", status) - } - } - - for _, status := range statusNotDone { - v := Step{Status: status} - if v.IsDone() == true { - t.Errorf("Expect status %s is not done", status) - } - } -} diff --git a/core/syncer.go b/core/syncer.go deleted file mode 100644 index 6c2ec6e1b9..0000000000 --- a/core/syncer.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Syncer synchronizes the account repository list. -type Syncer interface { - Sync(context.Context, *User) (*Batch, error) -} diff --git a/core/system.go b/core/system.go deleted file mode 100644 index 7cd0a17588..0000000000 --- a/core/system.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -// System stores system information. -type System struct { - Proto string `json:"proto,omitempty"` - Host string `json:"host,omitempty"` - Link string `json:"link,omitempty"` - Version string `json:"version,omitempty"` -} diff --git a/core/template.go b/core/template.go deleted file mode 100644 index 29fccc87d1..0000000000 --- a/core/template.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - - "github.com/drone/drone/handler/api/errors" -) - -var ( - errTemplateNameInvalid = errors.New("No Template Name Provided") - errTemplateDataInvalid = errors.New("No Template Data Provided") -) - -type ( - TemplateArgs struct { - Kind string - Load string - Data map[string]interface{} - } - - Template struct { - Id int64 `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Namespace string `json:"namespace,omitempty"` - Data string `json:"data,omitempty"` - Created int64 `json:"created,omitempty"` - Updated int64 `json:"updated,omitempty"` - } - - // TemplateStore manages repository templates. - TemplateStore interface { - // List returns template list at org level - List(ctx context.Context, namespace string) ([]*Template, error) - - // ListAll returns templates list from the datastore. - ListAll(ctx context.Context) ([]*Template, error) - - // Find returns a template from the datastore. - Find(ctx context.Context, id int64) (*Template, error) - - // FindName returns a template from the data store - FindName(ctx context.Context, name string, namespace string) (*Template, error) - - // Create persists a new template to the datastore. - Create(ctx context.Context, template *Template) error - - // Update persists an updated template to the datastore. - Update(ctx context.Context, template *Template) error - - // Delete deletes a template from the datastore. - Delete(ctx context.Context, template *Template) error - } -) - -// Validate validates the required fields and formats. -func (s *Template) Validate() error { - switch { - case len(s.Name) == 0: - return errTemplateNameInvalid - case len(s.Data) == 0: - return errTemplateDataInvalid - default: - return nil - } -} diff --git a/core/transfer.go b/core/transfer.go deleted file mode 100644 index 3e65351799..0000000000 --- a/core/transfer.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Transferer handles transferring repository ownership from one -// user to another user account. -type Transferer interface { - Transfer(ctx context.Context, user *User) error -} diff --git a/core/trigger.go b/core/trigger.go deleted file mode 100644 index 00b705a075..0000000000 --- a/core/trigger.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import "context" - -// Trigger types -const ( - TriggerHook = "@hook" - TriggerCron = "@cron" -) - -// Triggerer is responsible for triggering a Build from an -// incoming drone. If a build is skipped a nil value is -// returned. -type Triggerer interface { - Trigger(context.Context, *Repository, *Hook) (*Build, error) -} diff --git a/core/trigger_test.go b/core/trigger_test.go deleted file mode 100644 index 0912528b2f..0000000000 --- a/core/trigger_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core diff --git a/core/user.go b/core/user.go deleted file mode 100644 index e9fcaa9d73..0000000000 --- a/core/user.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - "errors" - - "github.com/asaskevich/govalidator" -) - -var ( - errUsernameLen = errors.New("Invalid username length") - errUsernameChar = errors.New("Invalid character in username") -) - -type ( - // User represents a user of the system. - User struct { - ID int64 `json:"id"` - Login string `json:"login"` - Email string `json:"email"` - Machine bool `json:"machine"` - Admin bool `json:"admin"` - Active bool `json:"active"` - Avatar string `json:"avatar"` - Syncing bool `json:"syncing"` - Synced int64 `json:"synced"` - Created int64 `json:"created"` - Updated int64 `json:"updated"` - LastLogin int64 `json:"last_login"` - Token string `json:"-"` - Refresh string `json:"-"` - Expiry int64 `json:"-"` - Hash string `json:"-"` - } - - // UserParams defines user query parameters. - UserParams struct { - // Sort instructs the system to sort by Login if true, - // else sort by primary key. - Sort bool - - Page int64 - Size int64 - } - - // UserStore defines operations for working with users. - UserStore interface { - // Find returns a user from the datastore. - Find(context.Context, int64) (*User, error) - - // FindLogin returns a user from the datastore by username. - FindLogin(context.Context, string) (*User, error) - - // FindToken returns a user from the datastore by token. - FindToken(context.Context, string) (*User, error) - - // List returns a list of users from the datastore. - List(context.Context) ([]*User, error) - - // ListRange returns a range of users from the datastore. - ListRange(context.Context, UserParams) ([]*User, error) - - // Create persists a new user to the datastore. - Create(context.Context, *User) error - - // Update persists an updated user to the datastore. - Update(context.Context, *User) error - - // Delete deletes a user from the datastore. - Delete(context.Context, *User) error - - // Count returns a count of human and machine users. - Count(context.Context) (int64, error) - - // CountHuman returns a count of human users. - CountHuman(context.Context) (int64, error) - } - - // UserService provides access to user account - // resources in the remote system (e.g. GitHub). - UserService interface { - // Find returns the authenticated user. - Find(ctx context.Context, access, refresh string) (*User, error) - - // FindLogin returns a user by username. - FindLogin(ctx context.Context, user *User, login string) (*User, error) - } -) - -// Validate validates the user and returns an error if the -// validation fails. -func (u *User) Validate() error { - switch { - case !govalidator.IsByteLength(u.Login, 1, 50): - return errUsernameLen - case !govalidator.Matches(u.Login, "^[.a-zA-Z0-9_-]+$"): - return errUsernameChar - default: - return nil - } -} diff --git a/core/user_test.go b/core/user_test.go deleted file mode 100644 index cb8af1ccd4..0000000000 --- a/core/user_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package core - -import ( - "testing" -) - -func TestValidateUser(t *testing.T) { - tests := []struct { - user *User - err error - }{ - { - user: &User{Login: ""}, - err: errUsernameLen, - }, - { - user: &User{Login: "Ā©"}, // non ascii character - err: errUsernameChar, - }, - { - user: &User{Login: "ģ†Œģ£¼"}, // non ascii character - err: errUsernameChar, - }, - { - user: &User{Login: "foo/bar"}, - err: errUsernameChar, - }, - { - user: &User{Login: "this-is-a-really-really-really-really-long-username"}, - err: errUsernameLen, - }, - { - user: &User{Login: "octocat"}, - err: nil, - }, - { - user: &User{Login: "octocat.with.dot"}, - err: nil, - }, - { - user: &User{Login: "OctO-Cat_01"}, - err: nil, - }, - } - for i, test := range tests { - got := test.user.Validate() - if got == nil && test.err == nil { - continue - } - if got == nil && test.err != nil { - t.Errorf("Expected error: %q at index %d", test.err, i) - continue - } - if got != nil && test.err == nil { - t.Errorf("Unexpected error: %q at index %d", got, i) - continue - } - if got, want := got.Error(), test.err.Error(); got != want { - t.Errorf("Want error %q, got %q at index %d", want, got, i) - } - } -} diff --git a/core/validate.go b/core/validate.go deleted file mode 100644 index cb07e8e61d..0000000000 --- a/core/validate.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" - "errors" -) - -var ( - // ErrValidatorSkip is returned if the pipeline - // validation fails, but the pipeline should be skipped - // and silently ignored instead of erroring. - ErrValidatorSkip = errors.New("validation failed: skip pipeline") - - // ErrValidatorBlock is returned if the pipeline - // validation fails, but the pipeline should be blocked - // pending manual approval instead of erroring. - ErrValidatorBlock = errors.New("validation failed: block pipeline") -) - -type ( - // ValidateArgs represents a request to the pipeline - // validation service. - ValidateArgs struct { - User *User `json:"-"` - Repo *Repository `json:"repo,omitempty"` - Build *Build `json:"build,omitempty"` - Config *Config `json:"config,omitempty"` - } - - // ValidateService validates the yaml configuration - // and returns an error if the yaml is deemed invalid. - ValidateService interface { - Validate(context.Context, *ValidateArgs) error - } -) diff --git a/core/webhook.go b/core/webhook.go deleted file mode 100644 index d4a87b3fda..0000000000 --- a/core/webhook.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package core - -import ( - "context" -) - -// Webhook event types. -const ( - WebhookEventBuild = "build" - WebhookEventRepo = "repo" - WebhookEventUser = "user" -) - -// Webhook action types. -const ( - WebhookActionCreated = "created" - WebhookActionUpdated = "updated" - WebhookActionDeleted = "deleted" - WebhookActionEnabled = "enabled" - WebhookActionDisabled = "disabled" -) - -type ( - // Webhook defines an integration endpoint. - Webhook struct { - Endpoint string `json:"endpoint,omitempty"` - Signer string `json:"-"` - SkipVerify bool `json:"skip_verify,omitempty"` - } - - // WebhookData provides the webhook data. - WebhookData struct { - Event string `json:"event"` - Action string `json:"action"` - User *User `json:"user,omitempty"` - Repo *Repository `json:"repo,omitempty"` - Build *Build `json:"build,omitempty"` - } - - // WebhookSender sends the webhook payload. - WebhookSender interface { - // Send sends the webhook to the global endpoint. - Send(context.Context, *WebhookData) error - } -) diff --git a/docker/Dockerfile.agent.linux.amd64 b/docker/Dockerfile.agent.linux.amd64 deleted file mode 100644 index bb9899a88b..0000000000 --- a/docker/Dockerfile.agent.linux.amd64 +++ /dev/null @@ -1,19 +0,0 @@ -FROM alpine:3.11 as alpine -RUN apk add -U --no-cache ca-certificates tzdata - -FROM alpine:3.11 -ENV GODEBUG netdns=go -ENV DRONE_RUNNER_OS=linux -ENV DRONE_RUNNER_ARCH=amd64 -ENV DRONE_RUNNER_PLATFORM=linux/amd64 -ENV DRONE_RUNNER_CAPACITY=1 -ADD release/linux/amd64/drone-agent /bin/ - -RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf - -COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=alpine /usr/share/zoneinfo /usr/share/zoneinfo - -LABEL com.centurylinklabs.watchtower.stop-signal="SIGINT" - -ENTRYPOINT ["/bin/drone-agent"] diff --git a/docker/Dockerfile.agent.linux.arm b/docker/Dockerfile.agent.linux.arm deleted file mode 100644 index 47f0d61553..0000000000 --- a/docker/Dockerfile.agent.linux.arm +++ /dev/null @@ -1,12 +0,0 @@ -FROM drone/ca-certs -ENV GODEBUG=netdns=go -ENV DRONE_RUNNER_OS=linux -ENV DRONE_RUNNER_ARCH=arm -ENV DRONE_RUNNER_PLATFORM=linux/arm -ENV DRONE_RUNNER_CAPACITY=1 -ENV DRONE_RUNNER_VARIANT=v7 -ADD release/linux/arm/drone-agent /bin/ - -LABEL com.centurylinklabs.watchtower.stop-signal="SIGINT" - -ENTRYPOINT ["/bin/drone-agent"] diff --git a/docker/Dockerfile.agent.linux.arm64 b/docker/Dockerfile.agent.linux.arm64 deleted file mode 100644 index 04ba015480..0000000000 --- a/docker/Dockerfile.agent.linux.arm64 +++ /dev/null @@ -1,12 +0,0 @@ -FROM drone/ca-certs -ENV GODEBUG=netdns=go -ENV DRONE_RUNNER_OS=linux -ENV DRONE_RUNNER_ARCH=arm64 -ENV DRONE_RUNNER_PLATFORM=linux/arm64 -ENV DRONE_RUNNER_CAPACITY=1 -ENV DRONE_RUNNER_VARIANT=v8 -ADD release/linux/arm64/drone-agent /bin/ - -LABEL com.centurylinklabs.watchtower.stop-signal="SIGINT" - -ENTRYPOINT ["/bin/drone-agent"] diff --git a/docker/Dockerfile.agent.windows.1803 b/docker/Dockerfile.agent.windows.1803 deleted file mode 100644 index 9c369ef3dd..0000000000 --- a/docker/Dockerfile.agent.windows.1803 +++ /dev/null @@ -1,14 +0,0 @@ -FROM mcr.microsoft.com/windows/nanoserver:1803 -USER ContainerAdministrator - -ENV GODEBUG=netdns=go -ENV DRONE_RUNNER_OS=windows -ENV DRONE_RUNNER_ARCH=amd64 -ENV DRONE_RUNNER_PLATFORM=windows/amd64 -ENV DRONE_RUNNER_KERNEL=1803 -ENV DRONE_RUNNER_CAPACITY=1 - -LABEL com.centurylinklabs.watchtower.stop-signal="SIGINT" - -ADD release/windows/amd64/drone-agent.exe C:/drone-agent.exe -ENTRYPOINT [ "C:\\drone-agent.exe" ] diff --git a/docker/Dockerfile.agent.windows.1809 b/docker/Dockerfile.agent.windows.1809 deleted file mode 100644 index 831401e0b2..0000000000 --- a/docker/Dockerfile.agent.windows.1809 +++ /dev/null @@ -1,14 +0,0 @@ -FROM mcr.microsoft.com/windows/nanoserver:1809 -USER ContainerAdministrator - -ENV GODEBUG=netdns=go -ENV DRONE_RUNNER_OS=windows -ENV DRONE_RUNNER_ARCH=amd64 -ENV DRONE_RUNNER_PLATFORM=windows/amd64 -ENV DRONE_RUNNER_KERNEL=1809 -ENV DRONE_RUNNER_CAPACITY=1 - -LABEL com.centurylinklabs.watchtower.stop-signal="SIGINT" - -ADD release/windows/amd64/drone-agent.exe C:/drone-agent.exe -ENTRYPOINT [ "C:\\drone-agent.exe" ] diff --git a/docker/Dockerfile.agent.windows.1903 b/docker/Dockerfile.agent.windows.1903 deleted file mode 100644 index a7d34426cd..0000000000 --- a/docker/Dockerfile.agent.windows.1903 +++ /dev/null @@ -1,14 +0,0 @@ -FROM mcr.microsoft.com/windows/nanoserver:1903 -USER ContainerAdministrator - -ENV GODEBUG=netdns=go -ENV DRONE_RUNNER_OS=windows -ENV DRONE_RUNNER_ARCH=amd64 -ENV DRONE_RUNNER_PLATFORM=windows/amd64 -ENV DRONE_RUNNER_KERNEL=1903 -ENV DRONE_RUNNER_CAPACITY=1 - -LABEL com.centurylinklabs.watchtower.stop-signal="SIGINT" - -ADD release/windows/amd64/drone-agent.exe C:/drone-agent.exe -ENTRYPOINT [ "C:\\drone-agent.exe" ] diff --git a/docker/Dockerfile.controller.linux.amd64 b/docker/Dockerfile.controller.linux.amd64 deleted file mode 100644 index 8161dc2cca..0000000000 --- a/docker/Dockerfile.controller.linux.amd64 +++ /dev/null @@ -1,15 +0,0 @@ -FROM alpine:3.11 as alpine -RUN apk add -U --no-cache ca-certificates tzdata - -FROM alpine:3.11 -ENV GODEBUG netdns=go -ENV DRONE_RUNNER_OS=linux -ENV DRONE_RUNNER_ARCH=amd64 -ENV DRONE_RUNNER_PLATFORM=linux/amd64 -ENV DRONE_RUNNER_CAPACITY=1 -ADD release/linux/amd64/drone-controller /bin/ - -COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=alpine /usr/share/zoneinfo /usr/share/zoneinfo - -ENTRYPOINT ["/bin/drone-controller"] diff --git a/docker/Dockerfile.controller.linux.arm b/docker/Dockerfile.controller.linux.arm deleted file mode 100644 index be5091259f..0000000000 --- a/docker/Dockerfile.controller.linux.arm +++ /dev/null @@ -1,16 +0,0 @@ -FROM alpine:3.11 as alpine -RUN apk add -U --no-cache ca-certificates tzdata - -FROM alpine:3.11 -ENV GODEBUG=netdns=go -ENV DRONE_RUNNER_OS=linux -ENV DRONE_RUNNER_ARCH=arm -ENV DRONE_RUNNER_PLATFORM=linux/arm -ENV DRONE_RUNNER_CAPACITY=1 -ENV DRONE_RUNNER_VARIANT=v7 -ADD release/linux/arm/drone-controller /bin/ - -COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=alpine /usr/share/zoneinfo /usr/share/zoneinfo - -ENTRYPOINT ["/bin/drone-controller"] diff --git a/docker/Dockerfile.controller.linux.arm64 b/docker/Dockerfile.controller.linux.arm64 deleted file mode 100644 index 54a1ca02c9..0000000000 --- a/docker/Dockerfile.controller.linux.arm64 +++ /dev/null @@ -1,16 +0,0 @@ -FROM alpine:3.11 as alpine -RUN apk add -U --no-cache ca-certificates tzdata - -FROM alpine:3.11 -ENV GODEBUG=netdns=go -ENV DRONE_RUNNER_OS=linux -ENV DRONE_RUNNER_ARCH=arm64 -ENV DRONE_RUNNER_PLATFORM=linux/arm64 -ENV DRONE_RUNNER_CAPACITY=1 -ENV DRONE_RUNNER_VARIANT=v8 -ADD release/linux/arm64/drone-controller /bin/ - -COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=alpine /usr/share/zoneinfo /usr/share/zoneinfo - -ENTRYPOINT ["/bin/drone-controller"] diff --git a/docker/Dockerfile.controller.windows.1803 b/docker/Dockerfile.controller.windows.1803 deleted file mode 100644 index b8f0f56642..0000000000 --- a/docker/Dockerfile.controller.windows.1803 +++ /dev/null @@ -1,12 +0,0 @@ -FROM mcr.microsoft.com/windows/nanoserver:1803 -USER ContainerAdministrator - -ENV GODEBUG=netdns=go -ENV DRONE_RUNNER_OS=windows -ENV DRONE_RUNNER_ARCH=amd64 -ENV DRONE_RUNNER_PLATFORM=windows/amd64 -ENV DRONE_RUNNER_KERNEL=1803 -ENV DRONE_RUNNER_CAPACITY=1 - -ADD release/windows/1803/amd64/drone-controller.exe C:/drone-controller.exe -ENTRYPOINT [ "C:\\drone-controller.exe" ] diff --git a/docker/Dockerfile.controller.windows.1809 b/docker/Dockerfile.controller.windows.1809 deleted file mode 100644 index 1664998e75..0000000000 --- a/docker/Dockerfile.controller.windows.1809 +++ /dev/null @@ -1,12 +0,0 @@ -FROM mcr.microsoft.com/windows/nanoserver:1809 -USER ContainerAdministrator - -ENV GODEBUG=netdns=go -ENV DRONE_RUNNER_OS=windows -ENV DRONE_RUNNER_ARCH=amd64 -ENV DRONE_RUNNER_PLATFORM=windows/amd64 -ENV DRONE_RUNNER_KERNEL=1809 -ENV DRONE_RUNNER_CAPACITY=1 - -ADD release/windows/1809/amd64/drone-controller.exe C:/drone-controller.exe -ENTRYPOINT [ "C:\\drone-controller.exe" ] diff --git a/docker/Dockerfile.server.linux.amd64 b/docker/Dockerfile.server.linux.amd64 deleted file mode 100644 index 1f4d604b14..0000000000 --- a/docker/Dockerfile.server.linux.amd64 +++ /dev/null @@ -1,27 +0,0 @@ -# docker build --rm -f docker/Dockerfile -t drone/drone . - -FROM alpine:3.11 as alpine -RUN apk add -U --no-cache ca-certificates tzdata - -FROM alpine:3.11 -EXPOSE 80 443 -VOLUME /data - -RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf - -ENV GODEBUG netdns=go -ENV XDG_CACHE_HOME /data -ENV DRONE_DATABASE_DRIVER sqlite3 -ENV DRONE_DATABASE_DATASOURCE /data/database.sqlite -ENV DRONE_RUNNER_OS=linux -ENV DRONE_RUNNER_ARCH=amd64 -ENV DRONE_SERVER_PORT=:80 -ENV DRONE_SERVER_HOST=localhost -ENV DRONE_DATADOG_ENABLED=true -ENV DRONE_DATADOG_ENDPOINT=https://stats.drone.ci/api/v1/series - -COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=alpine /usr/share/zoneinfo /usr/share/zoneinfo - -ADD release/linux/amd64/drone-server /bin/ -ENTRYPOINT ["/bin/drone-server"] \ No newline at end of file diff --git a/docker/Dockerfile.server.linux.arm b/docker/Dockerfile.server.linux.arm deleted file mode 100644 index 3f803d1536..0000000000 --- a/docker/Dockerfile.server.linux.arm +++ /dev/null @@ -1,27 +0,0 @@ -# docker build --rm -f docker/Dockerfile -t drone/drone . - -FROM alpine:3.11 as alpine -RUN apk add -U --no-cache ca-certificates tzdata - -FROM alpine:3.11 -EXPOSE 80 443 -VOLUME /data - -RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf - -ENV GODEBUG netdns=go -ENV XDG_CACHE_HOME /data -ENV DRONE_DATABASE_DRIVER sqlite3 -ENV DRONE_DATABASE_DATASOURCE /data/database.sqlite -ENV DRONE_RUNNER_OS=linux -ENV DRONE_RUNNER_ARCH=arm -ENV DRONE_SERVER_PORT=:80 -ENV DRONE_SERVER_HOST=localhost -ENV DRONE_DATADOG_ENABLED=true -ENV DRONE_DATADOG_ENDPOINT=https://stats.drone.ci/api/v1/series - -COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=alpine /usr/share/zoneinfo /usr/share/zoneinfo - -ADD release/linux/arm/drone-server /bin/ -ENTRYPOINT ["/bin/drone-server"] \ No newline at end of file diff --git a/docker/Dockerfile.server.linux.arm64 b/docker/Dockerfile.server.linux.arm64 deleted file mode 100644 index f0be82848c..0000000000 --- a/docker/Dockerfile.server.linux.arm64 +++ /dev/null @@ -1,27 +0,0 @@ -# docker build --rm -f docker/Dockerfile -t drone/drone . - -FROM alpine:3.11 as alpine -RUN apk add -U --no-cache ca-certificates tzdata - -FROM alpine:3.11 -EXPOSE 80 443 -VOLUME /data - -RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf - -ENV GODEBUG netdns=go -ENV XDG_CACHE_HOME /data -ENV DRONE_DATABASE_DRIVER sqlite3 -ENV DRONE_DATABASE_DATASOURCE /data/database.sqlite -ENV DRONE_RUNNER_OS=linux -ENV DRONE_RUNNER_ARCH=arm64 -ENV DRONE_SERVER_PORT=:80 -ENV DRONE_SERVER_HOST=localhost -ENV DRONE_DATADOG_ENABLED=true -ENV DRONE_DATADOG_ENDPOINT=https://stats.drone.ci/api/v1/series - -COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=alpine /usr/share/zoneinfo /usr/share/zoneinfo - -ADD release/linux/arm64/drone-server /bin/ -ENTRYPOINT ["/bin/drone-server"] \ No newline at end of file diff --git a/docker/compose/README.md b/docker/compose/README.md deleted file mode 100644 index b431aa7624..0000000000 --- a/docker/compose/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Local development - -This directory contains Docker compose files used by the core development team for local development and testing purposes only. These are not part of the core distribution, and are not intended for use outside of the core development team. We are not currently accepting changes or additions to these files. - -## Running a Drone deployment locally using Github - -At the end of this guide you will have a drone server and a drone runner that is hooked up to your Github account. This will allow you to trigger builds on your Github repositories. - -### (prerequisite) Setup a Github oauth application - -Create an oauth application here - -The most important entry is setting the `Authorization callback URL` you can set this to `http://localhost:8080/login` - -You will also need to create a client secret for the application. - -Now you have the `DRONE_GITHUB_CLIENT_ID` and `DRONE_GITHUB_CLIENT_SECRET` - -### (prerequisite) Setup Ngrok - -Ngrok allows us to send the webhooks from Github to our local Drone setup. - -Follow the guide here - -### Running Drone - -+ Move into the `drone/docker/compose/drone-github` folder. - -+ Run Ngrok against port `8080` it will run in the foreground. - -``` bash -./ngrok http 8080 -``` - -Take note of the forwarding hostname this is your `DRONE_SERVER_PROXY_HOST` EG - -``` bash -Forwarding http://c834c33asdde.ngrok.io -> http://localhost:8080 -``` - -+ You will want to edit the Docker compose file `docker-compose.yml` updating in the following entries. - -``` bash -DRONE_SERVER_PROXY_HOST=${DRONE_SERVER_PROXY_HOST} # taken from Ngrok -DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT_ID} # taken from your Github oauth application -DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_CLIENT_SECRET} # taken from your Github oauth application -``` - -NB for `DRONE_SERVER_PROXY_HOST` do not include http/https. - -+ Run docker compose - -``` bash -docker-compose up -``` - -Now you can go access the Drone ui at diff --git a/docker/compose/drone-gitea/docker-compose.yml b/docker/compose/drone-gitea/docker-compose.yml deleted file mode 100644 index 40da98cae2..0000000000 --- a/docker/compose/drone-gitea/docker-compose.yml +++ /dev/null @@ -1,40 +0,0 @@ -version: "3.8" -services: - drone: - image: drone/drone:latest - ports: - - "9000:80" - environment: - - DRONE_SERVER_HOST=localhost:9000 - - DRONE_SERVER_PROTO=http - - DRONE_RPC_SECRET=bea26a2221fd8090ea38720fc445eca6 - - DRONE_COOKIE_SECRET=e8206356c843d81e05ab6735e7ebf075 - - DRONE_COOKIE_TIMEOUT=720h - - DRONE_GITEA_CLIENT_ID=${DRONE_GITEA_CLIENT_ID} - - DRONE_GITEA_CLIENT_SECRET=${DRONE_GITEA_CLIENT_SECRET} - - DRONE_GITEA_SERVER=http://gitea:3000 - - DRONE_LOGS_DEBUG=true - - DRONE_CRON_DISABLED=true - volumes: - - ./data:/data - networks: - - default - - gitea - runner: - image: drone/drone-runner-docker:latest - environment: - - DRONE_RPC_HOST=drone - - DRONE_RPC_PROTO=http - - DRONE_RPC_SECRET=bea26a2221fd8090ea38720fc445eca6 - volumes: - - /var/run/docker.sock:/var/run/docker.sock - networks: - - default - - gitea - -networks: - default: - external: false - gitea: - external: - name: gitea diff --git a/docker/compose/drone-github/docker-compose.yml b/docker/compose/drone-github/docker-compose.yml deleted file mode 100644 index 732ad644f1..0000000000 --- a/docker/compose/drone-github/docker-compose.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: "3.8" -services: - drone: - image: drone/drone:latest - ports: - - "8080:80" - environment: - - DRONE_SERVER_HOST=localhost:8080 - - DRONE_SERVER_PROTO=http - - DRONE_SERVER_PROXY_HOST=${DRONE_SERVER_PROXY_HOST} - - DRONE_SERVER_PROXY_PROTO=https - - DRONE_RPC_SECRET=bea26a2221fd8090ea38720fc445eca6 - - DRONE_COOKIE_SECRET=e8206356c843d81e05ab6735e7ebf075 - - DRONE_COOKIE_TIMEOUT=720h - - DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT_ID} - - DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_CLIENT_SECRET} - - DRONE_LOGS_DEBUG=true - - DRONE_CRON_DISABLED=true - volumes: - - ./data:/data - runner: - image: drone/drone-runner-docker:latest - environment: - - DRONE_RPC_HOST=drone - - DRONE_RPC_PROTO=http - - DRONE_RPC_SECRET=bea26a2221fd8090ea38720fc445eca6 - - DRONE_TMATE_ENABLED=true - volumes: - - /var/run/docker.sock:/var/run/docker.sock diff --git a/docker/compose/gitea/docker-compose.yml b/docker/compose/gitea/docker-compose.yml deleted file mode 100644 index 1bec20fa2b..0000000000 --- a/docker/compose/gitea/docker-compose.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: "3.8" -services: - gitea: - image: gitea/gitea:latest - container_name: gitea - environment: - - USER_UID=1000 - - USER_GID=1000 - - ROOT_URL=http://gitea:3000 - networks: - - gitea - volumes: - - ./data:/data - ports: - - "3000:3000" - - "3022:22" - -networks: - gitea: - name: gitea - external: false diff --git a/docker/manifest.agent.tmpl b/docker/manifest.agent.tmpl deleted file mode 100644 index 1db9863bb2..0000000000 --- a/docker/manifest.agent.tmpl +++ /dev/null @@ -1,43 +0,0 @@ -image: drone/agent:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - - image: drone/agent:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - - image: drone/agent:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - - image: drone/agent:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 - - - image: drone/agent:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1803-amd64 - platform: - architecture: amd64 - os: windows - variant: 1803 - - - image: drone/agent:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1809-amd64 - platform: - architecture: amd64 - os: windows - variant: 1809 - - - image: drone/agent:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1903-amd64 - platform: - architecture: amd64 - os: windows - variant: 1903 \ No newline at end of file diff --git a/docker/manifest.controller.tmpl b/docker/manifest.controller.tmpl deleted file mode 100644 index ec34401fda..0000000000 --- a/docker/manifest.controller.tmpl +++ /dev/null @@ -1,37 +0,0 @@ -image: drone/controller:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - - image: drone/controller:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - - image: drone/controller:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - - image: drone/controller:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 - - - image: drone/controller:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1803-amd64 - platform: - architecture: amd64 - os: windows - variant: 1803 - - - image: drone/controller:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1809-amd64 - platform: - architecture: amd64 - os: windows - variant: 1809 diff --git a/docker/manifest.server.tmpl b/docker/manifest.server.tmpl deleted file mode 100644 index 55e6c87882..0000000000 --- a/docker/manifest.server.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -image: drone/drone:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - - image: drone/drone:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - - image: drone/drone:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 diff --git a/go.mod b/go.mod deleted file mode 100644 index 33066e1495..0000000000 --- a/go.mod +++ /dev/null @@ -1,63 +0,0 @@ -module github.com/drone/drone - -replace github.com/docker/docker => github.com/docker/engine v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible - -require ( - github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e - github.com/Azure/azure-storage-blob-go v0.7.0 - github.com/Azure/go-autorest/autorest/adal v0.8.3 // indirect - github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f - github.com/aws/aws-sdk-go v1.37.3 - github.com/codegangsta/negroni v1.0.0 // indirect - github.com/coreos/go-semver v0.2.0 - github.com/dchest/authcookie v0.0.0-20120917135355-fbdef6e99866 - github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9 - github.com/drone/drone-go v1.7.2-0.20220308165842-f9e4fe31c2af - github.com/drone/drone-runtime v1.1.1-0.20200623162453-61e33e2cab5d - github.com/drone/drone-ui v2.11.5+incompatible - github.com/drone/drone-yaml v1.2.4-0.20220204000225-01fb17858c9b - github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629 - github.com/drone/funcmap v0.0.0-20210823160631-9e9dec149056 - github.com/drone/go-license v1.0.2 - github.com/drone/go-login v1.1.0 - github.com/drone/go-scm v1.28.0 - github.com/drone/signal v1.0.0 - github.com/dustin/go-humanize v1.0.0 - github.com/go-chi/chi v3.3.3+incompatible - github.com/go-chi/cors v1.0.0 - github.com/go-redis/redis/v8 v8.11.0 - github.com/go-redsync/redsync/v4 v4.3.0 - github.com/go-sql-driver/mysql v1.4.0 - github.com/golang/mock v1.3.1 - github.com/google/go-cmp v0.5.6 - github.com/google/go-jsonnet v0.17.0 - github.com/google/wire v0.2.1 - github.com/gosimple/slug v1.3.0 - github.com/h2non/gock v1.0.15 - github.com/hashicorp/go-cleanhttp v0.5.1 // indirect - github.com/hashicorp/go-multierror v1.1.0 - github.com/hashicorp/go-retryablehttp v0.5.4 - github.com/hashicorp/golang-lru v0.5.1 - github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 - github.com/joho/godotenv v1.3.0 - github.com/kelseyhightower/envconfig v1.3.0 - github.com/kr/pretty v0.2.0 // indirect - github.com/lib/pq v1.1.0 - github.com/mattn/go-sqlite3 v1.9.0 - github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 - github.com/prometheus/client_golang v0.9.2 - github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect - github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 - github.com/segmentio/ksuid v1.0.2 - github.com/sirupsen/logrus v1.6.0 - github.com/unrolled/secure v0.0.0-20181022170031-4b6b7cf51606 - go.starlark.net v0.0.0-20221020143700-22309ac47eac - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 - golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - gopkg.in/yaml.v2 v2.3.0 -) - -replace github.com/h2non/gock => gopkg.in/h2non/gock.v1 v1.0.14 - -go 1.13 diff --git a/go.sum b/go.sum deleted file mode 100644 index 28b65e69e3..0000000000 --- a/go.sum +++ /dev/null @@ -1,414 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -docker.io/go-docker v1.0.0/go.mod h1:7tiAn5a0LFmjbPDbyTPOaTTOuG1ZRNXdPA6RvKY+fpY= -github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e h1:rl2Aq4ZODqTDkeSqQBy+fzpZPamacO1Srp8zq7jf2Sc= -github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY= -github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= -github.com/Azure/azure-storage-blob-go v0.7.0 h1:MuueVOYkufCxJw5YZzF842DY2MBsp+hLuh2apKY0mck= -github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.3 h1:O1AGG9Xig71FxdX9HO5pGNyZ7TbSyHaVg+5eJO/jSGw= -github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f h1:y2hSFdXeA1y5z5f0vfNO0Dg5qVY036qzlz3Pds0B92o= -github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.37.3 h1:1f0groABc4AuapskpHf6EBRaG2tqw0Sx3ebCMwfp1Ys= -github.com/aws/aws-sdk-go v1.37.3/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= -github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8= -github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY= -github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= -github.com/containerd/containerd v1.3.4 h1:3o0smo5SKY7H6AJCmJhsnCjR2/V2T8VmiHt7seN2/kI= -github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dchest/authcookie v0.0.0-20120917135355-fbdef6e99866 h1:98WJ4YCdjmB7uyrdT3P4A2Oa1hiRPKoa/0zInG6UnfQ= -github.com/dchest/authcookie v0.0.0-20120917135355-fbdef6e99866/go.mod h1:x7AK2h2QzaXVEFi1tbMYMDuvHcCEr1QdMDrg3hkW24Q= -github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9 h1:74lLNRzvsdIlkTgfDSMuaPjBr4cf6k7pwQQANm/yLKU= -github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/docker/distribution v0.0.0-20170726174610-edc3ab29cdff/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/engine v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible h1:hx8H7MbcmXUXAmphQuA/XB7CfSzX4DRrNuHFvfK9aIQ= -github.com/docker/engine v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= -github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o= -github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/drone/drone-go v1.7.2-0.20220308165842-f9e4fe31c2af h1:I+xfWPZLMaskFvfFO5fEJ6tVI1Q9bWR9tuORZeaCTPo= -github.com/drone/drone-go v1.7.2-0.20220308165842-f9e4fe31c2af/go.mod h1:fxCf9jAnXDZV1yDr0ckTuWd1intvcQwfJmTRpTZ1mXg= -github.com/drone/drone-runtime v1.0.7-0.20190729202838-87c84080f4a1/go.mod h1:+osgwGADc/nyl40J0fdsf8Z09bgcBZXvXXnLOY48zYs= -github.com/drone/drone-runtime v1.1.1-0.20200623162453-61e33e2cab5d h1:P5HI/Y9hARTZ3F3EKs0kYijhjXZWQRQHYn1neTi0pWM= -github.com/drone/drone-runtime v1.1.1-0.20200623162453-61e33e2cab5d/go.mod h1:4/2QToW5+HGD0y1sTw7X35W1f7YINS14UfDY4isggT8= -github.com/drone/drone-ui v2.11.5+incompatible h1:e+OmpCuxfCS1V8Szx+4OzCMXiM80ZJkV05/BuY8pVv8= -github.com/drone/drone-ui v2.11.5+incompatible/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI= -github.com/drone/drone-yaml v1.2.4-0.20220204000225-01fb17858c9b h1:NU8JZ2Py6dLa5kktIvcsQ5sokdDbvjL8GUOYEU53Wfk= -github.com/drone/drone-yaml v1.2.4-0.20220204000225-01fb17858c9b/go.mod h1:QsqliFK8nG04AHFN9tTn9XJomRBQHD4wcejWW1uz/10= -github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629 h1:rIaZZalMGGPb2cU/+ypuggZ8aMlpa17RUlJUtsMv8pw= -github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= -github.com/drone/funcmap v0.0.0-20210823160631-9e9dec149056 h1:SCJwMR0FMA0aKwAntCBh02YmtHEnU40zaDzeeCvIRug= -github.com/drone/funcmap v0.0.0-20210823160631-9e9dec149056/go.mod h1:Hph0/pT6ZxbujnE1Z6/08p5I0XXuOsppqF6NQlGOK0E= -github.com/drone/go-license v1.0.2 h1:7OwndfYk+Lp/cGHkxe4HUn/Ysrrw3WYH2pnd99yrkok= -github.com/drone/go-license v1.0.2/go.mod h1:fGRHf+F1cEaw3YVYiJ6js3G3dVhcxyS617RnNRUMsms= -github.com/drone/go-login v1.1.0 h1:anQFRh2Z5ketEJ/LvL6SJ6rIwDdfysGXK5bSXkFLInI= -github.com/drone/go-login v1.1.0/go.mod h1:FLxy9vRzLbyBxoCJYxGbG9R0WGn6OyuvBmAtYNt43uw= -github.com/drone/go-scm v1.28.0 h1:Y5SCf01Iw7/VO8hhjNgAX0eGIXZ225ctw7DlGQHh5g4= -github.com/drone/go-scm v1.28.0/go.mod h1:DFIJJjhMj0TSXPz+0ni4nyZ9gtTtC40Vh/TGRugtyWw= -github.com/drone/signal v1.0.0 h1:NrnM2M/4yAuU/tXs6RP1a1ZfxnaHwYkd0kJurA1p6uI= -github.com/drone/signal v1.0.0/go.mod h1:S8t92eFT0g4WUgEc/LxG+LCuiskpMNsG0ajAMGnyZpc= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi v3.3.3+incompatible h1:KHkmBEMNkwKuK4FdQL7N2wOeB9jnIx7jR5wsuSBEFI8= -github.com/go-chi/chi v3.3.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-chi/cors v1.0.0 h1:e6x8k7uWbUwYs+aXDoiUzeQFT6l0cygBYyNhD7/1Tg0= -github.com/go-chi/cors v1.0.0/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I= -github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= -github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4= -github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= -github.com/go-redis/redis/v8 v8.1.1/go.mod h1:ysgGY09J/QeDYbu3HikWEIPCwaeOkuNoTgKayTEaEOw= -github.com/go-redis/redis/v8 v8.11.0 h1:O1Td0mQ8UFChQ3N9zFQqo6kTU2cJ+/it88gDB+zg0wo= -github.com/go-redis/redis/v8 v8.11.0/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M= -github.com/go-redsync/redsync/v4 v4.3.0 h1:5cNxbHJc/tr1KX04piPv8nylURyuT3nqjmodkW8JCjM= -github.com/go-redsync/redsync/v4 v4.3.0/go.mod h1:QBOJAs1k8O6Eyrre4a++pxQgHe5eQ+HF56KuTVv+8Bs= -github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/gogo/protobuf v0.0.0-20170307180453-100ba4e88506 h1:zDlw+wgyXdfkRuvFCdEDUiPLmZp2cvf/dWHazY0a5VM= -github.com/gogo/protobuf v0.0.0-20170307180453-100ba4e88506/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= -github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY= -github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw= -github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/wire v0.2.1 h1:TYj4Z2qjqxa2ufb34UJqVeO9aznL+i0fLO6TqThKZ7Y= -github.com/google/wire v0.2.1/go.mod h1:ptBl5bWD3nzmJHVNwYHV3v4wdtKzBMlU2YbtKQCG9GI= -github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gosimple/slug v1.3.0 h1:NKQyQMjKkgCpD/Vd+wKtFc7N60bJNCLDubKU/UDKMFI= -github.com/gosimple/slug v1.3.0/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0= -github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE= -github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 h1:5B0uxl2lzNRVkJVg+uGHxWtRt4C0Wjc6kJKo5XYx8xE= -github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= -github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/kelseyhightower/envconfig v1.3.0 h1:IvRS4f2VcIQy6j4ORGIf9145T/AsUB+oY8LyvN8BXNM= -github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149 h1:HfxbT6/JcvIljmERptWhwa8XzP7H3T+Z2N26gTsaDaA= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 h1:dnMxwus89s86tI8rcGVp2HwZzlz7c5o92VOy7dSckBQ= -github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4/go.mod h1:cojhOHk1gbMeklOyDP2oKKLftefXoJreOQGOrXk+Z38= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4= -github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ= -github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= -github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso= -github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0= -github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ= -github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q= -github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhzioQqbC/KtuUhTigKlH/8ehhE= -github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/segmentio/ksuid v1.0.2 h1:9yBfKyw4ECGTdALaF09Snw3sLJmYIX6AbPJrAy6MrDc= -github.com/segmentio/ksuid v1.0.2/go.mod h1:BXuJDr2byAiHuQaQtSKoXh1J0YmUDurywOXgB2w+OSU= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= -github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= -github.com/unrolled/secure v0.0.0-20181022170031-4b6b7cf51606 h1:dU9yXzNi9rl6Mou7+3npdfPyeFPb2+7BHs3zL47bhPY= -github.com/unrolled/secure v0.0.0-20181022170031-4b6b7cf51606/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= -github.com/vinzenz/yaml v0.0.0-20170920082545-91409cdd725d h1:3wDi6J5APMqaHBVPuVd7RmHD2gRTfqbdcVSpCNoUWtk= -github.com/vinzenz/yaml v0.0.0-20170920082545-91409cdd725d/go.mod h1:mb5taDqMnJiZNRQ3+02W2IFG+oEz1+dTuCXkp4jpkfo= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0= -go.starlark.net v0.0.0-20221020143700-22309ac47eac h1:gBO5Qfcw5V9404yzsu2FEIsxK/u2mBNTNogK0uIoVhk= -go.starlark.net v0.0.0-20221020143700-22309ac47eac/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= -golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181005133103-4497e2df6f9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181017214349-06f26fdaaa28/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/h2non/gock.v1 v1.0.14 h1:fTeu9fcUvSnLNacYvYI54h+1/XEteDyHvrVCZEEEYNM= -gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.0.0-20181130031204-d04500c8c3dd/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= -k8s.io/apimachinery v0.0.0-20181201231028-18a5ff3097b4/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= -k8s.io/client-go v9.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= -k8s.io/klog v0.1.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/handler/api/acl/acl.go b/handler/api/acl/acl.go deleted file mode 100644 index e76b3121ec..0000000000 --- a/handler/api/acl/acl.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package acl - -import ( - "net/http" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" -) - -// AuthorizeUser returns an http.Handler middleware that authorizes only -// authenticated users to proceed to the next handler in the chain. Guest users -// are rejected with a 401 unauthorized error. -func AuthorizeUser(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, ok := request.UserFrom(r.Context()) - if !ok { - render.Unauthorized(w, errors.ErrUnauthorized) - logger.FromRequest(r). - Debugln("api: authentication required") - } else { - next.ServeHTTP(w, r) - } - }) -} - -// AuthorizeAdmin returns an http.Handler middleware that authorizes only -// system administrators to proceed to the next handler in the chain. -func AuthorizeAdmin(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - user, ok := request.UserFrom(r.Context()) - if !ok { - render.Unauthorized(w, errors.ErrUnauthorized) - logger.FromRequest(r). - Debugln("api: authentication required") - } else if !user.Admin { - render.Forbidden(w, errors.ErrForbidden) - logger.FromRequest(r). - Debugln("api: administrative access required") - } else { - next.ServeHTTP(w, r) - } - }) -} diff --git a/handler/api/acl/acl_test.go b/handler/api/acl/acl_test.go deleted file mode 100644 index 0be79fc2d1..0000000000 --- a/handler/api/acl/acl_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package acl - -import ( - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/request" - - "github.com/sirupsen/logrus" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -var ( - mockUser = &core.User{ - ID: 1, - Login: "octocat", - Admin: false, - Active: true, - } - - mockUserAdmin = &core.User{ - ID: 1, - Login: "octocat", - Admin: true, - Active: true, - } - - mockUserInactive = &core.User{ - ID: 1, - Login: "octocat", - Admin: false, - Active: false, - } - - mockRepo = &core.Repository{ - ID: 1, - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Counter: 42, - Branch: "master", - Private: true, - Visibility: core.VisibilityPrivate, - } -) - -func TestAuthorizeUser(t *testing.T) { - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - request.WithUser(r.Context(), mockUser), - ) - - AuthorizeUser( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // use dummy status code to signal the next handler in - // the middleware chain was properly invoked. - w.WriteHeader(http.StatusTeapot) - }), - ).ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestAuthorizeUserErr(t *testing.T) { - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - AuthorizeUser( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }), - ).ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestAuthorizeAdmin(t *testing.T) { - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - request.WithUser(r.Context(), &core.User{Admin: true}), - ) - - AuthorizeAdmin( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // use dummy status code to signal the next handler in - // the middleware chain was properly invoked. - w.WriteHeader(http.StatusTeapot) - }), - ).ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestAuthorizeAdminUnauthorized(t *testing.T) { - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - AuthorizeAdmin( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }), - ).ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestAuthorizeAdminForbidden(t *testing.T) { - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - request.WithUser(r.Context(), &core.User{Admin: false}), - ) - - AuthorizeAdmin( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }), - ).ServeHTTP(w, r) - - if got, want := w.Code, http.StatusForbidden; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} diff --git a/handler/api/acl/check.go b/handler/api/acl/check.go deleted file mode 100644 index 2eaf6f7ed8..0000000000 --- a/handler/api/acl/check.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package acl - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" - "github.com/sirupsen/logrus" -) - -// CheckReadAccess returns an http.Handler middleware that authorizes only -// authenticated users with read repository access to proceed to the next -// handler in the chain. -func CheckReadAccess() func(http.Handler) http.Handler { - return CheckAccess(true, false, false) -} - -// CheckWriteAccess returns an http.Handler middleware that authorizes only -// authenticated users with write repository access to proceed to the next -// handler in the chain. -func CheckWriteAccess() func(http.Handler) http.Handler { - return CheckAccess(true, true, false) -} - -// CheckAdminAccess returns an http.Handler middleware that authorizes only -// authenticated users with admin repository access to proceed to the next -// handler in the chain. -func CheckAdminAccess() func(http.Handler) http.Handler { - return CheckAccess(true, true, true) -} - -// CheckAccess returns an http.Handler middleware that authorizes only -// authenticated users with the required read, write or admin access -// permissions to the requested repository resource. -func CheckAccess(read, write, admin bool) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var ( - ctx = r.Context() - owner = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - log := logger.FromRequest(r). - WithField("namespace", owner). - WithField("name", name) - - user, ok := request.UserFrom(ctx) - switch { - case ok == false && write == true: - render.Unauthorized(w, errors.ErrUnauthorized) - log.Debugln("api: authentication required for write access") - return - case ok == false && admin == true: - render.Unauthorized(w, errors.ErrUnauthorized) - log.Debugln("api: authentication required for admin access") - return - case ok == true && user.Admin == true: - log.Debugln("api: root access granted") - next.ServeHTTP(w, r) - return - } - - repo, noRepo := request.RepoFrom(ctx) - if !noRepo { - // this should never happen. the repository - // should always be injected into the context - // by an upstream handler in the chain. - log.Errorln("api: null repository in context") - render.NotFound(w, errors.ErrNotFound) - return - } - - log = log.WithField("visibility", repo.Visibility) - - switch { - case admin == true: // continue - case write == true: // continue - case repo.Visibility == core.VisibilityPublic: - log.Debugln("api: read access granted") - next.ServeHTTP(w, r) - return - case ok == false: - render.Unauthorized(w, errors.ErrUnauthorized) - log.Debugln("api: authentication required") - return - case ok == true && repo.Visibility == core.VisibilityInternal: - log.Debugln("api: read access granted") - next.ServeHTTP(w, r) - return - } - - perm, ok := request.PermFrom(ctx) - if !ok { - render.NotFound(w, errors.ErrNotFound) - log.Debugln("api: repository permissions not found") - return - } - log = log.WithFields( - logrus.Fields{ - "read": perm.Read, - "write": perm.Write, - "admin": perm.Admin, - }, - ) - - switch { - case user.Active == false: - render.Forbidden(w, errors.ErrForbidden) - log.Debugln("api: active account required") - case read == true && perm.Read == false: - render.NotFound(w, errors.ErrNotFound) - log.Debugln("api: read access required") - case write == true && perm.Write == false: - render.NotFound(w, errors.ErrNotFound) - log.Debugln("api: write access required") - case admin == true && perm.Admin == false: - render.NotFound(w, errors.ErrNotFound) - log.Debugln("api: admin access required") - default: - log.Debug("api: access granted") - next.ServeHTTP(w, r.WithContext( - request.WithPerm(ctx, perm), - )) - } - }) - } -} diff --git a/handler/api/acl/check_test.go b/handler/api/acl/check_test.go deleted file mode 100644 index 02e396f41e..0000000000 --- a/handler/api/acl/check_test.go +++ /dev/null @@ -1,827 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package acl - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/request" - "github.com/google/go-cmp/cmp" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" -) - -var noContext = context.Background() - -// this test verifies that a 401 unauthorized error is written to -// the response if the client is not authenticated and repository -// visibility is internal or private. -func TestCheckAccess_Guest_Unauthorized(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithRepo(noContext, mockRepo), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckReadAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrUnauthorized - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies the the next handler in the middleware -// chain is processed if the user is not authenticated BUT -// the repository is publicly visible. -func TestCheckAccess_Guest_PublicVisibility(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := *mockRepo - mockRepo.Visibility = core.VisibilityPublic - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithRepo(noContext, &mockRepo), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckReadAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -// this test verifies that a 401 unauthorized error is written to -// the response if the repository visibility is internal, and the -// client is not authenticated. -func TestCheckAccess_Guest_InternalVisibility(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := *mockRepo - mockRepo.Visibility = core.VisibilityInternal - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithRepo(noContext, &mockRepo), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckReadAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -// this test verifies the the next handler in the middleware -// chain is processed if the user is authenticated AND -// the repository is publicly visible. -func TestCheckAccess_Authenticated_PublicVisibility(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := *mockRepo - mockRepo.Visibility = core.VisibilityPublic - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithUser( - request.WithRepo(noContext, &mockRepo), mockUser), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckReadAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -// this test verifies the the next handler in the middleware -// chain is processed if the user is authenticated AND -// the repository has internal visible. -func TestCheckAccess_Authenticated_InternalVisibility(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := *mockRepo - mockRepo.Visibility = core.VisibilityInternal - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithUser( - request.WithRepo(noContext, &mockRepo), mockUser), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckReadAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -// this test verifies that a 404 not found error is written to -// the response if the repository is not found AND the user is -// authenticated. -func TestCheckAccess_Authenticated_RepositoryNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckReadAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusNotFound; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found error is written to -// the response if the user does not have permissions to access -// the repository. -func TestCheckAccess_Permission_NotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithUser( - request.WithRepo(noContext, mockRepo), mockUser), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckReadAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusNotFound; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies the the next handler in the middleware -// chain is processed if the user has read access to the -// repository. -func TestCheckReadAccess(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - readAccess := &core.Perm{ - Synced: time.Now().Unix(), - Read: true, - Write: false, - Admin: false, - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithUser(r.Context(), mockUser), - ) - r = r.WithContext( - request.WithPerm( - request.WithUser( - request.WithRepo(noContext, mockRepo), - mockUser, - ), - readAccess, - ), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckReadAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -// this test verifies that a 404 not found error is written to -// the response if the user lacks read access to the repository. -func TestCheckReadAccess_InsufficientPermissions(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - noAccess := &core.Perm{ - Synced: time.Now().Unix(), - Read: false, - Write: false, - Admin: false, - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithPerm( - request.WithUser( - request.WithRepo(noContext, mockRepo), - mockUser, - ), - noAccess, - ), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckReadAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusNotFound; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies the the next handler in the middleware -// chain is processed if the user has write access to the -// repository. -func TestCheckWriteAccess(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - writeAccess := &core.Perm{ - Synced: time.Now().Unix(), - Read: true, - Write: true, - Admin: false, - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithPerm( - request.WithUser( - request.WithRepo(noContext, mockRepo), - mockUser, - ), - writeAccess, - ), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckWriteAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -// this test verifies the the next handler in the middleware -// chain is not processed if the user has write access BUT -// has been inactivated (e.g. blocked). -func TestCheckWriteAccess_InactiveUser(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - writeAccess := &core.Perm{ - Synced: time.Now().Unix(), - Read: true, - Write: true, - Admin: false, - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithPerm( - request.WithUser( - request.WithRepo(noContext, mockRepo), - mockUserInactive, - ), - writeAccess, - ), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckWriteAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Error("should not invoke handler") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusForbidden; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -// this test verifies that a 404 not found error is written to -// the response if the user lacks write access to the repository. -// -// TODO(bradrydzewski) we should consider returning a 403 forbidden -// if the user has read access. -func TestCheckWriteAccess_InsufficientPermissions(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - noAccess := &core.Perm{ - Synced: time.Now().Unix(), - Read: true, - Write: false, - Admin: false, - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithPerm( - request.WithUser( - request.WithRepo(noContext, mockRepo), - mockUser, - ), - noAccess, - ), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckWriteAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusNotFound; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies the the next handler in the middleware -// chain is processed if the user has admin access to the -// repository. -func TestCheckAdminAccess(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - noAccess := &core.Perm{ - Synced: time.Now().Unix(), - Read: true, - Write: true, - Admin: true, - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithPerm( - request.WithUser( - request.WithRepo(noContext, mockRepo), - mockUser, - ), - noAccess, - ), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckAdminAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -// this test verifies that a 404 not found error is written to -// the response if the user lacks admin access to the repository. -// -// TODO(bradrydzewski) we should consider returning a 403 forbidden -// if the user has read access. -func TestCheckAdminAccess_InsufficientPermissions(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - noAccess := &core.Perm{ - Synced: time.Now().Unix(), - Read: true, - Write: true, - Admin: false, - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithPerm( - request.WithUser( - request.WithRepo(noContext, mockRepo), - mockUser, - ), - noAccess, - ), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckAdminAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusNotFound; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies the the next handler in the middleware -// chain is processed if the authenticated user is a system -// administrator. -func TestCheckAdminAccess_SystemAdmin(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1, Admin: true, Active: true} - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithUser(r.Context(), user), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckAdminAccess()) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -// this test verifies that a 401 unauthorized error is written to -// the response if the client is not authenticated and write -// access is required. -func TestCheckAccess_Guest_Write(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithRepo(noContext, mockRepo), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckAccess(true, true, false)) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrUnauthorized - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 401 unauthorized error is written to -// the response if the client is not authenticated and admin -// access is required. -func TestCheckAccess_Guest_Admin(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext( - request.WithRepo(noContext, mockRepo), - ) - - router := chi.NewRouter() - router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { - router.Use(CheckAccess(true, false, true)) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrUnauthorized - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// // this test verifies the the next handler in the middleware -// // chain is processed if the authenticated has read permissions -// // that are successfully synchronized with the source. -// func TestCheckAccess_RefreshPerms(t *testing.T) { -// controller := gomock.NewController(t) -// defer controller.Finish() - -// expiredAccess := &core.Perm{ -// Synced: 0, -// Read: false, -// Write: false, -// Admin: false, -// } - -// updatedAccess := &core.Perm{ -// Read: true, -// Write: true, -// Admin: true, -// } - -// checkPermUpdate := func(ctx context.Context, perm *core.Perm) { -// if perm.Synced == 0 { -// t.Errorf("Expect synced timestamp updated") -// } -// if perm.Read == false { -// t.Errorf("Expect Read flag updated") -// } -// if perm.Write == false { -// t.Errorf("Expect Write flag updated") -// } -// if perm.Admin == false { -// t.Errorf("Expect Admin flag updated") -// } -// } - -// repos := mock.NewMockRepositoryStore(controller) -// repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(mockRepo, nil) - -// perms := mock.NewMockPermStore(controller) -// perms.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(expiredAccess, nil) -// perms.EXPECT().Update(gomock.Any(), expiredAccess).Return(nil).Do(checkPermUpdate) - -// service := mock.NewMockRepositoryService(controller) -// service.EXPECT().FindPerm(gomock.Any(), "octocat/hello-world").Return(updatedAccess, nil) - -// factory := mock.NewMockRepositoryServiceFactory(controller) -// factory.EXPECT().Create(mockUser).Return(service) - -// w := httptest.NewRecorder() -// r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) -// r = r.WithContext( -// request.WithUser(r.Context(), mockUser), -// ) - -// router := chi.NewRouter() -// router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { -// router.Use(CheckReadAccess(factory, repos, perms)) -// router.Get("/", func(w http.ResponseWriter, r *http.Request) { -// w.WriteHeader(http.StatusTeapot) -// }) -// }) - -// router.ServeHTTP(w, r) - -// if got, want := w.Code, http.StatusTeapot; got != want { -// t.Errorf("Want status code %d, got %d", want, got) -// } -// } - -// // this test verifies that a 404 not found error is written to -// // the response if the user permissions are expired and the -// // updated permissions cannot be fetched. -// func TestCheckAccess_RefreshPerms_Error(t *testing.T) { -// controller := gomock.NewController(t) -// defer controller.Finish() - -// expiredAccess := &core.Perm{ -// Synced: 0, -// Read: false, -// Write: false, -// Admin: false, -// } - -// repos := mock.NewMockRepositoryStore(controller) -// repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(mockRepo, nil) - -// perms := mock.NewMockPermStore(controller) -// perms.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(expiredAccess, nil) - -// service := mock.NewMockRepositoryService(controller) -// service.EXPECT().FindPerm(gomock.Any(), "octocat/hello-world").Return(nil, io.EOF) - -// factory := mock.NewMockRepositoryServiceFactory(controller) -// factory.EXPECT().Create(mockUser).Return(service) - -// w := httptest.NewRecorder() -// r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) -// r = r.WithContext( -// request.WithUser(r.Context(), mockUser), -// ) - -// router := chi.NewRouter() -// router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { -// router.Use(CheckReadAccess(factory, repos, perms)) -// router.Get("/", func(w http.ResponseWriter, r *http.Request) { -// w.WriteHeader(http.StatusTeapot) -// }) -// }) - -// router.ServeHTTP(w, r) -// if got, want := w.Code, 404; got != want { -// t.Errorf("Want status code %d, got %d", want, got) -// } -// } - -// // this test verifies the the next handler in the middleware -// // chain is processed if the user permissions are expired, -// // updated permissions are fetched, but fail the changes fail -// // to persist to the database. We know the user has access, -// // so we allow them to proceed even in the event of a failure. -// func TestCheckAccess_RefreshPerms_CannotSave(t *testing.T) { -// controller := gomock.NewController(t) -// defer controller.Finish() - -// expiredAccess := &core.Perm{ -// Synced: 0, -// Read: false, -// Write: false, -// Admin: false, -// } - -// updatedAccess := &core.Perm{ -// Read: true, -// Write: true, -// Admin: true, -// } - -// service := mock.NewMockRepositoryService(controller) -// service.EXPECT().FindPerm(gomock.Any(), "octocat/hello-world").Return(updatedAccess, nil) - -// factory := mock.NewMockRepositoryServiceFactory(controller) -// factory.EXPECT().Create(mockUser).Return(service) - -// repos := mock.NewMockRepositoryStore(controller) -// repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(mockRepo, nil) - -// perms := mock.NewMockPermStore(controller) -// perms.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(expiredAccess, nil) -// perms.EXPECT().Update(gomock.Any(), expiredAccess).Return(io.EOF) - -// w := httptest.NewRecorder() -// r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) -// r = r.WithContext( -// request.WithUser(r.Context(), mockUser), -// ) - -// router := chi.NewRouter() -// router.Route("/api/repos/{owner}/{name}", func(router chi.Router) { -// router.Use(CheckReadAccess(factory, repos, perms)) -// router.Get("/", func(w http.ResponseWriter, r *http.Request) { -// w.WriteHeader(http.StatusTeapot) -// }) -// }) - -// router.ServeHTTP(w, r) -// if got, want := w.Code, http.StatusTeapot; got != want { -// t.Errorf("Want status code %d, got %d", want, got) -// } -// } diff --git a/handler/api/acl/org.go b/handler/api/acl/org.go deleted file mode 100644 index 52d6c98890..0000000000 --- a/handler/api/acl/org.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package acl - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// CheckMembership returns an http.Handler middleware that authorizes only -// authenticated users with the required membership to an organization -// to the requested repository resource. -func CheckMembership(service core.OrganizationService, admin bool) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - namespace := chi.URLParam(r, "namespace") - log := logger.FromRequest(r) - ctx := r.Context() - - user, ok := request.UserFrom(ctx) - if !ok { - render.Unauthorized(w, errors.ErrUnauthorized) - log.Debugln("api: authentication required for access") - return - } - log = log.WithField("user.admin", user.Admin) - - // if the user is an administrator they are always - // granted access to the organization data. - if user.Admin { - next.ServeHTTP(w, r) - return - } - - if user.Login == namespace { - next.ServeHTTP(w, r) - return - } - - isMember, isAdmin, err := service.Membership(ctx, user, namespace) - if err != nil { - render.Unauthorized(w, errors.ErrForbidden) - log.Debugln("api: organization membership not found") - return - } - - log = log. - WithField("organization.member", isMember). - WithField("organization.admin", isAdmin) - - if isMember == false { - render.Unauthorized(w, errors.ErrForbidden) - log.Debugln("api: organization membership is required") - return - } - - if isAdmin == false && admin == true { - render.Unauthorized(w, errors.ErrForbidden) - log.Debugln("api: organization administrator is required") - return - } - - log.Debugln("api: organization membership verified") - next.ServeHTTP(w, r) - }) - } -} diff --git a/handler/api/acl/org_test.go b/handler/api/acl/org_test.go deleted file mode 100644 index c0082611ad..0000000000 --- a/handler/api/acl/org_test.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package acl - -import ( - "errors" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" -) - -func TestCheckMembership_Admin(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/secrets/github", nil) - r = r.WithContext( - request.WithUser(noContext, mockUserAdmin), - ) - - router := chi.NewRouter() - router.Route("/api/secrets/{namespace}", func(router chi.Router) { - router.Use(CheckMembership(nil, true)) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestCheckMembership_NilUser_Unauthorized(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/secrets/github", nil) - - router := chi.NewRouter() - router.Route("/api/secrets/{namespace}", func(router chi.Router) { - router.Use(CheckMembership(nil, true)) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestCheckMembership_AuthorizeRead(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/secrets/github", nil) - r = r.WithContext( - request.WithUser(noContext, mockUser), - ) - - mockOrgService := mock.NewMockOrganizationService(controller) - mockOrgService.EXPECT().Membership(gomock.Any(), gomock.Any(), "github").Return(true, false, nil).Times(1) - - router := chi.NewRouter() - router.Route("/api/secrets/{namespace}", func(router chi.Router) { - router.Use(CheckMembership(mockOrgService, false)) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestCheckMembership_AuthorizeAdmin(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/secrets/github", nil) - r = r.WithContext( - request.WithUser(noContext, mockUser), - ) - - mockOrgService := mock.NewMockOrganizationService(controller) - mockOrgService.EXPECT().Membership(gomock.Any(), gomock.Any(), "github").Return(true, true, nil).Times(1) - - router := chi.NewRouter() - router.Route("/api/secrets/{namespace}", func(router chi.Router) { - router.Use(CheckMembership(mockOrgService, true)) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestCheckMembership_Unauthorized_Admin(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/secrets/github", nil) - r = r.WithContext( - request.WithUser(noContext, mockUser), - ) - - mockOrgService := mock.NewMockOrganizationService(controller) - mockOrgService.EXPECT().Membership(gomock.Any(), gomock.Any(), "github").Return(true, false, nil).Times(1) - - router := chi.NewRouter() - router.Route("/api/secrets/{namespace}", func(router chi.Router) { - router.Use(CheckMembership(mockOrgService, true)) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestCheckMembership_Unauthorized_Read(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/secrets/github", nil) - r = r.WithContext( - request.WithUser(noContext, mockUser), - ) - - mockOrgService := mock.NewMockOrganizationService(controller) - mockOrgService.EXPECT().Membership(gomock.Any(), gomock.Any(), "github").Return(false, false, nil).Times(1) - - router := chi.NewRouter() - router.Route("/api/secrets/{namespace}", func(router chi.Router) { - router.Use(CheckMembership(mockOrgService, false)) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestCheckMembership_Unauthorized_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/secrets/github", nil) - r = r.WithContext( - request.WithUser(noContext, mockUser), - ) - - mockOrgService := mock.NewMockOrganizationService(controller) - mockOrgService.EXPECT().Membership(gomock.Any(), gomock.Any(), "github").Return(true, true, errors.New("")).Times(1) - - router := chi.NewRouter() - router.Route("/api/secrets/{namespace}", func(router chi.Router) { - router.Use(CheckMembership(mockOrgService, false)) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - t.Errorf("Must not invoke next handler in middleware chain") - }) - }) - - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusUnauthorized; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} diff --git a/handler/api/acl/repo.go b/handler/api/acl/repo.go deleted file mode 100644 index 1faa14c0fe..0000000000 --- a/handler/api/acl/repo.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package acl - -import ( - "net/http" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" - "github.com/sirupsen/logrus" -) - -// InjectRepository returns an http.Handler middleware that injects -// the repository and repository permissions into the context. -func InjectRepository( - repoz core.RepositoryService, - repos core.RepositoryStore, - perms core.PermStore, -) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var ( - ctx = r.Context() - owner = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - log := logger.FromRequest(r).WithFields( - logrus.Fields{ - "namespace": owner, - "name": name, - }, - ) - - // the user is stored in the context and is - // provided by a an ancestor middleware in the - // chain. - user, sessionExists := request.UserFrom(ctx) - - repo, err := repos.FindName(ctx, owner, name) - if err != nil { - if sessionExists { - render.NotFound(w, errors.ErrNotFound) - } else { - render.Unauthorized(w, errors.ErrUnauthorized) - } - log.WithError(err).Debugln("api: repository not found") - return - } - - // the repository is stored in the request context - // and can be accessed by subsequent handlers in the - // request chain. - ctx = request.WithRepo(ctx, repo) - - // if the user does not exist in the request context, - // this is a guest session, and there are no repository - // permissions to lookup. - if !sessionExists { - next.ServeHTTP(w, r.WithContext(ctx)) - return - } - - // else get the cached permissions from the database - // for the user and repository. - perm, err := perms.Find(ctx, repo.UID, user.ID) - if err != nil { - // if the permissions are not found we forward - // the request to the next handler in the chain - // with no permissions in the context. - // - // It is the responsibility to downstream - // middleware and handlers to decide if the - // request should be rejected. - next.ServeHTTP(w, r.WithContext(ctx)) - return - } - - log = log.WithFields( - logrus.Fields{ - "read": perm.Read, - "write": perm.Write, - "admin": perm.Admin, - }, - ) - - // because the permissions are synced with the remote - // system (e.g. github) they may be stale. If the permissions - // are stale they are refreshed below. - if perm.Synced == 0 || time.Unix(perm.Synced, 0).Add(time.Hour).Before(time.Now()) { - log.Debugln("api: sync repository permissions") - - permv, err := repoz.FindPerm(ctx, user, repo.Slug) - if err != nil { - render.NotFound(w, errors.ErrNotFound) - log.WithError(err). - Warnln("api: cannot sync repository permissions") - return - } - - perm.Synced = time.Now().Unix() - perm.Read = permv.Read - perm.Write = permv.Write - perm.Admin = permv.Admin - - err = perms.Update(ctx, perm) - if err != nil { - log.WithError(err).Debugln("api: cannot cache repository permissions") - } else { - log.Debugln("api: repository permissions synchronized") - } - } - - ctx = request.WithPerm(ctx, perm) - next.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} diff --git a/handler/api/acl/repo_test.go b/handler/api/acl/repo_test.go deleted file mode 100644 index 9959c8c258..0000000000 --- a/handler/api/acl/repo_test.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package acl - -import ( - "context" - "database/sql" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - "github.com/drone/drone/core" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" -) - -// this unit test ensures that the http request returns a -// 401 unauthorized if the session does not exist, and the -// repository is not found. -func TestInjectRepository_RepoNotFound_Guest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) { - t.Fail() - }) - - InjectRepository(nil, repos, nil)(next).ServeHTTP(w, r) - if got, want := w.Code, http.StatusUnauthorized; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -// this unit test ensures that the http request returns a -// 404 not found if the session does exist, but the -// repository is not found. -func TestInjectRepository_RepoNotFound_User(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue( - request.WithUser(r.Context(), &core.User{}), - chi.RouteCtxKey, c), - ) - - next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) { - t.Fail() - }) - - InjectRepository(nil, repos, nil)(next).ServeHTTP(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -// this unit test ensures that the middleware function -// invokes the next handler in the chain if the repository -// is found, but no user session exists. -func TestInjectRepository_RepoFound_Guest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(&core.Repository{}, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue( - r.Context(), - chi.RouteCtxKey, c), - ) - - invoked := false - next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) { - invoked = true - }) - - InjectRepository(nil, repos, nil)(next).ServeHTTP(w, r) - if !invoked { - t.Errorf("Expect middleware invoked") - } -} - -// this unit test ensures that the middleware function -// invokes the next handler and stores the permissions -// in the context if found. -func TestInjectRepository_PermsFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ID: 1} - mockRepo := &core.Repository{UID: "1"} - mockPerm := &core.Perm{Synced: time.Now().Unix()} - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(mockRepo, nil) - - perms := mock.NewMockPermStore(controller) - perms.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(mockPerm, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue( - request.WithUser(r.Context(), mockUser), - chi.RouteCtxKey, c), - ) - - invoked := false - next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - invoked = true - _, ok := request.PermFrom(r.Context()) - if !ok { - t.Errorf("Expect perm from context") - } - }) - - InjectRepository(nil, repos, perms)(next).ServeHTTP(w, r) - if !invoked { - t.Errorf("Expect middleware invoked") - } -} - -// this unit test ensures that the middleware function -// invokes the next handler even if the permissions are -// not found. It is the responsibility to downstream -// middleware and handlers to decide if the request -// should be rejected. -func TestInjectRepository_PermsNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ID: 1} - mockRepo := &core.Repository{UID: "1"} - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(mockRepo, nil) - - perms := mock.NewMockPermStore(controller) - perms.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue( - request.WithUser(r.Context(), mockUser), - chi.RouteCtxKey, c), - ) - - invoked := false - next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - invoked = true - _, ok := request.PermFrom(r.Context()) - if ok { - t.Errorf("Expect nil perm from context") - } - }) - - InjectRepository(nil, repos, perms)(next).ServeHTTP(w, r) - if !invoked { - t.Errorf("Expect middleware invoked") - } -} diff --git a/handler/api/api.go b/handler/api/api.go deleted file mode 100644 index a2c7d800ca..0000000000 --- a/handler/api/api.go +++ /dev/null @@ -1,401 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package api - -import ( - "net/http" - "os" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/acl" - "github.com/drone/drone/handler/api/auth" - "github.com/drone/drone/handler/api/badge" - globalbuilds "github.com/drone/drone/handler/api/builds" - "github.com/drone/drone/handler/api/card" - "github.com/drone/drone/handler/api/ccmenu" - "github.com/drone/drone/handler/api/events" - "github.com/drone/drone/handler/api/queue" - "github.com/drone/drone/handler/api/repos" - "github.com/drone/drone/handler/api/repos/builds" - "github.com/drone/drone/handler/api/repos/builds/branches" - "github.com/drone/drone/handler/api/repos/builds/deploys" - "github.com/drone/drone/handler/api/repos/builds/logs" - "github.com/drone/drone/handler/api/repos/builds/pulls" - "github.com/drone/drone/handler/api/repos/builds/stages" - "github.com/drone/drone/handler/api/repos/collabs" - "github.com/drone/drone/handler/api/repos/crons" - "github.com/drone/drone/handler/api/repos/encrypt" - "github.com/drone/drone/handler/api/repos/secrets" - "github.com/drone/drone/handler/api/repos/sign" - globalsecrets "github.com/drone/drone/handler/api/secrets" - "github.com/drone/drone/handler/api/system" - "github.com/drone/drone/handler/api/template" - "github.com/drone/drone/handler/api/user" - "github.com/drone/drone/handler/api/user/remote" - "github.com/drone/drone/handler/api/users" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" - "github.com/go-chi/cors" -) - -var corsOpts = cors.Options{ - AllowedOrigins: []string{"*"}, - AllowedMethods: []string{"GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"}, - AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, - ExposedHeaders: []string{"Link"}, - AllowCredentials: true, - MaxAge: 300, -} - -func New( - builds core.BuildStore, - commits core.CommitService, - card core.CardStore, - cron core.CronStore, - events core.Pubsub, - globals core.GlobalSecretStore, - hooks core.HookService, - logs core.LogStore, - license *core.License, - licenses core.LicenseService, - orgs core.OrganizationService, - perms core.PermStore, - repos core.RepositoryStore, - repoz core.RepositoryService, - scheduler core.Scheduler, - secrets core.SecretStore, - stages core.StageStore, - steps core.StepStore, - status core.StatusService, - session core.Session, - stream core.LogStream, - syncer core.Syncer, - system *core.System, - template core.TemplateStore, - transferer core.Transferer, - triggerer core.Triggerer, - users core.UserStore, - userz core.UserService, - webhook core.WebhookSender, -) Server { - return Server{ - Builds: builds, - Card: card, - Cron: cron, - Commits: commits, - Events: events, - Globals: globals, - Hooks: hooks, - Logs: logs, - License: license, - Licenses: licenses, - Orgs: orgs, - Perms: perms, - Repos: repos, - Repoz: repoz, - Scheduler: scheduler, - Secrets: secrets, - Stages: stages, - Steps: steps, - Status: status, - Session: session, - Stream: stream, - Syncer: syncer, - System: system, - Template: template, - Transferer: transferer, - Triggerer: triggerer, - Users: users, - Userz: userz, - Webhook: webhook, - } -} - -// Server is a http.Handler which exposes drone functionality over HTTP. -type Server struct { - Builds core.BuildStore - Card core.CardStore - Cron core.CronStore - Commits core.CommitService - Events core.Pubsub - Globals core.GlobalSecretStore - Hooks core.HookService - Logs core.LogStore - License *core.License - Licenses core.LicenseService - Orgs core.OrganizationService - Perms core.PermStore - Repos core.RepositoryStore - Repoz core.RepositoryService - Scheduler core.Scheduler - Secrets core.SecretStore - Stages core.StageStore - Steps core.StepStore - Status core.StatusService - Session core.Session - Stream core.LogStream - Syncer core.Syncer - System *core.System - Template core.TemplateStore - Transferer core.Transferer - Triggerer core.Triggerer - Users core.UserStore - Userz core.UserService - Webhook core.WebhookSender - Private bool -} - -// Handler returns an http.Handler -func (s Server) Handler() http.Handler { - r := chi.NewRouter() - r.Use(middleware.Recoverer) - r.Use(middleware.NoCache) - r.Use(logger.Middleware) - r.Use(auth.HandleAuthentication(s.Session)) - - cors := cors.New(corsOpts) - r.Use(cors.Handler) - - r.Route("/repos", func(r chi.Router) { - // temporary workaround to enable private mode - // for the drone server. - if os.Getenv("DRONE_SERVER_PRIVATE_MODE") == "true" { - r.Use(acl.AuthorizeUser) - } - - r.With( - acl.AuthorizeAdmin, - ).Get("/", repos.HandleAll(s.Repos)) - - r.Route("/{owner}/{name}", func(r chi.Router) { - r.Use(acl.InjectRepository(s.Repoz, s.Repos, s.Perms)) - r.Use(acl.CheckReadAccess()) - - r.Get("/", repos.HandleFind()) - r.With( - acl.CheckAdminAccess(), - ).Patch("/", repos.HandleUpdate(s.Repos)) - r.With( - acl.CheckAdminAccess(), - ).Post("/", repos.HandleEnable(s.Hooks, s.Repos, s.Webhook)) - r.With( - acl.CheckAdminAccess(), - ).Delete("/", repos.HandleDisable(s.Repos, s.Webhook)) - r.With( - acl.CheckAdminAccess(), - ).Post("/chown", repos.HandleChown(s.Repos)) - r.With( - acl.CheckAdminAccess(), - ).Post("/repair", repos.HandleRepair(s.Hooks, s.Repoz, s.Repos, s.Users, s.System.Link)) - - r.Route("/builds", func(r chi.Router) { - r.Get("/", builds.HandleList(s.Repos, s.Builds)) - r.With(acl.CheckWriteAccess()).Post("/", builds.HandleCreate(s.Users, s.Repos, s.Commits, s.Triggerer)) - - r.Get("/branches", branches.HandleList(s.Repos, s.Builds)) - r.With(acl.CheckWriteAccess()).Delete("/branches/*", branches.HandleDelete(s.Repos, s.Builds)) - - r.Get("/pulls", pulls.HandleList(s.Repos, s.Builds)) - r.With(acl.CheckWriteAccess()).Delete("/pulls/{pull}", pulls.HandleDelete(s.Repos, s.Builds)) - - r.Get("/deployments", deploys.HandleList(s.Repos, s.Builds)) - r.With(acl.CheckWriteAccess()).Delete("/deployments/*", deploys.HandleDelete(s.Repos, s.Builds)) - - r.Get("/latest", builds.HandleLast(s.Repos, s.Builds, s.Stages)) - r.Get("/{number}", builds.HandleFind(s.Repos, s.Builds, s.Stages)) - r.Get("/{number}/logs/{stage}/{step}", logs.HandleFind(s.Repos, s.Builds, s.Stages, s.Steps, s.Logs)) - - r.With( - acl.CheckWriteAccess(), - ).Post("/{number}", builds.HandleRetry(s.Repos, s.Builds, s.Triggerer)) - - r.With( - acl.CheckWriteAccess(), - ).Delete("/{number}", builds.HandleCancel(s.Users, s.Repos, s.Builds, s.Stages, s.Steps, s.Status, s.Scheduler, s.Webhook)) - - r.With( - acl.CheckWriteAccess(), - ).Post("/{number}/promote", builds.HandlePromote(s.Repos, s.Builds, s.Triggerer)) - - r.With( - acl.CheckWriteAccess(), - ).Post("/{number}/rollback", builds.HandleRollback(s.Repos, s.Builds, s.Triggerer)) - - r.With( - acl.CheckAdminAccess(), - ).Post("/{number}/decline/{stage}", stages.HandleDecline(s.Repos, s.Builds, s.Stages)) - - r.With( - acl.CheckAdminAccess(), - ).Post("/{number}/approve/{stage}", stages.HandleApprove(s.Repos, s.Builds, s.Stages, s.Scheduler)) - - r.With( - acl.CheckAdminAccess(), - ).Delete("/{number}/logs/{stage}/{step}", logs.HandleDelete(s.Repos, s.Builds, s.Stages, s.Steps, s.Logs)) - - r.With( - acl.CheckAdminAccess(), - ).Delete("/", builds.HandlePurge(s.Repos, s.Builds)) - }) - - r.Route("/secrets", func(r chi.Router) { - r.Use(acl.CheckWriteAccess()) - r.Get("/", secrets.HandleList(s.Repos, s.Secrets)) - r.Post("/", secrets.HandleCreate(s.Repos, s.Secrets)) - r.Get("/{secret}", secrets.HandleFind(s.Repos, s.Secrets)) - r.Patch("/{secret}", secrets.HandleUpdate(s.Repos, s.Secrets)) - r.Delete("/{secret}", secrets.HandleDelete(s.Repos, s.Secrets)) - }) - - r.Route("/sign", func(r chi.Router) { - r.Use(acl.CheckWriteAccess()) - r.Post("/", sign.HandleSign(s.Repos)) - }) - - r.Route("/encrypt", func(r chi.Router) { - r.Use(acl.CheckWriteAccess()) - r.Post("/", encrypt.Handler(s.Repos)) - r.Post("/secret", encrypt.Handler(s.Repos)) - }) - - r.Route("/cron", func(r chi.Router) { - r.Use(acl.CheckWriteAccess()) - r.Post("/", crons.HandleCreate(s.Repos, s.Cron)) - r.Get("/", crons.HandleList(s.Repos, s.Cron)) - r.Get("/{cron}", crons.HandleFind(s.Repos, s.Cron)) - r.Post("/{cron}", crons.HandleExec(s.Users, s.Repos, s.Cron, s.Commits, s.Triggerer)) - r.Patch("/{cron}", crons.HandleUpdate(s.Repos, s.Cron)) - r.Delete("/{cron}", crons.HandleDelete(s.Repos, s.Cron)) - }) - - r.Route("/collaborators", func(r chi.Router) { - r.Get("/", collabs.HandleList(s.Repos, s.Perms)) - r.Get("/{member}", collabs.HandleFind(s.Users, s.Repos, s.Perms)) - r.With( - acl.CheckAdminAccess(), - ).Delete("/{member}", collabs.HandleDelete(s.Users, s.Repos, s.Perms)) - }) - - r.Route("/cards", func(r chi.Router) { - r.Get("/{build}/{stage}/{step}", card.HandleFind(s.Builds, s.Card, s.Stages, s.Steps, s.Repos)) - r.With( - acl.CheckAdminAccess(), - ).Post("/{build}/{stage}/{step}", card.HandleCreate(s.Builds, s.Card, s.Stages, s.Steps, s.Repos)) - r.With( - acl.CheckAdminAccess(), - ).Delete("/{build}/{stage}/{step}", card.HandleDelete(s.Builds, s.Card, s.Stages, s.Steps, s.Repos)) - }) - }) - }) - - r.Route("/badges/{owner}/{name}", func(r chi.Router) { - r.Get("/status.svg", badge.Handler(s.Repos, s.Builds)) - r.With( - acl.InjectRepository(s.Repoz, s.Repos, s.Perms), - acl.CheckReadAccess(), - ).Get("/cc.xml", ccmenu.Handler(s.Repos, s.Builds, s.System.Link)) - }) - - r.Route("/queue", func(r chi.Router) { - r.Use(acl.AuthorizeAdmin) - r.Get("/", queue.HandleItems(s.Stages)) - r.Post("/", queue.HandleResume(s.Scheduler)) - r.Delete("/", queue.HandlePause(s.Scheduler)) - }) - - r.Route("/user", func(r chi.Router) { - r.Use(acl.AuthorizeUser) - r.Get("/", user.HandleFind()) - r.Patch("/", user.HandleUpdate(s.Users)) - r.Post("/token", user.HandleToken(s.Users)) - r.Get("/repos", user.HandleRepos(s.Repos)) - r.Post("/repos", user.HandleSync(s.Syncer, s.Repos)) - - // TODO(bradrydzewski) finalize the name for this endpoint. - r.Get("/builds", user.HandleRecent(s.Repos)) - r.Get("/builds/recent", user.HandleRecent(s.Repos)) - - // expose remote endpoints (e.g. to github) - r.Get("/remote/repos", remote.HandleRepos(s.Repoz)) - r.Get("/remote/repos/{owner}/{name}", remote.HandleRepo(s.Repoz)) - }) - - r.Route("/users", func(r chi.Router) { - r.Use(acl.AuthorizeAdmin) - r.Get("/", users.HandleList(s.Users)) - r.Post("/", users.HandleCreate(s.Users, s.Userz, s.Webhook)) - r.Get("/{user}", users.HandleFind(s.Users)) - r.Patch("/{user}", users.HandleUpdate(s.Users, s.Transferer)) - r.Post("/{user}/token/rotate", users.HandleTokenRotation(s.Users)) - r.Delete("/{user}", users.HandleDelete(s.Users, s.Transferer, s.Webhook)) - r.Get("/{user}/repos", users.HandleRepoList(s.Users, s.Repos)) - }) - - r.Route("/stream", func(r chi.Router) { - r.Get("/", events.HandleGlobal(s.Repos, s.Events)) - - r.Route("/{owner}/{name}", func(r chi.Router) { - r.Use(acl.InjectRepository(s.Repoz, s.Repos, s.Perms)) - r.Use(acl.CheckReadAccess()) - - r.Get("/", events.HandleEvents(s.Repos, s.Events)) - r.Get("/{number}/{stage}/{step}", events.HandleLogStream(s.Repos, s.Builds, s.Stages, s.Steps, s.Stream)) - }) - }) - - r.Route("/builds", func(r chi.Router) { - r.Use(acl.AuthorizeAdmin) - r.Get("/incomplete", globalbuilds.HandleIncomplete(s.Repos)) - r.Get("/incomplete/v2", globalbuilds.HandleRunningStatus(s.Repos)) - }) - - r.Route("/secrets", func(r chi.Router) { - r.With(acl.AuthorizeAdmin).Get("/", globalsecrets.HandleAll(s.Globals)) - r.With(acl.CheckMembership(s.Orgs, false)).Get("/{namespace}", globalsecrets.HandleList(s.Globals)) - r.With(acl.CheckMembership(s.Orgs, true)).Post("/{namespace}", globalsecrets.HandleCreate(s.Globals)) - r.With(acl.CheckMembership(s.Orgs, false)).Get("/{namespace}/{name}", globalsecrets.HandleFind(s.Globals)) - r.With(acl.CheckMembership(s.Orgs, true)).Post("/{namespace}/{name}", globalsecrets.HandleUpdate(s.Globals)) - r.With(acl.CheckMembership(s.Orgs, true)).Patch("/{namespace}/{name}", globalsecrets.HandleUpdate(s.Globals)) - r.With(acl.CheckMembership(s.Orgs, true)).Delete("/{namespace}/{name}", globalsecrets.HandleDelete(s.Globals)) - }) - - r.Route("/templates", func(r chi.Router) { - r.With(acl.CheckMembership(s.Orgs, false)).Get("/", template.HandleListAll(s.Template)) - r.With(acl.CheckMembership(s.Orgs, true)).Post("/{namespace}", template.HandleCreate(s.Template)) - r.With(acl.CheckMembership(s.Orgs, false)).Get("/{namespace}", template.HandleList(s.Template)) - r.With(acl.CheckMembership(s.Orgs, false)).Get("/{namespace}/{name}", template.HandleFind(s.Template)) - r.With(acl.CheckMembership(s.Orgs, true)).Put("/{namespace}/{name}", template.HandleUpdate(s.Template)) - r.With(acl.CheckMembership(s.Orgs, true)).Patch("/{namespace}/{name}", template.HandleUpdate(s.Template)) - r.With(acl.CheckMembership(s.Orgs, true)).Delete("/{namespace}/{name}", template.HandleDelete(s.Template)) - }) - - r.Route("/system", func(r chi.Router) { - r.Use(acl.AuthorizeAdmin) - // r.Get("/license", system.HandleLicense()) - // r.Get("/limits", system.HandleLimits()) - r.Get("/stats", system.HandleStats( - s.Builds, - s.Stages, - s.Users, - s.Repos, - s.Events, - s.Stream, - )) - }) - - return r -} diff --git a/handler/api/auth/auth.go b/handler/api/auth/auth.go deleted file mode 100644 index 68d25d1f91..0000000000 --- a/handler/api/auth/auth.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package auth - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" -) - -// HandleAuthentication returns an http.HandlerFunc middleware that authenticates -// the http.Request and errors if the account cannot be authenticated. -func HandleAuthentication(session core.Session) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - log := logger.FromContext(ctx) - user, err := session.Get(r) - - // this block of code checks the error message and user - // returned from the session, including some edge cases, - // to prevent a session from being falsely created. - if err != nil || user == nil || user.ID == 0 { - next.ServeHTTP(w, r) - log.Debugln("api: guest access") - return - } - - if user.Machine { - log = log.WithField("user.machine", user.Machine) - } - if user.Admin { - log = log.WithField("user.admin", user.Admin) - } - log = log.WithField("user.login", user.Login) - ctx = logger.WithContext(ctx, log) - next.ServeHTTP(w, r.WithContext( - request.WithUser(ctx, user), - )) - }) - } -} diff --git a/handler/api/auth/auth_test.go b/handler/api/auth/auth_test.go deleted file mode 100644 index 5efb62a350..0000000000 --- a/handler/api/auth/auth_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package auth - -import ( - "database/sql" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - "github.com/sirupsen/logrus" - - "github.com/golang/mock/gomock" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -func TestAuth(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - ID: 1, - Login: "octocat", - Admin: true, - Machine: true, - Hash: "$2a$04$rR2VvGjM9iqAAoyLSE4IrexAlxDbIS3M5YKtj9ANs7vraki0ybYJq 197XXbZablx0RPQ8", - } - - session := mock.NewMockSession(controller) - session.EXPECT().Get(gomock.Any()).Return(mockUser, nil) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/?access_token=VA.197XXbZablx0RPQ8", nil) - - HandleAuthentication(session)( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // use dummy status code to signal the next handler in - // the middleware chain was properly invoked. - w.WriteHeader(http.StatusTeapot) - - // verify the user was added to the request context - if user, _ := request.UserFrom(r.Context()); user != mockUser { - t.Errorf("Expect user in context") - } - }), - ).ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestAuth_Guest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - session := mock.NewMockSession(controller) - session.EXPECT().Get(gomock.Any()).Return(nil, sql.ErrNoRows) - - HandleAuthentication(session)( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // use dummy status code to signal the next handler in - // the middleware chain was properly invoked. - w.WriteHeader(http.StatusTeapot) - - // verify the user was added to the request context - if _, ok := request.UserFrom(r.Context()); ok { - t.Errorf("Expect guest mode, no user in context") - } - }), - ).ServeHTTP(w, r) - - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} diff --git a/handler/api/badge/badge.go b/handler/api/badge/badge.go deleted file mode 100644 index 1152dd54dd..0000000000 --- a/handler/api/badge/badge.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package badge - -var ( - badgeSuccess = `buildbuildsuccesssuccess` - badgeFailure = `buildbuildfailurefailure` - badgeStarted = `buildbuildstartedstarted` - badgeError = `buildbuilderrorerror` - badgeNone = `buildbuildnonenone` -) diff --git a/handler/api/badge/status.go b/handler/api/badge/status.go deleted file mode 100644 index d90762e987..0000000000 --- a/handler/api/badge/status.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package badge - -import ( - "fmt" - "io" - "net/http" - "time" - - "github.com/drone/drone/core" - - "github.com/go-chi/chi" -) - -// Handler returns an http.HandlerFunc that writes an svg status -// badge to the response. -func Handler( - repos core.RepositoryStore, - builds core.BuildStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - namespace := chi.URLParam(r, "owner") - name := chi.URLParam(r, "name") - ref := r.FormValue("ref") - branch := r.FormValue("branch") - if branch != "" { - ref = "refs/heads/" + branch - } - - // an SVG response is always served, even when error, so - // we can go ahead and set the content type appropriately. - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value") - w.Header().Set("Expires", "Thu, 01 Jan 1970 00:00:00 GMT") - w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat)) - w.Header().Set("Content-Type", "image/svg+xml") - - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - io.WriteString(w, badgeNone) - return - } - - if ref == "" { - ref = fmt.Sprintf("refs/heads/%s", repo.Branch) - } - build, err := builds.FindRef(r.Context(), repo.ID, ref) - if err != nil { - io.WriteString(w, badgeNone) - return - } - - switch build.Status { - case core.StatusPending, core.StatusRunning, core.StatusBlocked: - io.WriteString(w, badgeStarted) - case core.StatusPassing: - io.WriteString(w, badgeSuccess) - case core.StatusError: - io.WriteString(w, badgeError) - default: - io.WriteString(w, badgeFailure) - } - } -} diff --git a/handler/api/badge/status_test.go b/handler/api/badge/status_test.go deleted file mode 100644 index d8da8b8221..0000000000 --- a/handler/api/badge/status_test.go +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package badge - -import ( - "context" - "database/sql" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" -) - -var ( - mockRepo = &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Branch: "master", - } - - mockBuild = &core.Build{ - ID: 1, - RepoID: 1, - Number: 1, - Status: core.StatusPassing, - Ref: "refs/heads/develop", - } - - mockBuildFailing = &core.Build{ - ID: 2, - RepoID: 1, - Number: 2, - Status: core.StatusFailing, - Ref: "refs/heads/master", - } - - mockBuildRunning = &core.Build{ - ID: 3, - RepoID: 1, - Number: 3, - Status: core.StatusRunning, - Ref: "refs/heads/master", - } - - mockBuildError = &core.Build{ - ID: 4, - RepoID: 1, - Number: 4, - Status: core.StatusError, - Ref: "refs/heads/master", - } -) - -func TestHandler(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindRef(gomock.Any(), mockRepo.ID, "refs/heads/develop").Return(mockBuild, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/?ref=refs/heads/develop", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - Handler(repos, builds)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - if got, want := w.Header().Get("Access-Control-Allow-Origin"), "*"; got != want { - t.Errorf("Want Access-Control-Allow-Origin %q, got %q", want, got) - } - if got, want := w.Header().Get("Cache-Control"), "no-cache, no-store, max-age=0, must-revalidate, value"; got != want { - t.Errorf("Want Cache-Control %q, got %q", want, got) - } - if got, want := w.Header().Get("Content-Type"), "image/svg+xml"; got != want { - t.Errorf("Want Access-Control-Allow-Origin %q, got %q", want, got) - } - if got, want := w.Body.String(), string(badgeSuccess); got != want { - t.Errorf("Want badge %q, got %q", got, want) - } -} - -func TestHandler_Failing(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindRef(gomock.Any(), mockRepo.ID, "refs/heads/master").Return(mockBuildFailing, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - Handler(repos, builds)(w, r) - if got, want := w.Body.String(), string(badgeFailure); got != want { - t.Errorf("Want badge %q, got %q", got, want) - } -} - -func TestHandler_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindRef(gomock.Any(), mockRepo.ID, "refs/heads/master").Return(mockBuildError, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - Handler(repos, builds)(w, r) - if got, want := w.Body.String(), string(badgeError); got != want { - t.Errorf("Want badge %q, got %q", got, want) - } -} - -func TestHandler_Running(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindRef(gomock.Any(), mockRepo.ID, "refs/heads/master").Return(mockBuildRunning, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - Handler(repos, builds)(w, r) - if got, want := w.Body.String(), string(badgeStarted); got != want { - t.Errorf("Want badge %q, got %q", got, want) - } -} - -func TestHandler_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - Handler(repos, nil)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - if got, want := w.Body.String(), string(badgeNone); got != want { - t.Errorf("Want badge %q, got %q", got, want) - } -} - -func TestHandler_BuildNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindRef(gomock.Any(), mockRepo.ID, "refs/heads/master").Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - Handler(repos, builds)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - if got, want := w.Body.String(), string(badgeNone); got != want { - t.Errorf("Want badge %q, got %q", got, want) - } -} diff --git a/handler/api/builds/builds.go b/handler/api/builds/builds.go deleted file mode 100644 index 52b25cb88b..0000000000 --- a/handler/api/builds/builds.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package builds - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" -) - -// HandleIncomplete returns an http.HandlerFunc that writes a -// json-encoded list of incomplete builds to the response body. -func HandleIncomplete(repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - list, err := repos.ListIncomplete(r.Context()) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot list incomplete builds") - } else { - render.JSON(w, list, 200) - } - } -} - -func HandleRunningStatus(repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - list, err := repos.ListRunningStatus(r.Context()) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot list incomplete builds") - } else { - render.JSON(w, list, 200) - } - } -} diff --git a/handler/api/builds/builds_oss.go b/handler/api/builds/builds_oss.go deleted file mode 100644 index 1ab6f7fb50..0000000000 --- a/handler/api/builds/builds_oss.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package builds - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -// HandleIncomplete returns a no-op http.HandlerFunc. -func HandleIncomplete(repos core.RepositoryStore) http.HandlerFunc { - return notImplemented -} - -func HandleRunningStatus(repos core.RepositoryStore) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/builds/builds_test.go b/handler/api/builds/builds_test.go deleted file mode 100644 index cbe91d1830..0000000000 --- a/handler/api/builds/builds_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package builds - -import ( - "encoding/json" - "io/ioutil" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" - "github.com/sirupsen/logrus" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -func TestHandleBuilds(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - want := []*core.Repository{ - {ID: 1, Slug: "octocat/hello-world"}, - {ID: 2, Slug: "octocat/spoon-fork"}, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().ListIncomplete(gomock.Any()).Return(want, nil) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - HandleIncomplete(repos)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got := []*core.Repository{} - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleBuilds_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().ListIncomplete(gomock.Any()).Return(nil, errors.ErrNotFound) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - HandleIncomplete(repos)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/card/create.go b/handler/api/card/create.go deleted file mode 100644 index a59d09999c..0000000000 --- a/handler/api/card/create.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package card - -import ( - "bytes" - "encoding/json" - "io/ioutil" - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleCreate returns an http.HandlerFunc that processes http -// requests to create a new card. -func HandleCreate( - buildStore core.BuildStore, - cardStore core.CardStore, - stageStore core.StageStore, - stepStore core.StepStore, - repoStore core.RepositoryStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - buildNumber, err := strconv.ParseInt(chi.URLParam(r, "build"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - - stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage")) - if err != nil { - render.BadRequest(w, err) - return - } - - stepNumber, err := strconv.Atoi(chi.URLParam(r, "step")) - if err != nil { - render.BadRequest(w, err) - return - } - - in := new(core.CardInput) - err = json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - repo, err := repoStore.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - build, err := buildStore.FindNumber(r.Context(), repo.ID, buildNumber) - if err != nil { - render.NotFound(w, err) - return - } - stage, err := stageStore.FindNumber(r.Context(), build.ID, stageNumber) - if err != nil { - render.NotFound(w, err) - return - } - step, err := stepStore.FindNumber(r.Context(), stage.ID, stepNumber) - if err != nil { - render.NotFound(w, err) - return - } - - data := ioutil.NopCloser( - bytes.NewBuffer(in.Data), - ) - - /// create card - err = cardStore.Create(r.Context(), step.ID, data) - if err != nil { - render.InternalError(w, err) - return - } - - // add schema - step.Schema = in.Schema - err = stepStore.Update(r.Context(), step) - if err != nil { - render.InternalError(w, err) - return - } - render.JSON(w, step.ID, 200) - } -} diff --git a/handler/api/card/create_test.go b/handler/api/card/create_test.go deleted file mode 100644 index d657f7b636..0000000000 --- a/handler/api/card/create_test.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package card - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -type card struct { - Id int64 `json:"id,omitempty"` - Data []byte `json:"card_data"` -} - -var ( - dummyRepo = &core.Repository{ - ID: 1, - UserID: 1, - Slug: "octocat/hello-world", - } - dummyBuild = &core.Build{ - ID: 1, - RepoID: 1, - Number: 1, - } - dummyStage = &core.Stage{ - ID: 1, - BuildID: 1, - } - dummyStep = &core.Step{ - ID: 1, - StageID: 1, - Schema: "https://myschema.com", - } - dummyCard = &card{ - Id: dummyStep.ID, - Data: []byte("{\"type\": \"AdaptiveCard\"}"), - } -) - -func TestHandleCreate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil) - - build := mock.NewMockBuildStore(controller) - build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil) - - stage := mock.NewMockStageStore(controller) - stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil) - - step := mock.NewMockStepStore(controller) - step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil) - step.EXPECT().Update(gomock.Any(), gomock.Any()).Return(nil) - - card := mock.NewMockCardStore(controller) - card.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("build", "1") - c.URLParams.Add("stage", "1") - c.URLParams.Add("step", "1") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummyCard) - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(build, card, stage, step, repos).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleCreate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("build", "1") - c.URLParams.Add("stage", "1") - c.URLParams.Add("step", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(nil, nil, nil, nil, nil).ServeHTTP(w, r) - got, want := &errors.Error{}, &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_CreateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil) - - build := mock.NewMockBuildStore(controller) - build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil) - - stage := mock.NewMockStageStore(controller) - stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil) - - card := mock.NewMockCardStore(controller) - card.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - step := mock.NewMockStepStore(controller) - step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("build", "1") - c.URLParams.Add("stage", "1") - c.URLParams.Add("step", "1") - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummyCard) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(build, card, stage, step, repos).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/card/delete.go b/handler/api/card/delete.go deleted file mode 100644 index a9fa8d1eaa..0000000000 --- a/handler/api/card/delete.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package card - -import ( - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "net/http" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that processes http -// requests to delete a card. -func HandleDelete( - buildStore core.BuildStore, - cardStore core.CardStore, - stageStore core.StageStore, - stepStore core.StepStore, - repoStore core.RepositoryStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - buildNumber, err := strconv.ParseInt(chi.URLParam(r, "build"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - - stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage")) - if err != nil { - render.BadRequest(w, err) - return - } - - stepNumber, err := strconv.Atoi(chi.URLParam(r, "step")) - if err != nil { - render.BadRequest(w, err) - return - } - - repo, err := repoStore.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - build, err := buildStore.FindNumber(r.Context(), repo.ID, buildNumber) - if err != nil { - render.NotFound(w, err) - return - } - stage, err := stageStore.FindNumber(r.Context(), build.ID, stageNumber) - if err != nil { - render.NotFound(w, err) - return - } - step, err := stepStore.FindNumber(r.Context(), stage.ID, stepNumber) - if err != nil { - render.NotFound(w, err) - return - } - - _, err = cardStore.Find(r.Context(), step.ID) - if err != nil { - render.NotFound(w, err) - return - } - err = cardStore.Delete(r.Context(), step.ID) - if err != nil { - render.InternalError(w, err) - return - } - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/card/delete_test.go b/handler/api/card/delete_test.go deleted file mode 100644 index 0644e2c220..0000000000 --- a/handler/api/card/delete_test.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package card - -import ( - "bytes" - "context" - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleDelete(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil) - - build := mock.NewMockBuildStore(controller) - build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil) - - stage := mock.NewMockStageStore(controller) - stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil) - - step := mock.NewMockStepStore(controller) - step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil) - - card := mock.NewMockCardStore(controller) - card.EXPECT().Find(gomock.Any(), dummyStep.ID).Return(ioutil.NopCloser( - bytes.NewBuffer(dummyCard.Data), - ), nil) - card.EXPECT().Delete(gomock.Any(), dummyCard.Id).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("build", "1") - c.URLParams.Add("stage", "1") - c.URLParams.Add("step", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(build, card, stage, step, repos).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNoContent; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleDelete_CardNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil) - - build := mock.NewMockBuildStore(controller) - build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil) - - stage := mock.NewMockStageStore(controller) - stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil) - - step := mock.NewMockStepStore(controller) - step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil) - - card := mock.NewMockCardStore(controller) - card.EXPECT().Find(gomock.Any(), dummyStep.ID).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("build", "1") - c.URLParams.Add("stage", "1") - c.URLParams.Add("step", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(build, card, stage, step, repos).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleDelete_DeleteError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil) - - build := mock.NewMockBuildStore(controller) - build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil) - - stage := mock.NewMockStageStore(controller) - stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil) - - step := mock.NewMockStepStore(controller) - step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil) - - card := mock.NewMockCardStore(controller) - card.EXPECT().Find(gomock.Any(), dummyStep.ID).Return(ioutil.NopCloser( - bytes.NewBuffer(dummyCard.Data), - ), nil) - card.EXPECT().Delete(gomock.Any(), dummyCard.Id).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("build", "1") - c.URLParams.Add("stage", "1") - c.URLParams.Add("step", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(build, card, stage, step, repos).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/card/find.go b/handler/api/card/find.go deleted file mode 100644 index 852021909b..0000000000 --- a/handler/api/card/find.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package card - -import ( - "io" - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleFind returns an http.HandlerFunc that writes a json-encoded -func HandleFind( - buildStore core.BuildStore, - cardStore core.CardStore, - stageStore core.StageStore, - stepStore core.StepStore, - repoStore core.RepositoryStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - buildNumber, err := strconv.ParseInt(chi.URLParam(r, "build"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - - stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage")) - if err != nil { - render.BadRequest(w, err) - return - } - - stepNumber, err := strconv.Atoi(chi.URLParam(r, "step")) - if err != nil { - render.BadRequest(w, err) - return - } - - repo, err := repoStore.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - build, err := buildStore.FindNumber(r.Context(), repo.ID, buildNumber) - if err != nil { - render.NotFound(w, err) - return - } - stage, err := stageStore.FindNumber(r.Context(), build.ID, stageNumber) - if err != nil { - render.NotFound(w, err) - return - } - step, err := stepStore.FindNumber(r.Context(), stage.ID, stepNumber) - if err != nil { - render.NotFound(w, err) - return - } - - cardData, err := cardStore.Find(r.Context(), step.ID) - if err != nil { - render.NotFound(w, err) - return - } - w.Header().Set("Content-Type", "application/json") - io.Copy(w, cardData) - cardData.Close() - } -} diff --git a/handler/api/card/find_test.go b/handler/api/card/find_test.go deleted file mode 100644 index 72451c0c54..0000000000 --- a/handler/api/card/find_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package card - -import ( - "bytes" - "context" - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil) - - build := mock.NewMockBuildStore(controller) - build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil) - - stage := mock.NewMockStageStore(controller) - stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil) - - step := mock.NewMockStepStore(controller) - step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil) - - card := mock.NewMockCardStore(controller) - card.EXPECT().Find(gomock.Any(), dummyStep.ID).Return(ioutil.NopCloser( - bytes.NewBuffer(dummyCard.Data), - ), nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("build", "1") - c.URLParams.Add("stage", "1") - c.URLParams.Add("step", "1") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummyCard) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(build, card, stage, step, repos).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleFind_CardNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil) - - build := mock.NewMockBuildStore(controller) - build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil) - - stage := mock.NewMockStageStore(controller) - stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil) - - step := mock.NewMockStepStore(controller) - step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil) - - card := mock.NewMockCardStore(controller) - card.EXPECT().Find(gomock.Any(), dummyStep.ID).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("build", "1") - c.URLParams.Add("stage", "1") - c.URLParams.Add("step", "1") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummyCard) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(build, card, stage, step, repos).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/card/none.go b/handler/api/card/none.go deleted file mode 100644 index 5b843468c5..0000000000 --- a/handler/api/card/none.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package card - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -func HandleCreate( - buildStore core.BuildStore, - cardStore core.CardStore, - stageStore core.StageStore, - stepStore core.StepStore, - repoStore core.RepositoryStore, -) http.HandlerFunc { - return notImplemented -} - -func HandleDelete( - buildStore core.BuildStore, - cardStore core.CardStore, - stageStore core.StageStore, - stepStore core.StepStore, - repoStore core.RepositoryStore, -) http.HandlerFunc { - return notImplemented -} - -func HandleFind( - buildStore core.BuildStore, - cardStore core.CardStore, - stageStore core.StageStore, - stepStore core.StepStore, - repoStore core.RepositoryStore, -) http.HandlerFunc { - return notImplemented -} - -func HandleFindAll( - buildStore core.BuildStore, - cardStore core.CardStore, - repoStore core.RepositoryStore, -) http.HandlerFunc { - return notImplemented -} - -func HandleFindData( - buildStore core.BuildStore, - cardStore core.CardStore, - stageStore core.StageStore, - stepStore core.StepStore, - repoStore core.RepositoryStore, -) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/ccmenu/cc.go b/handler/api/ccmenu/cc.go deleted file mode 100644 index d55fdd03a4..0000000000 --- a/handler/api/ccmenu/cc.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package ccmenu - -import ( - "encoding/xml" - "fmt" - "time" - - "github.com/drone/drone/core" -) - -type CCProjects struct { - XMLName xml.Name `xml:"Projects"` - Project *CCProject `xml:"Project"` -} - -type CCProject struct { - XMLName xml.Name `xml:"Project"` - Name string `xml:"name,attr"` - Activity string `xml:"activity,attr"` - LastBuildStatus string `xml:"lastBuildStatus,attr"` - LastBuildLabel string `xml:"lastBuildLabel,attr"` - LastBuildTime string `xml:"lastBuildTime,attr"` - WebURL string `xml:"webUrl,attr"` -} - -// New creates a new CCProject from the Repository and Build details. -func New(r *core.Repository, b *core.Build, link string) *CCProjects { - proj := &CCProject{ - Name: r.Slug, - WebURL: link, - Activity: "Building", - LastBuildStatus: "Unknown", - LastBuildLabel: "Unknown", - } - - // if the build is not currently running then - // we can return the latest build status. - if b.Status != core.StatusPending && - b.Status != core.StatusRunning && - b.Status != core.StatusBlocked { - proj.Activity = "Sleeping" - proj.LastBuildTime = time.Unix(b.Started, 0).Format(time.RFC3339) - proj.LastBuildLabel = fmt.Sprint(b.Number) - } - - // ensure the last build Status accepts a valid - // ccmenu enumeration - switch b.Status { - case core.StatusError, core.StatusKilled, core.StatusDeclined: - proj.LastBuildStatus = "Exception" - case core.StatusPassing: - proj.LastBuildStatus = "Success" - case core.StatusFailing: - proj.LastBuildStatus = "Failure" - } - - return &CCProjects{Project: proj} -} diff --git a/handler/api/ccmenu/cc_test.go b/handler/api/ccmenu/cc_test.go deleted file mode 100644 index fe06e849f1..0000000000 --- a/handler/api/ccmenu/cc_test.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package ccmenu - -import ( - "encoding/xml" - "testing" - - "github.com/drone/drone/core" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -var ignore = cmpopts.IgnoreFields(CCProjects{}, "Project.LastBuildTime") - -func TestNew(t *testing.T) { - repo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - build := &core.Build{ - Number: 1, - Status: core.StatusRunning, - Started: 1524251054, - } - link := "https://drone.company.com" - - want := &CCProjects{ - XMLName: xml.Name{}, - Project: &CCProject{ - XMLName: xml.Name{}, - Name: "octocat/hello-world", - Activity: "Building", - LastBuildStatus: "Unknown", - LastBuildLabel: "Unknown", - LastBuildTime: "", - WebURL: "https://drone.company.com", - }, - } - - got := New(repo, build, link) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestNew_Success(t *testing.T) { - repo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - build := &core.Build{ - Number: 1, - Status: core.StatusPassing, - Started: 1524251054, - } - link := "https://drone.company.com" - - want := &CCProjects{ - XMLName: xml.Name{}, - Project: &CCProject{ - XMLName: xml.Name{}, - Name: "octocat/hello-world", - Activity: "Sleeping", - LastBuildStatus: "Success", - LastBuildLabel: "1", - LastBuildTime: "2018-04-20T12:04:14-07:00", - WebURL: "https://drone.company.com", - }, - } - - got := New(repo, build, link) - if diff := cmp.Diff(got, want, ignore); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestNew_Failure(t *testing.T) { - repo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - build := &core.Build{ - Number: 1, - Status: core.StatusFailing, - Started: 1524251054, - } - link := "https://drone.company.com" - - want := &CCProjects{ - XMLName: xml.Name{}, - Project: &CCProject{ - XMLName: xml.Name{}, - Name: "octocat/hello-world", - Activity: "Sleeping", - LastBuildStatus: "Failure", - LastBuildLabel: "1", - LastBuildTime: "2018-04-20T12:04:14-07:00", - WebURL: "https://drone.company.com", - }, - } - - got := New(repo, build, link) - if diff := cmp.Diff(got, want, ignore); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestNew_Error(t *testing.T) { - repo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - build := &core.Build{ - Number: 1, - Status: core.StatusError, - Started: 1524251054, - } - link := "https://drone.company.com" - - want := &CCProjects{ - XMLName: xml.Name{}, - Project: &CCProject{ - XMLName: xml.Name{}, - Name: "octocat/hello-world", - Activity: "Sleeping", - LastBuildStatus: "Exception", - LastBuildLabel: "1", - LastBuildTime: "2018-04-20T12:04:14-07:00", - WebURL: "https://drone.company.com", - }, - } - - got := New(repo, build, link) - if diff := cmp.Diff(got, want, ignore); len(diff) > 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/ccmenu/ccmenu.go b/handler/api/ccmenu/ccmenu.go deleted file mode 100644 index 02ae548254..0000000000 --- a/handler/api/ccmenu/ccmenu.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package ccmenu - -import ( - "encoding/xml" - "fmt" - "net/http" - - "github.com/drone/drone/core" - - "github.com/go-chi/chi" -) - -// Handler returns an http.HandlerFunc that writes an svg status -// badge to the response. -func Handler( - repos core.RepositoryStore, - builds core.BuildStore, - link string, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - namespace := chi.URLParam(r, "owner") - name := chi.URLParam(r, "name") - - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - w.WriteHeader(404) - return - } - - build, err := builds.FindNumber(r.Context(), repo.ID, repo.Counter) - if err != nil { - w.WriteHeader(404) - return - } - - project := New(repo, build, - fmt.Sprintf("%s/%s/%s/%d", link, namespace, name, build.Number), - ) - - xml.NewEncoder(w).Encode(project) - } -} diff --git a/handler/api/ccmenu/ccmenu_oss.go b/handler/api/ccmenu/ccmenu_oss.go deleted file mode 100644 index 7beb3796dc..0000000000 --- a/handler/api/ccmenu/ccmenu_oss.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package ccmenu - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -// Handler returns a no-op http.HandlerFunc. -func Handler(core.RepositoryStore, core.BuildStore, string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) - } -} diff --git a/handler/api/ccmenu/ccmenu_test.go b/handler/api/ccmenu/ccmenu_test.go deleted file mode 100644 index 8b13313640..0000000000 --- a/handler/api/ccmenu/ccmenu_test.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package ccmenu - -import ( - "context" - "database/sql" - "encoding/xml" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var ( - mockRepo = &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Branch: "master", - Counter: 42, - } - - mockBuild = &core.Build{ - ID: 1, - RepoID: 1, - Number: 1, - Status: core.StatusPassing, - Ref: "refs/heads/develop", - } -) - -func TestHandler(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockRepo.Counter).Return(mockBuild, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/?ref=refs/heads/develop", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - Handler(repos, builds, "https://drone.company.com")(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &CCProjects{}, &CCProjects{ - XMLName: xml.Name{ - Space: "", - Local: "Projects", - }, - Project: &CCProject{ - XMLName: xml.Name{Space: "", Local: "Project"}, - Name: "", - Activity: "Sleeping", - LastBuildStatus: "Success", - LastBuildLabel: "1", - LastBuildTime: "1969-12-31T16:00:00-08:00", - WebURL: "https://drone.company.com/octocat/hello-world/1", - }, - } - xml.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandler_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - Handler(repos, nil, "")(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandler_BuildNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockRepo.Counter).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - Handler(repos, builds, "")(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} diff --git a/handler/api/errors/errors.go b/handler/api/errors/errors.go deleted file mode 100644 index e599daf774..0000000000 --- a/handler/api/errors/errors.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package errors - -var ( - // ErrInvalidToken is returned when the api request token is invalid. - ErrInvalidToken = New("Invalid or missing token") - - // ErrUnauthorized is returned when the user is not authorized. - ErrUnauthorized = New("Unauthorized") - - // ErrForbidden is returned when user access is forbidden. - ErrForbidden = New("Forbidden") - - // ErrNotFound is returned when a resource is not found. - ErrNotFound = New("Not Found") -) - -// Error represents a json-encoded API error. -type Error struct { - Message string `json:"message"` -} - -func (e *Error) Error() string { - return e.Message -} - -// New returns a new error message. -func New(text string) error { - return &Error{Message: text} -} diff --git a/handler/api/errors/errors_test.go b/handler/api/errors/errors_test.go deleted file mode 100644 index 3b28ed7336..0000000000 --- a/handler/api/errors/errors_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package errors - -import "testing" - -func TestError(t *testing.T) { - got, want := ErrNotFound.Error(), ErrNotFound.(*Error).Message - if got != want { - t.Errorf("Want error string %q, got %q", got, want) - } -} diff --git a/handler/api/events/build.go b/handler/api/events/build.go deleted file mode 100644 index 4930abff67..0000000000 --- a/handler/api/events/build.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package events - -import ( - "context" - "io" - "net/http" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - "github.com/sirupsen/logrus" - - "github.com/go-chi/chi" -) - -// interval at which the client is pinged to prevent -// reverse proxy and load balancers from closing the -// connection. -var pingInterval = time.Second * 30 - -// implements a 24-hour timeout for connections. This -// should not be necessary, but is put in place just -// in case we encounter dangling connections. -var timeout = time.Hour * 24 - -// HandleEvents creates an http.HandlerFunc that streams builds events -// to the http.Response in an event stream format. -func HandleEvents( - repos core.RepositoryStore, - events core.Pubsub, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - logger := logger.FromRequest(r).WithFields( - logrus.Fields{ - "namespace": namespace, - "name": name, - }, - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.WithError(err).Debugln("events: cannot find repository") - return - } - - h := w.Header() - h.Set("Content-Type", "text/event-stream") - h.Set("Cache-Control", "no-cache") - h.Set("Connection", "keep-alive") - h.Set("X-Accel-Buffering", "no") - - f, ok := w.(http.Flusher) - if !ok { - return - } - - io.WriteString(w, ": ping\n\n") - f.Flush() - - ctx, cancel := context.WithCancel(r.Context()) - defer cancel() - - events, errc := events.Subscribe(ctx) - logger.Debugln("events: stream opened") - - timeoutChan := time.After(24 * time.Hour) - L: - for { - select { - case <-ctx.Done(): - logger.Debugln("events: stream cancelled") - break L - case <-errc: - logger.Debugln("events: stream error") - break L - case <-timeoutChan: - logger.Debugln("events: stream timeout") - break L - case <-time.After(pingInterval): - io.WriteString(w, ": ping\n\n") - f.Flush() - case event := <-events: - if event.Repository == repo.Slug { - io.WriteString(w, "data: ") - w.Write(event.Data) - io.WriteString(w, "\n\n") - f.Flush() - } - } - } - - io.WriteString(w, "event: error\ndata: eof\n\n") - f.Flush() - - logger.Debugln("events: stream closed") - } -} diff --git a/handler/api/events/build_test.go b/handler/api/events/build_test.go deleted file mode 100644 index fdfd5b6849..0000000000 --- a/handler/api/events/build_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package events diff --git a/handler/api/events/global.go b/handler/api/events/global.go deleted file mode 100644 index 25651bcf0a..0000000000 --- a/handler/api/events/global.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package events - -import ( - "context" - "io" - "net/http" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" -) - -// HandleGlobal creates an http.HandlerFunc that streams builds events -// to the http.Response in an event stream format. -func HandleGlobal( - repos core.RepositoryStore, - events core.Pubsub, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - logger := logger.FromRequest(r) - - h := w.Header() - h.Set("Content-Type", "text/event-stream") - h.Set("Cache-Control", "no-cache") - h.Set("Connection", "keep-alive") - h.Set("X-Accel-Buffering", "no") - - f, ok := w.(http.Flusher) - if !ok { - return - } - - access := map[string]struct{}{} - user, authenticated := request.UserFrom(r.Context()) - if authenticated { - list, _ := repos.List(r.Context(), user.ID) - for _, repo := range list { - access[repo.Slug] = struct{}{} - } - } - - io.WriteString(w, ": ping\n\n") - f.Flush() - - ctx, cancel := context.WithCancel(r.Context()) - defer cancel() - - events, errc := events.Subscribe(ctx) - logger.Debugln("events: stream opened") - - timeoutChan := time.After(24 * time.Hour) - L: - for { - select { - case <-ctx.Done(): - logger.Debugln("events: stream cancelled") - break L - case <-errc: - logger.Debugln("events: stream error") - break L - case <-timeoutChan: - logger.Debugln("events: stream timeout") - break L - case <-time.After(pingInterval): - io.WriteString(w, ": ping\n\n") - f.Flush() - case event := <-events: - _, authorized := access[event.Repository] - if event.Visibility == core.VisibilityPublic { - authorized = true - } - if event.Visibility == core.VisibilityInternal && authenticated { - authorized = true - } - if authorized { - io.WriteString(w, "data: ") - w.Write(event.Data) - io.WriteString(w, "\n\n") - f.Flush() - } - } - } - - io.WriteString(w, "event: error\ndata: eof\n\n") - f.Flush() - - logger.Debugln("events: stream closed") - } -} diff --git a/handler/api/events/logs.go b/handler/api/events/logs.go deleted file mode 100644 index 8f5fdf6776..0000000000 --- a/handler/api/events/logs.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package events - -import ( - "context" - "encoding/json" - "io" - "net/http" - "strconv" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleLogStream creates an http.HandlerFunc that streams builds logs -// to the http.Response in an event stream format. -func HandleLogStream( - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, - steps core.StepStore, - stream core.LogStream, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - number, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage")) - if err != nil { - render.BadRequest(w, err) - return - } - stepNumber, err := strconv.Atoi(chi.URLParam(r, "step")) - if err != nil { - render.BadRequest(w, err) - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - build, err := builds.FindNumber(r.Context(), repo.ID, number) - if err != nil { - render.NotFound(w, err) - return - } - stage, err := stages.FindNumber(r.Context(), build.ID, stageNumber) - if err != nil { - render.NotFound(w, err) - return - } - step, err := steps.FindNumber(r.Context(), stage.ID, stepNumber) - if err != nil { - render.NotFound(w, err) - return - } - - h := w.Header() - h.Set("Content-Type", "text/event-stream") - h.Set("Cache-Control", "no-cache") - h.Set("Connection", "keep-alive") - h.Set("X-Accel-Buffering", "no") - - f, ok := w.(http.Flusher) - if !ok { - return - } - - io.WriteString(w, ": ping\n\n") - f.Flush() - - ctx, cancel := context.WithCancel(r.Context()) - defer cancel() - - enc := json.NewEncoder(w) - linec, errc := stream.Tail(ctx, step.ID) - if errc == nil { - io.WriteString(w, "event: error\ndata: eof\n\n") - return - } - - timeoutChan := time.After(24 * time.Hour) - L: - for { - select { - case <-ctx.Done(): - break L - case <-errc: - break L - case <-timeoutChan: - break L - case <-time.After(pingInterval): - io.WriteString(w, ": ping\n\n") - case line := <-linec: - io.WriteString(w, "data: ") - enc.Encode(line) - io.WriteString(w, "\n\n") - f.Flush() - } - } - - io.WriteString(w, "event: error\ndata: eof\n\n") - f.Flush() - } -} diff --git a/handler/api/events/logs_test.go b/handler/api/events/logs_test.go deleted file mode 100644 index fa533a08db..0000000000 --- a/handler/api/events/logs_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package events - -import ( - "io/ioutil" - - "github.com/sirupsen/logrus" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} diff --git a/handler/api/queue/items.go b/handler/api/queue/items.go deleted file mode 100644 index c4bd42892e..0000000000 --- a/handler/api/queue/items.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package queue - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" -) - -// HandleItems returns an http.HandlerFunc that writes a -// json-encoded list of queue items to the response body. -func HandleItems(store core.StageStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - items, err := store.ListIncomplete(ctx) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("api: cannot get running items") - return - } - render.JSON(w, items, 200) - } -} diff --git a/handler/api/queue/items_test.go b/handler/api/queue/items_test.go deleted file mode 100644 index 1ef484b925..0000000000 --- a/handler/api/queue/items_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package queue - -import ( - "io/ioutil" - - "github.com/sirupsen/logrus" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} diff --git a/handler/api/queue/none.go b/handler/api/queue/none.go deleted file mode 100644 index 0c1f9c5f87..0000000000 --- a/handler/api/queue/none.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package queue - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -func HandleItems(store core.StageStore) http.HandlerFunc { - return notImplemented -} - -func HandlePause(core.Scheduler) http.HandlerFunc { - return notImplemented -} - -func HandleResume(core.Scheduler) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/queue/pause.go b/handler/api/queue/pause.go deleted file mode 100644 index cefbf3d4cc..0000000000 --- a/handler/api/queue/pause.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package queue - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" -) - -// HandlePause returns an http.HandlerFunc that processes -// an http.Request to pause the scheduler. -func HandlePause(scheduler core.Scheduler) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := scheduler.Pause(ctx) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Errorln("api: cannot pause scheduler") - return - } - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/queue/pause_test.go b/handler/api/queue/pause_test.go deleted file mode 100644 index 6937390566..0000000000 --- a/handler/api/queue/pause_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package queue diff --git a/handler/api/queue/resume.go b/handler/api/queue/resume.go deleted file mode 100644 index 39a88e7b2c..0000000000 --- a/handler/api/queue/resume.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package queue - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" -) - -// HandleResume returns an http.HandlerFunc that processes -// an http.Request to pause the scheduler. -func HandleResume(scheduler core.Scheduler) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := scheduler.Resume(ctx) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Errorln("api: cannot resume scheduler") - return - } - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/queue/resume_test.go b/handler/api/queue/resume_test.go deleted file mode 100644 index 6937390566..0000000000 --- a/handler/api/queue/resume_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package queue diff --git a/handler/api/render/render.go b/handler/api/render/render.go deleted file mode 100644 index 31469c8771..0000000000 --- a/handler/api/render/render.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package render - -import ( - "encoding/json" - "fmt" - "net/http" - "os" - "strconv" - - "github.com/drone/drone/handler/api/errors" -) - -// indent the json-encoded API responses -var indent bool - -func init() { - indent, _ = strconv.ParseBool( - os.Getenv("HTTP_JSON_INDENT"), - ) -} - -var ( - // ErrInvalidToken is returned when the api request token is invalid. - ErrInvalidToken = errors.New("Invalid or missing token") - - // ErrUnauthorized is returned when the user is not authorized. - ErrUnauthorized = errors.New("Unauthorized") - - // ErrForbidden is returned when user access is forbidden. - ErrForbidden = errors.New("Forbidden") - - // ErrNotFound is returned when a resource is not found. - ErrNotFound = errors.New("Not Found") - - // ErrNotImplemented is returned when an endpoint is not implemented. - ErrNotImplemented = errors.New("Not Implemented") -) - -// ErrorCode writes the json-encoded error message to the response. -func ErrorCode(w http.ResponseWriter, err error, status int) { - JSON(w, &errors.Error{Message: err.Error()}, status) -} - -// InternalError writes the json-encoded error message to the response -// with a 500 internal server error. -func InternalError(w http.ResponseWriter, err error) { - ErrorCode(w, err, 500) -} - -// InternalErrorf writes the json-encoded error message to the response -// with a 500 internal server error. -func InternalErrorf(w http.ResponseWriter, format string, a ...interface{}) { - ErrorCode(w, fmt.Errorf(format, a...), 500) -} - -// NotImplemented writes the json-encoded error message to the -// response with a 501 not found status code. -func NotImplemented(w http.ResponseWriter, err error) { - ErrorCode(w, err, 501) -} - -// NotFound writes the json-encoded error message to the response -// with a 404 not found status code. -func NotFound(w http.ResponseWriter, err error) { - ErrorCode(w, err, 404) -} - -// NotFoundf writes the json-encoded error message to the response -// with a 404 not found status code. -func NotFoundf(w http.ResponseWriter, format string, a ...interface{}) { - ErrorCode(w, fmt.Errorf(format, a...), 404) -} - -// Unauthorized writes the json-encoded error message to the response -// with a 401 unauthorized status code. -func Unauthorized(w http.ResponseWriter, err error) { - ErrorCode(w, err, 401) -} - -// Forbidden writes the json-encoded error message to the response -// with a 403 forbidden status code. -func Forbidden(w http.ResponseWriter, err error) { - ErrorCode(w, err, 403) -} - -// BadRequest writes the json-encoded error message to the response -// with a 400 bad request status code. -func BadRequest(w http.ResponseWriter, err error) { - ErrorCode(w, err, 400) -} - -// BadRequestf writes the json-encoded error message to the response -// with a 400 bad request status code. -func BadRequestf(w http.ResponseWriter, format string, a ...interface{}) { - ErrorCode(w, fmt.Errorf(format, a...), 400) -} - -// JSON writes the json-encoded error message to the response -// with a 400 bad request status code. -func JSON(w http.ResponseWriter, v interface{}, status int) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(status) - enc := json.NewEncoder(w) - if indent { - enc.SetIndent("", " ") - } - enc.Encode(v) -} diff --git a/handler/api/render/render_test.go b/handler/api/render/render_test.go deleted file mode 100644 index dd1e08296c..0000000000 --- a/handler/api/render/render_test.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package render - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" -) - -func TestWriteError(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - InternalError(w, err) - - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteErrorCode(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - ErrorCode(w, err, 418) - - if got, want := w.Code, 418; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteNotFound(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - NotFound(w, err) - - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteNotFoundf(t *testing.T) { - w := httptest.NewRecorder() - - NotFoundf(w, "pc %s", "load letter") - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, "pc load letter"; got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteInternalError(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - InternalError(w, err) - - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteInternalErrorf(t *testing.T) { - w := httptest.NewRecorder() - - InternalErrorf(w, "pc %s", "load letter") - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, "pc load letter"; got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteUnauthorized(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - Unauthorized(w, err) - - if got, want := w.Code, 401; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteForbidden(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - Forbidden(w, err) - - if got, want := w.Code, 403; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteBadRequest(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - BadRequest(w, err) - - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestBadRequestf(t *testing.T) { - w := httptest.NewRecorder() - - BadRequestf(w, "pc %s", "load letter") - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &errors.Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, "pc load letter"; got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteJSON(t *testing.T) { - // without indent - { - w := httptest.NewRecorder() - JSON(w, map[string]string{"hello": "world"}, http.StatusTeapot) - if got, want := w.Body.String(), "{\"hello\":\"world\"}\n"; got != want { - t.Errorf("Want JSON body %q, got %q", want, got) - } - if got, want := w.HeaderMap.Get("Content-Type"), "application/json"; got != want { - t.Errorf("Want Content-Type %q, got %q", want, got) - } - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - } - // with indent - { - indent = true - defer func() { - indent = false - }() - w := httptest.NewRecorder() - JSON(w, map[string]string{"hello": "world"}, http.StatusTeapot) - if got, want := w.Body.String(), "{\n \"hello\": \"world\"\n}\n"; got != want { - t.Errorf("Want JSON body %q, got %q", want, got) - } - } -} diff --git a/handler/api/repos/all.go b/handler/api/repos/all.go deleted file mode 100644 index fa7f3ae700..0000000000 --- a/handler/api/repos/all.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" -) - -// HandleAll returns an http.HandlerFunc that processes http -// requests to list all repositories in the database. -func HandleAll(repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - page = r.FormValue("page") - perPage = r.FormValue("per_page") - ) - offset, _ := strconv.Atoi(page) - limit, _ := strconv.Atoi(perPage) - if limit < 1 { // || limit > 100 - limit = 25 - } - switch offset { - case 0, 1: - offset = 0 - default: - offset = (offset - 1) * limit - } - repo, err := repos.ListAll(r.Context(), limit, offset) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - Debugln("api: cannot list repositories") - } else { - render.JSON(w, repo, 200) - } - } -} diff --git a/handler/api/repos/builds/branches/create.go b/handler/api/repos/builds/branches/create.go deleted file mode 100644 index b85043db1e..0000000000 --- a/handler/api/repos/builds/branches/create.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package branches diff --git a/handler/api/repos/builds/branches/create_test.go b/handler/api/repos/builds/branches/create_test.go deleted file mode 100644 index 8860172ff2..0000000000 --- a/handler/api/repos/builds/branches/create_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package branches diff --git a/handler/api/repos/builds/branches/delete.go b/handler/api/repos/builds/branches/delete.go deleted file mode 100644 index 93d3a1d8e5..0000000000 --- a/handler/api/repos/builds/branches/delete.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package branches - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that handles an -// http.Request to delete a branch entry from the datastore. -func HandleDelete( - repos core.RepositoryStore, - builds core.BuildStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - branch = chi.URLParam(r, "*") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot find repository") - return - } - - err = builds.DeleteBranch(r.Context(), repo.ID, branch) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot delete branch") - } else { - w.WriteHeader(http.StatusNoContent) - } - } -} diff --git a/handler/api/repos/builds/branches/delete_test.go b/handler/api/repos/builds/branches/delete_test.go deleted file mode 100644 index 8860172ff2..0000000000 --- a/handler/api/repos/builds/branches/delete_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package branches diff --git a/handler/api/repos/builds/branches/list.go b/handler/api/repos/builds/branches/list.go deleted file mode 100644 index 84c6a51da4..0000000000 --- a/handler/api/repos/builds/branches/list.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package branches - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleList returns an http.HandlerFunc that writes a json-encoded -// list of build history to the response body. -func HandleList( - repos core.RepositoryStore, - builds core.BuildStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot find repository") - return - } - - results, err := builds.LatestBranches(r.Context(), repo.ID) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot list builds") - } else { - render.JSON(w, results, 200) - } - } -} diff --git a/handler/api/repos/builds/branches/list_test.go b/handler/api/repos/builds/branches/list_test.go deleted file mode 100644 index 8860172ff2..0000000000 --- a/handler/api/repos/builds/branches/list_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package branches diff --git a/handler/api/repos/builds/cancel.go b/handler/api/repos/builds/cancel.go deleted file mode 100644 index 04c9943cdd..0000000000 --- a/handler/api/repos/builds/cancel.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builds - -import ( - "context" - "net/http" - "strconv" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleCancel returns an http.HandlerFunc that processes http -// requests to cancel a pending or running build. -func HandleCancel( - users core.UserStore, - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, - steps core.StepStore, - status core.StatusService, - scheduler core.Scheduler, - webhooks core.WebhookSender, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - number, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot find repository") - render.NotFound(w, err) - return - } - - build, err := builds.FindNumber(r.Context(), repo.ID, number) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("build", build.Number). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot find build") - render.NotFound(w, err) - return - } - - done := build.Status != core.StatusPending && - build.Status != core.StatusRunning - - // do not cancel the build if the build status is - // complete. only cancel the build if the status is - // running or pending. - if !done { - build.Status = core.StatusKilled - build.Finished = time.Now().Unix() - if build.Started == 0 { - build.Started = time.Now().Unix() - } - - err = builds.Update(r.Context(), build) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("build", build.Number). - WithField("namespace", namespace). - WithField("name", name). - Warnln("api: cannot update build status to cancelled") - render.ErrorCode(w, err, http.StatusConflict) - return - } - - err = scheduler.Cancel(r.Context(), build.ID) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("build", build.Number). - WithField("namespace", namespace). - WithField("name", name). - Warnln("api: cannot signal cancelled build is complete") - } - - user, err := users.Find(r.Context(), repo.UserID) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot repository owner") - } else { - err := status.Send(r.Context(), user, &core.StatusInput{ - Repo: repo, - Build: build, - }) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("build", build.Number). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot set status") - } - } - } - - stagez, err := stages.ListSteps(r.Context(), build.ID) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("build", build). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot list build stages") - } - - for _, stage := range stagez { - if stage.IsDone() { - continue - } - if stage.Started != 0 { - stage.Status = core.StatusKilled - } else { - stage.Status = core.StatusSkipped - stage.Started = time.Now().Unix() - } - stage.Stopped = time.Now().Unix() - err := stages.Update(context.Background(), stage) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("stage", stage.Number). - WithField("build", build.Number). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot update stage status") - } - - for _, step := range stage.Steps { - if step.IsDone() { - continue - } - if step.Started != 0 { - step.Status = core.StatusKilled - } else { - step.Status = core.StatusSkipped - step.Started = time.Now().Unix() - } - step.Stopped = time.Now().Unix() - step.ExitCode = 130 - err := steps.Update(context.Background(), step) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("stage", stage.Number). - WithField("build", build.Number). - WithField("step", step.Number). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot update step status") - } - } - } - - logger.FromRequest(r). - WithField("build", build.Number). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: successfully cancelled build") - - build.Stages = stagez - - // do not trigger a webhook if the build was already - // complete. only trigger a webhook if the build was - // pending or running and then cancelled. - if !done { - payload := &core.WebhookData{ - Event: core.WebhookEventBuild, - Action: core.WebhookActionUpdated, - Repo: repo, - Build: build, - } - err = webhooks.Send(context.Background(), payload) - if err != nil { - logger.FromRequest(r).WithError(err). - Warnln("manager: cannot send global webhook") - } - } - - render.JSON(w, build, 200) - } -} diff --git a/handler/api/repos/builds/cancel_test.go b/handler/api/repos/builds/cancel_test.go deleted file mode 100644 index 5b37b7498a..0000000000 --- a/handler/api/repos/builds/cancel_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package builds - -import ( - "context" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" -) - -func TestCancel(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockStages := []*core.Stage{ - {Status: core.StatusPassing}, - { - Status: core.StatusPending, - Steps: []*core.Step{ - {Status: core.StatusPassing}, - {Status: core.StatusPending}, - }, - }, - } - - mockBuildCopy := new(core.Build) - *mockBuildCopy = *mockBuild - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuildCopy, nil) - builds.EXPECT().Update(gomock.Any(), mockBuildCopy).Return(nil) - - users := mock.NewMockUserStore(controller) - users.EXPECT().Find(gomock.Any(), mockRepo.UserID).Return(mockUser, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().ListSteps(gomock.Any(), mockBuild.ID).Return(mockStages, nil) - stages.EXPECT().Update(gomock.Any(), mockStages[1]).Return(nil) - - steps := mock.NewMockStepStore(controller) - steps.EXPECT().Update(gomock.Any(), mockStages[1].Steps[1]).Return(nil) - - statusService := mock.NewMockStatusService(controller) - statusService.EXPECT().Send(gomock.Any(), mockUser, gomock.Any()).Return(nil) - - webhook := mock.NewMockWebhookSender(controller) - webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil) - - scheduler := mock.NewMockScheduler(controller) - scheduler.EXPECT().Cancel(gomock.Any(), mockBuild.ID).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCancel(users, repos, builds, stages, steps, statusService, scheduler, webhook)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} diff --git a/handler/api/repos/builds/create.go b/handler/api/repos/builds/create.go deleted file mode 100644 index 1ff0a8be94..0000000000 --- a/handler/api/repos/builds/create.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builds - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/go-scm/scm" - - "github.com/go-chi/chi" -) - -// HandleCreate returns an http.HandlerFunc that processes http -// requests to create a build for the specified commit. -func HandleCreate( - users core.UserStore, - repos core.RepositoryStore, - commits core.CommitService, - triggerer core.Triggerer, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - ctx = r.Context() - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - sha = r.FormValue("commit") - branch = r.FormValue("branch") - message = r.FormValue("message") - action = r.FormValue("action") - user, _ = request.UserFrom(ctx) - ) - - repo, err := repos.FindName(ctx, namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - - owner, err := users.Find(ctx, repo.UserID) - if err != nil { - render.NotFound(w, err) - return - } - - // if the user does not provide a branch, assume the - // default repository branch. - if branch == "" { - branch = repo.Branch - } - // expand the branch to a git reference. - ref := scm.ExpandRef(branch, "refs/heads") - - var commit *core.Commit - if sha != "" { - commit, err = commits.Find(ctx, owner, repo.Slug, sha) - } else { - commit, err = commits.FindRef(ctx, owner, repo.Slug, ref) - } - if err != nil { - render.NotFound(w, err) - return - } - - hook := &core.Hook{ - Trigger: user.Login, - Event: core.EventCustom, - Link: commit.Link, - Timestamp: commit.Author.Date, - Title: "", // we expect this to be empty. - Message: commit.Message, - Before: commit.Sha, - After: commit.Sha, - Ref: ref, - Source: branch, - Target: branch, - Author: commit.Author.Login, - AuthorName: commit.Author.Name, - AuthorEmail: commit.Author.Email, - AuthorAvatar: commit.Author.Avatar, - Sender: user.Login, - Params: map[string]string{}, - } - if len(message) > 0 { - hook.Message = message - } - if len(action) > 0 { - hook.Action = action - } - - for key, value := range r.URL.Query() { - if key == "access_token" || - key == "commit" || - key == "branch" { - continue - } - if len(value) == 0 { - continue - } - hook.Params[key] = value[0] - } - - result, err := triggerer.Trigger(r.Context(), repo, hook) - if err != nil { - render.InternalError(w, err) - } else { - render.JSON(w, result, 200) - } - } -} diff --git a/handler/api/repos/builds/create_test.go b/handler/api/repos/builds/create_test.go deleted file mode 100644 index a4b527005d..0000000000 --- a/handler/api/repos/builds/create_test.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package builds - -import ( - "context" - "encoding/json" - "net/http/httptest" - "net/url" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestCreate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockCommit := &core.Commit{ - Sha: "cce10d5c4760d1d6ede99db850ab7e77efe15579", - Ref: "refs/heads/master", - Message: "updated README.md", - Link: "https://github.com/octocatl/hello-world/commit/cce10d5c4760d1d6ede99db850ab7e77efe15579", - Author: &core.Committer{ - Name: "The Octocat", - Email: "octocat@github.com", - Login: "octocat", - Avatar: "https://github.com/octocat.png", - }, - } - - checkBuild := func(_ context.Context, _ *core.Repository, hook *core.Hook) error { - if got, want := hook.Trigger, mockUser.Login; got != want { - t.Errorf("Want hook Trigger By %s, got %s", want, got) - } - if got, want := hook.Event, core.EventCustom; got != want { - t.Errorf("Want hook Event %s, got %s", want, got) - } - if got, want := hook.Link, mockCommit.Link; got != want { - t.Errorf("Want hook Link %s, got %s", want, got) - } - if got, want := hook.Message, mockCommit.Message; got != want { - t.Errorf("Want hook Message %s, got %s", want, got) - } - if got, want := hook.Before, mockCommit.Sha; got != want { - t.Errorf("Want hook Before %s, got %s", want, got) - } - if got, want := hook.After, mockCommit.Sha; got != want { - t.Errorf("Want hook After %s, got %s", want, got) - } - if got, want := hook.Ref, mockCommit.Ref; got != want { - t.Errorf("Want hook Ref %s, got %s", want, got) - } - if got, want := hook.Source, "master"; got != want { - t.Errorf("Want hook Source %s, got %s", want, got) - } - if got, want := hook.Target, "master"; got != want { - t.Errorf("Want hook Target %s, got %s", want, got) - } - if got, want := hook.Author, mockCommit.Author.Login; got != want { - t.Errorf("Want hook Author %s, got %s", want, got) - } - if got, want := hook.AuthorName, mockCommit.Author.Name; got != want { - t.Errorf("Want hook AuthorName %s, got %s", want, got) - } - if got, want := hook.AuthorEmail, mockCommit.Author.Email; got != want { - t.Errorf("Want hook AuthorEmail %s, got %s", want, got) - } - if got, want := hook.AuthorAvatar, mockCommit.Author.Avatar; got != want { - t.Errorf("Want hook AuthorAvatar %s, got %s", want, got) - } - if got, want := hook.Sender, mockUser.Login; got != want { - t.Errorf("Want hook Sender %s, got %s", want, got) - } - return nil - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().Find(gomock.Any(), mockRepo.UserID).Return(mockUser, nil) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - commits := mock.NewMockCommitService(controller) - commits.EXPECT().Find(gomock.Any(), mockUser, mockRepo.Slug, mockCommit.Sha).Return(mockCommit, nil) - - triggerer := mock.NewMockTriggerer(controller) - triggerer.EXPECT().Trigger(gomock.Any(), mockRepo, gomock.Any()).Return(mockBuild, nil).Do(checkBuild) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - params := &url.Values{} - params.Set("branch", "master") - params.Set("commit", mockCommit.Sha) - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?"+params.Encode(), nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandleCreate(users, repos, commits, triggerer)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Build), mockBuild - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestCreate_FromHead(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockCommit := &core.Commit{ - Sha: "cce10d5c4760d1d6ede99db850ab7e77efe15579", - Ref: "refs/heads/master", - Message: "updated README.md", - Link: "https://github.com/octocatl/hello-world/commit/cce10d5c4760d1d6ede99db850ab7e77efe15579", - Author: &core.Committer{ - Name: "The Octocat", - Email: "octocat@github.com", - Login: "octocat", - Avatar: "https://github.com/octocat.png", - }, - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().Find(gomock.Any(), mockRepo.UserID).Return(mockUser, nil) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - commits := mock.NewMockCommitService(controller) - commits.EXPECT().FindRef(gomock.Any(), mockUser, mockRepo.Slug, mockCommit.Ref).Return(mockCommit, nil) - - triggerer := mock.NewMockTriggerer(controller) - triggerer.EXPECT().Trigger(gomock.Any(), mockRepo, gomock.Any()).Return(mockBuild, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandleCreate(users, repos, commits, triggerer)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Build), mockBuild - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/builds/deploys/create.go b/handler/api/repos/builds/deploys/create.go deleted file mode 100644 index 4b57008aed..0000000000 --- a/handler/api/repos/builds/deploys/create.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deploys diff --git a/handler/api/repos/builds/deploys/create_test.go b/handler/api/repos/builds/deploys/create_test.go deleted file mode 100644 index dc720aea04..0000000000 --- a/handler/api/repos/builds/deploys/create_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package deploys diff --git a/handler/api/repos/builds/deploys/delete.go b/handler/api/repos/builds/deploys/delete.go deleted file mode 100644 index 45d9a5f187..0000000000 --- a/handler/api/repos/builds/deploys/delete.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deploys - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that handles an -// http.Request to delete a branch entry from the datastore. -func HandleDelete( - repos core.RepositoryStore, - builds core.BuildStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - target = chi.URLParam(r, "*") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot find repository") - return - } - - err = builds.DeleteDeploy(r.Context(), repo.ID, target) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot delete deployment") - } else { - w.WriteHeader(http.StatusNoContent) - } - } -} diff --git a/handler/api/repos/builds/deploys/delete_test.go b/handler/api/repos/builds/deploys/delete_test.go deleted file mode 100644 index dc720aea04..0000000000 --- a/handler/api/repos/builds/deploys/delete_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package deploys diff --git a/handler/api/repos/builds/deploys/list.go b/handler/api/repos/builds/deploys/list.go deleted file mode 100644 index 77d9359baa..0000000000 --- a/handler/api/repos/builds/deploys/list.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package deploys - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleList returns an http.HandlerFunc that writes a json-encoded -// list of build history to the response body. -func HandleList( - repos core.RepositoryStore, - builds core.BuildStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot find repository") - return - } - - results, err := builds.LatestDeploys(r.Context(), repo.ID) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot list builds") - } else { - render.JSON(w, results, 200) - } - } -} diff --git a/handler/api/repos/builds/deploys/list_test.go b/handler/api/repos/builds/deploys/list_test.go deleted file mode 100644 index dc720aea04..0000000000 --- a/handler/api/repos/builds/deploys/list_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package deploys diff --git a/handler/api/repos/builds/find.go b/handler/api/repos/builds/find.go deleted file mode 100644 index 508603a293..0000000000 --- a/handler/api/repos/builds/find.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builds - -import ( - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleFind returns an http.HandlerFunc that writes json-encoded -// build details to the response body. -func HandleFind( - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - number, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - build, err := builds.FindNumber(r.Context(), repo.ID, number) - if err != nil { - render.NotFound(w, err) - return - } - stages, err := stages.ListSteps(r.Context(), build.ID) - if err != nil { - render.InternalError(w, err) - return - } - render.JSON(w, &buildWithStages{build, stages}, 200) - } -} - -type buildWithStages struct { - *core.Build - Stages []*core.Stage `json:"stages,omitempty"` -} diff --git a/handler/api/repos/builds/find_test.go b/handler/api/repos/builds/find_test.go deleted file mode 100644 index 52d1bf4198..0000000000 --- a/handler/api/repos/builds/find_test.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package builds - -import ( - "context" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().ListSteps(gomock.Any(), mockBuild.ID).Return(mockStages, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, builds, stages)(w, r) - - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &buildWithStages{}, &buildWithStages{mockBuild, mockStages} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestFind_BadRequest(t *testing.T) { - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "one") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(nil, nil, nil)(w, r) - - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "strconv.ParseInt: parsing \"one\": invalid syntax"} - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestFind_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, nil, nil)(w, r) - - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestFind_BuildNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - builds := mock.NewMockBuildStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, builds, nil)(w, r) - - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestFind_StagesNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().ListSteps(gomock.Any(), mockBuild.ID).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, builds, stages)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/builds/latest.go b/handler/api/repos/builds/latest.go deleted file mode 100644 index a15be13080..0000000000 --- a/handler/api/repos/builds/latest.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builds - -import ( - "fmt" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleLast returns an http.HandlerFunc that writes json-encoded -// build details to the the response body for the latest build. -func HandleLast( - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ref = r.FormValue("ref") - branch = r.FormValue("branch") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - if ref == "" { - ref = fmt.Sprintf("refs/heads/%s", repo.Branch) - } - if branch != "" { - ref = fmt.Sprintf("refs/heads/%s", branch) - } - build, err := builds.FindRef(r.Context(), repo.ID, ref) - if err != nil { - render.NotFound(w, err) - return - } - stages, err := stages.ListSteps(r.Context(), build.ID) - if err != nil { - render.InternalError(w, err) - return - } - render.JSON(w, &buildWithStages{build, stages}, 200) - } -} diff --git a/handler/api/repos/builds/latest_test.go b/handler/api/repos/builds/latest_test.go deleted file mode 100644 index 4527e1f273..0000000000 --- a/handler/api/repos/builds/latest_test.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package builds - -import ( - "context" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/mock" - "github.com/drone/drone/handler/api/errors" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestLast(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindRef(gomock.Any(), mockRepo.ID, "refs/heads/master").Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().ListSteps(gomock.Any(), mockBuild.ID).Return(mockStages, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleLast(repos, builds, stages)(w, r) - - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &buildWithStages{}, &buildWithStages{mockBuild, mockStages} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestLast_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleLast(repos, nil, nil)(w, r) - - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestLast_BuildNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindRef(gomock.Any(), mockRepo.ID, "refs/heads/master").Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleLast(repos, builds, nil)(w, r) - - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestLast_StagesNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindRef(gomock.Any(), mockRepo.ID, "refs/heads/master").Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().ListSteps(gomock.Any(), mockBuild.ID).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleLast(repos, builds, stages)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/builds/list.go b/handler/api/repos/builds/list.go deleted file mode 100644 index 699e81c36f..0000000000 --- a/handler/api/repos/builds/list.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builds - -import ( - "fmt" - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleList returns an http.HandlerFunc that writes a json-encoded -// list of build history to the response body. -func HandleList( - repos core.RepositoryStore, - builds core.BuildStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - branch = r.FormValue("branch") - tag = r.FormValue("tag") - page = r.FormValue("page") - perPage = r.FormValue("per_page") - ) - offset, _ := strconv.Atoi(page) - limit, _ := strconv.Atoi(perPage) - if limit < 1 || limit > 100 { - limit = 25 - } - switch offset { - case 0, 1: - offset = 0 - default: - offset = (offset - 1) * limit - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot find repository") - return - } - - var results []*core.Build - if branch != "" { - ref := fmt.Sprintf("refs/heads/%s", branch) - results, err = builds.ListRef(r.Context(), repo.ID, ref, limit, offset) - } else if tag != "" { - ref := fmt.Sprintf("refs/tags/%s", tag) - results, err = builds.ListRef(r.Context(), repo.ID, ref, limit, offset) - } else { - results, err = builds.List(r.Context(), repo.ID, limit, offset) - } - - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot list builds") - } else { - render.JSON(w, results, 200) - } - } -} diff --git a/handler/api/repos/builds/list_test.go b/handler/api/repos/builds/list_test.go deleted file mode 100644 index 453582cfc1..0000000000 --- a/handler/api/repos/builds/list_test.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package builds - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var ( - mockRepo = &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Counter: 42, - Branch: "master", - } - - mockBuild = &core.Build{ - ID: 1, - Number: 1, - RepoID: 1, - Status: core.StatusPending, - Event: core.EventPush, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Timestamp: 1299283200, - Message: "first commit", - Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e", - After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Ref: "refs/heads/master", - Source: "master", - Target: "master", - Author: "octocat", - AuthorName: "The Octocat", - AuthorEmail: "octocat@hello-world.com", - AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231", - Sender: "octocat", - } - - mockBuilds = []*core.Build{ - { - ID: 1, - Number: 1, - }, - } - - mockStage = &core.Stage{ - BuildID: 1, - Number: 1, - Name: "clone", - Status: core.StatusPassing, - } - - mockStages = []*core.Stage{ - mockStage, - } - - mockUser = &core.User{ - ID: 1, - Login: "octocat", - } -) - -func TestList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().List(gomock.Any(), mockRepo.ID, 25, 0).Return(mockBuilds, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, builds)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.Build{}, mockBuilds - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestListBranch(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().ListRef(gomock.Any(), mockRepo.ID, "refs/heads/develop", 25, 0).Return(mockBuilds, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/?branch=develop", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, builds)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.Build{}, mockBuilds - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestListTag(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().ListRef(gomock.Any(), mockRepo.ID, "refs/tags/1.33.7", 25, 0).Return(mockBuilds, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/?tag=1.33.7", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, builds)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.Build{}, mockBuilds - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestList_RepositoryNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - builds := mock.NewMockBuildStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "one") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, builds)(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestList_InternalError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - builds := mock.NewMockBuildStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - builds.EXPECT().List(gomock.Any(), mockRepo.ID, 25, 0).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "one") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, builds)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/builds/logs/delete.go b/handler/api/repos/builds/logs/delete.go deleted file mode 100644 index a26e8e9a30..0000000000 --- a/handler/api/repos/builds/logs/delete.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package logs - -import ( - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that processes http -// requests to delete the logs. -func HandleDelete( - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, - steps core.StepStore, - logs core.LogStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - number, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage")) - if err != nil { - render.BadRequest(w, err) - return - } - stepNumber, err := strconv.Atoi(chi.URLParam(r, "step")) - if err != nil { - render.BadRequest(w, err) - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - build, err := builds.FindNumber(r.Context(), repo.ID, number) - if err != nil { - render.NotFound(w, err) - return - } - stage, err := stages.FindNumber(r.Context(), build.ID, stageNumber) - if err != nil { - render.NotFound(w, err) - return - } - step, err := steps.FindNumber(r.Context(), stage.ID, stepNumber) - if err != nil { - render.NotFound(w, err) - return - } - err = logs.Delete(r.Context(), step.ID) - if err != nil { - render.InternalError(w, err) - return - } - w.WriteHeader(204) - } -} diff --git a/handler/api/repos/builds/logs/delete_test.go b/handler/api/repos/builds/logs/delete_test.go deleted file mode 100644 index b2415dc13a..0000000000 --- a/handler/api/repos/builds/logs/delete_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package logs diff --git a/handler/api/repos/builds/logs/find.go b/handler/api/repos/builds/logs/find.go deleted file mode 100644 index 84abb0b2cc..0000000000 --- a/handler/api/repos/builds/logs/find.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package logs - -import ( - "io" - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleFind returns an http.HandlerFunc that writes the -// json-encoded logs to the response body. -func HandleFind( - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, - steps core.StepStore, - logs core.LogStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - number, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage")) - if err != nil { - render.BadRequest(w, err) - return - } - stepNumber, err := strconv.Atoi(chi.URLParam(r, "step")) - if err != nil { - render.BadRequest(w, err) - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - build, err := builds.FindNumber(r.Context(), repo.ID, number) - if err != nil { - render.NotFound(w, err) - return - } - stage, err := stages.FindNumber(r.Context(), build.ID, stageNumber) - if err != nil { - render.NotFound(w, err) - return - } - step, err := steps.FindNumber(r.Context(), stage.ID, stepNumber) - if err != nil { - render.NotFound(w, err) - return - } - rc, err := logs.Find(r.Context(), step.ID) - if err != nil { - render.NotFound(w, err) - return - } - w.Header().Set("Content-Type", "application/json") - io.Copy(w, rc) - rc.Close() - - // TODO: logs are stored in jsonl format and therefore - // need to be converted to valid json. - // ELSE: JSON.parse('['+x.split('\n').join(',')+']') - } -} diff --git a/handler/api/repos/builds/logs/find_test.go b/handler/api/repos/builds/logs/find_test.go deleted file mode 100644 index b2415dc13a..0000000000 --- a/handler/api/repos/builds/logs/find_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package logs diff --git a/handler/api/repos/builds/promote.go b/handler/api/repos/builds/promote.go deleted file mode 100644 index 790ad62e59..0000000000 --- a/handler/api/repos/builds/promote.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package builds - -import ( - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - - "github.com/go-chi/chi" -) - -// HandlePromote returns an http.HandlerFunc that processes http -// requests to promote and re-execute a build. -func HandlePromote( - repos core.RepositoryStore, - builds core.BuildStore, - triggerer core.Triggerer, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - environ = r.FormValue("target") - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - user, _ = request.UserFrom(r.Context()) - ) - number, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - prev, err := builds.FindNumber(r.Context(), repo.ID, number) - if err != nil { - render.NotFound(w, err) - return - } - if environ == "" { - render.BadRequestf(w, "Missing target environment") - return - } - - hook := &core.Hook{ - Parent: prev.Number, - Trigger: user.Login, - Event: core.EventPromote, - Action: prev.Action, - Link: prev.Link, - Timestamp: prev.Timestamp, - Title: prev.Title, - Message: prev.Message, - Before: prev.Before, - After: prev.After, - Ref: prev.Ref, - Fork: prev.Fork, - Source: prev.Source, - Target: prev.Target, - Author: prev.Author, - AuthorName: prev.AuthorName, - AuthorEmail: prev.AuthorEmail, - AuthorAvatar: prev.AuthorAvatar, - Deployment: environ, - Cron: prev.Cron, - Sender: prev.Sender, - Params: map[string]string{}, - } - - for k, v := range prev.Params { - hook.Params[k] = v - } - - for key, value := range r.URL.Query() { - if key == "access_token" { - continue - } - if key == "target" { - continue - } - if len(value) == 0 { - continue - } - hook.Params[key] = value[0] - } - - result, err := triggerer.Trigger(r.Context(), repo, hook) - if err != nil { - render.InternalError(w, err) - } else { - render.JSON(w, result, 200) - } - } -} diff --git a/handler/api/repos/builds/promote_oss.go b/handler/api/repos/builds/promote_oss.go deleted file mode 100644 index 0664cd6015..0000000000 --- a/handler/api/repos/builds/promote_oss.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package builds - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -// HandlePromote returns a non-op http.HandlerFunc. -func HandlePromote( - core.RepositoryStore, - core.BuildStore, - core.Triggerer, -) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/repos/builds/promote_test.go b/handler/api/repos/builds/promote_test.go deleted file mode 100644 index 5f9db4e9b3..0000000000 --- a/handler/api/repos/builds/promote_test.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package builds - -import ( - "context" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestPromote(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - checkBuild := func(_ context.Context, _ *core.Repository, hook *core.Hook) error { - if got, want := hook.Trigger, mockUser.Login; got != want { - t.Errorf("Want Trigger By %s, got %s", want, got) - } - if got, want := hook.Event, core.EventPromote; got != want { - t.Errorf("Want Build Event %s, got %s", want, got) - } - if got, want := hook.Link, mockBuild.Link; got != want { - t.Errorf("Want Build Link %s, got %s", want, got) - } - if got, want := hook.Message, mockBuild.Message; got != want { - t.Errorf("Want Build Message %s, got %s", want, got) - } - if got, want := hook.Before, mockBuild.Before; got != want { - t.Errorf("Want Build Before %s, got %s", want, got) - } - if got, want := hook.After, mockBuild.After; got != want { - t.Errorf("Want Build After %s, got %s", want, got) - } - if got, want := hook.Ref, mockBuild.Ref; got != want { - t.Errorf("Want Build Ref %s, got %s", want, got) - } - if got, want := hook.Source, mockBuild.Source; got != want { - t.Errorf("Want Build Source %s, got %s", want, got) - } - if got, want := hook.Target, mockBuild.Target; got != want { - t.Errorf("Want Build Target %s, got %s", want, got) - } - if got, want := hook.Author, mockBuild.Author; got != want { - t.Errorf("Want Build Author %s, got %s", want, got) - } - if got, want := hook.AuthorName, mockBuild.AuthorName; got != want { - t.Errorf("Want Build AuthorName %s, got %s", want, got) - } - if got, want := hook.AuthorEmail, mockBuild.AuthorEmail; got != want { - t.Errorf("Want Build AuthorEmail %s, got %s", want, got) - } - if got, want := hook.AuthorAvatar, mockBuild.AuthorAvatar; got != want { - t.Errorf("Want Build AuthorAvatar %s, got %s", want, got) - } - if got, want := hook.Deployment, "production"; got != want { - t.Errorf("Want Build Deployment %s, got %s", want, got) - } - if got, want := hook.Sender, mockBuild.Sender; got != want { - t.Errorf("Want Build Sender %s, got %s", want, got) - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - triggerer := mock.NewMockTriggerer(controller) - triggerer.EXPECT().Trigger(gomock.Any(), mockRepo, gomock.Any()).Return(mockBuild, nil).Do(checkBuild) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?target=production", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePromote(repos, builds, triggerer)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Build), mockBuild - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestPromote_InvalidBuildNumber(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "XLII") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?target=production", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePromote(nil, nil, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{ - Message: `strconv.ParseInt: parsing "XLII": invalid syntax`, - } - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestPromote_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?target=production", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePromote(repos, nil, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestPromote_BuildNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?target=production", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePromote(repos, builds, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestPromote_MissingTargetError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?target=", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePromote(repos, builds, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "Missing target environment"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestPromote_TriggerError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - triggerer := mock.NewMockTriggerer(controller) - triggerer.EXPECT().Trigger(gomock.Any(), mockRepo, gomock.Any()).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?target=production", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePromote(repos, builds, triggerer)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/builds/pulls/create.go b/handler/api/repos/builds/pulls/create.go deleted file mode 100644 index 4a8d007c88..0000000000 --- a/handler/api/repos/builds/pulls/create.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pulls diff --git a/handler/api/repos/builds/pulls/create_test.go b/handler/api/repos/builds/pulls/create_test.go deleted file mode 100644 index f141d870c3..0000000000 --- a/handler/api/repos/builds/pulls/create_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package pulls diff --git a/handler/api/repos/builds/pulls/delete.go b/handler/api/repos/builds/pulls/delete.go deleted file mode 100644 index e08af36694..0000000000 --- a/handler/api/repos/builds/pulls/delete.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pulls - -import ( - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that handles an -// http.Request to delete a branch entry from the datastore. -func HandleDelete( - repos core.RepositoryStore, - builds core.BuildStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - number, _ = strconv.Atoi(chi.URLParam(r, "pull")) - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot find repository") - return - } - - err = builds.DeletePull(r.Context(), repo.ID, number) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot delete pr") - } else { - w.WriteHeader(http.StatusNoContent) - } - } -} diff --git a/handler/api/repos/builds/pulls/delete_test.go b/handler/api/repos/builds/pulls/delete_test.go deleted file mode 100644 index f141d870c3..0000000000 --- a/handler/api/repos/builds/pulls/delete_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package pulls diff --git a/handler/api/repos/builds/pulls/list.go b/handler/api/repos/builds/pulls/list.go deleted file mode 100644 index a284623f97..0000000000 --- a/handler/api/repos/builds/pulls/list.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pulls - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleList returns an http.HandlerFunc that writes a json-encoded -// list of build history to the response body. -func HandleList( - repos core.RepositoryStore, - builds core.BuildStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot find repository") - return - } - - results, err := builds.LatestPulls(r.Context(), repo.ID) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot list builds") - } else { - render.JSON(w, results, 200) - } - } -} diff --git a/handler/api/repos/builds/pulls/list_test.go b/handler/api/repos/builds/pulls/list_test.go deleted file mode 100644 index f141d870c3..0000000000 --- a/handler/api/repos/builds/pulls/list_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package pulls diff --git a/handler/api/repos/builds/purge.go b/handler/api/repos/builds/purge.go deleted file mode 100644 index 070251a1b1..0000000000 --- a/handler/api/repos/builds/purge.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package builds - -import ( - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandlePurge returns an http.HandlerFunc that purges the -// build history. If successful a 204 status code is returned. -func HandlePurge(repos core.RepositoryStore, builds core.BuildStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - before = r.FormValue("before") - ) - number, err := strconv.ParseInt(before, 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - err = builds.Purge(r.Context(), repo.ID, number) - if err != nil { - render.InternalError(w, err) - return - } - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/repos/builds/purge_oss.go b/handler/api/repos/builds/purge_oss.go deleted file mode 100644 index d9937051a8..0000000000 --- a/handler/api/repos/builds/purge_oss.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package builds - -import ( - "net/http" - - "github.com/drone/drone/core" -) - -// HandlePurge returns a non-op http.HandlerFunc. -func HandlePurge(core.RepositoryStore, core.BuildStore) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/repos/builds/purge_test.go b/handler/api/repos/builds/purge_test.go deleted file mode 100644 index 01b9bf2187..0000000000 --- a/handler/api/repos/builds/purge_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package builds - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestPurge(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().Purge(gomock.Any(), mockRepo.ID, int64(50)).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/?before=50", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePurge(repos, builds)(w, r) - if got, want := w.Code, http.StatusNoContent; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -// The test verifies that a 404 Not Found error is returned -// if the repository store returns an error. -func TestPurge_NotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/?before=50", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePurge(repos, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// The test verifies that a 400 Bad Request error is returned -// if the user provides an invalid ?before query parameter -// that cannot be parsed. -func TestPurge_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/?before=XLII", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePurge(nil, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{ - Message: `strconv.ParseInt: parsing "XLII": invalid syntax`, - } - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// The test verifies that a 500 Internal server error is -// returned if the database purge transaction fails. -func TestPurge_InternalError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().Purge(gomock.Any(), mockRepo.ID, int64(50)).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/?before=50", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandlePurge(repos, builds)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/builds/retry.go b/handler/api/repos/builds/retry.go deleted file mode 100644 index 9d297d6920..0000000000 --- a/handler/api/repos/builds/retry.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package builds - -import ( - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - - "github.com/go-chi/chi" -) - -// HandleRetry returns an http.HandlerFunc that processes http -// requests to retry and re-execute a build. -func HandleRetry( - repos core.RepositoryStore, - builds core.BuildStore, - triggerer core.Triggerer, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - user, _ = request.UserFrom(r.Context()) - ) - number, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - prev, err := builds.FindNumber(r.Context(), repo.ID, number) - if err != nil { - render.NotFound(w, err) - return - } - - switch prev.Status { - case core.StatusBlocked: - render.BadRequestf(w, "cannot start a blocked build") - return - case core.StatusDeclined: - render.BadRequestf(w, "cannot start a declined build") - return - } - - hook := &core.Hook{ - Parent: prev.Number, - Trigger: user.Login, - Event: prev.Event, - Action: prev.Action, - Link: prev.Link, - Timestamp: prev.Timestamp, - Title: prev.Title, - Message: prev.Message, - Before: prev.Before, - After: prev.After, - Ref: prev.Ref, - Fork: prev.Fork, - Source: prev.Source, - Target: prev.Target, - Author: prev.Author, - AuthorName: prev.AuthorName, - AuthorEmail: prev.AuthorEmail, - AuthorAvatar: prev.AuthorAvatar, - Deployment: prev.Deploy, - DeploymentID: prev.DeployID, - Debug: r.FormValue("debug") == "true", - Cron: prev.Cron, - Sender: prev.Sender, - Params: map[string]string{}, - } - - for key, value := range r.URL.Query() { - if key == "access_token" { - continue - } - if key == "debug" { - continue - } - if len(value) == 0 { - continue - } - hook.Params[key] = value[0] - } - for key, value := range prev.Params { - hook.Params[key] = value - } - - result, err := triggerer.Trigger(r.Context(), repo, hook) - if err != nil { - render.InternalError(w, err) - } else { - render.JSON(w, result, 200) - } - } -} diff --git a/handler/api/repos/builds/retry_test.go b/handler/api/repos/builds/retry_test.go deleted file mode 100644 index 8b5ab46b19..0000000000 --- a/handler/api/repos/builds/retry_test.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package builds - -import ( - "context" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - "github.com/drone/drone/core" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestRetry(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - checkBuild := func(_ context.Context, _ *core.Repository, hook *core.Hook) error { - if got, want := hook.Trigger, mockUser.Login; got != want { - t.Errorf("Want Trigger By %s, got %s", want, got) - } - if got, want := hook.Event, mockBuild.Event; got != want { - t.Errorf("Want Build Event %s, got %s", want, got) - } - if got, want := hook.Link, mockBuild.Link; got != want { - t.Errorf("Want Build Link %s, got %s", want, got) - } - if got, want := hook.Message, mockBuild.Message; got != want { - t.Errorf("Want Build Message %s, got %s", want, got) - } - if got, want := hook.Before, mockBuild.Before; got != want { - t.Errorf("Want Build Before %s, got %s", want, got) - } - if got, want := hook.After, mockBuild.After; got != want { - t.Errorf("Want Build After %s, got %s", want, got) - } - if got, want := hook.Ref, mockBuild.Ref; got != want { - t.Errorf("Want Build Ref %s, got %s", want, got) - } - if got, want := hook.Source, mockBuild.Source; got != want { - t.Errorf("Want Build Source %s, got %s", want, got) - } - if got, want := hook.Target, mockBuild.Target; got != want { - t.Errorf("Want Build Target %s, got %s", want, got) - } - if got, want := hook.Author, mockBuild.Author; got != want { - t.Errorf("Want Build Author %s, got %s", want, got) - } - if got, want := hook.AuthorName, mockBuild.AuthorName; got != want { - t.Errorf("Want Build AuthorName %s, got %s", want, got) - } - if got, want := hook.AuthorEmail, mockBuild.AuthorEmail; got != want { - t.Errorf("Want Build AuthorEmail %s, got %s", want, got) - } - if got, want := hook.AuthorAvatar, mockBuild.AuthorAvatar; got != want { - t.Errorf("Want Build AuthorAvatar %s, got %s", want, got) - } - if got, want := hook.Sender, mockBuild.Sender; got != want { - t.Errorf("Want Build Sender %s, got %s", want, got) - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - triggerer := mock.NewMockTriggerer(controller) - triggerer.EXPECT().Trigger(gomock.Any(), mockRepo, gomock.Any()).Return(mockBuild, nil).Do(checkBuild) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandleRetry(repos, builds, triggerer)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Build), mockBuild - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestRetry_InvalidBuildNumber(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "XLII") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandleRetry(nil, nil, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{ - Message: `strconv.ParseInt: parsing "XLII": invalid syntax`, - } - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestRetry_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandleRetry(repos, nil, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestRetry_BuildNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandleRetry(repos, builds, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestRetry_TriggerError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - triggerer := mock.NewMockTriggerer(controller) - triggerer.EXPECT().Trigger(gomock.Any(), mockRepo, gomock.Any()).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), mockUser), chi.RouteCtxKey, c), - ) - - HandleRetry(repos, builds, triggerer)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/builds/rollback.go b/handler/api/repos/builds/rollback.go deleted file mode 100644 index 794df1320c..0000000000 --- a/handler/api/repos/builds/rollback.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package builds - -import ( - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - - "github.com/go-chi/chi" -) - -// HandleRollback returns an http.HandlerFunc that processes http -// requests to rollback and re-execute a build. -func HandleRollback( - repos core.RepositoryStore, - builds core.BuildStore, - triggerer core.Triggerer, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - environ = r.FormValue("target") - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - user, _ = request.UserFrom(r.Context()) - ) - number, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequest(w, err) - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - prev, err := builds.FindNumber(r.Context(), repo.ID, number) - if err != nil { - render.NotFound(w, err) - return - } - if environ == "" { - render.BadRequestf(w, "Missing target environment") - return - } - - hook := &core.Hook{ - Parent: prev.Number, - Trigger: user.Login, - Event: core.EventRollback, - Action: prev.Action, - Link: prev.Link, - Timestamp: prev.Timestamp, - Title: prev.Title, - Message: prev.Message, - Before: prev.Before, - After: prev.After, - Ref: prev.Ref, - Fork: prev.Fork, - Source: prev.Source, - Target: prev.Target, - Author: prev.Author, - AuthorName: prev.AuthorName, - AuthorEmail: prev.AuthorEmail, - AuthorAvatar: prev.AuthorAvatar, - Deployment: environ, - Cron: prev.Cron, - Sender: prev.Sender, - Params: map[string]string{}, - } - - for k, v := range prev.Params { - hook.Params[k] = v - } - - for key, value := range r.URL.Query() { - if key == "access_token" { - continue - } - if key == "target" { - continue - } - if len(value) == 0 { - continue - } - hook.Params[key] = value[0] - } - - result, err := triggerer.Trigger(r.Context(), repo, hook) - if err != nil { - render.InternalError(w, err) - } else { - render.JSON(w, result, 200) - } - } -} diff --git a/handler/api/repos/builds/rollback_oss.go b/handler/api/repos/builds/rollback_oss.go deleted file mode 100644 index 3f1c3217b9..0000000000 --- a/handler/api/repos/builds/rollback_oss.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package builds - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var rollbackNotImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -// HandleRollback returns a non-op http.HandlerFunc. -func HandleRollback( - core.RepositoryStore, - core.BuildStore, - core.Triggerer, -) http.HandlerFunc { - return rollbackNotImplemented -} diff --git a/handler/api/repos/builds/rollback_test.go b/handler/api/repos/builds/rollback_test.go deleted file mode 100644 index 3047a80e93..0000000000 --- a/handler/api/repos/builds/rollback_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package builds diff --git a/handler/api/repos/builds/stages/approve.go b/handler/api/repos/builds/stages/approve.go deleted file mode 100644 index fe3d0af518..0000000000 --- a/handler/api/repos/builds/stages/approve.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stages - -import ( - "context" - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -var noContext = context.Background() - -// HandleApprove returns an http.HandlerFunc that processes http -// requests to approve a blocked build that is pending review. -func HandleApprove( - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, - sched core.Scheduler, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - buildNumber, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequestf(w, "Invalid build number") - return - } - stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage")) - if err != nil { - render.BadRequestf(w, "Invalid stage number") - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFoundf(w, "Repository not found") - return - } - build, err := builds.FindNumber(r.Context(), repo.ID, buildNumber) - if err != nil { - render.NotFoundf(w, "Build not found") - return - } - stage, err := stages.FindNumber(r.Context(), build.ID, stageNumber) - if err != nil { - render.NotFoundf(w, "Stage not found") - return - } - if stage.Status != core.StatusBlocked { - render.BadRequestf(w, "Cannot approve a Pipeline with Status %q", stage.Status) - return - } - if len(stage.DependsOn) > 0 { - stage.Status = core.StatusWaiting - } else { - stage.Status = core.StatusPending - } - err = stages.Update(r.Context(), stage) - if err != nil { - render.InternalErrorf(w, "There was a problem approving the Pipeline") - return - } - err = sched.Schedule(noContext, stage) - if err != nil { - render.InternalErrorf(w, "There was a problem scheduling the Pipeline") - return - } - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/repos/builds/stages/approve_test.go b/handler/api/repos/builds/stages/approve_test.go deleted file mode 100644 index 7f73d34bc9..0000000000 --- a/handler/api/repos/builds/stages/approve_test.go +++ /dev/null @@ -1,628 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package stages - -import ( - "context" - "database/sql" - "encoding/json" - "io" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestApprove(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - mockBuild := &core.Build{ - ID: 111, - Number: 1, - Status: core.StatusPending, - } - mockStage := &core.Stage{ - ID: 222, - Number: 2, - Status: core.StatusBlocked, - OS: "linux", - Arch: "arm", - } - - checkStage := func(_ context.Context, stage *core.Stage) error { - if stage.Status != core.StatusPending { - t.Errorf("Want stage status changed to Pending") - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, mockStage.Number).Return(mockStage, nil) - stages.EXPECT().Update(gomock.Any(), mockStage).Return(nil).Do(checkStage) - - sched := mock.NewMockScheduler(controller) - sched.EXPECT().Schedule(gomock.Any(), mockStage).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(repos, builds, stages, sched)(w, r) - if got, want := w.Code, 204; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -// this test verifies that a 400 bad request status is returned -// from the http.Handler with a human-readable error message if -// the build status is not Blocked. -func TestApprove_InvalidStatus(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - mockBuild := &core.Build{ - ID: 111, - Number: 1, - Status: core.StatusPending, - } - mockStage := &core.Stage{ - ID: 222, - Number: 2, - Status: core.StatusPending, - OS: "linux", - Arch: "arm", - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, mockStage.Number).Return(mockStage, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(repos, builds, stages, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New(`Cannot approve a Pipeline with Status "pending"`) - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 400 bad request status is returned -// from the http.Handler with a human-readable error message if -// the build number url parameter fails to parse. -func TestApprove_InvalidBuildNumber(t *testing.T) { - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "I") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(nil, nil, nil, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Invalid build number") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 400 bad request status is returned -// from the http.Handler with a human-readable error message if -// the stage number url parameter fails to parse. -func TestApprove_InvalidStageNumber(t *testing.T) { - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "II") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(nil, nil, nil, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Invalid stage number") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found status is returned -// from the http.Handler with a human-readable error message if -// the stage is not found in the database. -func TestApprove_StageNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - mockBuild := &core.Build{ - ID: 111, - Number: 1, - Status: core.StatusPending, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, 2).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(repos, builds, stages, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Stage not found") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found status is returned -// from the http.Handler with a human-readable error message if -// the build is not found in the database. -func TestApprove_BuildNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, int64(1)).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(repos, builds, nil, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Build not found") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found status is returned -// from the http.Handler with a human-readable error message if -// the repository is not found in the database. -func TestApprove_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(repos, nil, nil, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Repository not found") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 500 internal error status is returned -// from the http.Handler with a human-readable error message if -// the the server fails to persist stage updates to the database. -func TestApprove_CannotSaveStage(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - mockBuild := &core.Build{ - ID: 111, - Number: 1, - Status: core.StatusPending, - } - mockStage := &core.Stage{ - ID: 222, - Number: 2, - Status: core.StatusBlocked, - OS: "linux", - Arch: "arm", - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, mockStage.Number).Return(mockStage, nil) - stages.EXPECT().Update(gomock.Any(), mockStage).Return(sql.ErrConnDone) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(repos, builds, stages, nil)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("There was a problem approving the Pipeline") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 500 internal error status is returned -// from the http.Handler with a human-readable error message if -// the the server fails to enqueue the approved pipeline. -func TestApprove_CannotEnqueue(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - mockBuild := &core.Build{ - ID: 111, - Number: 1, - Status: core.StatusPending, - } - mockStage := &core.Stage{ - ID: 222, - Number: 2, - Status: core.StatusBlocked, - OS: "linux", - Arch: "arm", - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, mockStage.Number).Return(mockStage, nil) - stages.EXPECT().Update(gomock.Any(), mockStage).Return(nil) - - sched := mock.NewMockScheduler(controller) - sched.EXPECT().Schedule(gomock.Any(), gomock.Any()).Return(io.EOF) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(repos, builds, stages, sched)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("There was a problem scheduling the Pipeline") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} -func TestApprove_ParallelStages(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - mockBuild := &core.Build{ - ID: 111, - Number: 1, - Status: core.StatusPending, - } - mockStage := &core.Stage{ - ID: 222, - Number: 2, - Status: core.StatusBlocked, - OS: "linux", - Arch: "arm", - } - mockStage2 := &core.Stage{ - ID: 333, - Number: 3, - Status: core.StatusBlocked, - OS: "linux", - Arch: "arm", - } - - checkPendingStage := func(_ context.Context, stage *core.Stage) error { - if stage.Status != core.StatusPending { - t.Errorf("Want stage status changed to Pending") - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil).Times(2) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil).Times(2) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, mockStage.Number).Return(mockStage, nil) - stages.EXPECT().Update(gomock.Any(), mockStage).Return(nil).Do(checkPendingStage) - - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, mockStage2.Number).Return(mockStage2, nil) - stages.EXPECT().Update(gomock.Any(), mockStage2).Return(nil).Do(checkPendingStage) - - sched := mock.NewMockScheduler(controller) - sched.EXPECT().Schedule(gomock.Any(), mockStage).Return(nil) - sched.EXPECT().Schedule(gomock.Any(), mockStage2).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(repos, builds, stages, sched)(w, r) - if got, want := w.Code, 204; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - // now approve the second stage - c2 := new(chi.Context) - c2.URLParams.Add("owner", "octocat") - c2.URLParams.Add("name", "hello-world") - c2.URLParams.Add("number", "1") - c2.URLParams.Add("stage", "3") - - w = httptest.NewRecorder() - r = httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c2), - ) - - HandleApprove(repos, builds, stages, sched)(w, r) - if got, want := w.Code, 204; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -// test for a build that has 2 parallel stages, and we approve both of them -func TestApprove_DependantStages(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - mockBuild := &core.Build{ - ID: 111, - Number: 1, - Status: core.StatusPending, - } - mockStage := &core.Stage{ - ID: 222, - Number: 2, - Status: core.StatusBlocked, - OS: "linux", - Arch: "arm", - } - mockStage2 := &core.Stage{ - ID: 333, - Number: 3, - Status: core.StatusBlocked, - OS: "linux", - Arch: "arm", - DependsOn: []string{"2"}, - } - - checkPendingStage := func(_ context.Context, stage *core.Stage) error { - if stage.Status != core.StatusPending { - t.Errorf("Want stage status changed to Pending") - } - return nil - } - - checkWaitingStage := func(_ context.Context, stage *core.Stage) error { - if stage.Status != core.StatusWaiting { - t.Errorf("Want stage status changed to waiting") - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil).Times(2) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, mockBuild.Number).Return(mockBuild, nil).Times(2) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, mockStage.Number).Return(mockStage, nil) - stages.EXPECT().Update(gomock.Any(), mockStage).Return(nil).Do(checkPendingStage) - - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, mockStage2.Number).Return(mockStage2, nil) - stages.EXPECT().Update(gomock.Any(), mockStage2).Return(nil).Do(checkWaitingStage) - - sched := mock.NewMockScheduler(controller) - sched.EXPECT().Schedule(gomock.Any(), mockStage).Return(nil) - sched.EXPECT().Schedule(gomock.Any(), mockStage2).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleApprove(repos, builds, stages, sched)(w, r) - if got, want := w.Code, 204; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - // now approve the second stage - c2 := new(chi.Context) - c2.URLParams.Add("owner", "octocat") - c2.URLParams.Add("name", "hello-world") - c2.URLParams.Add("number", "1") - c2.URLParams.Add("stage", "3") - - w = httptest.NewRecorder() - r = httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c2), - ) - - HandleApprove(repos, builds, stages, sched)(w, r) - if got, want := w.Code, 204; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} diff --git a/handler/api/repos/builds/stages/decline.go b/handler/api/repos/builds/stages/decline.go deleted file mode 100644 index 7ff0c7b3ca..0000000000 --- a/handler/api/repos/builds/stages/decline.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stages - -import ( - "fmt" - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleDecline returns an http.HandlerFunc that processes http -// requests to decline a blocked build that is pending review. -func HandleDecline( - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - buildNumber, err := strconv.ParseInt(chi.URLParam(r, "number"), 10, 64) - if err != nil { - render.BadRequestf(w, "Invalid build number") - return - } - stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage")) - if err != nil { - render.BadRequestf(w, "Invalid stage number") - return - } - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFoundf(w, "Repository not found") - return - } - build, err := builds.FindNumber(r.Context(), repo.ID, buildNumber) - if err != nil { - render.NotFoundf(w, "Build not found") - return - } - stage, err := stages.FindNumber(r.Context(), build.ID, stageNumber) - if err != nil { - render.NotFoundf(w, "Stage not found") - return - } - if stage.Status != core.StatusBlocked { - err := fmt.Errorf("Cannot decline build with status %q", stage.Status) - render.BadRequest(w, err) - return - } - stage.Status = core.StatusDeclined - err = stages.Update(r.Context(), stage) - if err != nil { - render.InternalError(w, err) - return - } - build.Status = core.StatusDeclined - err = builds.Update(r.Context(), build) - if err != nil { - render.InternalError(w, err) - return - } - - // TODO delete any pending stages from the build queue - // TODO update any pending stages to skipped in the database - // TODO update the build status to error in the source code management system - - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/repos/builds/stages/decline_test.go b/handler/api/repos/builds/stages/decline_test.go deleted file mode 100644 index d822dd0b60..0000000000 --- a/handler/api/repos/builds/stages/decline_test.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package stages - -import ( - "context" - "database/sql" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - "github.com/drone/drone/core" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -// this test verifies that a 400 bad request status is returned -// from the http.Handler with a human-readable error message if -// the build number url parameter fails to parse. -func TestDecline_InvalidBuildNumber(t *testing.T) { - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "I") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDecline(nil, nil, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Invalid build number") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 400 bad request status is returned -// from the http.Handler with a human-readable error message if -// the stage number url parameter fails to parse. -func TestDecline_InvalidStageNumber(t *testing.T) { - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "II") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDecline(nil, nil, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Invalid stage number") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found status is returned -// from the http.Handler with a human-readable error message if -// the repository is not found in the database. -func TestDecline_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDecline(repos, nil, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Repository not found") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found status is returned -// from the http.Handler with a human-readable error message if -// the build is not found in the database. -func TestDecline_BuildNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, int64(1)).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDecline(repos, builds, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Build not found") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found status is returned -// from the http.Handler with a human-readable error message if -// the stage is not found in the database. -func TestDecline_StageNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - mockBuild := &core.Build{ - ID: 111, - Number: 1, - Status: core.StatusPending, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, int64(1)).Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, 2).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDecline(repos, builds, stages)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("Stage not found") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 400 bad request status is returned -// from the http.Handler with a human-readable error message if -// the build status is not Blocked. -func TestDecline_InvalidStatus(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - mockBuild := &core.Build{ - ID: 111, - Number: 1, - Status: core.StatusPending, - } - mockStage := &core.Stage{ - ID: 222, - Number: 2, - Status: core.StatusPending, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().FindNumber(gomock.Any(), mockRepo.ID, int64(1)).Return(mockBuild, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().FindNumber(gomock.Any(), mockBuild.ID, 2).Return(mockStage, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - c.URLParams.Add("stage", "2") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDecline(repos, builds, stages)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New(`Cannot decline build with status "pending"`) - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/chown.go b/handler/api/repos/chown.go deleted file mode 100644 index efd0f39950..0000000000 --- a/handler/api/repos/chown.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleChown returns an http.HandlerFunc that processes http -// requests to chown the repository to the currently authenticated user. -func HandleChown(repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - owner = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - repo, err := repos.FindName(r.Context(), owner, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Debugln("api: repository not found") - return - } - - user, _ := request.UserFrom(r.Context()) - repo.UserID = user.ID - - err = repos.Update(r.Context(), repo) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Debugln("api: cannot chown repository") - } else { - render.JSON(w, repo, 200) - } - } -} diff --git a/handler/api/repos/chown_test.go b/handler/api/repos/chown_test.go deleted file mode 100644 index 1d85ac9f76..0000000000 --- a/handler/api/repos/chown_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package repos - -import ( - "context" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - "github.com/drone/drone/core" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestChown(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ - ID: 42, - } - repo := &core.Repository{ - ID: 1, - UserID: 1, - } - - checkChown := func(_ context.Context, updated *core.Repository) error { - if got, want := updated.UserID, user.ID; got != want { - t.Errorf("Want repository owner updated to %d, got %d", want, got) - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(nil).Do(checkChown) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), user), chi.RouteCtxKey, c), - ) - - HandleChown(repos)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.Repository{}, repo - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestChown_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), &core.User{}), chi.RouteCtxKey, c), - ) - - HandleChown(repos)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestChown_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(&core.Repository{}, nil) - repos.EXPECT().Update(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), &core.User{}), chi.RouteCtxKey, c), - ) - - HandleChown(repos)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/collabs/find.go b/handler/api/repos/collabs/find.go deleted file mode 100644 index e2dd450663..0000000000 --- a/handler/api/repos/collabs/find.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package collabs - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleFind returns an http.HandlerFunc that writes a json-encoded -// repository collaborator details to the response body. -func HandleFind( - users core.UserStore, - repos core.RepositoryStore, - members core.PermStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - login = chi.URLParam(r, "member") - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: repository not found") - return - } - user, err := users.FindLogin(r.Context(), login) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - WithField("member", login). - Debugln("api: user not found") - return - } - member, err := members.Find(r.Context(), repo.UID, user.ID) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("member", login). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: membership not found") - return - } - render.JSON(w, member, 200) - } -} diff --git a/handler/api/repos/collabs/find_test.go b/handler/api/repos/collabs/find_test.go deleted file mode 100644 index 015861696a..0000000000 --- a/handler/api/repos/collabs/find_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package collabs - -import ( - "context" - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - "github.com/sirupsen/logrus" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -func TestFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - repos := mock.NewMockRepositoryStore(controller) - perms := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - users.EXPECT().FindLogin(gomock.Any(), "octocat").Return(mockUser, nil) - perms.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(mockMember, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("member", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(users, repos, perms)(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.Perm{}, mockMember - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestFind_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("member", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(users, repos, members)(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestFind_UserNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - users.EXPECT().FindLogin(gomock.Any(), "octocat").Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("member", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(users, repos, members)(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestFind_MemberNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - users.EXPECT().FindLogin(gomock.Any(), "octocat").Return(mockUser, nil) - members.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("member", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(users, repos, members)(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/collabs/list.go b/handler/api/repos/collabs/list.go deleted file mode 100644 index 4868e685c7..0000000000 --- a/handler/api/repos/collabs/list.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package collabs - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleList returns an http.HandlerFunc that write a json-encoded -// list of repository collaborators to the response body. -func HandleList( - repos core.RepositoryStore, - members core.PermStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: repository not found") - return - } - members, err := members.List(r.Context(), repo.UID) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Warnln("api: cannot get member list") - } else { - render.JSON(w, members, 200) - } - } -} diff --git a/handler/api/repos/collabs/list_test.go b/handler/api/repos/collabs/list_test.go deleted file mode 100644 index e8b073a0af..0000000000 --- a/handler/api/repos/collabs/list_test.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package collabs - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var ( - mockUser = &core.User{ - ID: 1, - Login: "octocat", - } - - mockRepo = &core.Repository{ - ID: 1, - UID: "42", - Namespace: "octocat", - Name: "hello-world", - } - - mockMember = &core.Perm{ - Read: true, - Write: true, - Admin: true, - } - - mockMembers = []*core.Collaborator{ - { - Login: "octocat", - Read: true, - Write: true, - Admin: true, - }, - { - Login: "spaceghost", - Read: true, - Write: true, - Admin: true, - }, - } -) - -func TestList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - members.EXPECT().List(gomock.Any(), mockRepo.UID).Return(mockMembers, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, members)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.Collaborator{}, mockMembers - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestList_NotFoundError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, members)(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestList_InternalError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - members.EXPECT().List(gomock.Any(), mockRepo.UID).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, members)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/collabs/none.go b/handler/api/repos/collabs/none.go deleted file mode 100644 index 47c62a78d7..0000000000 --- a/handler/api/repos/collabs/none.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package collabs - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -func HandleDelete(core.UserStore, core.RepositoryStore, core.PermStore) http.HandlerFunc { - return notImplemented -} - -func HandleFind(core.UserStore, core.RepositoryStore, core.PermStore) http.HandlerFunc { - return notImplemented -} - -func HandleList(core.RepositoryStore, core.PermStore) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/repos/collabs/remove.go b/handler/api/repos/collabs/remove.go deleted file mode 100644 index dd3328abf4..0000000000 --- a/handler/api/repos/collabs/remove.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package collabs - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that processes -// a request to delete account membership to a repository. This should -// only be used if the datastore is out-of-sync with github. -func HandleDelete( - users core.UserStore, - repos core.RepositoryStore, - members core.PermStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - login = chi.URLParam(r, "member") - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: repository not found") - return - } - user, err := users.FindLogin(r.Context(), login) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("member", login). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: user not found") - return - } - member, err := members.Find(r.Context(), repo.UID, user.ID) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("member", member). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: membership not found") - return - } - err = members.Delete(r.Context(), member) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("member", login). - WithField("namespace", namespace). - WithField("name", name). - Debugln("api: cannot delete membership") - } else { - w.WriteHeader(http.StatusNoContent) - } - } -} diff --git a/handler/api/repos/collabs/remove_test.go b/handler/api/repos/collabs/remove_test.go deleted file mode 100644 index 109ed74abf..0000000000 --- a/handler/api/repos/collabs/remove_test.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package collabs - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestDelete(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - users.EXPECT().FindLogin(gomock.Any(), "octocat").Return(mockUser, nil) - members.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(mockMember, nil) - members.EXPECT().Delete(gomock.Any(), mockMember).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("member", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(users, repos, members)(w, r) - if got, want := w.Code, http.StatusNoContent; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestDelete_UserNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - users.EXPECT().FindLogin(gomock.Any(), "octocat").Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("member", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(users, repos, members)(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestDelete_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("member", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(users, repos, members)(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestDelete_MemberNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - users.EXPECT().FindLogin(gomock.Any(), "octocat").Return(mockUser, nil) - members.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("member", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(users, repos, members)(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestDelete_InternalError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - repos := mock.NewMockRepositoryStore(controller) - members := mock.NewMockPermStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(mockRepo, nil) - users.EXPECT().FindLogin(gomock.Any(), "octocat").Return(mockUser, nil) - members.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(mockMember, nil) - members.EXPECT().Delete(gomock.Any(), mockMember).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("member", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(users, repos, members)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/crons/create.go b/handler/api/repos/crons/create.go deleted file mode 100644 index bffa767887..0000000000 --- a/handler/api/repos/crons/create.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleCreate returns an http.HandlerFunc that processes http -// requests to create a new cronjob. -func HandleCreate( - repos core.RepositoryStore, - crons core.CronStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - in := new(core.Cron) - err = json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - cronjob := new(core.Cron) - cronjob.Event = core.EventPush - cronjob.Branch = in.Branch - cronjob.RepoID = repo.ID - cronjob.SetName(in.Name) - err = cronjob.SetExpr(in.Expr) - if err != nil { - render.BadRequest(w, err) - return - } - - err = cronjob.Validate() - if err != nil { - render.BadRequest(w, err) - return - } - - err = crons.Create(r.Context(), cronjob) - if err != nil { - render.InternalError(w, err) - return - } - render.JSON(w, cronjob, 200) - } -} diff --git a/handler/api/repos/crons/create_test.go b/handler/api/repos/crons/create_test.go deleted file mode 100644 index 113c6ebb77..0000000000 --- a/handler/api/repos/crons/create_test.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -func TestHandleCreate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummyCron) - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, crons)(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.Cron{}, dummyCron - json.NewDecoder(w.Body).Decode(got) - - ignore := cmpopts.IgnoreFields(core.Cron{}, "Next") - if diff := cmp.Diff(got, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } - if got.Next == 0 { - t.Errorf("Expect next execution date scheduled") - } -} - -func TestHandleCreate_ValidationError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Cron{Name: "", Expr: "* * * * *"}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "Invalid Cronjob Name"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_BadExpression(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Cron{Name: "", Expr: "a b c d e"}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "Invalid Cronjob Expression"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_CreateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().Create(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummyCron) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/crons/delete.go b/handler/api/repos/crons/delete.go deleted file mode 100644 index 5a67a90b69..0000000000 --- a/handler/api/repos/crons/delete.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that processes http -// requests to delete the cron job. -func HandleDelete( - repos core.RepositoryStore, - crons core.CronStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - cron = chi.URLParam(r, "cron") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - cronjob, err := crons.FindName(r.Context(), repo.ID, cron) - if err != nil { - render.NotFound(w, err) - return - } - err = crons.Delete(r.Context(), cronjob) - if err != nil { - render.InternalError(w, err) - return - } - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/repos/crons/delete_test.go b/handler/api/repos/crons/delete_test.go deleted file mode 100644 index 26a0a89efb..0000000000 --- a/handler/api/repos/crons/delete_test.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleDelete(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().FindName(gomock.Any(), dummyCronRepo.ID, dummyCron.Name).Return(dummyCron, nil) - crons.EXPECT().Delete(gomock.Any(), dummyCron).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNoContent; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleDelete_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleDelete_CronNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().FindName(gomock.Any(), dummyCronRepo.ID, dummyCron.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleDelete_DeleteError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().FindName(gomock.Any(), dummyCronRepo.ID, dummyCron.Name).Return(dummyCron, nil) - crons.EXPECT().Delete(gomock.Any(), dummyCron).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/crons/exec.go b/handler/api/repos/crons/exec.go deleted file mode 100644 index b533ec725c..0000000000 --- a/handler/api/repos/crons/exec.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "context" - "fmt" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/sirupsen/logrus" - - "github.com/go-chi/chi" -) - -// HandleExec returns an http.HandlerFunc that processes http -// requests to execute a cronjob on-demand. -func HandleExec( - users core.UserStore, - repos core.RepositoryStore, - crons core.CronStore, - commits core.CommitService, - trigger core.Triggerer, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - ctx = r.Context() - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - cron = chi.URLParam(r, "cron") - ) - - repo, err := repos.FindName(ctx, namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - - cronjob, err := crons.FindName(ctx, repo.ID, cron) - if err != nil { - render.NotFound(w, err) - logger := logrus.WithError(err) - logger.Debugln("api: cannot find cron") - return - } - - user, err := users.Find(ctx, repo.UserID) - if err != nil { - logger := logrus.WithError(err) - logger.Debugln("api: cannot find repository owner") - render.NotFound(w, err) - return - } - - commit, err := commits.FindRef(ctx, user, repo.Slug, cronjob.Branch) - if err != nil { - logger := logrus.WithError(err). - WithField("namespace", repo.Namespace). - WithField("name", repo.Name). - WithField("cron", cronjob.Name) - logger.Debugln("api: cannot find commit") - render.NotFound(w, err) - return - } - - hook := &core.Hook{ - Trigger: core.TriggerCron, - Event: core.EventCron, - Link: commit.Link, - Timestamp: commit.Author.Date, - Message: commit.Message, - After: commit.Sha, - Ref: fmt.Sprintf("refs/heads/%s", cronjob.Branch), - Target: cronjob.Branch, - Author: commit.Author.Login, - AuthorName: commit.Author.Name, - AuthorEmail: commit.Author.Email, - AuthorAvatar: commit.Author.Avatar, - Cron: cronjob.Name, - Sender: commit.Author.Login, - } - - build, err := trigger.Trigger(context.Background(), repo, hook) - if err != nil { - logger := logrus.WithError(err). - WithField("namespace", repo.Namespace). - WithField("name", repo.Name). - WithField("cron", cronjob.Name) - logger.Debugln("api: cannot trigger cron") - render.InternalError(w, err) - return - } - - render.JSON(w, build, 200) - } -} diff --git a/handler/api/repos/crons/exec_test.go b/handler/api/repos/crons/exec_test.go deleted file mode 100644 index 403d96f1ac..0000000000 --- a/handler/api/repos/crons/exec_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons diff --git a/handler/api/repos/crons/find.go b/handler/api/repos/crons/find.go deleted file mode 100644 index c98cc37f74..0000000000 --- a/handler/api/repos/crons/find.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package crons - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleFind returns an http.HandlerFunc that writes json-encoded -// cronjob details to the response body. -func HandleFind( - repos core.RepositoryStore, - crons core.CronStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - cron = chi.URLParam(r, "cron") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - cronjob, err := crons.FindName(r.Context(), repo.ID, cron) - if err != nil { - render.NotFound(w, err) - return - } - render.JSON(w, cronjob, 200) - } -} diff --git a/handler/api/repos/crons/find_test.go b/handler/api/repos/crons/find_test.go deleted file mode 100644 index 942789f1f5..0000000000 --- a/handler/api/repos/crons/find_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().FindName(gomock.Any(), dummyCronRepo.ID, dummyCron.Name).Return(dummyCron, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.Cron{}, dummyCron - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleFind_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleFind_CronNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().FindName(gomock.Any(), dummyCronRepo.ID, dummyCron.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/crons/list.go b/handler/api/repos/crons/list.go deleted file mode 100644 index 06af147e8a..0000000000 --- a/handler/api/repos/crons/list.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleList returns an http.HandlerFunc that writes a json-encoded -// list of cron jobs to the response body. -func HandleList( - repos core.RepositoryStore, - crons core.CronStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - list, err := crons.List(r.Context(), repo.ID) - if err != nil { - render.NotFound(w, err) - return - } - render.JSON(w, list, 200) - } -} diff --git a/handler/api/repos/crons/list_test.go b/handler/api/repos/crons/list_test.go deleted file mode 100644 index 7762cd52cf..0000000000 --- a/handler/api/repos/crons/list_test.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var ( - dummyCronRepo = &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - } - - dummyCron = &core.Cron{ - RepoID: 1, - Event: core.EventPush, - Name: "nightly", - Expr: "* * * * * *", - Next: 0, - Branch: "master", - } - - dummyCronList = []*core.Cron{ - dummyCron, - } -) - -func TestHandleList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().List(gomock.Any(), dummyCronRepo.ID).Return(dummyCronList, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.Cron{}, dummyCronList - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleList_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleList_CronListErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().List(gomock.Any(), dummyCronRepo.ID).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/crons/none.go b/handler/api/repos/crons/none.go deleted file mode 100644 index fe375541bd..0000000000 --- a/handler/api/repos/crons/none.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package crons - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -func HandleCreate(core.RepositoryStore, core.CronStore) http.HandlerFunc { - return notImplemented -} - -func HandleUpdate(core.RepositoryStore, core.CronStore) http.HandlerFunc { - return notImplemented -} - -func HandleDelete(core.RepositoryStore, core.CronStore) http.HandlerFunc { - return notImplemented -} - -func HandleFind(core.RepositoryStore, core.CronStore) http.HandlerFunc { - return notImplemented -} - -func HandleList(core.RepositoryStore, core.CronStore) http.HandlerFunc { - return notImplemented -} - -func HandleExec(core.UserStore, core.RepositoryStore, core.CronStore, - core.CommitService, core.Triggerer) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/repos/crons/update.go b/handler/api/repos/crons/update.go deleted file mode 100644 index 4cf49baa11..0000000000 --- a/handler/api/repos/crons/update.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -type cronUpdate struct { - Branch *string `json:"branch"` - Target *string `json:"target"` - Disabled *bool `json:"disabled"` -} - -// HandleUpdate returns an http.HandlerFunc that processes http -// requests to enable or disable a cron job. -func HandleUpdate( - repos core.RepositoryStore, - crons core.CronStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - cron = chi.URLParam(r, "cron") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - cronjob, err := crons.FindName(r.Context(), repo.ID, cron) - if err != nil { - render.NotFound(w, err) - return - } - - in := new(cronUpdate) - json.NewDecoder(r.Body).Decode(in) - if in.Branch != nil { - cronjob.Branch = *in.Branch - } - if in.Target != nil { - cronjob.Target = *in.Target - } - if in.Disabled != nil { - cronjob.Disabled = *in.Disabled - } - - err = crons.Update(r.Context(), cronjob) - if err != nil { - render.InternalError(w, err) - return - } - render.JSON(w, cronjob, 200) - } -} diff --git a/handler/api/repos/crons/update_test.go b/handler/api/repos/crons/update_test.go deleted file mode 100644 index 0160458358..0000000000 --- a/handler/api/repos/crons/update_test.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package crons - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleUpdate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockCron := new(core.Cron) - *mockCron = *dummyCron - mockCron.Disabled = false - mockCron.Branch = "develop" - mockCron.Target = "staging" - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().FindName(gomock.Any(), dummyCronRepo.ID, mockCron.Name).Return(mockCron, nil) - crons.EXPECT().Update(gomock.Any(), mockCron).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(mockCron) - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.Cron{}, mockCron - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_CronNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().FindName(gomock.Any(), dummyCronRepo.ID, dummyCron.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_UpdateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummyCronRepo.Namespace, dummyCronRepo.Name).Return(dummyCronRepo, nil) - - crons := mock.NewMockCronStore(controller) - crons.EXPECT().FindName(gomock.Any(), dummyCronRepo.ID, dummyCron.Name).Return(dummyCron, nil) - crons.EXPECT().Update(gomock.Any(), dummyCron).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("cron", "nightly") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos, crons).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/disable.go b/handler/api/repos/disable.go deleted file mode 100644 index d449c027e3..0000000000 --- a/handler/api/repos/disable.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleDisable returns an http.HandlerFunc that processes http -// requests to disable a repository in the system. -func HandleDisable( - repos core.RepositoryStore, - sender core.WebhookSender, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - owner = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - repo, err := repos.FindName(r.Context(), owner, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Debugln("api: repository not found") - return - } - repo.Active = false - err = repos.Update(r.Context(), repo) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Warnln("api: cannot update repository") - return - } - - action := core.WebhookActionDisabled - if r.FormValue("remove") == "true" { - action = core.WebhookActionDeleted - err = repos.Delete(r.Context(), repo) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Warnln("api: cannot delete repository") - return - } - } - - err = sender.Send(r.Context(), &core.WebhookData{ - Event: core.WebhookEventRepo, - Action: action, - Repo: repo, - }) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Warnln("api: cannot send webhook") - } - - render.JSON(w, repo, 200) - } -} diff --git a/handler/api/repos/disable_test.go b/handler/api/repos/disable_test.go deleted file mode 100644 index 7f4f687a59..0000000000 --- a/handler/api/repos/disable_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package repos - -import ( - "encoding/json" - "io" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestDisable(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Active: true, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), repo.Name).Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(nil) - - // a failed webhook should result in a warning message in the - // logs, but should not cause the endpoint to error. - webhook := mock.NewMockWebhookSender(controller) - webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(io.EOF) - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/api/repos/octocat/hello-world", nil) - - router := chi.NewRouter() - router.Delete("/api/repos/{owner}/{name}", HandleDisable(repos, webhook)) - router.ServeHTTP(w, r) - - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - if got, want := repo.Active, false; got != want { - t.Errorf("Want repository activate %v, got %v", want, got) - } - - got, want := new(core.Repository), repo - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestDisable_NotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(nil, errors.ErrNotFound) - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/api/repos/octocat/hello-world", nil) - - router := chi.NewRouter() - router.Delete("/api/repos/{owner}/{name}", HandleDisable(repos, nil)) - router.ServeHTTP(w, r) - - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestDisable_InternalError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Active: false, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), repo.Name).Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(errors.ErrNotFound) - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/api/repos/octocat/hello-world", nil) - - router := chi.NewRouter() - router.Delete("/api/repos/{owner}/{name}", HandleDisable(repos, nil)) - router.ServeHTTP(w, r) - - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestDelete(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Active: true, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), gomock.Any(), repo.Name).Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(nil) - repos.EXPECT().Delete(gomock.Any(), repo).Return(nil) - - // a failed webhook should result in a warning message in the - // logs, but should not cause the endpoint to error. - webhook := mock.NewMockWebhookSender(controller) - webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(io.EOF) - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/api/repos/octocat/hello-world?remove=true", nil) - - router := chi.NewRouter() - router.Delete("/api/repos/{owner}/{name}", HandleDisable(repos, webhook)) - router.ServeHTTP(w, r) - - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Repository), repo - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/enable.go b/handler/api/repos/enable.go deleted file mode 100644 index 2564414aa1..0000000000 --- a/handler/api/repos/enable.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "net/http" - "os" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" - - "github.com/dchest/uniuri" - "github.com/go-chi/chi" -) - -// FEATURE FLAG enables a static secret value used to sign -// incoming requests routed through a proxy. This was implemented -// based on feedback from @chiraggadasc and should not be -// removed until we have a permanent solution in place. -var staticSigner = os.Getenv("DRONE_FEATURE_SERVER_PROXY_SECRET") - -// HandleEnable returns an http.HandlerFunc that processes http -// requests to enable a repository in the system. -func HandleEnable( - hooks core.HookService, - repos core.RepositoryStore, - sender core.WebhookSender, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - owner = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - user, _ := request.UserFrom(r.Context()) - repo, err := repos.FindName(r.Context(), owner, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Debugln("api: repository not found") - return - } - repo.Active = true - repo.UserID = user.ID - - if repo.Config == "" { - repo.Config = ".drone.yml" - } - if repo.Signer == "" { - repo.Signer = uniuri.NewLen(32) - } - if repo.Secret == "" { - repo.Secret = uniuri.NewLen(32) - } - if repo.Timeout == 0 { - repo.Timeout = 60 - } - - if staticSigner != "" { - repo.Signer = staticSigner - } - - err = hooks.Create(r.Context(), user, repo) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Debugln("api: cannot create or update hook") - return - } - - err = repos.Activate(r.Context(), repo) - if err == core.ErrRepoLimit { - render.ErrorCode(w, err, 402) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Errorln("api: cannot activate repository") - return - } - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Debugln("api: cannot activate repository") - return - } - - err = sender.Send(r.Context(), &core.WebhookData{ - Event: core.WebhookEventRepo, - Action: core.WebhookActionEnabled, - User: user, - Repo: repo, - }) - if err != nil { - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Warnln("api: cannot send webhook") - } - - render.JSON(w, repo, 200) - } -} diff --git a/handler/api/repos/enable_test.go b/handler/api/repos/enable_test.go deleted file mode 100644 index 88689b54fe..0000000000 --- a/handler/api/repos/enable_test.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package repos - -import ( - "context" - "encoding/json" - "io" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -func TestEnable(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - - service := mock.NewMockHookService(controller) - service.EXPECT().Create(gomock.Any(), gomock.Any(), repo).Return(nil) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), repo.Namespace, repo.Name).Return(repo, nil) - repos.EXPECT().Activate(gomock.Any(), repo).Return(nil) - - // a failed webhook should result in a warning message in the - // logs, but should not cause the endpoint to error. - webhook := mock.NewMockWebhookSender(controller) - webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(io.EOF) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), &core.User{ID: 1}), chi.RouteCtxKey, c), - ) - - HandleEnable(service, repos, webhook)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - if got, want := repo.Active, true; got != want { - t.Errorf("Want repository activate %v, got %v", want, got) - } - - got, want := new(core.Repository), repo - json.NewDecoder(w.Body).Decode(got) - diff := cmp.Diff(got, want, cmpopts.IgnoreFields(core.Repository{}, "Secret", "Signer")) - if diff != "" { - t.Errorf(diff) - } -} - -func TestEnable_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), mockRepo.Namespace, mockRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleEnable(nil, repos, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestEnable_HookError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Active: false, - } - - service := mock.NewMockHookService(controller) - service.EXPECT().Create(gomock.Any(), gomock.Any(), repo).Return(io.EOF) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), repo.Namespace, repo.Name).Return(repo, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), &core.User{ID: 1}), chi.RouteCtxKey, c), - ) - - HandleEnable(service, repos, nil)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestEnable_ActivateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - } - - service := mock.NewMockHookService(controller) - service.EXPECT().Create(gomock.Any(), gomock.Any(), repo).Return(nil) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), repo.Namespace, repo.Name).Return(repo, nil) - repos.EXPECT().Activate(gomock.Any(), repo).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(request.WithUser(r.Context(), &core.User{ID: 1}), chi.RouteCtxKey, c), - ) - - HandleEnable(service, repos, nil)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/encrypt/encrypt.go b/handler/api/repos/encrypt/encrypt.go deleted file mode 100644 index 3ad156a7fa..0000000000 --- a/handler/api/repos/encrypt/encrypt.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package encrypt - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/base64" - "encoding/json" - "io" - "net/http" - - "github.com/drone/drone-go/drone" - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/go-chi/chi" -) - -type respEncrypted struct { - Data string `json:"data"` -} - -// Handler returns an http.HandlerFunc that processes http -// requests to create an encrypted secret. -func Handler(repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - namespace := chi.URLParam(r, "owner") - name := chi.URLParam(r, "name") - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - - in := new(drone.Secret) - err = json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - // the secret is encrypted with a per-repository 256-bit - // key. If the key is missing or malformed we should - // return an error to the client. - encrypted, err := encrypt([]byte(in.Data), []byte(repo.Secret)) - if err != nil { - render.InternalError(w, err) - return - } - - // the encrypted secret is embedded in the yaml - // configuration file and is json-encoded for - // inclusion as a !binary attribute. - encoded := base64.StdEncoding.EncodeToString(encrypted) - - render.JSON(w, &respEncrypted{Data: encoded}, 200) - } -} - -func encrypt(plaintext, key []byte) (ciphertext []byte, err error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - nonce := make([]byte, gcm.NonceSize()) - _, err = io.ReadFull(rand.Reader, nonce) - if err != nil { - return nil, err - } - - return gcm.Seal(nonce, nonce, plaintext, nil), nil -} diff --git a/handler/api/repos/encrypt/encrypt_test.go b/handler/api/repos/encrypt/encrypt_test.go deleted file mode 100644 index 7e5445a872..0000000000 --- a/handler/api/repos/encrypt/encrypt_test.go +++ /dev/null @@ -1 +0,0 @@ -package encrypt diff --git a/handler/api/repos/find.go b/handler/api/repos/find.go deleted file mode 100644 index 79171a5dc4..0000000000 --- a/handler/api/repos/find.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "net/http" - - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" -) - -// HandleFind returns an http.HandlerFunc that writes the -// json-encoded repository details to the response body. -func HandleFind() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - repo, _ := request.RepoFrom(ctx) - perm, _ := request.PermFrom(ctx) - repo.Perms = perm - render.JSON(w, repo, 200) - } -} diff --git a/handler/api/repos/find_test.go b/handler/api/repos/find_test.go deleted file mode 100644 index cded024eb2..0000000000 --- a/handler/api/repos/find_test.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package repos - -import ( - "context" - "encoding/json" - "io/ioutil" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/core" - "github.com/sirupsen/logrus" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -var ( - mockRepo = &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Counter: 42, - Branch: "master", - } - - mockRepos = []*core.Repository{ - { - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - }, - { - ID: 1, - Namespace: "octocat", - Name: "spoon-knife", - Slug: "octocat/spoon-knife", - }, - } -) - -func TestFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil) - r = r.WithContext(request.WithRepo( - context.Background(), mockRepo, - )) - - router := chi.NewRouter() - router.Get("/api/repos/{owner}/{name}", HandleFind()) - router.ServeHTTP(w, r) - - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Repository), mockRepo - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/repair.go b/handler/api/repos/repair.go deleted file mode 100644 index 8c94078ebc..0000000000 --- a/handler/api/repos/repair.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleRepair returns an http.HandlerFunc that processes http -// requests to repair the repository hooks and sync the repository -// details. -func HandleRepair( - hooks core.HookService, - repoz core.RepositoryService, - repos core.RepositoryStore, - users core.UserStore, - link string, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - owner = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - - repo, err := repos.FindName(r.Context(), owner, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Debugln("api: repository not found") - return - } - - user, err := users.Find(r.Context(), repo.UserID) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Warnln("api: cannot find repository owner") - return - } - - remote, err := repoz.Find(r.Context(), user, repo.Slug) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Warnln("api: remote repository not found") - return - } - - repo.Branch = remote.Branch - repo.HTTPURL = remote.HTTPURL - repo.Private = remote.Private - repo.SSHURL = remote.SSHURL - - // the gitea and gogs repository endpoints do not - // return the http url, so we need to ensure we do - // not replace the existing value with a zero value. - if remote.Link != "" { - repo.Link = remote.Link - } - - err = repos.Update(r.Context(), repo) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Warnln("api: cannot chown repository") - return - } - - err = hooks.Create(r.Context(), user, repo) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("namespace", owner). - WithField("name", name). - Debugln("api: cannot create or update hook") - return - } - - render.JSON(w, repo, 200) - } -} diff --git a/handler/api/repos/repair_test.go b/handler/api/repos/repair_test.go deleted file mode 100644 index aacddd1b95..0000000000 --- a/handler/api/repos/repair_test.go +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. -package repos - -import ( - "context" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - "github.com/drone/drone/core" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestRepair(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ - ID: 1, - } - repo := &core.Repository{ - ID: 1, - UserID: 1, - Private: true, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - remoteRepo := &core.Repository{ - Branch: "master", - Private: false, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - - checkRepair := func(_ context.Context, updated *core.Repository) error { - if got, want := updated.Branch, remoteRepo.Branch; got != want { - t.Errorf("Want repository Branch updated to %s, got %s", want, got) - } - if got, want := updated.Private, remoteRepo.Private; got != want { - t.Errorf("Want repository Private updated to %v, got %v", want, got) - } - if got, want := updated.HTTPURL, remoteRepo.HTTPURL; got != want { - t.Errorf("Want repository Clone updated to %s, got %s", want, got) - } - if got, want := updated.SSHURL, remoteRepo.SSHURL; got != want { - t.Errorf("Want repository CloneSSH updated to %s, got %s", want, got) - } - if got, want := updated.Link, remoteRepo.Link; got != want { - t.Errorf("Want repository Link updated to %s, got %s", want, got) - } - return nil - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().Find(gomock.Any(), repo.UserID).Return(user, nil) - - hooks := mock.NewMockHookService(controller) - hooks.EXPECT().Create(gomock.Any(), gomock.Any(), repo).Return(nil) - - repoz := mock.NewMockRepositoryService(controller) - repoz.EXPECT().Find(gomock.Any(), user, repo.Slug).Return(remoteRepo, nil) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(nil).Do(checkRepair) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleRepair(hooks, repoz, repos, users, "https://company.drone.io")(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Repository), &core.Repository{ - ID: 1, - UserID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Branch: "master", - Private: false, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found error is returned -// from the http.Handler if the named repository cannot be -// found in the local database. -func TestRepair_LocalRepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleRepair(nil, nil, repos, nil, "https://company.drone.io")(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found error is returned -// from the http.Handler if the remote repository cannot be -// found (e.g. in GitHub). -func TestRepair_RemoteRepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ - ID: 1, - } - repo := &core.Repository{ - ID: 1, - UserID: 1, - Private: true, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - - repoz := mock.NewMockRepositoryService(controller) - repoz.EXPECT().Find(gomock.Any(), user, repo.Slug).Return(nil, errors.ErrNotFound) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - - users := mock.NewMockUserStore(controller) - users.EXPECT().Find(gomock.Any(), repo.UserID).Return(user, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleRepair(nil, repoz, repos, users, "https://company.drone.io")(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found error is returned -// from the http.Handler if the repository owner cannot be -// found in the database. -func TestRepair_OwnerNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - UserID: 1, - Private: true, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().Find(gomock.Any(), repo.UserID).Return(nil, errors.ErrNotFound) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleRepair(nil, nil, repos, users, "https://company.drone.io")(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 500 internal server error is -// returned from the http.Handler if the repository updates -// fail to persist in the datastore. -func TestRepair_CannotUpdate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ - ID: 1, - } - repo := &core.Repository{ - ID: 1, - UserID: 1, - Private: true, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - remoteRepo := &core.Repository{ - Branch: "master", - Private: false, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - - repoz := mock.NewMockRepositoryService(controller) - repoz.EXPECT().Find(gomock.Any(), user, repo.Slug).Return(remoteRepo, nil) - - users := mock.NewMockUserStore(controller) - users.EXPECT().Find(gomock.Any(), repo.UserID).Return(user, nil) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleRepair(nil, repoz, repos, users, "https://company.drone.io")(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 500 internal server error is -// returned from the http.Handler if the hook cannot be -// added or replaced in the remote system (e.g. github). -func TestRepair_CannotReplaceHook(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ - ID: 1, - } - repo := &core.Repository{ - ID: 1, - UserID: 1, - Private: true, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - } - remoteRepo := &core.Repository{ - Branch: "master", - Private: false, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - - hooks := mock.NewMockHookService(controller) - hooks.EXPECT().Create(gomock.Any(), gomock.Any(), repo).Return(errors.ErrNotFound) - - repoz := mock.NewMockRepositoryService(controller) - repoz.EXPECT().Find(gomock.Any(), user, repo.Slug).Return(remoteRepo, nil) - - users := mock.NewMockUserStore(controller) - users.EXPECT().Find(gomock.Any(), repo.UserID).Return(user, nil) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleRepair(hooks, repoz, repos, users, "https://company.drone.io")(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/secrets/create.go b/handler/api/repos/secrets/create.go deleted file mode 100644 index 809b883b19..0000000000 --- a/handler/api/repos/secrets/create.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -type secretInput struct { - Type string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - PullRequest bool `json:"pull_request"` - PullRequestPush bool `json:"pull_request_push"` -} - -// HandleCreate returns an http.HandlerFunc that processes http -// requests to create a new secret. -func HandleCreate( - repos core.RepositoryStore, - secrets core.SecretStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - in := new(secretInput) - err = json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - s := &core.Secret{ - RepoID: repo.ID, - Name: in.Name, - Data: in.Data, - PullRequest: in.PullRequest, - PullRequestPush: in.PullRequestPush, - } - - err = s.Validate() - if err != nil { - render.BadRequest(w, err) - return - } - - err = secrets.Create(r.Context(), s) - if err != nil { - render.InternalError(w, err) - return - } - - s = s.Copy() - render.JSON(w, s, 200) - } -} diff --git a/handler/api/repos/secrets/create_test.go b/handler/api/repos/secrets/create_test.go deleted file mode 100644 index 5de51d98b9..0000000000 --- a/handler/api/repos/secrets/create_test.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleCreate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummySecret) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.Secret{}, dummySecretScrubbed - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_ValidationError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{Name: "", Data: "pa55word"}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "Invalid Secret Name"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_CreateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().Create(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummySecret) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/secrets/delete.go b/handler/api/repos/secrets/delete.go deleted file mode 100644 index a88878b7b0..0000000000 --- a/handler/api/repos/secrets/delete.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that processes http -// requests to delete the secret. -func HandleDelete( - repos core.RepositoryStore, - secrets core.SecretStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - secret = chi.URLParam(r, "secret") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - s, err := secrets.FindName(r.Context(), repo.ID, secret) - if err != nil { - render.NotFound(w, err) - return - } - - err = secrets.Delete(r.Context(), s) - if err != nil { - render.InternalError(w, err) - return - } - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/repos/secrets/delete_test.go b/handler/api/repos/secrets/delete_test.go deleted file mode 100644 index 67a982f0c5..0000000000 --- a/handler/api/repos/secrets/delete_test.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleDelete(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecretRepo.ID, dummySecret.Name).Return(dummySecret, nil) - secrets.EXPECT().Delete(gomock.Any(), dummySecret).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNoContent; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleDelete_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleDelete_SecretNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecretRepo.ID, dummySecret.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleDelete_DeleteError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecretRepo.ID, dummySecret.Name).Return(dummySecret, nil) - secrets.EXPECT().Delete(gomock.Any(), dummySecret).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/secrets/find.go b/handler/api/repos/secrets/find.go deleted file mode 100644 index dab6f1ebbf..0000000000 --- a/handler/api/repos/secrets/find.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package secrets - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleFind returns an http.HandlerFunc that writes json-encoded -// secret details to the response body. -func HandleFind( - repos core.RepositoryStore, - secrets core.SecretStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - secret = chi.URLParam(r, "secret") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - result, err := secrets.FindName(r.Context(), repo.ID, secret) - if err != nil { - render.NotFound(w, err) - return - } - safe := result.Copy() - render.JSON(w, safe, 200) - } -} diff --git a/handler/api/repos/secrets/find_test.go b/handler/api/repos/secrets/find_test.go deleted file mode 100644 index dbf8c9defd..0000000000 --- a/handler/api/repos/secrets/find_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecretRepo.ID, dummySecret.Name).Return(dummySecret, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.Secret{}, dummySecretScrubbed - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleFind_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleFind_SecretNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecretRepo.ID, dummySecret.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/secrets/list.go b/handler/api/repos/secrets/list.go deleted file mode 100644 index ebdcf4bfe9..0000000000 --- a/handler/api/repos/secrets/list.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleList returns an http.HandlerFunc that writes a json-encoded -// list of secrets to the response body. -func HandleList( - repos core.RepositoryStore, - secrets core.SecretStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - list, err := secrets.List(r.Context(), repo.ID) - if err != nil { - render.NotFound(w, err) - return - } - // the secret list is copied and the secret value is - // removed from the response. - secrets := []*core.Secret{} - for _, secret := range list { - secrets = append(secrets, secret.Copy()) - } - render.JSON(w, secrets, 200) - } -} diff --git a/handler/api/repos/secrets/list_test.go b/handler/api/repos/secrets/list_test.go deleted file mode 100644 index cf1b9b6a3a..0000000000 --- a/handler/api/repos/secrets/list_test.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var ( - dummySecretRepo = &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - } - - dummySecret = &core.Secret{ - RepoID: 1, - Name: "github_password", - Data: "pa55word", - } - - dummySecretScrubbed = &core.Secret{ - RepoID: 1, - Name: "github_password", - Data: "", - } - - dummySecretList = []*core.Secret{ - dummySecret, - } - - dummySecretListScrubbed = []*core.Secret{ - dummySecretScrubbed, - } -) - -// -// HandleList -// - -func TestHandleList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().List(gomock.Any(), dummySecretRepo.ID).Return(dummySecretList, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.Secret{}, dummySecretListScrubbed - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleList_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleList_SecretListErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().List(gomock.Any(), dummySecretRepo.ID).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/secrets/none.go b/handler/api/repos/secrets/none.go deleted file mode 100644 index 894e070a5b..0000000000 --- a/handler/api/repos/secrets/none.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package secrets - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -func HandleCreate(core.RepositoryStore, core.SecretStore) http.HandlerFunc { - return notImplemented -} - -func HandleUpdate(core.RepositoryStore, core.SecretStore) http.HandlerFunc { - return notImplemented -} - -func HandleDelete(core.RepositoryStore, core.SecretStore) http.HandlerFunc { - return notImplemented -} - -func HandleFind(core.RepositoryStore, core.SecretStore) http.HandlerFunc { - return notImplemented -} - -func HandleList(core.RepositoryStore, core.SecretStore) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/repos/secrets/update.go b/handler/api/repos/secrets/update.go deleted file mode 100644 index b1e56e6033..0000000000 --- a/handler/api/repos/secrets/update.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -type secretUpdate struct { - Data *string `json:"data"` - PullRequest *bool `json:"pull_request"` - PullRequestPush *bool `json:"pull_request_push"` -} - -// HandleUpdate returns an http.HandlerFunc that processes http -// requests to update a secret. -func HandleUpdate( - repos core.RepositoryStore, - secrets core.SecretStore, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - secret = chi.URLParam(r, "secret") - ) - - in := new(secretUpdate) - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - - s, err := secrets.FindName(r.Context(), repo.ID, secret) - if err != nil { - render.NotFound(w, err) - return - } - - if in.Data != nil { - s.Data = *in.Data - } - if in.PullRequest != nil { - s.PullRequest = *in.PullRequest - } - if in.PullRequestPush != nil { - s.PullRequestPush = *in.PullRequestPush - } - - err = s.Validate() - if err != nil { - render.BadRequest(w, err) - return - } - - err = secrets.Update(r.Context(), s) - if err != nil { - render.InternalError(w, err) - return - } - - s = s.Copy() - render.JSON(w, s, 200) - } -} diff --git a/handler/api/repos/secrets/update_test.go b/handler/api/repos/secrets/update_test.go deleted file mode 100644 index 2ff79f34b8..0000000000 --- a/handler/api/repos/secrets/update_test.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleUpdate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecretRepo.ID, dummySecret.Name).Return(dummySecret, nil) - secrets.EXPECT().Update(gomock.Any(), gomock.Any()).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummySecret) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Secret), dummySecretScrubbed - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_ValidationError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecretRepo.ID, dummySecret.Name).Return(&core.Secret{Name: "github_password"}, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{Data: ""}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "Invalid Secret Value"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(nil, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos, nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_SecretNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecretRepo.ID, dummySecret.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_UpdateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), dummySecretRepo.Namespace, dummySecretRepo.Name).Return(dummySecretRepo, nil) - - secrets := mock.NewMockSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecretRepo.ID, dummySecret.Name).Return(&core.Secret{Name: "github_password"}, nil) - secrets.EXPECT().Update(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("secret", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{Data: "password"}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos, secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/repos/sign/sign.go b/handler/api/repos/sign/sign.go deleted file mode 100644 index ff070acc96..0000000000 --- a/handler/api/repos/sign/sign.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sign - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone-yaml/yaml/signer" - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -type payload struct { - Data string `json:"data"` -} - -// HandleSign returns an http.HandlerFunc that processes http -// requests to sign a pipeline configuration file. -func HandleSign(repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - ) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - - in := new(payload) - err = json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - k := []byte(repo.Secret) - d := []byte(in.Data) - out, err := signer.Sign(d, k) - if err != nil { - render.InternalError(w, err) - return - } - - render.JSON(w, &payload{Data: out}, 200) - } -} diff --git a/handler/api/repos/sign/sign_test.go b/handler/api/repos/sign/sign_test.go deleted file mode 100644 index e5166fce27..0000000000 --- a/handler/api/repos/sign/sign_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package sign diff --git a/handler/api/repos/update.go b/handler/api/repos/update.go deleted file mode 100644 index 8736c7c1b3..0000000000 --- a/handler/api/repos/update.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -type ( - repositoryInput struct { - Visibility *string `json:"visibility"` - Config *string `json:"config_path"` - Trusted *bool `json:"trusted"` - Protected *bool `json:"protected"` - IgnoreForks *bool `json:"ignore_forks"` - IgnorePulls *bool `json:"ignore_pull_requests"` - CancelPulls *bool `json:"auto_cancel_pull_requests"` - CancelPush *bool `json:"auto_cancel_pushes"` - CancelRunning *bool `json:"auto_cancel_running"` - Timeout *int64 `json:"timeout"` - Throttle *int64 `json:"throttle"` - Counter *int64 `json:"counter"` - } -) - -// HandleUpdate returns an http.HandlerFunc that processes http -// requests to update the repository details. -func HandleUpdate(repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - owner = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - slug = owner + "/" + name - ) - user, _ := request.UserFrom(r.Context()) - - repo, err := repos.FindName(r.Context(), owner, name) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("repository", slug). - Debugln("api: repository not found") - return - } - - in := new(repositoryInput) - err = json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - logger.FromRequest(r). - WithError(err). - WithField("repository", slug). - Debugln("api: cannot unmarshal json input") - return - } - - if in.Visibility != nil { - repo.Visibility = *in.Visibility - } - if in.Config != nil { - repo.Config = *in.Config - } - if in.Protected != nil { - repo.Protected = *in.Protected - } - if in.IgnoreForks != nil { - repo.IgnoreForks = *in.IgnoreForks - } - if in.IgnorePulls != nil { - repo.IgnorePulls = *in.IgnorePulls - } - if in.CancelPulls != nil { - repo.CancelPulls = *in.CancelPulls - } - if in.CancelPush != nil { - repo.CancelPush = *in.CancelPush - } - if in.CancelRunning != nil { - repo.CancelRunning = *in.CancelRunning - } - - // - // system administrator only - // - if user != nil && user.Admin { - if in.Trusted != nil { - repo.Trusted = *in.Trusted - } - if in.Timeout != nil { - repo.Timeout = *in.Timeout - } - if in.Throttle != nil { - repo.Throttle = *in.Throttle - } - if in.Counter != nil { - repo.Counter = *in.Counter - } - } - - // // right now the only repository field that a user - // // can update is the visibility field. - // if govalidator.IsIn(in.Visibility, - // core.VisibilityInternal, - // core.VisibilityPrivate, - // core.VisibilityPublic, - // ) { - // repo.Visibility = in.Visibility - // } - - err = repos.Update(r.Context(), repo) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("repository", slug). - Warnln("api: cannot update repository") - return - } - - render.JSON(w, repo, 200) - } -} diff --git a/handler/api/repos/update_test.go b/handler/api/repos/update_test.go deleted file mode 100644 index 0d221e1932..0000000000 --- a/handler/api/repos/update_test.go +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package repos - -import ( - "bytes" - "context" - "encoding/json" - "net/http/httptest" - "strings" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestUpdate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - UserID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Branch: "master", - Private: false, - Visibility: core.VisibilityPrivate, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - - repoInput := &core.Repository{ - Visibility: core.VisibilityPublic, - } - - checkUpdate := func(_ context.Context, updated *core.Repository) error { - if got, want := updated.Visibility, core.VisibilityPublic; got != want { - t.Errorf("Want repository visibility updated to %s, got %s", want, got) - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(nil).Do(checkUpdate) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(repoInput) - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Repository), &core.Repository{ - ID: 1, - UserID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Branch: "master", - Private: false, - Visibility: core.VisibilityPublic, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 404 not found error is returned -// from the http.Handler if the named repository cannot be -// found in the database. -func TestUpdate_RepoNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 400 bad request error is -// returned from the http.Handler if the request body -// is invalid json. -func TestUpdate_InvalidInput(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - UserID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Branch: "master", - Private: false, - Visibility: core.VisibilityPrivate, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", strings.NewReader("")) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.New("EOF") - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that a 500 internal server error is -// returned from the http.Handler if the repository updates -// cannot be persisted to the database. -func TestUpdate_UpdateFailed(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - UserID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Branch: "master", - Private: false, - Visibility: core.VisibilityPrivate, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - - repoInput := &core.Repository{ - Visibility: core.VisibilityPublic, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(repoInput) - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestUpdateAutoCancelRunning(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - repo := &core.Repository{ - ID: 1, - UserID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Branch: "master", - Private: false, - Visibility: core.VisibilityPrivate, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - CancelRunning: false, - } - - repoInput := &core.Repository{ - CancelRunning: true, - Visibility: core.VisibilityPrivate, - } - - shouldBeValue := true - checkUpdate := func(_ context.Context, updated *core.Repository) error { - if got, want := updated.CancelRunning, shouldBeValue; got != want { - t.Errorf("Want repository visibility updated to %v, got %v", want, got) - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(repo, nil) - repos.EXPECT().Update(gomock.Any(), repo).Return(nil).Do(checkUpdate) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(repoInput) - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - r = r.WithContext( - context.WithValue(r.Context(), chi.RouteCtxKey, c), - ) - - HandleUpdate(repos)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Repository), &core.Repository{ - ID: 1, - UserID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Branch: "master", - Private: false, - Visibility: core.VisibilityPrivate, - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - CancelRunning: true, - } - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/request/context.go b/handler/api/request/context.go deleted file mode 100644 index a1fe086a4c..0000000000 --- a/handler/api/request/context.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package request - -// https://github.com/kubernetes/apiserver/blob/master/pkg/endpoints/request/context.go - -import ( - "context" - - "github.com/drone/drone/core" -) - -type key int - -const ( - userKey key = iota - permKey - repoKey -) - -// WithUser returns a copy of parent in which the user value is set -func WithUser(parent context.Context, user *core.User) context.Context { - return context.WithValue(parent, userKey, user) -} - -// UserFrom returns the value of the user key on the ctx -func UserFrom(ctx context.Context) (*core.User, bool) { - user, ok := ctx.Value(userKey).(*core.User) - return user, ok -} - -// WithPerm returns a copy of parent in which the perm value is set -func WithPerm(parent context.Context, perm *core.Perm) context.Context { - return context.WithValue(parent, permKey, perm) -} - -// PermFrom returns the value of the perm key on the ctx -func PermFrom(ctx context.Context) (*core.Perm, bool) { - perm, ok := ctx.Value(permKey).(*core.Perm) - return perm, ok -} - -// WithRepo returns a copy of parent in which the repo value is set -func WithRepo(parent context.Context, repo *core.Repository) context.Context { - return context.WithValue(parent, repoKey, repo) -} - -// RepoFrom returns the value of the repo key on the ctx -func RepoFrom(ctx context.Context) (*core.Repository, bool) { - repo, ok := ctx.Value(repoKey).(*core.Repository) - return repo, ok -} diff --git a/handler/api/request/context_test.go b/handler/api/request/context_test.go deleted file mode 100644 index 87378f8661..0000000000 --- a/handler/api/request/context_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package request diff --git a/handler/api/secrets/all.go b/handler/api/secrets/all.go deleted file mode 100644 index f882e5c763..0000000000 --- a/handler/api/secrets/all.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -// HandleAll returns an http.HandlerFunc that writes a json-encoded -// list of secrets to the response body. -func HandleAll(secrets core.GlobalSecretStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - list, err := secrets.ListAll(r.Context()) - if err != nil { - render.NotFound(w, err) - return - } - // the secret list is copied and the secret value is - // removed from the response. - secrets := []*core.Secret{} - for _, secret := range list { - secrets = append(secrets, secret.Copy()) - } - render.JSON(w, secrets, 200) - } -} diff --git a/handler/api/secrets/all_test.go b/handler/api/secrets/all_test.go deleted file mode 100644 index 1f8e7633be..0000000000 --- a/handler/api/secrets/all_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleAll(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().ListAll(gomock.Any()).Return(dummySecretList, nil) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - HandleAll(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.Secret{}, dummySecretListScrubbed - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleAll_SecretListErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().ListAll(gomock.Any()).Return(nil, errors.ErrNotFound) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - HandleAll(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/secrets/create.go b/handler/api/secrets/create.go deleted file mode 100644 index d80ab6fc07..0000000000 --- a/handler/api/secrets/create.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/go-chi/chi" -) - -type secretInput struct { - Type string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - PullRequest bool `json:"pull_request"` - PullRequestPush bool `json:"pull_request_push"` -} - -// HandleCreate returns an http.HandlerFunc that processes http -// requests to create a new secret. -func HandleCreate(secrets core.GlobalSecretStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - in := new(secretInput) - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - s := &core.Secret{ - Namespace: chi.URLParam(r, "namespace"), - Name: in.Name, - Data: in.Data, - PullRequest: in.PullRequest, - PullRequestPush: in.PullRequestPush, - } - - err = s.Validate() - if err != nil { - render.BadRequest(w, err) - return - } - - err = secrets.Create(r.Context(), s) - if err != nil { - render.InternalError(w, err) - return - } - - s = s.Copy() - render.JSON(w, s, 200) - } -} diff --git a/handler/api/secrets/create_test.go b/handler/api/secrets/create_test.go deleted file mode 100644 index ff6dbfec8c..0000000000 --- a/handler/api/secrets/create_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleCreate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummySecret) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.Secret{}, dummySecretScrubbed - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_ValidationError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{Name: "", Data: "pa55word"}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "Invalid Secret Name"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_CreateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().Create(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummySecret) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/secrets/delete.go b/handler/api/secrets/delete.go deleted file mode 100644 index 2948cec2ba..0000000000 --- a/handler/api/secrets/delete.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that processes http -// requests to delete the secret. -func HandleDelete(secrets core.GlobalSecretStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "namespace") - name = chi.URLParam(r, "name") - ) - s, err := secrets.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - err = secrets.Delete(r.Context(), s) - if err != nil { - render.InternalError(w, err) - return - } - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/secrets/delete_test.go b/handler/api/secrets/delete_test.go deleted file mode 100644 index 124ab87adf..0000000000 --- a/handler/api/secrets/delete_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleDelete(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecret.Namespace, dummySecret.Name).Return(dummySecret, nil) - secrets.EXPECT().Delete(gomock.Any(), dummySecret).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNoContent; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleDelete_SecretNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecret.Namespace, dummySecret.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleDelete_DeleteError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecret.Namespace, dummySecret.Name).Return(dummySecret, nil) - secrets.EXPECT().Delete(gomock.Any(), dummySecret).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/secrets/find.go b/handler/api/secrets/find.go deleted file mode 100644 index b44af80eba..0000000000 --- a/handler/api/secrets/find.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package secrets - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleFind returns an http.HandlerFunc that writes json-encoded -// secret details to the response body. -func HandleFind(secrets core.GlobalSecretStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "namespace") - name = chi.URLParam(r, "name") - ) - secret, err := secrets.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - safe := secret.Copy() - render.JSON(w, safe, 200) - } -} diff --git a/handler/api/secrets/find_test.go b/handler/api/secrets/find_test.go deleted file mode 100644 index 9a2d144460..0000000000 --- a/handler/api/secrets/find_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecret.Namespace, dummySecret.Name).Return(dummySecret, nil) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.Secret{}, dummySecretScrubbed - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleFind_SecretNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecret.Namespace, dummySecret.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/secrets/list.go b/handler/api/secrets/list.go deleted file mode 100644 index 9daea2d96e..0000000000 --- a/handler/api/secrets/list.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleList returns an http.HandlerFunc that writes a json-encoded -// list of secrets to the response body. -func HandleList(secrets core.GlobalSecretStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - namespace := chi.URLParam(r, "namespace") - list, err := secrets.List(r.Context(), namespace) - if err != nil { - render.NotFound(w, err) - return - } - // the secret list is copied and the secret value is - // removed from the response. - secrets := []*core.Secret{} - for _, secret := range list { - secrets = append(secrets, secret.Copy()) - } - render.JSON(w, secrets, 200) - } -} diff --git a/handler/api/secrets/list_test.go b/handler/api/secrets/list_test.go deleted file mode 100644 index 9db84ad9b7..0000000000 --- a/handler/api/secrets/list_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var ( - dummySecret = &core.Secret{ - Namespace: "octocat", - Name: "github_password", - Data: "pa55word", - } - - dummySecretScrubbed = &core.Secret{ - Namespace: "octocat", - Name: "github_password", - Data: "", - } - - dummySecretList = []*core.Secret{ - dummySecret, - } - - dummySecretListScrubbed = []*core.Secret{ - dummySecretScrubbed, - } -) - -// -// HandleList -// - -func TestHandleList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().List(gomock.Any(), dummySecret.Namespace).Return(dummySecretList, nil) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.Secret{}, dummySecretListScrubbed - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleList_SecretListErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().List(gomock.Any(), dummySecret.Namespace).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/secrets/none.go b/handler/api/secrets/none.go deleted file mode 100644 index a602c41ea0..0000000000 --- a/handler/api/secrets/none.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package secrets - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -func HandleCreate(core.GlobalSecretStore) http.HandlerFunc { - return notImplemented -} - -func HandleUpdate(core.GlobalSecretStore) http.HandlerFunc { - return notImplemented -} - -func HandleDelete(core.GlobalSecretStore) http.HandlerFunc { - return notImplemented -} - -func HandleFind(core.GlobalSecretStore) http.HandlerFunc { - return notImplemented -} - -func HandleList(core.GlobalSecretStore) http.HandlerFunc { - return notImplemented -} - -func HandleAll(core.GlobalSecretStore) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/secrets/update.go b/handler/api/secrets/update.go deleted file mode 100644 index 7251eb254a..0000000000 --- a/handler/api/secrets/update.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -type secretUpdate struct { - Data *string `json:"data"` - PullRequest *bool `json:"pull_request"` - PullRequestPush *bool `json:"pull_request_push"` -} - -// HandleUpdate returns an http.HandlerFunc that processes http -// requests to update a secret. -func HandleUpdate(secrets core.GlobalSecretStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - namespace = chi.URLParam(r, "namespace") - name = chi.URLParam(r, "name") - ) - - in := new(secretUpdate) - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - s, err := secrets.FindName(r.Context(), namespace, name) - if err != nil { - render.NotFound(w, err) - return - } - - if in.Data != nil { - s.Data = *in.Data - } - if in.PullRequest != nil { - s.PullRequest = *in.PullRequest - } - if in.PullRequestPush != nil { - s.PullRequestPush = *in.PullRequestPush - } - - err = s.Validate() - if err != nil { - render.BadRequest(w, err) - return - } - - err = secrets.Update(r.Context(), s) - if err != nil { - render.InternalError(w, err) - return - } - - s = s.Copy() - render.JSON(w, s, 200) - } -} diff --git a/handler/api/secrets/update_test.go b/handler/api/secrets/update_test.go deleted file mode 100644 index 4cce15fe2d..0000000000 --- a/handler/api/secrets/update_test.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secrets - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleUpdate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecret.Namespace, dummySecret.Name).Return(dummySecret, nil) - secrets.EXPECT().Update(gomock.Any(), gomock.Any()).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummySecret) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(core.Secret), dummySecretScrubbed - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_ValidationError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecret.Namespace, dummySecret.Name).Return(&core.Secret{Name: "github_password"}, nil) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{Data: ""}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "Invalid Secret Value"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_SecretNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecret.Namespace, dummySecret.Name).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_UpdateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - secrets := mock.NewMockGlobalSecretStore(controller) - secrets.EXPECT().FindName(gomock.Any(), dummySecret.Namespace, dummySecret.Name).Return(&core.Secret{Name: "github_password"}, nil) - secrets.EXPECT().Update(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("namespace", "octocat") - c.URLParams.Add("name", "github_password") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{Data: "password"}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(secrets).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/system/license.go b/handler/api/system/license.go deleted file mode 100644 index 01743c6f3b..0000000000 --- a/handler/api/system/license.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package system - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -// HandleLicense returns an http.HandlerFunc that writes -// json-encoded license details to the response body. -func HandleLicense(license core.License) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - render.JSON(w, license, 200) - } -} diff --git a/handler/api/system/limits.go b/handler/api/system/limits.go deleted file mode 100644 index 82f2daaec5..0000000000 --- a/handler/api/system/limits.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package system diff --git a/handler/api/system/none.go b/handler/api/system/none.go deleted file mode 100644 index ebb1e09486..0000000000 --- a/handler/api/system/none.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package system - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -// HandleLicense returns a no-op http.HandlerFunc. -func HandleLicense(license core.License) http.HandlerFunc { - return notImplemented -} - -// HandleStats returns a no-op http.HandlerFunc. -func HandleStats( - core.BuildStore, - core.StageStore, - core.UserStore, - core.RepositoryStore, - core.Pubsub, - core.LogStream, -) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/system/stats.go b/handler/api/system/stats.go deleted file mode 100644 index db5a429dc3..0000000000 --- a/handler/api/system/stats.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package system - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" -) - -type ( - users struct { - Total int64 `json:"total"` - } - - repos struct { - Active int64 `json:"active"` - } - - builds struct { - Pending int `json:"pending"` - Running int `json:"running"` - Total int64 `json:"total"` - } - - events struct { - Subscribers int `json:"subscribers"` - } - - streams struct { - Subscribers int `json:"subscribers"` - Channels int `json:"channels"` - } - - platform struct { - Subscribers int `json:"subscribers"` - OS string `json:"os"` - Arch string `json:"arch"` - Variant string `json:"variant"` - Kernel string `json:"kernel"` - Pending int `json:"pending"` - Running int `json:"running"` - } - - stats struct { - Users users `json:"users"` - Repos repos `json:"repos"` - Builds builds `json:"builds"` - Pipelines []*platform `json:"pipelines"` - Events events `json:"events"` - Streams map[int64]int `json:"streams"` - Watchers map[int64]int `json:"watchers"` - } -) - -// HandleStats returns an http.HandlerFunc that writes a -// json-encoded list of system stats to the response body. -func HandleStats( - builds core.BuildStore, - stages core.StageStore, - users core.UserStore, - repos core.RepositoryStore, - bus core.Pubsub, - streams core.LogStream, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ctx = r.Context() - var err error - - // - // User Stats - // - - stats := &stats{} - stats.Users.Total, err = users.Count(ctx) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("stats: cannot get user count") - return - } - - // - // Repo Stats - // - - stats.Repos.Active, err = repos.Count(ctx) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("stats: cannot get repo count") - return - } - - // - // Build Stats - // - - stats.Builds.Total, err = builds.Count(ctx) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("stats: cannot get build count") - return - } - buildsPending, err := builds.Pending(ctx) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("stats: cannot get pending build count") - return - } - buildsRunning, err := builds.Running(ctx) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("stats: cannot get running build count") - return - } - stats.Builds.Pending = len(buildsPending) - stats.Builds.Running = len(buildsRunning) - - // - // Queue Stats - // - - incomplete, err := stages.ListIncomplete(ctx) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("stats: cannot get pending stage count") - return - } - platforms := newPlatformList() - aggregatePlatformStats(platforms, incomplete) - stats.Pipelines = platforms - - // - // Event Stats - // - - stats.Events.Subscribers, err = bus.Subscribers() - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("stats: cannot get number of subscribers") - return - } - - // - // Stream Stats - // - - stats.Streams = streams.Info(ctx).Streams - - render.JSON(w, stats, 200) - } -} - -// platform statistics are returned in a fixed array. these -// are pointers to the platform index in the array. -const ( - linuxArm6 int = iota - linuxArm7 - linuxArm8 - linuxArm9 - linuxAmd64 - windows1709 - windows1803 - windows1809 -) - -// helper function returns a list of all platforms -// and variants currently supported by core. -func newPlatformList() []*platform { - platforms := [8]*platform{} - platforms[linuxArm6] = &platform{OS: "linux", Arch: "arm", Variant: "v6"} - platforms[linuxArm7] = &platform{OS: "linux", Arch: "arm", Variant: "v7"} - platforms[linuxArm8] = &platform{OS: "linux", Arch: "arm64", Variant: "v8"} - platforms[linuxArm9] = &platform{OS: "linux", Arch: "arm", Variant: "v9"} - platforms[linuxAmd64] = &platform{OS: "linux", Arch: "amd64"} - platforms[windows1709] = &platform{OS: "windows", Arch: "arm64", Kernel: "1709"} - platforms[windows1803] = &platform{OS: "windows", Arch: "arm64", Kernel: "1803"} - platforms[windows1809] = &platform{OS: "windows", Arch: "arm64", Kernel: "1809"} - return platforms[:] -} - -// helper function counts the number of running and -// pending stages by os, architecture, and variant. -func aggregatePlatformStats(platforms []*platform, stages []*core.Stage) { - for _, stage := range stages { - var index int - switch { - case stage.OS == "windows" && stage.Kernel == "1709": - index = windows1709 - case stage.OS == "windows" && stage.Kernel == "1803": - index = windows1803 - case stage.OS == "windows" && stage.Kernel == "1809": - index = windows1809 - case stage.OS == "windows": - // default to 1803 when no variant specified - index = windows1809 - case stage.Arch == "arm" && stage.Variant == "v6": - index = linuxArm6 - case stage.Arch == "arm" && stage.Variant == "v7": - index = linuxArm7 - case stage.Arch == "arm" && stage.Variant == "v9": - index = linuxArm9 - case stage.Arch == "arm": - // default to arm7 when no variant specified - index = linuxArm7 - case stage.Arch == "arm64" && stage.Variant == "v8": - index = linuxArm8 - case stage.Arch == "arm64": - // default to arm8 when arm64 - index = linuxArm8 - default: - index = linuxAmd64 - continue - } - - switch stage.Status { - case core.StatusPending: - platforms[index].Pending++ - case core.StatusRunning: - platforms[index].Running++ - } - } -} diff --git a/handler/api/system/stats_test.go b/handler/api/system/stats_test.go deleted file mode 100644 index 03c3cb2243..0000000000 --- a/handler/api/system/stats_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package system - -import ( - "io/ioutil" - - "github.com/sirupsen/logrus" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} diff --git a/handler/api/template/all.go b/handler/api/template/all.go deleted file mode 100644 index 837d67d34a..0000000000 --- a/handler/api/template/all.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -// HandleListAll returns an http.HandlerFunc that writes a json-encoded -// list of templates to the response body. -func HandleListAll(templateStore core.TemplateStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - list, err := templateStore.ListAll(r.Context()) - if err != nil { - render.NotFound(w, err) - return - } - render.JSON(w, list, 200) - } -} diff --git a/handler/api/template/all_test.go b/handler/api/template/all_test.go deleted file mode 100644 index c50aae24d3..0000000000 --- a/handler/api/template/all_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var ( - dummyTemplate = &core.Template{ - Name: "my_template.yml", - Data: "my_data", - Created: 1, - Updated: 2, - Namespace: "my_org", - } - dummyTemplateList = []*core.Template{ - dummyTemplate, - } -) - -func TestHandleAll(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().ListAll(gomock.Any()).Return(dummyTemplateList, nil) - - c := new(chi.Context) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleListAll(templates).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleAll_TemplateListErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().ListAll(gomock.Any()).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleListAll(templates).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/template/create.go b/handler/api/template/create.go deleted file mode 100644 index 1a5cbaa1da..0000000000 --- a/handler/api/template/create.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "encoding/json" - "net/http" - "path/filepath" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -var ( - errTemplateExtensionInvalid = errors.New("Template extension invalid. Must be yaml, starlark or jsonnet") -) - -type templateInput struct { - Name string `json:"name"` - Data string `json:"data"` -} - -// HandleCreate returns an http.HandlerFunc that processes http -// requests to create a new template. -func HandleCreate(templateStore core.TemplateStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - namespace := chi.URLParam(r, "namespace") - in := new(templateInput) - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - // check valid template extension type - switch filepath.Ext(in.Name) { - case ".yml", ".yaml": - case ".star", ".starlark", ".script": - case ".jsonnet": - default: - render.BadRequest(w, errTemplateExtensionInvalid) - return - } - - t := &core.Template{ - Name: in.Name, - Data: in.Data, - Namespace: namespace, - } - - err = t.Validate() - if err != nil { - render.BadRequest(w, err) - return - } - - err = templateStore.Create(r.Context(), t) - if err != nil { - render.InternalError(w, err) - return - } - - render.JSON(w, t, 200) - } -} diff --git a/handler/api/template/create_test.go b/handler/api/template/create_test.go deleted file mode 100644 index 3df7230ba1..0000000000 --- a/handler/api/template/create_test.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "bytes" - "context" - "encoding/json" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "net/http" - "net/http/httptest" - "testing" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleCreate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) - - c := new(chi.Context) - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummyTemplate) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(templates).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleCreate_NotValidTemplateExtensionName(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Template{Name: "my_template", Data: "my_data"}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "Template extension invalid. Must be yaml, starlark or jsonnet"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_ValidationErrorData(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Template{Name: "my_template.yml", Data: ""}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "No Template Data Provided"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(nil).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleCreate_CreateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().Create(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - c := new(chi.Context) - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummyTemplate) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleCreate(templates).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/template/delete.go b/handler/api/template/delete.go deleted file mode 100644 index bca24ca245..0000000000 --- a/handler/api/template/delete.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that processes http -// requests to delete a template. -func HandleDelete(template core.TemplateStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - name = chi.URLParam(r, "name") - namespace = chi.URLParam(r, "namespace") - ) - s, err := template.FindName(r.Context(), name, namespace) - if err != nil { - render.NotFound(w, err) - return - } - err = template.Delete(r.Context(), s) - if err != nil { - render.InternalError(w, err) - return - } - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/template/delete_test.go b/handler/api/template/delete_test.go deleted file mode 100644 index aae3c1617a..0000000000 --- a/handler/api/template/delete_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleDelete(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - template := mock.NewMockTemplateStore(controller) - template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil) - template.EXPECT().Delete(gomock.Any(), dummyTemplate).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("name", "my_template.yml") - c.URLParams.Add("namespace", "my_org") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(template).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNoContent; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleDelete_TemplateNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - template := mock.NewMockTemplateStore(controller) - template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("name", "my_template.yml") - c.URLParams.Add("namespace", "my_org") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(template).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleDelete_DeleteError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - template := mock.NewMockTemplateStore(controller) - template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil) - template.EXPECT().Delete(gomock.Any(), dummyTemplate).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("name", "my_template.yml") - c.URLParams.Add("namespace", "my_org") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(template).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/template/find.go b/handler/api/template/find.go deleted file mode 100644 index b843e4e2bf..0000000000 --- a/handler/api/template/find.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package template - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleFind returns an http.HandlerFunc that writes json-encoded -// template details to the response body. -func HandleFind(templateStore core.TemplateStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - name = chi.URLParam(r, "name") - namespace = chi.URLParam(r, "namespace") - ) - template, err := templateStore.FindName(r.Context(), name, namespace) - if err != nil { - render.NotFound(w, err) - return - } - render.JSON(w, template, 200) - } -} diff --git a/handler/api/template/find_test.go b/handler/api/template/find_test.go deleted file mode 100644 index a5687b04af..0000000000 --- a/handler/api/template/find_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - template := mock.NewMockTemplateStore(controller) - template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil) - - c := new(chi.Context) - c.URLParams.Add("name", "my_template.yml") - c.URLParams.Add("namespace", "my_org") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(template).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleFind_TemplateNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - template := mock.NewMockTemplateStore(controller) - template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("name", "my_template.yml") - c.URLParams.Add("namespace", "my_org") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(template).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/template/list.go b/handler/api/template/list.go deleted file mode 100644 index c414dba8ee..0000000000 --- a/handler/api/template/list.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -// HandleList returns an http.HandlerFunc that writes a json-encoded -// list of templates to the response body by namespace -func HandleList(templateStore core.TemplateStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - namespace := chi.URLParam(r, "namespace") - list, err := templateStore.List(r.Context(), namespace) - if err != nil { - render.NotFound(w, err) - return - } - render.JSON(w, list, 200) - } -} diff --git a/handler/api/template/list_test.go b/handler/api/template/list_test.go deleted file mode 100644 index d066274ef8..0000000000 --- a/handler/api/template/list_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package template - -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().List(gomock.Any(), dummyTemplate.Namespace).Return(dummyTemplateList, nil) - - c := new(chi.Context) - c.URLParams.Add("namespace", "my_org") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(templates).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleList_TemplateListErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().List(gomock.Any(), dummyTemplate.Namespace).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("namespace", "my_org") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleList(templates).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/template/none.go b/handler/api/template/none.go deleted file mode 100644 index 43c51aa6b3..0000000000 --- a/handler/api/template/none.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package template - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" -) - -var notImplemented = func(w http.ResponseWriter, r *http.Request) { - render.NotImplemented(w, render.ErrNotImplemented) -} - -func HandleCreate(store core.TemplateStore) http.HandlerFunc { - return notImplemented -} - -func HandleUpdate(core.TemplateStore) http.HandlerFunc { - return notImplemented -} - -func HandleDelete(core.TemplateStore) http.HandlerFunc { - return notImplemented -} - -func HandleFind(core.TemplateStore) http.HandlerFunc { - return notImplemented -} - -func HandleList(core.TemplateStore) http.HandlerFunc { - return notImplemented -} - -func HandleListAll(core.TemplateStore) http.HandlerFunc { - return notImplemented -} - -func HandleAll(core.TemplateStore) http.HandlerFunc { - return notImplemented -} diff --git a/handler/api/template/update.go b/handler/api/template/update.go deleted file mode 100644 index 5541244c7b..0000000000 --- a/handler/api/template/update.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - - "github.com/go-chi/chi" -) - -type templateUpdate struct { - Data *string `json:"data"` - Namespace *string `json:"namespace"` -} - -// HandleUpdate returns an http.HandlerFunc that processes http -// requests to update a template. -func HandleUpdate(templateStore core.TemplateStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - name = chi.URLParam(r, "name") - namespace = chi.URLParam(r, "namespace") - ) - - in := new(templateUpdate) - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - s, err := templateStore.FindName(r.Context(), name, namespace) - if err != nil { - render.NotFound(w, err) - return - } - - if in.Data != nil { - s.Data = *in.Data - } - if in.Namespace != nil { - s.Namespace = *in.Namespace - } - - err = s.Validate() - if err != nil { - render.BadRequest(w, err) - return - } - - err = templateStore.Update(r.Context(), s) - if err != nil { - render.InternalError(w, err) - return - } - - render.JSON(w, s, 200) - } -} diff --git a/handler/api/template/update_test.go b/handler/api/template/update_test.go deleted file mode 100644 index 7ec96bbab9..0000000000 --- a/handler/api/template/update_test.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestHandleUpdate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - template := mock.NewMockTemplateStore(controller) - template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil) - template.EXPECT().Update(gomock.Any(), gomock.Any()).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("name", "my_template.yml") - c.URLParams.Add("namespace", "my_org") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(dummyTemplate) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(template).ServeHTTP(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestHandleUpdate_ValidationErrorData(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - template := mock.NewMockTemplateStore(controller) - template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(&core.Template{Name: "my_template.yml"}, nil) - - c := new(chi.Context) - c.URLParams.Add("name", "my_template.yml") - c.URLParams.Add("namespace", "my_org") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{Data: ""}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(template).ServeHTTP(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "No Template Data Provided"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_TemplateNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - template := mock.NewMockTemplateStore(controller) - template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(nil, errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("name", "my_template.yml") - c.URLParams.Add("namespace", "my_org") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Secret{}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(template).ServeHTTP(w, r) - if got, want := w.Code, http.StatusNotFound; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestHandleUpdate_UpdateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - template := mock.NewMockTemplateStore(controller) - template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(&core.Template{Name: "my_template.yml"}, nil) - template.EXPECT().Update(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("name", "my_template.yml") - c.URLParams.Add("namespace", "my_org") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.Template{Data: "my_data"}) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(template).ServeHTTP(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/user/activity.go b/handler/api/user/activity.go deleted file mode 100644 index cdd668ba88..0000000000 --- a/handler/api/user/activity.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package user - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" -) - -// HandleRecent returns an http.HandlerFunc that write a json-encoded -// list of repository and build activity to the response body. -func HandleRecent(repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - viewer, _ := request.UserFrom(r.Context()) - list, err := repos.ListRecent(r.Context(), viewer.ID) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("api: cannot list repositories") - } else { - render.JSON(w, list, 200) - } - } -} diff --git a/handler/api/user/find.go b/handler/api/user/find.go deleted file mode 100644 index e8607f1830..0000000000 --- a/handler/api/user/find.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package user - -import ( - "net/http" - - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" -) - -// HandleFind returns an http.HandlerFunc that writes json-encoded -// account information to the http response body. -func HandleFind() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - viewer, _ := request.UserFrom(ctx) - render.JSON(w, viewer, 200) - } -} diff --git a/handler/api/user/find_test.go b/handler/api/user/find_test.go deleted file mode 100644 index 8235b6b1e6..0000000000 --- a/handler/api/user/find_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package user - -import ( - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/core" - - "github.com/google/go-cmp/cmp" -) - -func TestFind(t *testing.T) { - mockUser := &core.User{ - ID: 1, - Login: "octocat", - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/api/user", nil) - r = r.WithContext( - request.WithUser(r.Context(), mockUser), - ) - - HandleFind()(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.User{}, mockUser - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/user/remote/repo.go b/handler/api/user/remote/repo.go deleted file mode 100644 index a932f7c7e3..0000000000 --- a/handler/api/user/remote/repo.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package remote - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" - "github.com/drone/go-scm/scm" - - "github.com/go-chi/chi" -) - -// HandleRepo returns an http.HandlerFunc that writes a json-encoded -// repository to the response body. -func HandleRepo(repos core.RepositoryService) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - viewer, _ = request.UserFrom(r.Context()) - - owner = chi.URLParam(r, "owner") - name = chi.URLParam(r, "name") - slug = scm.Join(owner, name) - ) - - repo, err := repos.Find(r.Context(), viewer, slug) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot get remote repository") - return - } - - perms, err := repos.FindPerm(r.Context(), viewer, slug) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot get remote repository permissions") - } else { - repo.Perms = perms - } - - render.JSON(w, repo, 200) - } -} diff --git a/handler/api/user/remote/repos.go b/handler/api/user/remote/repos.go deleted file mode 100644 index 53193916e8..0000000000 --- a/handler/api/user/remote/repos.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package remote - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" -) - -// HandleRepos returns an http.HandlerFunc that write a json-encoded -// list of repositories to the response body. -func HandleRepos(repos core.RepositoryService) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - viewer, _ := request.UserFrom(r.Context()) - - list, err := repos.List(r.Context(), viewer) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot list remote repositories") - } else { - render.JSON(w, list, 200) - } - } -} diff --git a/handler/api/user/repos.go b/handler/api/user/repos.go deleted file mode 100644 index d79c071bde..0000000000 --- a/handler/api/user/repos.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package user - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" -) - -// HandleRepos returns an http.HandlerFunc that write a json-encoded -// list of repositories to the response body. -func HandleRepos(repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - viewer, _ := request.UserFrom(r.Context()) - - var list []*core.Repository - var err error - if r.FormValue("latest") != "true" { - list, err = repos.List(r.Context(), viewer.ID) - } else { - list, err = repos.ListLatest(r.Context(), viewer.ID) - } - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot list repositories") - } else { - render.JSON(w, list, 200) - } - } -} diff --git a/handler/api/user/repos_test.go b/handler/api/user/repos_test.go deleted file mode 100644 index 3ab2ebace7..0000000000 --- a/handler/api/user/repos_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package user - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - "github.com/drone/drone/core" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" - "github.com/sirupsen/logrus" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -func TestRepositoryList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - ID: 1, - Login: "octocat", - } - - mockRepos := []*core.Repository{ - { - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - }, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().List(gomock.Any(), mockUser.ID).Return(mockRepos, nil) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - request.WithUser(r.Context(), mockUser), - ) - - HandleRepos(repos)(w, r) - if got, want := w.Code, http.StatusOK; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.Repository{}, mockRepos - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestRepositoryListErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - ID: 1, - Login: "octocat", - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().List(gomock.Any(), mockUser.ID).Return(nil, errors.ErrNotFound) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - request.WithUser(r.Context(), mockUser), - ) - - HandleRepos(repos)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &errors.Error{}, errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/user/sync.go b/handler/api/user/sync.go deleted file mode 100644 index 42512d7379..0000000000 --- a/handler/api/user/sync.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package user - -import ( - "context" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" -) - -// HandleSync returns an http.HandlerFunc synchronizes and then -// write a json-encoded list of repositories to the response body. -func HandleSync(syncer core.Syncer, repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - viewer, _ := request.UserFrom(r.Context()) - - // performs asynchronous account synchronization. - // this requires long polling to determine when the - // sync is complete. - if r.FormValue("async") == "true" { - ctx := context.Background() - go func(ctx context.Context, viewer *core.User) { - _, err := syncer.Sync(ctx, viewer) - if err != nil { - logger.FromContext(ctx).WithError(err). - Debugln("api: cannot synchronize account") - } - }(ctx, viewer) - w.WriteHeader(204) - return - } - - _, err := syncer.Sync(r.Context(), viewer) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("api: cannot synchronize account") - return - } - list, err := repos.List(r.Context(), viewer.ID) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("api: cannot synchronize account") - } else { - render.JSON(w, list, 200) - } - } -} diff --git a/handler/api/user/sync_test.go b/handler/api/user/sync_test.go deleted file mode 100644 index 0c032c9e43..0000000000 --- a/handler/api/user/sync_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package user diff --git a/handler/api/user/token.go b/handler/api/user/token.go deleted file mode 100644 index dd8d9dbabc..0000000000 --- a/handler/api/user/token.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package user - -import ( - "net/http" - - "github.com/dchest/uniuri" - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" -) - -type userWithToken struct { - *core.User - Token string `json:"token"` -} - -// HandleToken returns an http.HandlerFunc that writes json-encoded -// account information to the http response body with the user token. -func HandleToken(users core.UserStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - viewer, _ := request.UserFrom(ctx) - if r.FormValue("rotate") == "true" { - viewer.Hash = uniuri.NewLen(32) - if err := users.Update(ctx, viewer); err != nil { - render.InternalError(w, err) - return - } - } - render.JSON(w, &userWithToken{viewer, viewer.Hash}, 200) - } -} diff --git a/handler/api/user/token_test.go b/handler/api/user/token_test.go deleted file mode 100644 index 6737a6e933..0000000000 --- a/handler/api/user/token_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package user - -import ( - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - "github.com/drone/drone/core" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -func TestToken(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - ID: 1, - Login: "octocat", - Hash: "MjAxOC0wOC0xMVQxNTo1ODowN1o", - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - request.WithUser(r.Context(), mockUser), - ) - - HandleToken(nil)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &userWithToken{}, mockUser - json.NewDecoder(w.Body).Decode(got) - - if got, want := got.Token, want.Hash; got != want { - t.Errorf("Expect user secret returned") - } -} - -// the purpose of this unit test is to verify that the token -// is refreshed if the user ?refresh=true query parameter is -// included in the http request. -func TestTokenRotate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - ID: 1, - Login: "octocat", - Hash: "MjAxOC0wOC0xMVQxNTo1ODowN1o", - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?rotate=true", nil) - r = r.WithContext( - request.WithUser(r.Context(), mockUser), - ) - - users := mock.NewMockUserStore(controller) - users.EXPECT().Update(gomock.Any(), gomock.Any()).Return(nil) - - HandleToken(users)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &userWithToken{}, mockUser - json.NewDecoder(w.Body).Decode(got) - - ignore := cmpopts.IgnoreFields(core.User{}, "Hash") - if diff := cmp.Diff(got.User, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } - if got.Token == "" { - t.Errorf("Expect user token returned") - } - if got, want := got.Token, "MjAxOC0wOC0xMVQxNTo1ODowN1o"; got == want { - t.Errorf("Expect user hash updated") - } -} - -// the purpose of this unit test is to verify that an error -// updating the database will result in an internal server -// error returned to the client. -func TestToken_UpdateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - ID: 1, - Login: "octocat", - } - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?rotate=true", nil) - r = r.WithContext( - request.WithUser(r.Context(), mockUser), - ) - - users := mock.NewMockUserStore(controller) - users.EXPECT().Update(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - HandleToken(users)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/user/update.go b/handler/api/user/update.go deleted file mode 100644 index 7d91bea2f6..0000000000 --- a/handler/api/user/update.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package user - -import ( - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" -) - -// HandleUpdate returns an http.HandlerFunc that processes an http.Request -// to update the current user account. -func HandleUpdate(users core.UserStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - viewer, _ := request.UserFrom(r.Context()) - - in := new(core.User) - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot unmarshal request body") - return - } - - viewer.Email = in.Email - err = users.Update(r.Context(), viewer) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("api: cannot update user") - } else { - render.JSON(w, viewer, 200) - } - } -} diff --git a/handler/api/user/update_test.go b/handler/api/user/update_test.go deleted file mode 100644 index f26766f2a8..0000000000 --- a/handler/api/user/update_test.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package user - -import ( - "bytes" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/mock" - "github.com/drone/drone/core" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestUpdate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - userInput := &core.User{ - Login: "octocat", - Email: "octocat@github.com", - } - user := &core.User{ - Login: "octocat", - Email: "", - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().Update(gomock.Any(), user) - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(userInput) - w := httptest.NewRecorder() - r := httptest.NewRequest("PATCH", "/api/user", in) - r = r.WithContext( - request.WithUser(r.Context(), user), - ) - - HandleUpdate(users)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - if got, want := user.Email, "octocat@github.com"; got != want { - t.Errorf("Want user email %v, got %v", want, got) - } - - got, want := new(core.User), user - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// the purpose of this unit test is to verify that an invalid -// (in this case missing) request body will result in a bad -// request error returned to the client. -func TestUpdate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - ID: 1, - Login: "octocat", - } - - in := new(bytes.Buffer) - w := httptest.NewRecorder() - r := httptest.NewRequest("PATCH", "/api/user", in) - r = r.WithContext( - request.WithUser(r.Context(), mockUser), - ) - - HandleUpdate(nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// the purpose of this unit test is to verify that an error -// updating the database will result in an internal server -// error returned to the client. -func TestUpdate_ServerError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - userInput := &core.User{ - Login: "octocat", - Email: "octocat@github.com", - } - user := &core.User{ - Login: "octocat", - Email: "", - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().Update(gomock.Any(), user).Return(errors.ErrNotFound) - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(userInput) - w := httptest.NewRecorder() - r := httptest.NewRequest("PATCH", "/api/user", in) - r = r.WithContext( - request.WithUser(r.Context(), user), - ) - - HandleUpdate(users)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/users/create.go b/handler/api/users/create.go deleted file mode 100644 index 575680678e..0000000000 --- a/handler/api/users/create.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package users - -import ( - "encoding/json" - "net/http" - "time" - - "github.com/dchest/uniuri" - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/handler/api/request" - "github.com/drone/drone/logger" -) - -type userWithToken struct { - *core.User - Token string `json:"token"` -} - -// HandleCreate returns an http.HandlerFunc that processes an http.Request -// to create the named user account in the system. -func HandleCreate(users core.UserStore, service core.UserService, sender core.WebhookSender) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - in := new(userWithToken) - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot unmarshal request body") - return - } - - user := &core.User{ - Login: in.Login, - Active: true, - Admin: in.Admin, - Machine: in.Machine, - Created: time.Now().Unix(), - Updated: time.Now().Unix(), - Hash: in.Token, - } - if user.Hash == "" { - user.Hash = uniuri.NewLen(32) - } - - // if the user is not a machine account, we lookup - // the user in the remote system. We can then augment - // the user input with the remote system data. - if !user.Machine { - viewer, _ := request.UserFrom(r.Context()) - remote, err := service.FindLogin(r.Context(), viewer, user.Login) - if err == nil { - if user.Login != remote.Login && remote.Login != "" { - user.Login = remote.Login - } - if user.Email == "" { - user.Email = remote.Email - } - } - } - - err = user.Validate() - if err != nil { - render.ErrorCode(w, err, 400) - logger.FromRequest(r).WithError(err). - Errorln("api: invalid username") - return - } - - err = users.Create(r.Context(), user) - if err == core.ErrUserLimit { - render.ErrorCode(w, err, 402) - logger.FromRequest(r).WithError(err). - Errorln("api: cannot create user") - return - } - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("api: cannot create user") - return - } - - err = sender.Send(r.Context(), &core.WebhookData{ - Event: core.WebhookEventUser, - Action: core.WebhookActionCreated, - User: user, - }) - if err != nil { - logger.FromRequest(r).WithError(err). - Warnln("api: cannot send webhook") - } - - var out interface{} = user - // if the user is a machine account the api token - // is included in the response. - if user.Machine { - out = &userWithToken{user, user.Hash} - } - render.JSON(w, out, 200) - } -} diff --git a/handler/api/users/create_test.go b/handler/api/users/create_test.go deleted file mode 100644 index 77253edb61..0000000000 --- a/handler/api/users/create_test.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package users - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestCreate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().Create(gomock.Any(), gomock.Any()).Do(func(_ context.Context, in *core.User) error { - if got, want := in.Login, "octocat"; got != want { - t.Errorf("Want user login %s, got %s", want, got) - } - if in.Hash == "" { - t.Errorf("Expect user secret generated") - } - return nil - }) - - webhook := mock.NewMockWebhookSender(controller) - webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil) - - service := mock.NewMockUserService(controller) - service.EXPECT().FindLogin(gomock.Any(), gomock.Any(), "octocat").Return(nil, errors.New("not found")) - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.User{Login: "octocat"}) - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - - HandleCreate(users, service, webhook)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - out := new(core.User) - json.NewDecoder(w.Body).Decode(out) - if got, want := out.Login, "octocat"; got != want { - t.Errorf("Want user login %s, got %s", want, got) - } - if got, want := out.Active, true; got != want { - t.Errorf("Want user active %v, got %v", want, got) - } - if got := out.Created; got == 0 { - t.Errorf("Want user created set to current unix timestamp, got %v", got) - } - if got := out.Updated; got == 0 { - t.Errorf("Want user updated set to current unix timestamp, got %v", got) - } -} - -func TestCreateWithToken(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().Create(gomock.Any(), gomock.Any()).Do(func(_ context.Context, in *core.User) error { - if got, want := in.Login, "octocat"; got != want { - t.Errorf("Want user login %s, got %s", want, got) - } - if got, want := in.Machine, true; got != want { - t.Errorf("Want user machine %v, got %v", want, got) - } - if got, want := in.Hash, "abc123"; got != want { - t.Errorf("Want user hash %s, got %s", want, got) - } - return nil - }) - - webhook := mock.NewMockWebhookSender(controller) - webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil) - - service := mock.NewMockUserService(controller) - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&userWithToken{&core.User{Login: "octocat", Machine: true}, "abc123"}) - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - - HandleCreate(users, service, webhook)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - out := new(userWithToken) - json.NewDecoder(w.Body).Decode(out) - if got, want := out.Login, "octocat"; got != want { - t.Errorf("Want user login %s, got %s", want, got) - } - if got, want := out.Active, true; got != want { - t.Errorf("Want user active %v, got %v", want, got) - } - if got := out.Created; got == 0 { - t.Errorf("Want user created set to current unix timestamp, got %v", got) - } - if got := out.Updated; got == 0 { - t.Errorf("Want user updated set to current unix timestamp, got %v", got) - } - if got, want := out.Token, "abc123"; got != want { - t.Errorf("Want user token %s, got %s", want, got) - } -} - -func TestCreate_CorrectName(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().Create(gomock.Any(), gomock.Any()).Do(func(_ context.Context, in *core.User) error { - if got, want := in.Login, "octocat"; got != want { - t.Errorf("Want user login %s, got %s", want, got) - } - if got, want := in.Email, "octocat@github.com"; got != want { - t.Errorf("Want user email %s, got %s", want, got) - } - if in.Hash == "" { - t.Errorf("Expect user secert generated") - } - return nil - }) - - webhook := mock.NewMockWebhookSender(controller) - webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil) - - service := mock.NewMockUserService(controller) - service.EXPECT().FindLogin(gomock.Any(), gomock.Any(), "Octocat").Return(&core.User{ - Login: "octocat", - Email: "octocat@github.com", - }, nil) - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.User{Login: "Octocat"}) - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - - HandleCreate(users, service, webhook)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - out := new(core.User) - json.NewDecoder(w.Body).Decode(out) - if got, want := out.Login, "octocat"; got != want { - t.Errorf("Want user login %s, got %s", want, got) - } - if got, want := out.Active, true; got != want { - t.Errorf("Want user active %v, got %v", want, got) - } - if got := out.Created; got == 0 { - t.Errorf("Want user created set to current unix timestamp, got %v", got) - } - if got := out.Updated; got == 0 { - t.Errorf("Want user updated set to current unix timestamp, got %v", got) - } -} - -func TestCreate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - in := new(bytes.Buffer) - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - - HandleCreate(nil, nil, nil)(w, r) - if got, want := w.Code, http.StatusBadRequest; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestCreateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().Create(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - webhook := mock.NewMockWebhookSender(controller) - - service := mock.NewMockUserService(controller) - service.EXPECT().FindLogin(gomock.Any(), gomock.Any(), "octocat").Return(nil, errors.New("not found")) - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(&core.User{Login: "octocat"}) - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", in) - - HandleCreate(users, service, webhook)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/users/delete.go b/handler/api/users/delete.go deleted file mode 100644 index d5fd6fdc42..0000000000 --- a/handler/api/users/delete.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package users - -import ( - "context" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleDelete returns an http.HandlerFunc that processes an http.Request -// to delete the named user account from the system. -func HandleDelete( - users core.UserStore, - transferer core.Transferer, - sender core.WebhookSender, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - login := chi.URLParam(r, "user") - user, err := users.FindLogin(r.Context(), login) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot find user") - return - } - - err = transferer.Transfer(context.Background(), user) - if err != nil { - logger.FromRequest(r).WithError(err). - Warnln("api: cannot transfer repository ownership") - } - - err = users.Delete(r.Context(), user) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("api: cannot delete user") - return - } - - err = sender.Send(r.Context(), &core.WebhookData{ - Event: core.WebhookEventUser, - Action: core.WebhookActionDeleted, - User: user, - }) - if err != nil { - logger.FromRequest(r).WithError(err). - Warnln("api: cannot send webhook") - } - - w.WriteHeader(http.StatusNoContent) - } -} diff --git a/handler/api/users/delete_test.go b/handler/api/users/delete_test.go deleted file mode 100644 index e43b2dee52..0000000000 --- a/handler/api/users/delete_test.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package users - -import ( - "context" - "database/sql" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" -) - -func TestUserDelete(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), mockUser.Login).Return(mockUser, nil) - users.EXPECT().Delete(gomock.Any(), mockUser).Return(nil) - - transferer := mock.NewMockTransferer(controller) - transferer.EXPECT().Transfer(gomock.Any(), mockUser).Return(nil) - - webhook := mock.NewMockWebhookSender(controller) - webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(users, transferer, webhook)(w, r) - if got, want := w.Body.Len(), 0; want != got { - t.Errorf("Want response body size %d, got %d", want, got) - } - if got, want := w.Code, 204; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestUserDelete_NotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), mockUser.Login).Return(nil, sql.ErrNoRows) - - webhook := mock.NewMockWebhookSender(controller) - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(users, nil, webhook)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} - -func TestUserDelete_InternalError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), mockUser.Login).Return(mockUser, nil) - users.EXPECT().Delete(gomock.Any(), mockUser).Return(sql.ErrConnDone) - - transferer := mock.NewMockTransferer(controller) - transferer.EXPECT().Transfer(gomock.Any(), mockUser).Return(nil) - - webhook := mock.NewMockWebhookSender(controller) - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("DELETE", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleDelete(users, transferer, webhook)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} diff --git a/handler/api/users/find.go b/handler/api/users/find.go deleted file mode 100644 index dcff1a0aa2..0000000000 --- a/handler/api/users/find.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package users - -import ( - "net/http" - "strconv" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleFind returns an http.HandlerFunc that writes json-encoded -// user account information to the response body. -func HandleFind(users core.UserStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - login := chi.URLParam(r, "user") - - user, err := users.FindLogin(r.Context(), login) - if err != nil { - // the client can make a user request by providing - // the user id as opposed to the username. If a - // numeric user id is provided as input, attempt - // to lookup the user by id. - if id, _ := strconv.ParseInt(login, 10, 64); id != 0 { - user, err = users.Find(r.Context(), id) - if err == nil { - render.JSON(w, user, 200) - return - } - } - render.NotFound(w, err) - logger.FromRequest(r).Debugln("api: cannot find user") - } else { - render.JSON(w, user, 200) - } - } -} diff --git a/handler/api/users/find_test.go b/handler/api/users/find_test.go deleted file mode 100644 index 1df64c9b36..0000000000 --- a/handler/api/users/find_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package users - -import ( - "context" - "database/sql" - "encoding/json" - "io/ioutil" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/sirupsen/logrus" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -// var ( -// mockUser = &core.User{ -// Login: "octocat", -// } - -// mockUsers = []*core.User{ -// { -// Login: "octocat", -// }, -// } - -// // mockNotFound = &Error{ -// // Message: "sql: no rows in result set", -// // } - -// // mockBadRequest = &Error{ -// // Message: "EOF", -// // } - -// // mockInternalError = &Error{ -// // Message: "database/sql: connection is already closed", -// // } -// ) - -func TestUserFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), mockUser.Login).Return(mockUser, nil) - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(users)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.User{}, mockUser - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestUserFindID(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), "1").Return(nil, sql.ErrNoRows) - users.EXPECT().Find(gomock.Any(), mockUser.ID).Return(mockUser, nil) - - c := new(chi.Context) - c.URLParams.Add("user", "1") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(users)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &core.User{}, mockUser - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestUserFindErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), mockUser.Login).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleFind(users)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} diff --git a/handler/api/users/list.go b/handler/api/users/list.go deleted file mode 100644 index b2af9372a3..0000000000 --- a/handler/api/users/list.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package users - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" -) - -// HandleList returns an http.HandlerFunc that writes a json-encoded -// list of all registered system users to the response body. -func HandleList(users core.UserStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - users, err := users.List(r.Context()) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("api: cannot list users") - } else { - render.JSON(w, users, 200) - } - } -} diff --git a/handler/api/users/list_test.go b/handler/api/users/list_test.go deleted file mode 100644 index 6e5e4083eb..0000000000 --- a/handler/api/users/list_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package users - -import ( - "database/sql" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/mock" - "github.com/drone/drone/core" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var ( - mockUser = &core.User{ - ID: 1, - Login: "octocat", - Email: "octocat@github.com", - Admin: false, - Active: true, - Avatar: "https://avatars1.githubusercontent.com/u/583231", - } - - mockUserList = []*core.User{ - mockUser, - } -) - -func TestHandleList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().List(gomock.Any()).Return(mockUserList, nil) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - h := HandleList(users) - - h(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := []*core.User{}, mockUserList - json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestUserList_Err(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().List(gomock.Any()).Return(nil, sql.ErrNoRows) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - HandleList(users)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - // got, want := new(render.Error), &render.Error{Message: "sql: no rows in result set"} - // json.NewDecoder(w.Body).Decode(got) - // if diff := cmp.Diff(got, want); len(diff) > 0 { - // t.Errorf(diff) - // } -} diff --git a/handler/api/users/repos.go b/handler/api/users/repos.go deleted file mode 100644 index b1f99bdfa8..0000000000 --- a/handler/api/users/repos.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package users - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -// HandleRepoList returns an http.HandlerFunc that writes a json-encoded -// list of all user repositories to the response body. -func HandleRepoList(users core.UserStore, repos core.RepositoryStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - login := chi.URLParam(r, "user") - - user, err := users.FindLogin(r.Context(), login) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r). - WithError(err). - WithField("user", login). - Debugln("api: cannot find user") - return - } - - repos, err := repos.List(r.Context(), user.ID) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r). - WithError(err). - WithField("user", login). - Warnln("api: cannot list user repositories") - } else { - render.JSON(w, repos, 200) - } - } -} diff --git a/handler/api/users/token.go b/handler/api/users/token.go deleted file mode 100644 index b277483a40..0000000000 --- a/handler/api/users/token.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package users - -import ( - "net/http" - - "github.com/dchest/uniuri" - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - "github.com/go-chi/chi" -) - -type userWithMessage struct { - *core.User - Message string `json:"message"` -} - -// HandleToken returns an http.HandlerFunc that writes json-encoded -// account information to the http response body with the user token. -func HandleTokenRotation(users core.UserStore) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - login := chi.URLParam(r, "user") - user, err := users.FindLogin(r.Context(), login) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot find user") - return - } - user.Hash = uniuri.NewLen(32) - if err := users.Update(r.Context(), user); err != nil { - render.InternalError(w, err) - return - } - render.JSON(w, &userWithMessage{user, "Token rotated successfully."}, 200) - } -} diff --git a/handler/api/users/token_test.go b/handler/api/users/token_test.go deleted file mode 100644 index 1d40547ca1..0000000000 --- a/handler/api/users/token_test.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package users - -import ( - "context" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - "github.com/go-chi/chi" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -// The purpose of this test is to make sure admins can rotate someone -// else's token. -func TestTokenRotate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - startingHash := "MjAxOC0wOC0xMVQxNTo1ODowN1o" - mockUser := &core.User{ - ID: 1, - Login: "octocat", - Hash: startingHash, - } - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), mockUser.Login).Return(mockUser, nil) - users.EXPECT().Update(gomock.Any(), gomock.Any()).Return(nil) - - HandleTokenRotation(users)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &userWithMessage{}, mockUser - json.NewDecoder(w.Body).Decode(got) - - ignore := cmpopts.IgnoreFields(core.User{}, "Hash") - if diff := cmp.Diff(got.User, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } - if got.Message == "" { - t.Errorf("Expect Message returned") - } - if got, want := mockUser.Hash, startingHash; got == want { - t.Errorf("Expect user hash updated") - } -} - -// the purpose of this unit test is to verify we fail safely when a non existing user is provided -func TestToken_UserNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - startingHash := "MjAxOC0wOC0xMVQxNTo1ODowN1o" - mockUser := &core.User{ - ID: 1, - Login: "octocat", - Hash: startingHash, - } - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?rotate=true", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), mockUser.Login).Return(mockUser, nil) - users.EXPECT().Update(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound) - - HandleTokenRotation(users)(w, r) - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// the purpose of this unit test is to verify we fail safely when a non existing user is provided -func TestToken_UpdateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/?rotate=true", nil) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), mockUser.Login).Return(nil, errors.ErrNotFound) - - HandleTokenRotation(users)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/handler/api/users/update.go b/handler/api/users/update.go deleted file mode 100644 index af320c1e66..0000000000 --- a/handler/api/users/update.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package users - -import ( - "context" - "encoding/json" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/logger" - - "github.com/go-chi/chi" -) - -type userInput struct { - Admin *bool `json:"admin"` - Active *bool `json:"active"` -} - -// HandleUpdate returns an http.HandlerFunc that processes an http.Request -// to update a user account. -func HandleUpdate(users core.UserStore, transferer core.Transferer) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - login := chi.URLParam(r, "user") - - in := new(userInput) - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot unmarshal request body") - return - } - - user, err := users.FindLogin(r.Context(), login) - if err != nil { - render.NotFound(w, err) - logger.FromRequest(r).WithError(err). - Debugln("api: cannot find user") - return - } - - if in.Admin != nil { - user.Admin = *in.Admin - } - if in.Active != nil { - user.Active = *in.Active - // if the user is inactive we should always - // disable administrative privileges since - // the user may still have some API access. - if user.Active == false { - user.Admin = false - } - } - err = users.Update(r.Context(), user) - if err != nil { - render.InternalError(w, err) - logger.FromRequest(r).WithError(err). - Warnln("api: cannot update user") - } else { - render.JSON(w, user, 200) - } - - if user.Active { - return - } - - err = transferer.Transfer(context.Background(), user) - if err != nil { - logger.FromRequest(r).WithError(err). - Warnln("api: cannot transfer repository ownership") - } - } -} diff --git a/handler/api/users/update_test.go b/handler/api/users/update_test.go deleted file mode 100644 index c2d848f4be..0000000000 --- a/handler/api/users/update_test.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package users - -import ( - "bytes" - "context" - "database/sql" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - "github.com/drone/drone/mock" - - "github.com/go-chi/chi" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestUpdate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - admin := true - userInput := &userInput{ - Admin: &admin, - } - user := &core.User{ - Login: "octocat", - Admin: false, - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), user.Login).Return(user, nil) - users.EXPECT().Update(gomock.Any(), user) - - transferer := mock.NewMockTransferer(controller) - transferer.EXPECT().Transfer(gomock.Any(), user).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(userInput) - w := httptest.NewRecorder() - r := httptest.NewRequest("PATCH", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(users, transferer)(w, r) - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - if got, want := user.Admin, true; got != want { - t.Errorf("Want user admin %v, got %v", want, got) - } - - got, want := new(core.User), user - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestUpdate_BadRequest(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - in := new(bytes.Buffer) - w := httptest.NewRecorder() - r := httptest.NewRequest("PATCH", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(users, nil)(w, r) - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "EOF"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestUpdate_NotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), mockUser.Login).Return(nil, sql.ErrNoRows) - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(mockUser) - w := httptest.NewRecorder() - r := httptest.NewRequest("PATCH", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(users, nil)(w, r) - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), &errors.Error{Message: "sql: no rows in result set"} - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestUpdate_UpdateFailed(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - userInput := &core.User{ - Login: "octocat", - Admin: true, - } - user := &core.User{ - Login: "octocat", - Admin: false, - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), userInput.Login).Return(user, nil) - users.EXPECT().Update(gomock.Any(), user).Return(errors.ErrNotFound) - - c := new(chi.Context) - c.URLParams.Add("user", "octocat") - - in := new(bytes.Buffer) - json.NewEncoder(in).Encode(mockUser) - w := httptest.NewRecorder() - r := httptest.NewRequest("PATCH", "/", in) - r = r.WithContext( - context.WithValue(context.Background(), chi.RouteCtxKey, c), - ) - - HandleUpdate(users, nil)(w, r) - if got, want := w.Code, http.StatusInternalServerError; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := new(errors.Error), errors.ErrNotFound - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); len(diff) > 0 { - t.Errorf(diff) - } -} diff --git a/handler/health/health.go b/handler/health/health.go deleted file mode 100644 index 920728c64c..0000000000 --- a/handler/health/health.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package health - -import ( - "io" - "net/http" - - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" -) - -// New returns a new health check router. -func New() http.Handler { - r := chi.NewRouter() - r.Use(middleware.Recoverer) - r.Use(middleware.NoCache) - r.Handle("/", Handler()) - return r -} - -// Handler creates an http.HandlerFunc that performs system -// healthchecks and returns 500 if the system is in an unhealthy state. -func Handler() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(200) - w.Header().Set("Content-Type", "text/plain") - io.WriteString(w, "OK") - } -} - diff --git a/handler/health/health_test.go b/handler/health/health_test.go deleted file mode 100644 index 1b8ec44110..0000000000 --- a/handler/health/health_test.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package health - -import ( - "net/http/httptest" - "testing" -) - -func TestHandleHealthz(t *testing.T) { - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/healthz", nil) - - Handler().ServeHTTP(w, r) - - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } -} diff --git a/handler/web/hook.go b/handler/web/hook.go deleted file mode 100644 index c836995dd0..0000000000 --- a/handler/web/hook.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package web - -import ( - "context" - "net/http" - "net/http/httputil" - "os" - "strconv" - "time" - - "github.com/sirupsen/logrus" - - "github.com/drone/drone/core" - "github.com/drone/drone/logger" - "github.com/drone/go-scm/scm" -) - -// this is intended for local testing and instructs the handler -// to print the contents of the hook to stdout. -var debugPrintHook = false - -func init() { - debugPrintHook, _ = strconv.ParseBool( - os.Getenv("DRONE_DEBUG_DUMP_HOOK"), - ) -} - -// HandleHook returns an http.HandlerFunc that handles webhooks -// triggered by source code management. -func HandleHook( - repos core.RepositoryStore, - builds core.BuildStore, - triggerer core.Triggerer, - parser core.HookParser, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - - if debugPrintHook { - // if DRONE_DEBUG_DUMP_HOOK=true print the http.Request - // headers and body to stdout. - out, _ := httputil.DumpRequest(r, true) - os.Stderr.Write(out) - } - - hook, remote, err := parser.Parse(r, func(slug string) string { - namespace, name := scm.Split(slug) - repo, err := repos.FindName(r.Context(), namespace, name) - if err != nil { - logrus.WithFields( - logrus.Fields{ - "namespace": namespace, - "name": name, - }).Debugln("cannot find repository") - return "" - } - return repo.Signer - }) - - if err != nil { - logrus.Debugf("cannot parse webhook: %s", err) - writeBadRequest(w, err) - return - } - - if hook == nil { - logrus.Debugf("webhook ignored") - return - } - - // TODO handle ping requests - // TODO consider using scm.Repository in the function callback. - - log := logrus.WithFields(logrus.Fields{ - "namespace": remote.Namespace, - "name": remote.Name, - "event": hook.Event, - "commit": hook.After, - }) - - log.Debugln("webhook parsed") - - repo, err := repos.FindName(r.Context(), remote.Namespace, remote.Name) - if err != nil { - log = log.WithError(err) - log.Debugln("cannot find repository") - writeNotFound(w, err) - return - } - - if !repo.Active { - log.Debugln("ignore webhook, repository inactive") - w.WriteHeader(200) - return - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) - ctx = logger.WithContext(ctx, log) - defer cancel() - - if hook.Event == core.EventPush && hook.Action == core.ActionDelete { - log.WithField("branch", hook.Target).Debugln("branch deleted") - builds.DeleteBranch(ctx, repo.ID, hook.Target) - w.WriteHeader(http.StatusNoContent) - return - } - if hook.Event == core.EventPullRequest && hook.Action == core.ActionClose { - log.WithField("ref", hook.Ref).Debugln("pull request closed") - builds.DeletePull(ctx, repo.ID, scm.ExtractPullRequest(hook.Ref)) - w.WriteHeader(http.StatusNoContent) - return - } - - builds, err := triggerer.Trigger(ctx, repo, hook) - if err != nil { - writeError(w, err) - return - } - - writeJSON(w, builds, 200) - } -} diff --git a/handler/web/link/link.go b/handler/web/link/link.go deleted file mode 100644 index 489b6292c6..0000000000 --- a/handler/web/link/link.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package link - -import ( - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" - - "github.com/go-chi/chi" -) - -// HandleCommit returns an http.HandlerFunc that redirects the -// user to the git resource in the remote source control -// management system. -func HandleCommit(linker core.Linker) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - ctx = r.Context() - namespace = chi.URLParam(r, "namespace") - name = chi.URLParam(r, "name") - commit = chi.URLParam(r, "commit") - ref = r.FormValue("ref") - ) - repo := scm.Join(namespace, name) - to, err := linker.Link(ctx, repo, ref, commit) - if err != nil { - http.Error(w, "Not Found", http.StatusNotFound) - return - } - http.Redirect(w, r, to, http.StatusSeeOther) - } -} - -// HandleTree returns an http.HandlerFunc that redirects the -// user to the git resource in the remote source control -// management system. -func HandleTree(linker core.Linker) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - ctx = r.Context() - namespace = chi.URLParam(r, "namespace") - name = chi.URLParam(r, "name") - ref = chi.URLParam(r, "*") - commit = r.FormValue("sha") - ) - repo := scm.Join(namespace, name) - to, err := linker.Link(ctx, repo, ref, commit) - if err != nil { - http.Error(w, "Not Found", http.StatusNotFound) - return - } - http.Redirect(w, r, to, http.StatusSeeOther) - } -} diff --git a/handler/web/link/link_test.go b/handler/web/link/link_test.go deleted file mode 100644 index 2a10f2fe4e..0000000000 --- a/handler/web/link/link_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package link diff --git a/handler/web/login.go b/handler/web/login.go deleted file mode 100644 index 6b2e660d82..0000000000 --- a/handler/web/login.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package web - -import ( - "context" - "database/sql" - "errors" - "fmt" - "net/http" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/logger" - "github.com/drone/go-login/login" - - "github.com/dchest/uniuri" - "github.com/sirupsen/logrus" -) - -// period at which the user account is synchronized -// with the remote system. Default is weekly. -var syncPeriod = time.Hour * 24 * 7 - -// period at which the sync should timeout -var syncTimeout = time.Minute * 30 - -// HandleLogin creates and http.HandlerFunc that handles user -// authentication and session initialization. -func HandleLogin( - users core.UserStore, - userz core.UserService, - syncer core.Syncer, - session core.Session, - admission core.AdmissionService, - sender core.WebhookSender, -) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := login.ErrorFrom(ctx) - if err != nil { - writeLoginError(w, r, err) - logrus.Debugf("cannot authenticate user: %s", err) - return - } - - // The authorization token is passed from the - // login middleware in the context. - tok := login.TokenFrom(ctx) - - account, err := userz.Find(ctx, tok.Access, tok.Refresh) - if err != nil { - writeLoginError(w, r, err) - logrus.Debugf("cannot find remote user: %s", err) - return - } - - logger := logrus.WithField("login", account.Login) - logger.Debugf("attempting authentication") - - redirect := "/" - user, err := users.FindLogin(ctx, account.Login) - if err == sql.ErrNoRows { - redirect = "/register" - - user = &core.User{ - Login: account.Login, - Avatar: account.Avatar, - Admin: false, - Machine: false, - Active: true, - Syncing: true, - Synced: 0, - LastLogin: time.Now().Unix(), - Created: time.Now().Unix(), - Updated: time.Now().Unix(), - Token: tok.Access, - Refresh: tok.Refresh, - Hash: uniuri.NewLen(32), - } - if !tok.Expires.IsZero() { - user.Expiry = tok.Expires.Unix() - } - - err = admission.Admit(ctx, user) - if err != nil { - writeLoginError(w, r, err) - logger.Errorf("cannot admit user: %s", err) - return - } - - err = users.Create(ctx, user) - if err != nil { - writeLoginError(w, r, err) - logger.Errorf("cannot create user: %s", err) - return - } - - err = sender.Send(ctx, &core.WebhookData{ - Event: core.WebhookEventUser, - Action: core.WebhookActionCreated, - User: user, - }) - if err != nil { - logger.Errorf("cannot send webhook: %s", err) - } else { - logger.Debugf("successfully created user") - } - } else if err != nil { - writeLoginError(w, r, err) - logger.Errorf("cannot find user: %s", err) - return - } else { - err = admission.Admit(ctx, user) - if err != nil { - writeLoginError(w, r, err) - logger.Errorf("cannot admit user: %s", err) - return - } - } - - if user.Machine { - writeLoginErrorStr(w, r, "Machine account login is forbidden") - return - } - - if user.Active == false { - writeLoginErrorStr(w, r, "Account is not active") - return - } - - user.Avatar = account.Avatar - user.Token = tok.Access - user.Refresh = tok.Refresh - user.LastLogin = time.Now().Unix() - if !tok.Expires.IsZero() { - user.Expiry = tok.Expires.Unix() - } - - // If the user account has never been synchronized we - // execute the synchronization logic. - if time.Unix(user.Synced, 0).Add(syncPeriod).Before(time.Now()) { - user.Syncing = true - } - - err = users.Update(ctx, user) - if err != nil { - // if the account update fails we should still - // proceed to create the user session. This is - // considered a non-fatal error. - logger.Errorf("cannot update user: %s", err) - } - - // launch the synchronization process in a go-routine, - // since it is a long-running process and can take up - // to a few minutes. - if user.Syncing { - go synchronize(ctx, syncer, user) - } - - // If the user account has not completed registration, - // redirect to the registration form. - if len(user.Email) == 0 && user.Created > 1619841600 { - redirect = "/register" - } - - logger.Debugf("authentication successful") - - session.Create(w, user) - http.Redirect(w, r, redirect, http.StatusSeeOther) - } -} - -func synchronize(ctx context.Context, syncer core.Syncer, user *core.User) { - log := logrus.WithField("login", user.Login) - log.Debugf("begin synchronization") - - timeout, cancel := context.WithTimeout(context.Background(), syncTimeout) - timeout = logger.WithContext(timeout, log) - defer cancel() - _, err := syncer.Sync(timeout, user) - if err != nil { - log.Debugf("synchronization failed: %s", err) - } else { - log.Debugf("synchronization success") - } -} - -func writeLoginError(w http.ResponseWriter, r *http.Request, err error) { - http.Redirect(w, r, "/login/error?message="+err.Error(), http.StatusSeeOther) -} - -func writeLoginErrorStr(w http.ResponseWriter, r *http.Request, s string) { - writeLoginError(w, r, errors.New(s)) -} - -func writeCookie(w http.ResponseWriter, cookie *http.Cookie) { - w.Header().Set("Set-Cookie", cookie.String()+"; SameSite=lax") -} - -// HandleLoginForm creates and http.HandlerFunc that presents the -// user with an Login form for password-based authentication. -func HandleLoginForm() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/html") - fmt.Fprint(w, loginForm) - } -} - -// html page displayed to collect credentials. -var loginForm = ` -
- - - -
-` diff --git a/handler/web/login_test.go b/handler/web/login_test.go deleted file mode 100644 index e988047378..0000000000 --- a/handler/web/login_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package web diff --git a/handler/web/logout.go b/handler/web/logout.go deleted file mode 100644 index a969a32ef8..0000000000 --- a/handler/web/logout.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package web - -import ( - "net/http" - - "github.com/drone/drone-ui/dist" -) - -// HandleLogout creates an http.HandlerFunc that handles -// session termination. -func HandleLogout() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("Set-Cookie", "_session_=deleted; Path=/; Max-Age=0") - w.Header().Set("Content-Type", "text/html; charset=UTF-8") - w.Write( - dist.MustLookup("/index.html"), - ) - } -} diff --git a/handler/web/logout_test.go b/handler/web/logout_test.go deleted file mode 100644 index 1666b27a33..0000000000 --- a/handler/web/logout_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package web - -import ( - "net/http/httptest" - "testing" -) - -func TestLogout(t *testing.T) { - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/logout", nil) - - HandleLogout().ServeHTTP(w, r) - - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - if got, want := w.Header().Get("Set-Cookie"), "_session_=deleted; Path=/; Max-Age=0"; want != got { - t.Errorf("Want response code %q, got %q", want, got) - } -} diff --git a/handler/web/pages.go b/handler/web/pages.go deleted file mode 100644 index 4bf347e42c..0000000000 --- a/handler/web/pages.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package web - -import ( - "bytes" - "crypto/md5" - "fmt" - "net/http" - "time" - - "github.com/drone/drone-ui/dist" - "github.com/drone/drone/core" -) - -func HandleIndex(host string, session core.Session, license core.LicenseService) http.HandlerFunc { - return func(rw http.ResponseWriter, r *http.Request) { - user, _ := session.Get(r) - if user == nil && r.URL.Path == "/" { - http.Redirect(rw, r, "/welcome", 303) - return - } - - out := dist.MustLookup("/index.html") - ctx := r.Context() - - if ok, _ := license.Exceeded(ctx); ok { - out = bytes.Replace(out, head, exceeded, -1) - } else if license.Expired(ctx) { - out = bytes.Replace(out, head, expired, -1) - } - rw.Header().Set("Content-Type", "text/html; charset=UTF-8") - rw.Write(out) - } -} - -var ( - head = []byte(``) - expired = []byte(``) - exceeded = []byte(``) -) - -func setupCache(h http.Handler) http.Handler { - data := []byte(time.Now().String()) - etag := fmt.Sprintf("%x", md5.Sum(data)) - - return http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Cache-Control", "public, max-age=31536000") - w.Header().Del("Expires") - w.Header().Del("Pragma") - w.Header().Set("ETag", etag) - h.ServeHTTP(w, r) - }, - ) -} - -// func userFromSession(r *http.Request, users core.UserStore, secret string) *core.User { -// cookie, err := r.Cookie("_session_") -// if err != nil { -// return nil -// } -// login := authcookie.Login(cookie.Value, []byte(secret)) -// if login == "" { -// return nil -// } -// user, err := users.FindLogin(r.Context(), login) -// if err != nil { -// return nil -// } -// return user -// } - -// var tmpl = mustCreateTemplate( -// string(dist.MustLookup("/index.html")), -// ) - -// // default func map with json parser. -// var funcMap = template.FuncMap{ -// "json": func(v interface{}) template.JS { -// a, _ := json.Marshal(v) -// return template.JS(a) -// }, -// } - -// // helper function creates a new template from the text string. -// func mustCreateTemplate(text string) *template.Template { -// templ, err := createTemplate(text) -// if err != nil { -// panic(err) -// } -// return templ -// } - -// // helper function creates a new template from the text string. -// func createTemplate(text string) (*template.Template, error) { -// templ, err := template.New("_").Funcs(funcMap).Parse(partials) -// if err != nil { -// return nil, err -// } -// return templ.Parse( -// injectPartials(text), -// ) -// } - -// // helper function that parses the html file and injects -// // named partial templates. -// func injectPartials(s string) string { -// w := new(bytes.Buffer) -// r := bytes.NewBufferString(s) -// t := html.NewTokenizer(r) -// for { -// tt := t.Next() -// if tt == html.ErrorToken { -// break -// } -// if tt == html.CommentToken { -// txt := string(t.Text()) -// txt = strings.TrimSpace(txt) -// seg := strings.Split(txt, ":") -// if len(seg) == 2 && seg[0] == "drone" { -// fmt.Fprintf(w, "{{ template %q . }}", seg[1]) -// continue -// } -// } -// w.Write(t.Raw()) -// } -// return w.String() -// } - -// const partials = ` -// {{define "user"}} -// {{ if .user }} -// -// {{ end }} -// {{end}} -// {{define "csrf"}} -// {{ if .csrf -}} -// -// {{- end }} -// {{end}} -// {{define "version"}} -// -// {{end}} -// {{define "docs"}} -// {{ if .docs -}} -// -// {{- end }} -// {{end}} -// ` - -var landingPage = ` -` diff --git a/handler/web/varz.go b/handler/web/varz.go deleted file mode 100644 index 6799bea045..0000000000 --- a/handler/web/varz.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package web - -import ( - "net/http" - "time" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -type varz struct { - SCM *scmInfo `json:"scm"` - License *licenseInfo `json:"license"` -} - -type scmInfo struct { - URL string `json:"url"` - Rate *rateInfo `json:"rate"` -} - -type rateInfo struct { - Limit int `json:"limit"` - Remaining int `json:"remaining"` - Reset int64 `json:"reset"` -} - -type licenseInfo struct { - Kind string `json:"kind"` - Seats int64 `json:"seats"` - SeatsUsed int64 `json:"seats_used,omitempty"` - SeatsAvail int64 `json:"seats_available,omitempty"` - Repos int64 `json:"repos"` - ReposUsed int64 `json:"repos_used,omitempty"` - ReposAvail int64 `json:"repos_available,omitempty"` - Expires time.Time `json:"expire_at,omitempty"` -} - -// HandleVarz creates an http.HandlerFunc that exposes internal system -// information. -func HandleVarz(client *scm.Client, license *core.License) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - rate := client.Rate() - v := &varz{ - License: &licenseInfo{ - Kind: license.Kind, - Seats: license.Users, - Repos: license.Repos, - Expires: license.Expires, - }, - SCM: &scmInfo{ - URL: client.BaseURL.String(), - Rate: &rateInfo{ - Limit: rate.Limit, - Remaining: rate.Remaining, - Reset: rate.Reset, - }, - }, - } - writeJSON(w, v, 200) - } -} diff --git a/handler/web/varz_test.go b/handler/web/varz_test.go deleted file mode 100644 index 345b8a0fc2..0000000000 --- a/handler/web/varz_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package web - -import ( - "encoding/json" - "net/http/httptest" - "net/url" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" - "github.com/google/go-cmp/cmp" -) - -func TestHandleVarz(t *testing.T) { - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - client := new(scm.Client) - client.BaseURL, _ = url.Parse("https://github.com") - client.SetRate(scm.Rate{ - Limit: 5000, - Remaining: 875, - Reset: 1523640878, - }) - - license := &core.License{ - Kind: core.LicenseStandard, - Repos: 50, - Users: 100, - } - HandleVarz(client, license).ServeHTTP(w, r) - - if got, want := w.Code, 200; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - got, want := &varz{}, mockVarz - json.NewDecoder(w.Body).Decode(got) - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -var mockVarz = &varz{ - SCM: &scmInfo{ - URL: "https://github.com", - Rate: &rateInfo{ - Limit: 5000, - Remaining: 875, - Reset: 1523640878, - }, - }, - License: &licenseInfo{ - Kind: "standard", - Seats: 100, - SeatsUsed: 0, - SeatsAvail: 0, - Repos: 50, - ReposUsed: 0, - ReposAvail: 0, - }, -} diff --git a/handler/web/version.go b/handler/web/version.go deleted file mode 100644 index 7c48cf6b57..0000000000 --- a/handler/web/version.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package web - -import ( - "net/http" - - "github.com/drone/drone/version" -) - -// HandleVersion creates an http.HandlerFunc that returns the -// version number and build details. -func HandleVersion(w http.ResponseWriter, r *http.Request) { - v := struct { - Source string `json:"source,omitempty"` - Version string `json:"version,omitempty"` - Commit string `json:"commit,omitempty"` - }{ - Source: version.GitRepository, - Commit: version.GitCommit, - Version: version.Version.String(), - } - writeJSON(w, &v, 200) -} diff --git a/handler/web/version_test.go b/handler/web/version_test.go deleted file mode 100644 index 1c7e513e10..0000000000 --- a/handler/web/version_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package web - -// func TestHandleVersion(t *testing.T) { -// controller := gomock.NewController(t) -// defer controller.Finish() - -// w := httptest.NewRecorder() -// r := httptest.NewRequest("GET", "/version", nil) - -// mockVersion := &core.Version{ -// Source: "github.com/octocat/hello-world", -// Version: "1.0.0", -// Commit: "ad2aec", -// } - -// h := HandleVersion(mockVersion) -// h.ServeHTTP(w, r) - -// if got, want := w.Code, 200; want != got { -// t.Errorf("Want response code %d, got %d", want, got) -// } - -// got, want := &core.Version{}, mockVersion -// json.NewDecoder(w.Body).Decode(got) -// if !reflect.DeepEqual(got, want) { -// t.Errorf("response body does match expected result") -// pretty.Ldiff(t, got, want) -// } -// } diff --git a/handler/web/web.go b/handler/web/web.go deleted file mode 100644 index c886f6d7ba..0000000000 --- a/handler/web/web.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package web - -import ( - "net/http" - - "github.com/drone/drone-ui/dist" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/web/link" - "github.com/drone/drone/logger" - "github.com/drone/go-login/login" - "github.com/drone/go-scm/scm" - - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" - "github.com/unrolled/secure" -) - -func New( - admitter core.AdmissionService, - builds core.BuildStore, - client *scm.Client, - hooks core.HookParser, - license *core.License, - licenses core.LicenseService, - linker core.Linker, - login login.Middleware, - repos core.RepositoryStore, - session core.Session, - syncer core.Syncer, - triggerer core.Triggerer, - users core.UserStore, - userz core.UserService, - webhook core.WebhookSender, - options secure.Options, - system *core.System, -) Server { - return Server{ - Admitter: admitter, - Builds: builds, - Client: client, - Hooks: hooks, - License: license, - Licenses: licenses, - Linker: linker, - Login: login, - Repos: repos, - Session: session, - Syncer: syncer, - Triggerer: triggerer, - Users: users, - Userz: userz, - Webhook: webhook, - Options: options, - Host: system.Host, - } -} - -// Server is a http.Handler which exposes drone functionality over HTTP. -type Server struct { - Admitter core.AdmissionService - Builds core.BuildStore - Client *scm.Client - Hooks core.HookParser - License *core.License - Licenses core.LicenseService - Linker core.Linker - Login login.Middleware - Repos core.RepositoryStore - Session core.Session - Syncer core.Syncer - Triggerer core.Triggerer - Users core.UserStore - Userz core.UserService - Webhook core.WebhookSender - Options secure.Options - Host string -} - -// Handler returns an http.Handler -func (s Server) Handler() http.Handler { - r := chi.NewRouter() - r.Use(middleware.Recoverer) - r.Use(middleware.NoCache) - r.Use(logger.Middleware) - r.Use(middleware.StripSlashes) - - sec := secure.New(s.Options) - r.Use(sec.Handler) - - r.Route("/hook", func(r chi.Router) { - r.Post("/", HandleHook(s.Repos, s.Builds, s.Triggerer, s.Hooks)) - }) - - r.Get("/link/{namespace}/{name}/tree/*", link.HandleTree(s.Linker)) - r.Get("/link/{namespace}/{name}/src/*", link.HandleTree(s.Linker)) - r.Get("/link/{namespace}/{name}/commit/{commit}", link.HandleCommit(s.Linker)) - r.Get("/version", HandleVersion) - r.Get("/varz", HandleVarz(s.Client, s.License)) - - r.Handle("/login", - s.Login.Handler( - http.HandlerFunc( - HandleLogin( - s.Users, - s.Userz, - s.Syncer, - s.Session, - s.Admitter, - s.Webhook, - ), - ), - ), - ) - r.Get("/logout", HandleLogout()) - r.Post("/logout", HandleLogout()) - - h := http.FileServer(dist.New()) - h = setupCache(h) - r.Handle("/favicon.png", h) - r.Handle("/manifest.json", h) - r.Handle("/asset-manifest.json", h) - r.Handle("/static/*filepath", h) - r.NotFound(HandleIndex(s.Host, s.Session, s.Licenses)) - - return r -} diff --git a/handler/web/writer.go b/handler/web/writer.go deleted file mode 100644 index 55fba239cb..0000000000 --- a/handler/web/writer.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package web - -import ( - "encoding/json" - "errors" - "net/http" - "os" - "strconv" -) - -// indent the json-encoded API responses -var indent bool - -func init() { - indent, _ = strconv.ParseBool( - os.Getenv("HTTP_JSON_INDENT"), - ) -} - -var ( - // errInvalidToken is returned when the api request token is invalid. - errInvalidToken = errors.New("Invalid or missing token") - - // errUnauthorized is returned when the user is not authorized. - errUnauthorized = errors.New("Unauthorized") - - // errForbidden is returned when user access is forbidden. - errForbidden = errors.New("Forbidden") - - // errNotFound is returned when a resource is not found. - errNotFound = errors.New("Not Found") -) - -// Error represents a json-encoded API error. -type Error struct { - Message string `json:"message"` -} - -// writeErrorCode writes the json-encoded error message to the response. -func writeErrorCode(w http.ResponseWriter, err error, status int) { - writeJSON(w, &Error{Message: err.Error()}, status) -} - -// writeError writes the json-encoded error message to the response -// with a 500 internal server error. -func writeError(w http.ResponseWriter, err error) { - writeErrorCode(w, err, 500) -} - -// writeNotFound writes the json-encoded error message to the response -// with a 404 not found status code. -func writeNotFound(w http.ResponseWriter, err error) { - writeErrorCode(w, err, 404) -} - -// writeUnauthorized writes the json-encoded error message to the response -// with a 401 unauthorized status code. -func writeUnauthorized(w http.ResponseWriter, err error) { - writeErrorCode(w, err, 401) -} - -// writeForbidden writes the json-encoded error message to the response -// with a 403 forbidden status code. -func writeForbidden(w http.ResponseWriter, err error) { - writeErrorCode(w, err, 403) -} - -// writeBadRequest writes the json-encoded error message to the response -// with a 400 bad request status code. -func writeBadRequest(w http.ResponseWriter, err error) { - writeErrorCode(w, err, 400) -} - -// writeJSON writes the json-encoded error message to the response -// with a 400 bad request status code. -func writeJSON(w http.ResponseWriter, v interface{}, status int) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(status) - enc := json.NewEncoder(w) - if indent { - enc.SetIndent("", " ") - } - enc.Encode(v) -} diff --git a/handler/web/writer_test.go b/handler/web/writer_test.go deleted file mode 100644 index e5cb8ad985..0000000000 --- a/handler/web/writer_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package web - -import ( - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "testing" -) - -func TestWriteError(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - writeError(w, err) - - if got, want := w.Code, 500; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteErrorCode(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - writeErrorCode(w, err, 418) - - if got, want := w.Code, 418; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteNotFound(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - writeNotFound(w, err) - - if got, want := w.Code, 404; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteUnauthorized(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - writeUnauthorized(w, err) - - if got, want := w.Code, 401; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteForbidden(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - writeForbidden(w, err) - - if got, want := w.Code, 403; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteBadRequest(t *testing.T) { - w := httptest.NewRecorder() - - err := errors.New("pc load letter") - writeBadRequest(w, err) - - if got, want := w.Code, 400; want != got { - t.Errorf("Want response code %d, got %d", want, got) - } - - errjson := &Error{} - json.NewDecoder(w.Body).Decode(errjson) - if got, want := errjson.Message, err.Error(); got != want { - t.Errorf("Want error message %s, got %s", want, got) - } -} - -func TestWriteJSON(t *testing.T) { - // without indent - { - w := httptest.NewRecorder() - writeJSON(w, map[string]string{"hello": "world"}, http.StatusTeapot) - if got, want := w.Body.String(), "{\"hello\":\"world\"}\n"; got != want { - t.Errorf("Want JSON body %q, got %q", want, got) - } - if got, want := w.HeaderMap.Get("Content-Type"), "application/json"; got != want { - t.Errorf("Want Content-Type %q, got %q", want, got) - } - if got, want := w.Code, http.StatusTeapot; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - } - // with indent - { - indent = true - defer func() { - indent = false - }() - w := httptest.NewRecorder() - writeJSON(w, map[string]string{"hello": "world"}, http.StatusTeapot) - if got, want := w.Body.String(), "{\n \"hello\": \"world\"\n}\n"; got != want { - t.Errorf("Want JSON body %q, got %q", want, got) - } - } -} diff --git a/livelog/livelog.go b/livelog/livelog.go deleted file mode 100644 index 5e288189b5..0000000000 --- a/livelog/livelog.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package livelog - -import ( - "github.com/drone/drone/core" - "github.com/drone/drone/service/redisdb" -) - -// New creates a new log streamer. If Redis client passed as parameter is not nil it uses -// a Redis implementation, otherwise it uses an in-memory implementation. -func New(rdb redisdb.RedisDB) core.LogStream { - if rdb != nil { - return newStreamRedis(rdb) - } - - return newStreamer() -} diff --git a/livelog/livelog_oss.go b/livelog/livelog_oss.go deleted file mode 100644 index c638b25e55..0000000000 --- a/livelog/livelog_oss.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package livelog - -import ( - "github.com/drone/drone/core" - "github.com/drone/drone/service/redisdb" -) - -// New creates a new in-memory log streamer. -func New(r redisdb.RedisDB) core.LogStream { - return newStreamer() -} diff --git a/livelog/stream.go b/livelog/stream.go deleted file mode 100644 index eec557fdc8..0000000000 --- a/livelog/stream.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package livelog - -import ( - "context" - "sync" - - "github.com/drone/drone/core" -) - -// this is the amount of items that are stored in memory -// in the buffer. This should result in approximately 10kb -// of memory allocated per-stream and per-subscriber, not -// including any logdata stored in these structures. -const bufferSize = 5000 - -type stream struct { - sync.Mutex - - hist []*core.Line - list map[*subscriber]struct{} -} - -func newStream() *stream { - return &stream{ - list: map[*subscriber]struct{}{}, - } -} - -func (s *stream) write(line *core.Line) error { - s.Lock() - s.hist = append(s.hist, line) - for l := range s.list { - l.publish(line) - } - // the history should not be unbounded. The history - // slice is capped and items are removed in a FIFO - // ordering when capacity is reached. - if size := len(s.hist); size >= bufferSize { - s.hist = s.hist[size-bufferSize:] - } - s.Unlock() - return nil -} - -func (s *stream) subscribe(ctx context.Context) (<-chan *core.Line, <-chan error) { - sub := &subscriber{ - handler: make(chan *core.Line, bufferSize), - closec: make(chan struct{}), - } - err := make(chan error) - - s.Lock() - for _, line := range s.hist { - sub.publish(line) - } - s.list[sub] = struct{}{} - s.Unlock() - - go func() { - defer close(err) - select { - case <-sub.closec: - case <-ctx.Done(): - sub.close() - } - }() - return sub.handler, err -} - -func (s *stream) close() error { - s.Lock() - defer s.Unlock() - for sub := range s.list { - delete(s.list, sub) - sub.close() - } - return nil -} diff --git a/livelog/stream_redis.go b/livelog/stream_redis.go deleted file mode 100644 index 9a72fab79f..0000000000 --- a/livelog/stream_redis.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package livelog - -import ( - "context" - "encoding/json" - "fmt" - "strconv" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/service/redisdb" - - "github.com/go-redis/redis/v8" -) - -func newStreamRedis(r redisdb.RedisDB) core.LogStream { - return streamRedis{ - rdb: r, - } -} - -const ( - redisKeyExpiryTime = 5 * time.Hour // How long each key exists in redis - redisPollTime = 100 * time.Millisecond // should not be too large to avoid redis clients getting occupied for long - redisTailMaxTime = 1 * time.Hour // maximum duration a tail can last - redisEntryKey = "line" - redisStreamPrefix = "drone-log-" -) - -type streamRedis struct { - rdb redisdb.RedisDB -} - -// Create creates a redis stream and sets an expiry on it. -func (r streamRedis) Create(ctx context.Context, id int64) error { - // Delete if a stream already exists with the same key - _ = r.Delete(ctx, id) - - client := r.rdb.Client() - - key := redisStreamPrefix + strconv.FormatInt(id, 10) - - addResp := client.XAdd(ctx, &redis.XAddArgs{ - Stream: key, - ID: "*", // auto-generate a unique incremental ID - MaxLen: bufferSize, - Approx: true, - Values: map[string]interface{}{redisEntryKey: []byte{}}, - }) - if err := addResp.Err(); err != nil { - return fmt.Errorf("livelog/redis: could not create stream with key %s", key) - } - - res := client.Expire(ctx, key, redisKeyExpiryTime) - if err := res.Err(); err != nil { - return fmt.Errorf("livelog/redis: could not set expiry for key %s", key) - } - - return nil -} - -// Delete deletes a stream -func (r streamRedis) Delete(ctx context.Context, id int64) error { - client := r.rdb.Client() - - key := redisStreamPrefix + strconv.FormatInt(id, 10) - - if err := r._exists(ctx, key); err != nil { - return err - } - - deleteResp := client.Del(ctx, key) - if err := deleteResp.Err(); err != nil { - return fmt.Errorf("livelog/redis: could not delete stream for step %d", id) - } - - return nil -} - -// Write writes information into the Redis stream -func (r streamRedis) Write(ctx context.Context, id int64, line *core.Line) error { - client := r.rdb.Client() - - key := redisStreamPrefix + strconv.FormatInt(id, 10) - - if err := r._exists(ctx, key); err != nil { - return err - } - - lineJsonData, _ := json.Marshal(line) - addResp := client.XAdd(ctx, &redis.XAddArgs{ - Stream: key, - ID: "*", // auto-generate a unique incremental ID - MaxLen: bufferSize, - Approx: true, - Values: map[string]interface{}{redisEntryKey: lineJsonData}, - }) - if err := addResp.Err(); err != nil { - return err - } - - return nil -} - -// Tail returns back all the lines in the stream. -func (r streamRedis) Tail(ctx context.Context, id int64) (<-chan *core.Line, <-chan error) { - client := r.rdb.Client() - - key := redisStreamPrefix + strconv.FormatInt(id, 10) - - if err := r._exists(ctx, key); err != nil { - return nil, nil - } - - chLines := make(chan *core.Line, bufferSize) - chErr := make(chan error, 1) - - go func() { - defer close(chErr) - defer close(chLines) - timeout := time.After(redisTailMaxTime) // polling should not last for longer than tailMaxTime - - // Keep reading from the stream and writing to the channel - lastID := "0" - - for { - select { - case <-ctx.Done(): - return - case <-timeout: - return - default: - readResp := client.XRead(ctx, &redis.XReadArgs{ - Streams: append([]string{key}, lastID), - Block: redisPollTime, // periodically check for ctx.Done - }) - if readResp.Err() != nil && readResp.Err() != redis.Nil { // readResp.Err() is sometimes set to "redis: nil" instead of nil - chErr <- readResp.Err() - return - } - - for _, msg := range readResp.Val() { - messages := msg.Messages - if len(messages) > 0 { - lastID = messages[len(messages)-1].ID - } else { // should not happen - return - } - - for _, message := range messages { - values := message.Values - if val, ok := values[redisEntryKey]; ok { - var line *core.Line - if err := json.Unmarshal([]byte(val.(string)), &line); err != nil { - continue // ignore errors in the stream - } - chLines <- line - } - } - } - } - } - }() - - return chLines, chErr -} - -// Info returns info about log streams present in redis -func (r streamRedis) Info(ctx context.Context) (info *core.LogStreamInfo) { - client := r.rdb.Client() - - info = &core.LogStreamInfo{ - Streams: make(map[int64]int), - } - - keysResp := client.Keys(ctx, redisStreamPrefix+"*") - if err := keysResp.Err(); err != nil { - return - } - - for _, key := range keysResp.Val() { - ids := key[len(redisStreamPrefix):] - id, err := strconv.ParseInt(ids, 10, 64) - if err != nil { - continue - } - - lenResp := client.XLen(ctx, key) - if err := lenResp.Err(); err != nil { - continue - } - - size := int(lenResp.Val()) - - info.Streams[id] = size - } - - return -} - -func (r streamRedis) _exists(ctx context.Context, key string) error { - client := r.rdb.Client() - - exists := client.Exists(ctx, key) - if exists.Err() != nil || exists.Val() == 0 { - return fmt.Errorf("livelog/redis: log stream %s not found", key) - } - - return nil -} diff --git a/livelog/stream_test.go b/livelog/stream_test.go deleted file mode 100644 index 3548b5e3f6..0000000000 --- a/livelog/stream_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package livelog - -import ( - "context" - "sync" - "testing" - "time" - - "github.com/drone/drone/core" -) - -func TestStream(t *testing.T) { - w := sync.WaitGroup{} - - s := newStream() - - // test ability to replay history. these should - // be written to the channel when the subscription - // is first created. - - s.write(&core.Line{Number: 1}) - s.write(&core.Line{Number: 2}) - s.write(&core.Line{Number: 3}) - w.Add(3) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - stream, errc := s.subscribe(ctx) - - w.Add(4) - go func() { - s.write(&core.Line{Number: 4}) - s.write(&core.Line{Number: 5}) - s.write(&core.Line{Number: 6}) - w.Done() - }() - - // the code above adds 6 lines to the log stream. - // the wait group blocks until all 6 items are - // received. - - go func() { - for { - select { - case <-errc: - return - case <-stream: - w.Done() - } - } - }() - - w.Wait() -} - -func TestStream_Close(t *testing.T) { - s := newStream() - s.hist = []*core.Line{ - &core.Line{}, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - s.subscribe(ctx) - if got, want := len(s.list), 1; got != want { - t.Errorf("Want %d subscribers before close, got %d", want, got) - } - - var sub *subscriber - for sub = range s.list { - } - - if got, want := sub.closed, false; got != want { - t.Errorf("Want subscriber open") - } - - if err := s.close(); err != nil { - t.Error(err) - } - - if got, want := len(s.list), 0; got != want { - t.Errorf("Want %d subscribers after close, got %d", want, got) - } - - <-time.After(time.Millisecond) - - if got, want := sub.closed, true; got != want { - t.Errorf("Want subscriber closed") - } -} - -func TestStream_BufferHistory(t *testing.T) { - s := newStream() - - // exceeds the history buffer by +10 - x := new(core.Line) - for i := 0; i < bufferSize+10; i++ { - s.write(x) - } - - if got, want := len(s.hist), bufferSize; got != want { - t.Errorf("Want %d history items, got %d", want, got) - } - - latest := &core.Line{Number: 1} - s.write(latest) - - if got, want := s.hist[len(s.hist)-1], latest; got != want { - t.Errorf("Expect history stored in FIFO order") - } -} diff --git a/livelog/streamer.go b/livelog/streamer.go deleted file mode 100644 index 09f0b166ab..0000000000 --- a/livelog/streamer.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package livelog - -import ( - "context" - "errors" - "sync" - - "github.com/drone/drone/core" -) - -// error returned when a stream is not registered with -// the streamer. -var errStreamNotFound = errors.New("stream: not found") - -type streamer struct { - sync.Mutex - - streams map[int64]*stream -} - -// New returns a new in-memory log streamer. -func newStreamer() core.LogStream { - return &streamer{ - streams: make(map[int64]*stream), - } -} - -func (s *streamer) Create(ctx context.Context, id int64) error { - s.Lock() - s.streams[id] = newStream() - s.Unlock() - return nil -} - -func (s *streamer) Delete(ctx context.Context, id int64) error { - s.Lock() - stream, ok := s.streams[id] - if ok { - delete(s.streams, id) - } - s.Unlock() - if !ok { - return errStreamNotFound - } - return stream.close() -} - -func (s *streamer) Write(ctx context.Context, id int64, line *core.Line) error { - s.Lock() - stream, ok := s.streams[id] - s.Unlock() - if !ok { - return errStreamNotFound - } - return stream.write(line) -} - -func (s *streamer) Tail(ctx context.Context, id int64) (<-chan *core.Line, <-chan error) { - s.Lock() - stream, ok := s.streams[id] - s.Unlock() - if !ok { - return nil, nil - } - return stream.subscribe(ctx) -} - -func (s *streamer) Info(ctx context.Context) *core.LogStreamInfo { - s.Lock() - defer s.Unlock() - info := &core.LogStreamInfo{ - Streams: map[int64]int{}, - } - for id, stream := range s.streams { - stream.Lock() - info.Streams[id] = len(stream.list) - stream.Unlock() - } - return info -} diff --git a/livelog/streamer_test.go b/livelog/streamer_test.go deleted file mode 100644 index df7f5f84e3..0000000000 --- a/livelog/streamer_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package livelog - -import ( - "context" - "sync" - "testing" - - "github.com/drone/drone/core" - - "github.com/google/go-cmp/cmp" -) - -func TestStreamer(t *testing.T) { - s := newStreamer().(*streamer) - err := s.Create(context.Background(), 1) - if err != nil { - t.Error(err) - } - if len(s.streams) == 0 { - t.Errorf("Want stream registered") - } - - w := sync.WaitGroup{} - w.Add(4) - go func() { - s.Write(context.Background(), 1, &core.Line{}) - s.Write(context.Background(), 1, &core.Line{}) - s.Write(context.Background(), 1, &core.Line{}) - w.Done() - }() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - tail, errc := s.Tail(ctx, 1) - - go func() { - for { - select { - case <-errc: - return - case <-ctx.Done(): - return - case <-tail: - w.Done() - } - } - }() - - w.Wait() -} - -func TestStreamerDelete(t *testing.T) { - s := newStreamer().(*streamer) - err := s.Create(context.Background(), 1) - if err != nil { - t.Error(err) - } - if len(s.streams) == 0 { - t.Errorf("Want stream registered") - } - err = s.Delete(context.Background(), 1) - if err != nil { - t.Error(err) - } - if len(s.streams) != 0 { - t.Errorf("Want stream unregistered") - } -} - -func TestStreamerDeleteErr(t *testing.T) { - s := newStreamer() - err := s.Delete(context.Background(), 1) - if err != errStreamNotFound { - t.Errorf("Want errStreamNotFound") - } -} - -func TestStreamerWriteErr(t *testing.T) { - s := newStreamer() - err := s.Write(context.Background(), 1, &core.Line{}) - if err != errStreamNotFound { - t.Errorf("Want errStreamNotFound") - } -} - -func TestStreamTailNotFound(t *testing.T) { - s := newStreamer() - outc, errc := s.Tail(context.Background(), 0) - if outc != nil && errc != nil { - t.Errorf("Expect nil channel when stream not found") - } -} - -func TestStreamerInfo(t *testing.T) { - s := newStreamer().(*streamer) - s.streams[1] = &stream{list: map[*subscriber]struct{}{{}: struct{}{}, {}: struct{}{}}} - s.streams[2] = &stream{list: map[*subscriber]struct{}{{}: struct{}{}}} - s.streams[3] = &stream{list: map[*subscriber]struct{}{}} - got := s.Info(context.Background()) - - want := &core.LogStreamInfo{ - Streams: map[int64]int{ - 1: 2, - 2: 1, - 3: 0, - }, - } - - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} diff --git a/livelog/sub.go b/livelog/sub.go deleted file mode 100644 index d96460e70f..0000000000 --- a/livelog/sub.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package livelog - -import ( - "sync" - - "github.com/drone/drone/core" -) - -type subscriber struct { - sync.Mutex - - handler chan *core.Line - closec chan struct{} - closed bool -} - -func (s *subscriber) publish(line *core.Line) { - select { - case <-s.closec: - case s.handler <- line: - default: - // lines are sent on a buffered channel. If there - // is a slow consumer that is not processing events, - // the buffered channel will fill and newer messages - // are ignored. - } -} - -func (s *subscriber) close() { - s.Lock() - if !s.closed { - close(s.closec) - s.closed = true - } - s.Unlock() -} diff --git a/livelog/sub_test.go b/livelog/sub_test.go deleted file mode 100644 index ab9bc07213..0000000000 --- a/livelog/sub_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package livelog - -import ( - "testing" - - "github.com/drone/drone/core" -) - -func TestSubscription_publish(t *testing.T) { - s := &subscriber{ - handler: make(chan *core.Line, 5), - closec: make(chan struct{}), - } - - e := new(core.Line) - s.publish(e) - - if got, want := len(s.handler), 1; got != want { - t.Errorf("Want buffered channel size %d, got %d", want, got) - } - if got, want := <-s.handler, e; got != want { - t.Errorf("Want log entry received from channel") - } - if got, want := len(s.handler), 0; got != want { - t.Errorf("Want buffered channel size %d, got %d", want, got) - } -} - -func TestSubscription_buffer(t *testing.T) { - s := &subscriber{ - handler: make(chan *core.Line, 1), - closec: make(chan struct{}), - } - - // the buffer size is 1 to simulate what happens - // if the subscriber cannot keep up with processing - // and the buffer fills up. In this case, lines - // should be ignored until pending lines are - // processed. - - e := new(core.Line) - s.publish(e) - s.publish(e) - s.publish(e) - s.publish(e) - s.publish(e) - - if got, want := len(s.handler), 1; got != want { - t.Errorf("Want buffered channel size %d, got %d", want, got) - } -} - -func TestSubscription_stop(t *testing.T) { - s := &subscriber{ - handler: make(chan *core.Line, 1), - closec: make(chan struct{}), - } - - if got, want := s.closed, false; got != want { - t.Errorf("Want subscription open") - } - - s.close() - if got, want := s.closed, true; got != want { - t.Errorf("Want subscription closed") - } - - // if the subscription is closed we should - // ignore any new events being published. - - e := new(core.Line) - s.publish(e) - s.publish(e) - s.publish(e) - s.publish(e) - s.publish(e) -} diff --git a/logger/handler.go b/logger/handler.go deleted file mode 100644 index 54440518b4..0000000000 --- a/logger/handler.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package logger - -import ( - "net/http" - "time" - - "github.com/segmentio/ksuid" - "github.com/sirupsen/logrus" -) - -// Middleware provides logging middleware. -func Middleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - id := r.Header.Get("X-Request-ID") - if id == "" { - id = ksuid.New().String() - } - ctx := r.Context() - log := FromContext(ctx).WithField("request-id", id) - ctx = WithContext(ctx, log) - start := time.Now() - next.ServeHTTP(w, r.WithContext(ctx)) - end := time.Now() - log.WithFields(logrus.Fields{ - "method": r.Method, - "request": r.RequestURI, - "remote": r.RemoteAddr, - "latency": end.Sub(start), - "time": end.Format(time.RFC3339), - "authtype": authType(r), - }).Debug() - }) -} - -func authType(r *http.Request) string { - if r.Header.Get("Authorization") != "" || r.FormValue("access_token") != "" { - return "token" - } - - return "cookie" -} diff --git a/logger/handler_test.go b/logger/handler_test.go deleted file mode 100644 index edddbfb50e..0000000000 --- a/logger/handler_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package logger - -import ( - "net/http/httptest" - "testing" -) - -func TestMiddleware(t *testing.T) { - t.Skip() -} - -func TestMiddleware_GenerateRequestID(t *testing.T) { - t.Skip() -} - -func TestAuthType(t *testing.T) { - cookieRequest := httptest.NewRequest("GET", "/", nil) - if authType(cookieRequest) != "cookie" { - t.Error("authtype is not cookie") - } - - headerRequest := httptest.NewRequest("GET", "/", nil) - headerRequest.Header.Add("Authorization", "test") - if authType(headerRequest) != "token" { - t.Error("authtype is not token") - } - - formRequest := httptest.NewRequest("GET", "/?access_token=test", nil) - if authType(formRequest) != "token" { - t.Error("authtype is not token") - } -} diff --git a/logger/logger.go b/logger/logger.go deleted file mode 100644 index 469fce4a7d..0000000000 --- a/logger/logger.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// Copyright 2016 The containerd Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package logger - -import ( - "context" - "net/http" - - "github.com/sirupsen/logrus" -) - -type loggerKey struct{} - -// L is an alias for the the standard logger. -var L = logrus.NewEntry(logrus.StandardLogger()) - -// WithContext returns a new context with the provided logger. Use in -// combination with logger.WithField(s) for great effect. -func WithContext(ctx context.Context, logger *logrus.Entry) context.Context { - return context.WithValue(ctx, loggerKey{}, logger) -} - -// FromContext retrieves the current logger from the context. If no -// logger is available, the default logger is returned. -func FromContext(ctx context.Context) *logrus.Entry { - logger := ctx.Value(loggerKey{}) - if logger == nil { - return L - } - return logger.(*logrus.Entry) -} - -// FromRequest retrieves the current logger from the request. If no -// logger is available, the default logger is returned. -func FromRequest(r *http.Request) *logrus.Entry { - return FromContext(r.Context()) -} diff --git a/logger/logger_test.go b/logger/logger_test.go deleted file mode 100644 index 37569fc8c6..0000000000 --- a/logger/logger_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package logger - -import ( - "context" - "net/http" - "testing" - - "github.com/sirupsen/logrus" -) - -func TestContext(t *testing.T) { - entry := logrus.NewEntry(logrus.StandardLogger()) - - ctx := WithContext(context.Background(), entry) - got := FromContext(ctx) - - if got != entry { - t.Errorf("Expected Logger from context") - } -} - -func TestEmptyContext(t *testing.T) { - got := FromContext(context.Background()) - if got != L { - t.Errorf("Expected default Logger from context") - } -} - -func TestRequest(t *testing.T) { - entry := logrus.NewEntry(logrus.StandardLogger()) - - ctx := WithContext(context.Background(), entry) - req := new(http.Request) - req = req.WithContext(ctx) - - got := FromRequest(req) - - if got != entry { - t.Errorf("Expected Logger from http.Request") - } -} diff --git a/metric/builds.go b/metric/builds.go deleted file mode 100644 index dd04622c81..0000000000 --- a/metric/builds.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "github.com/drone/drone/core" - - "github.com/prometheus/client_golang/prometheus" -) - -// BuildCount provides metrics for build counts. -func BuildCount(builds core.BuildStore) { - prometheus.MustRegister( - prometheus.NewGaugeFunc(prometheus.GaugeOpts{ - Name: "drone_build_count", - Help: "Total number of builds.", - }, func() float64 { - i, _ := builds.Count(noContext) - return float64(i) - }), - ) -} - -// PendingBuildCount provides metrics for pending build counts. -func PendingBuildCount(builds core.BuildStore) { - prometheus.MustRegister( - prometheus.NewGaugeFunc(prometheus.GaugeOpts{ - Name: "drone_pending_builds", - Help: "Total number of pending builds.", - }, func() float64 { - list, _ := builds.Pending(noContext) - return float64(len(list)) - }), - ) -} - -// RunningBuildCount provides metrics for running build counts. -func RunningBuildCount(builds core.BuildStore) { - prometheus.MustRegister( - prometheus.NewGaugeFunc(prometheus.GaugeOpts{ - Name: "drone_running_builds", - Help: "Total number of running builds.", - }, func() float64 { - list, _ := builds.Running(noContext) - return float64(len(list)) - }), - ) -} diff --git a/metric/builds_test.go b/metric/builds_test.go deleted file mode 100644 index 33d293a6f6..0000000000 --- a/metric/builds_test.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" - "github.com/prometheus/client_golang/prometheus" -) - -func TestBuildCount(t *testing.T) { - controller := gomock.NewController(t) - - // restore the default prometheus registerer - // when the unit test is complete. - snapshot := prometheus.DefaultRegisterer - defer func() { - prometheus.DefaultRegisterer = snapshot - controller.Finish() - }() - - // creates a blank registry - registry := prometheus.NewRegistry() - prometheus.DefaultRegisterer = registry - - // x2 repository count - count := int64(5) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().Count(gomock.Any()).Return(count, nil) - BuildCount(builds) - - metrics, err := registry.Gather() - if err != nil { - t.Error(err) - return - } - if want, got := len(metrics), 1; want != got { - t.Errorf("Expect registered metric") - return - } - metric := metrics[0] - if want, got := metric.GetName(), "drone_build_count"; want != got { - t.Errorf("Expect metric name %s, got %s", want, got) - } - if want, got := metric.Metric[0].Gauge.GetValue(), float64(count); want != got { - t.Errorf("Expect metric value %f, got %f", want, got) - } -} - -func TestBuildPendingCount(t *testing.T) { - controller := gomock.NewController(t) - - // restore the default prometheus registerer - // when the unit test is complete. - snapshot := prometheus.DefaultRegisterer - defer func() { - prometheus.DefaultRegisterer = snapshot - controller.Finish() - }() - - // creates a blank registry - registry := prometheus.NewRegistry() - prometheus.DefaultRegisterer = registry - - // x2 repository count - data := []*core.Build{{}, {}, {}, {}, {}} - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().Pending(gomock.Any()).Return(data, nil) - PendingBuildCount(builds) - - metrics, err := registry.Gather() - if err != nil { - t.Error(err) - return - } - if want, got := len(metrics), 1; want != got { - t.Errorf("Expect registered metric") - return - } - metric := metrics[0] - if want, got := metric.GetName(), "drone_pending_builds"; want != got { - t.Errorf("Expect metric name %s, got %s", want, got) - } - if want, got := metric.Metric[0].Gauge.GetValue(), float64(len(data)); want != got { - t.Errorf("Expect metric value %f, got %f", want, got) - } -} - -func TestBuildRunningCount(t *testing.T) { - controller := gomock.NewController(t) - - // restore the default prometheus registerer - // when the unit test is complete. - snapshot := prometheus.DefaultRegisterer - defer func() { - prometheus.DefaultRegisterer = snapshot - controller.Finish() - }() - - // creates a blank registry - registry := prometheus.NewRegistry() - prometheus.DefaultRegisterer = registry - - // x2 repository count - data := []*core.Build{{}, {}, {}, {}, {}} - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().Running(gomock.Any()).Return(data, nil) - RunningBuildCount(builds) - - metrics, err := registry.Gather() - if err != nil { - t.Error(err) - return - } - if want, got := len(metrics), 1; want != got { - t.Errorf("Expect registered metric") - return - } - metric := metrics[0] - if want, got := metric.GetName(), "drone_running_builds"; want != got { - t.Errorf("Expect metric name %s, got %s", want, got) - } - if want, got := metric.Metric[0].Gauge.GetValue(), float64(len(data)); want != got { - t.Errorf("Expect metric value %f, got %f", want, got) - } -} diff --git a/metric/handler.go b/metric/handler.go deleted file mode 100644 index 6f186aa114..0000000000 --- a/metric/handler.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "errors" - "net/http" - - "github.com/drone/drone/core" - - "github.com/prometheus/client_golang/prometheus/promhttp" -) - -// errInvalidToken is returned when the prometheus token is invalid. -var errInvalidToken = errors.New("Invalid or missing prometheus token") - -// errAccessDenied is returned when the authorized user does not -// have access to the metrics endpoint. -var errAccessDenied = errors.New("Access denied") - -// Server is an http Metrics server. -type Server struct { - metrics http.Handler - session core.Session - anonymous bool -} - -// NewServer returns a new metrics server. -func NewServer(session core.Session, anonymous bool) *Server { - return &Server{ - metrics: promhttp.Handler(), - session: session, - anonymous: anonymous, - } -} - -// ServeHTTP responds to an http.Request and writes system -// metrics to the response body in plain text format. -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - user, _ := s.session.Get(r) - switch { - case !s.anonymous && user == nil: - http.Error(w, errInvalidToken.Error(), http.StatusUnauthorized) - case !s.anonymous && !user.Admin && !user.Machine: - http.Error(w, errAccessDenied.Error(), http.StatusForbidden) - default: - s.metrics.ServeHTTP(w, r) - } -} diff --git a/metric/handler_oss.go b/metric/handler_oss.go deleted file mode 100644 index 253f3c2891..0000000000 --- a/metric/handler_oss.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package metric - -import ( - "net/http" - - "github.com/drone/drone/core" -) - -// Server is a no-op http Metrics server. -type Server struct { -} - -// NewServer returns a new metrics server. -func NewServer(session core.Session, anonymous bool) *Server { - return new(Server) -} - -// ServeHTTP is a no-op http handler. -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {} diff --git a/metric/handler_test.go b/metric/handler_test.go deleted file mode 100644 index 2931e135b4..0000000000 --- a/metric/handler_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "net/http/httptest" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/golang/mock/gomock" -) - -func TestHandleMetrics(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - mockUser := &core.User{Admin: false, Machine: true} - session := mock.NewMockSession(controller) - session.EXPECT().Get(r).Return(mockUser, nil) - - NewServer(session, false).ServeHTTP(w, r) - if got, want := w.Code, 200; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } - - if got, want := w.HeaderMap.Get("Content-Type"), "text/plain; version=0.0.4; charset=utf-8"; got != want { - t.Errorf("Want prometheus header %q, got %q", want, got) - } -} - -func TestHandleMetrics_NoSession(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - session := mock.NewMockSession(controller) - session.EXPECT().Get(r).Return(nil, nil) - - NewServer(session, false).ServeHTTP(w, r) - - if got, want := w.Code, 401; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestHandleMetrics_NoSessionButAnonymousAccessEnabled(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - session := mock.NewMockSession(controller) - session.EXPECT().Get(r).Return(nil, nil) - - NewServer(session, true).ServeHTTP(w, r) - - if got, want := w.Code, 200; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} - -func TestHandleMetrics_AccessDenied(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - mockUser := &core.User{Admin: false, Machine: false} - session := mock.NewMockSession(controller) - session.EXPECT().Get(r).Return(mockUser, nil) - - NewServer(session, false).ServeHTTP(w, r) - if got, want := w.Code, 403; got != want { - t.Errorf("Want status code %d, got %d", want, got) - } -} diff --git a/metric/license.go b/metric/license.go deleted file mode 100644 index a7b0e6929c..0000000000 --- a/metric/license.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import "github.com/drone/drone/core" - -// License registers the license metrics. -func License(license core.LicenseService) { - // track days until expires - // track user limit - // track repo limit -} diff --git a/metric/license_test.go b/metric/license_test.go deleted file mode 100644 index 66b058b4ec..0000000000 --- a/metric/license_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric diff --git a/metric/metric.go b/metric/metric.go deleted file mode 100644 index 66b058b4ec..0000000000 --- a/metric/metric.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric diff --git a/metric/metric_oss.go b/metric/metric_oss.go deleted file mode 100644 index fbf94954c2..0000000000 --- a/metric/metric_oss.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package metric - -import "github.com/drone/drone/core" - -func BuildCount(core.BuildStore) {} -func PendingBuildCount(core.BuildStore) {} -func RunningBuildCount(core.BuildStore) {} -func RunningJobCount(core.StageStore) {} -func PendingJobCount(core.StageStore) {} -func RepoCount(core.RepositoryStore) {} -func UserCount(core.UserStore) {} diff --git a/metric/repos.go b/metric/repos.go deleted file mode 100644 index 430be9fcc8..0000000000 --- a/metric/repos.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "github.com/drone/drone/core" - - "github.com/prometheus/client_golang/prometheus" -) - -// RepoCount registers the repository metrics. -func RepoCount(repos core.RepositoryStore) { - prometheus.MustRegister( - prometheus.NewGaugeFunc(prometheus.GaugeOpts{ - Name: "drone_repo_count", - Help: "Total number of registered repositories.", - }, func() float64 { - i, _ := repos.Count(noContext) - return float64(i) - }), - ) -} diff --git a/metric/repos_test.go b/metric/repos_test.go deleted file mode 100644 index dc012b884d..0000000000 --- a/metric/repos_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "testing" - - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" - "github.com/prometheus/client_golang/prometheus" -) - -func TestRepoCount(t *testing.T) { - controller := gomock.NewController(t) - - // restore the default prometheus registerer - // when the unit test is complete. - snapshot := prometheus.DefaultRegisterer - defer func() { - prometheus.DefaultRegisterer = snapshot - controller.Finish() - }() - - // creates a blank registry - registry := prometheus.NewRegistry() - prometheus.DefaultRegisterer = registry - - // x2 repository count - count := int64(5) - - store := mock.NewMockRepositoryStore(controller) - store.EXPECT().Count(gomock.Any()).Return(count, nil) - RepoCount(store) - - metrics, err := registry.Gather() - if err != nil { - t.Error(err) - return - } - if want, got := len(metrics), 1; want != got { - t.Errorf("Expect registered metric") - return - } - metric := metrics[0] - if want, got := metric.GetName(), "drone_repo_count"; want != got { - t.Errorf("Expect metric name %s, got %s", want, got) - } - if want, got := metric.Metric[0].Gauge.GetValue(), float64(count); want != got { - t.Errorf("Expect metric value %f, got %f", want, got) - } -} diff --git a/metric/sink/config.go b/metric/sink/config.go deleted file mode 100644 index 49b4dea5cc..0000000000 --- a/metric/sink/config.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sink - -// Config configures a Datadog sink. -type Config struct { - Endpoint string - Token string - - License string - Licensor string - Subscription string - EnableGithub bool - EnableGithubEnt bool - EnableGitlab bool - EnableBitbucket bool - EnableStash bool - EnableGogs bool - EnableGitea bool - EnableGitee bool - EnableAgents bool -} diff --git a/metric/sink/datadog.go b/metric/sink/datadog.go deleted file mode 100644 index ebe01f11ae..0000000000 --- a/metric/sink/datadog.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sink - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/drone/drone/core" -) - -type payload struct { - Series []series `json:"series"` -} - -type series struct { - Metric string `json:"metric"` - Points [][]int64 `json:"points"` - Host string `json:"host"` - Type string `json:"type"` - Tags []string `json:"tags,omitempty"` -} - -// Datadog defines a no-op sink to datadog. -type Datadog struct { - users core.UserStore - repos core.RepositoryStore - builds core.BuildStore - system core.System - config Config - client *http.Client -} - -// New returns a Datadog sink. -func New( - users core.UserStore, - repos core.RepositoryStore, - builds core.BuildStore, - system core.System, - config Config, -) *Datadog { - return &Datadog{ - users: users, - repos: repos, - builds: builds, - system: system, - config: config, - } -} - -// Start starts the sink. -func (d *Datadog) Start(ctx context.Context) error { - for { - diff := midnightDiff() - select { - case <-time.After(diff): - d.do(ctx, time.Now().Unix()) - case <-ctx.Done(): - return nil - } - } -} - -func (d *Datadog) do(ctx context.Context, unix int64) error { - users, err := d.users.Count(ctx) - if err != nil { - return err - } - repos, err := d.repos.Count(ctx) - if err != nil { - return err - } - builds, err := d.builds.Count(ctx) - if err != nil { - return err - } - userList, _ := d.users.ListRange(ctx, core.UserParams{ - Sort: false, - Page: 0, - Size: 5, - }) - tags := createTags(d.config) - data := new(payload) - data.Series = []series{ - { - Metric: "drone.users", - Points: [][]int64{[]int64{unix, users}}, - Type: "gauge", - Host: d.system.Host, - Tags: append(tags, createInstallerTags(userList)...), - }, - { - Metric: "drone.repos", - Points: [][]int64{[]int64{unix, repos}}, - Type: "gauge", - Host: d.system.Host, - Tags: tags, - }, - { - Metric: "drone.builds", - Points: [][]int64{[]int64{unix, builds}}, - Type: "gauge", - Host: d.system.Host, - Tags: tags, - }, - } - - buf := new(bytes.Buffer) - err = json.NewEncoder(buf).Encode(data) - if err != nil { - return err - } - - endpoint := fmt.Sprintf("%s?api_key=%s", d.config.Endpoint, d.config.Token) - req, err := http.NewRequest("POST", endpoint, buf) - if err != nil { - return err - } - req.Header.Add("Accept", "application/json") - req.Header.Add("Content-Type", "application/json") - - res, err := httpClient.Do(req) - if err != nil { - return err - } - - res.Body.Close() - return nil -} - -// Client returns the http client. If no custom -// client is provided, the default client is used. -func (d *Datadog) Client() *http.Client { - if d.client == nil { - return httpClient - } - return d.client -} - -// calculate the differences between now and midnight. -func midnightDiff() time.Duration { - a := time.Now() - b := time.Date(a.Year(), a.Month(), a.Day()+1, 0, 0, 0, 0, a.Location()) - return b.Sub(a) -} - -// httpClient should be used for HTTP requests. It -// is configured with a timeout for reliability. -var httpClient = &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - TLSHandshakeTimeout: 30 * time.Second, - DisableKeepAlives: true, - }, - Timeout: 1 * time.Minute, -} diff --git a/metric/sink/datadog_test.go b/metric/sink/datadog_test.go deleted file mode 100644 index e6c5fa1b1d..0000000000 --- a/metric/sink/datadog_test.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sink - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/drone/drone/version" - "github.com/golang/mock/gomock" - "github.com/h2non/gock" -) - -var noContext = context.Background() - -func TestDo(t *testing.T) { - controller := gomock.NewController(t) - - gock.InterceptClient(httpClient) - defer func() { - gock.RestoreClient(httpClient) - gock.Off() - controller.Finish() - }() - - users := mock.NewMockUserStore(controller) - users.EXPECT().Count(gomock.Any()).Return(int64(10), nil) - users.EXPECT().ListRange(gomock.Any(), gomock.Any()).Return([]*core.User{{Email: "jane@acme.com", Created: 1257894000}}, nil) - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().Count(gomock.Any()).Return(int64(20), nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().Count(gomock.Any()).Return(int64(30), nil) - - gock.New("https://api.datadoghq.com"). - Post("/api/v1/series"). - JSON(sample). - Reply(200) - - d := new(Datadog) - d.users = users - d.repos = repos - d.builds = builds - d.system.Host = "test.example.com" - d.config.License = "trial" - d.config.EnableGithub = true - d.config.EnableAgents = true - d.config.Endpoint = "https://api.datadoghq.com/api/v1/series" - d.do(noContext, 915148800) - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -var sample = `{ - "series" : [ - { - "metric": "drone.users", - "points": [[915148800, 10]], - "type": "gauge", - "host": "test.example.com", - "tags": ["version:` + version.Version.String() + `","remote:github:cloud","scheduler:internal:agents","license:trial","installer:jane@acme.com","installed:2009-11-10T23:00:00Z"] - }, - { - "metric": "drone.repos", - "points": [[915148800, 20]], - "type": "gauge", - "host": "test.example.com", - "tags": ["version:` + version.Version.String() + `","remote:github:cloud","scheduler:internal:agents","license:trial"] - }, - { - "metric": "drone.builds", - "points": [[915148800, 30]], - "type": "gauge", - "host": "test.example.com", - "tags": ["version:` + version.Version.String() + `","remote:github:cloud","scheduler:internal:agents","license:trial"] - } - ] -}` diff --git a/metric/sink/tags.go b/metric/sink/tags.go deleted file mode 100644 index 06d89c7063..0000000000 --- a/metric/sink/tags.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sink - -import ( - "fmt" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/version" -) - -func createTags(config Config) []string { - tags := []string{ - fmt.Sprintf("version:%s", version.Version), - } - - switch { - case config.EnableBitbucket: - tags = append(tags, "remote:bitbucket:cloud") - case config.EnableStash: - tags = append(tags, "remote:bitbucket:server") - case config.EnableGithubEnt: - tags = append(tags, "remote:github:enterprise") - case config.EnableGithub: - tags = append(tags, "remote:github:cloud") - case config.EnableGitlab: - tags = append(tags, "remote:gitlab") - case config.EnableGogs: - tags = append(tags, "remote:gogs") - case config.EnableGitea: - tags = append(tags, "remote:gitea") - case config.EnableGitee: - tags = append(tags, "remote:gitee") - default: - tags = append(tags, "remote:undefined") - } - - switch { - case config.EnableAgents: - tags = append(tags, "scheduler:internal:agents") - default: - tags = append(tags, "scheduler:internal:local") - } - - if config.Subscription != "" { - tag := fmt.Sprintf("license:%s:%s:%s", - config.License, - config.Licensor, - config.Subscription, - ) - tags = append(tags, tag) - } else if config.Licensor != "" { - tag := fmt.Sprintf("license:%s:%s", - config.License, - config.Licensor, - ) - tags = append(tags, tag) - } else { - tag := fmt.Sprintf("license:%s", config.License) - tags = append(tags, tag) - } - return tags -} - -func createInstallerTags(users []*core.User) []string { - var tags []string - for _, user := range users { - if user.Machine { - continue - } - if len(user.Email) == 0 { - continue - } - tag1 := fmt.Sprintf("installer:%s", user.Email) - tag2 := fmt.Sprintf("installed:%s", time.Unix(user.Created, 0).UTC().Format(time.RFC3339Nano)) - tags = append(tags, tag1, tag2) - break - } - return tags -} diff --git a/metric/stages.go b/metric/stages.go deleted file mode 100644 index 0cb4a42f42..0000000000 --- a/metric/stages.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "github.com/drone/drone/core" - - "github.com/prometheus/client_golang/prometheus" -) - -// RunningJobCount provides metrics for running job counts. -func RunningJobCount(stages core.StageStore) { - prometheus.MustRegister( - prometheus.NewGaugeFunc(prometheus.GaugeOpts{ - Name: "drone_running_jobs", - Help: "Total number of running jobs.", - }, func() float64 { - list, _ := stages.ListState(noContext, core.StatusRunning) - return float64(len(list)) - }), - ) -} - -// PendingJobCount provides metrics for pending job counts. -func PendingJobCount(stages core.StageStore) { - prometheus.MustRegister( - prometheus.NewGaugeFunc(prometheus.GaugeOpts{ - Name: "drone_pending_jobs", - Help: "Total number of pending jobs.", - }, func() float64 { - list, _ := stages.ListState(noContext, core.StatusPending) - return float64(len(list)) - }), - ) -} diff --git a/metric/stages_test.go b/metric/stages_test.go deleted file mode 100644 index d79b6922cd..0000000000 --- a/metric/stages_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" - "github.com/prometheus/client_golang/prometheus" -) - -func TestStagePendingCount(t *testing.T) { - controller := gomock.NewController(t) - - // restore the default prometheus registerer - // when the unit test is complete. - snapshot := prometheus.DefaultRegisterer - defer func() { - prometheus.DefaultRegisterer = snapshot - controller.Finish() - }() - - // creates a blank registry - registry := prometheus.NewRegistry() - prometheus.DefaultRegisterer = registry - - // x5 stage count - data := []*core.Stage{{}, {}, {}, {}, {}} - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().ListState(gomock.Any(), core.StatusPending).Return(data, nil) - PendingJobCount(stages) - - metrics, err := registry.Gather() - if err != nil { - t.Error(err) - return - } - if want, got := len(metrics), 1; want != got { - t.Errorf("Expect registered metric") - return - } - metric := metrics[0] - if want, got := metric.GetName(), "drone_pending_jobs"; want != got { - t.Errorf("Expect metric name %s, got %s", want, got) - } - if want, got := metric.Metric[0].Gauge.GetValue(), float64(len(data)); want != got { - t.Errorf("Expect metric value %f, got %f", want, got) - } -} - -func TestStageRunningCount(t *testing.T) { - controller := gomock.NewController(t) - - // restore the default prometheus registerer - // when the unit test is complete. - snapshot := prometheus.DefaultRegisterer - defer func() { - prometheus.DefaultRegisterer = snapshot - controller.Finish() - }() - - // creates a blank registry - registry := prometheus.NewRegistry() - prometheus.DefaultRegisterer = registry - - // x5 stage count - data := []*core.Stage{{}, {}, {}, {}, {}} - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().ListState(gomock.Any(), core.StatusRunning).Return(data, nil) - RunningJobCount(stages) - - metrics, err := registry.Gather() - if err != nil { - t.Error(err) - return - } - if want, got := len(metrics), 1; want != got { - t.Errorf("Expect registered metric") - return - } - metric := metrics[0] - if want, got := metric.GetName(), "drone_running_jobs"; want != got { - t.Errorf("Expect metric name %s, got %s", want, got) - } - if want, got := metric.Metric[0].Gauge.GetValue(), float64(len(data)); want != got { - t.Errorf("Expect metric value %f, got %f", want, got) - } -} diff --git a/metric/users.go b/metric/users.go deleted file mode 100644 index fd5162df8e..0000000000 --- a/metric/users.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "context" - - "github.com/drone/drone/core" - - "github.com/prometheus/client_golang/prometheus" -) - -var noContext = context.Background() - -// UserCount provides metrics for registered users. -func UserCount(users core.UserStore) { - prometheus.MustRegister( - prometheus.NewGaugeFunc(prometheus.GaugeOpts{ - Name: "drone_user_count", - Help: "Total number of active users.", - }, func() float64 { - i, _ := users.Count(noContext) - return float64(i) - }), - ) -} diff --git a/metric/users_test.go b/metric/users_test.go deleted file mode 100644 index 646d8c2035..0000000000 --- a/metric/users_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package metric - -import ( - "testing" - - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" - "github.com/prometheus/client_golang/prometheus" -) - -func TestUserCount(t *testing.T) { - controller := gomock.NewController(t) - - // restore the default prometheus registerer - // when the unit test is complete. - snapshot := prometheus.DefaultRegisterer - defer func() { - prometheus.DefaultRegisterer = snapshot - controller.Finish() - }() - - // creates a blank registry - registry := prometheus.NewRegistry() - prometheus.DefaultRegisterer = registry - - // x2 repository count - count := int64(5) - - store := mock.NewMockUserStore(controller) - store.EXPECT().Count(gomock.Any()).Return(count, nil) - UserCount(store) - - metrics, err := registry.Gather() - if err != nil { - t.Error(err) - return - } - if want, got := len(metrics), 1; want != got { - t.Errorf("Expect registered metric") - return - } - metric := metrics[0] - if want, got := metric.GetName(), "drone_user_count"; want != got { - t.Errorf("Expect metric name %s, got %s", want, got) - } - if want, got := metric.Metric[0].Gauge.GetValue(), float64(count); want != got { - t.Errorf("Expect metric value %f, got %f", want, got) - } -} diff --git a/mock/mock.go b/mock/mock.go deleted file mode 100644 index f91c1e5d52..0000000000 --- a/mock/mock.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package mock - -//go:generate mockgen -package=mock -destination=mock_gen.go github.com/drone/drone/core Pubsub,Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Transferer,Triggerer,Syncer,LogStream,WebhookSender,LicenseService,TemplateStore,CardStore diff --git a/mock/mock_gen.go b/mock/mock_gen.go deleted file mode 100644 index 9848282806..0000000000 --- a/mock/mock_gen.go +++ /dev/null @@ -1,2976 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/drone/drone/core (interfaces: Pubsub,Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Transferer,Triggerer,Syncer,LogStream,WebhookSender,LicenseService,TemplateStore,CardStore) - -// Package mock is a generated GoMock package. -package mock - -import ( - context "context" - io "io" - http "net/http" - reflect "reflect" - - core "github.com/drone/drone/core" - gomock "github.com/golang/mock/gomock" -) - -// MockPubsub is a mock of Pubsub interface. -type MockPubsub struct { - ctrl *gomock.Controller - recorder *MockPubsubMockRecorder -} - -// MockPubsubMockRecorder is the mock recorder for MockPubsub. -type MockPubsubMockRecorder struct { - mock *MockPubsub -} - -// NewMockPubsub creates a new mock instance. -func NewMockPubsub(ctrl *gomock.Controller) *MockPubsub { - mock := &MockPubsub{ctrl: ctrl} - mock.recorder = &MockPubsubMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockPubsub) EXPECT() *MockPubsubMockRecorder { - return m.recorder -} - -// Publish mocks base method. -func (m *MockPubsub) Publish(arg0 context.Context, arg1 *core.Message) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Publish", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Publish indicates an expected call of Publish. -func (mr *MockPubsubMockRecorder) Publish(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockPubsub)(nil).Publish), arg0, arg1) -} - -// Subscribe mocks base method. -func (m *MockPubsub) Subscribe(arg0 context.Context) (<-chan *core.Message, <-chan error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Subscribe", arg0) - ret0, _ := ret[0].(<-chan *core.Message) - ret1, _ := ret[1].(<-chan error) - return ret0, ret1 -} - -// Subscribe indicates an expected call of Subscribe. -func (mr *MockPubsubMockRecorder) Subscribe(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockPubsub)(nil).Subscribe), arg0) -} - -// Subscribers mocks base method. -func (m *MockPubsub) Subscribers() (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Subscribers") - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Subscribers indicates an expected call of Subscribers. -func (mr *MockPubsubMockRecorder) Subscribers() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribers", reflect.TypeOf((*MockPubsub)(nil).Subscribers)) -} - -// MockCanceler is a mock of Canceler interface. -type MockCanceler struct { - ctrl *gomock.Controller - recorder *MockCancelerMockRecorder -} - -// MockCancelerMockRecorder is the mock recorder for MockCanceler. -type MockCancelerMockRecorder struct { - mock *MockCanceler -} - -// NewMockCanceler creates a new mock instance. -func NewMockCanceler(ctrl *gomock.Controller) *MockCanceler { - mock := &MockCanceler{ctrl: ctrl} - mock.recorder = &MockCancelerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockCanceler) EXPECT() *MockCancelerMockRecorder { - return m.recorder -} - -// Cancel mocks base method. -func (m *MockCanceler) Cancel(arg0 context.Context, arg1 *core.Repository, arg2 *core.Build) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Cancel", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Cancel indicates an expected call of Cancel. -func (mr *MockCancelerMockRecorder) Cancel(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cancel", reflect.TypeOf((*MockCanceler)(nil).Cancel), arg0, arg1, arg2) -} - -// CancelPending mocks base method. -func (m *MockCanceler) CancelPending(arg0 context.Context, arg1 *core.Repository, arg2 *core.Build) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CancelPending", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// CancelPending indicates an expected call of CancelPending. -func (mr *MockCancelerMockRecorder) CancelPending(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelPending", reflect.TypeOf((*MockCanceler)(nil).CancelPending), arg0, arg1, arg2) -} - -// MockConvertService is a mock of ConvertService interface. -type MockConvertService struct { - ctrl *gomock.Controller - recorder *MockConvertServiceMockRecorder -} - -// MockConvertServiceMockRecorder is the mock recorder for MockConvertService. -type MockConvertServiceMockRecorder struct { - mock *MockConvertService -} - -// NewMockConvertService creates a new mock instance. -func NewMockConvertService(ctrl *gomock.Controller) *MockConvertService { - mock := &MockConvertService{ctrl: ctrl} - mock.recorder = &MockConvertServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockConvertService) EXPECT() *MockConvertServiceMockRecorder { - return m.recorder -} - -// Convert mocks base method. -func (m *MockConvertService) Convert(arg0 context.Context, arg1 *core.ConvertArgs) (*core.Config, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Convert", arg0, arg1) - ret0, _ := ret[0].(*core.Config) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Convert indicates an expected call of Convert. -func (mr *MockConvertServiceMockRecorder) Convert(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Convert", reflect.TypeOf((*MockConvertService)(nil).Convert), arg0, arg1) -} - -// MockValidateService is a mock of ValidateService interface. -type MockValidateService struct { - ctrl *gomock.Controller - recorder *MockValidateServiceMockRecorder -} - -// MockValidateServiceMockRecorder is the mock recorder for MockValidateService. -type MockValidateServiceMockRecorder struct { - mock *MockValidateService -} - -// NewMockValidateService creates a new mock instance. -func NewMockValidateService(ctrl *gomock.Controller) *MockValidateService { - mock := &MockValidateService{ctrl: ctrl} - mock.recorder = &MockValidateServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockValidateService) EXPECT() *MockValidateServiceMockRecorder { - return m.recorder -} - -// Validate mocks base method. -func (m *MockValidateService) Validate(arg0 context.Context, arg1 *core.ValidateArgs) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Validate", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Validate indicates an expected call of Validate. -func (mr *MockValidateServiceMockRecorder) Validate(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockValidateService)(nil).Validate), arg0, arg1) -} - -// MockNetrcService is a mock of NetrcService interface. -type MockNetrcService struct { - ctrl *gomock.Controller - recorder *MockNetrcServiceMockRecorder -} - -// MockNetrcServiceMockRecorder is the mock recorder for MockNetrcService. -type MockNetrcServiceMockRecorder struct { - mock *MockNetrcService -} - -// NewMockNetrcService creates a new mock instance. -func NewMockNetrcService(ctrl *gomock.Controller) *MockNetrcService { - mock := &MockNetrcService{ctrl: ctrl} - mock.recorder = &MockNetrcServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockNetrcService) EXPECT() *MockNetrcServiceMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockNetrcService) Create(arg0 context.Context, arg1 *core.User, arg2 *core.Repository) (*core.Netrc, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Netrc) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create. -func (mr *MockNetrcServiceMockRecorder) Create(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockNetrcService)(nil).Create), arg0, arg1, arg2) -} - -// MockRenewer is a mock of Renewer interface. -type MockRenewer struct { - ctrl *gomock.Controller - recorder *MockRenewerMockRecorder -} - -// MockRenewerMockRecorder is the mock recorder for MockRenewer. -type MockRenewerMockRecorder struct { - mock *MockRenewer -} - -// NewMockRenewer creates a new mock instance. -func NewMockRenewer(ctrl *gomock.Controller) *MockRenewer { - mock := &MockRenewer{ctrl: ctrl} - mock.recorder = &MockRenewerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRenewer) EXPECT() *MockRenewerMockRecorder { - return m.recorder -} - -// Renew mocks base method. -func (m *MockRenewer) Renew(arg0 context.Context, arg1 *core.User, arg2 bool) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Renew", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Renew indicates an expected call of Renew. -func (mr *MockRenewerMockRecorder) Renew(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Renew", reflect.TypeOf((*MockRenewer)(nil).Renew), arg0, arg1, arg2) -} - -// MockHookParser is a mock of HookParser interface. -type MockHookParser struct { - ctrl *gomock.Controller - recorder *MockHookParserMockRecorder -} - -// MockHookParserMockRecorder is the mock recorder for MockHookParser. -type MockHookParserMockRecorder struct { - mock *MockHookParser -} - -// NewMockHookParser creates a new mock instance. -func NewMockHookParser(ctrl *gomock.Controller) *MockHookParser { - mock := &MockHookParser{ctrl: ctrl} - mock.recorder = &MockHookParserMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockHookParser) EXPECT() *MockHookParserMockRecorder { - return m.recorder -} - -// Parse mocks base method. -func (m *MockHookParser) Parse(arg0 *http.Request, arg1 func(string) string) (*core.Hook, *core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Parse", arg0, arg1) - ret0, _ := ret[0].(*core.Hook) - ret1, _ := ret[1].(*core.Repository) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Parse indicates an expected call of Parse. -func (mr *MockHookParserMockRecorder) Parse(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parse", reflect.TypeOf((*MockHookParser)(nil).Parse), arg0, arg1) -} - -// MockUserService is a mock of UserService interface. -type MockUserService struct { - ctrl *gomock.Controller - recorder *MockUserServiceMockRecorder -} - -// MockUserServiceMockRecorder is the mock recorder for MockUserService. -type MockUserServiceMockRecorder struct { - mock *MockUserService -} - -// NewMockUserService creates a new mock instance. -func NewMockUserService(ctrl *gomock.Controller) *MockUserService { - mock := &MockUserService{ctrl: ctrl} - mock.recorder = &MockUserServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockUserService) EXPECT() *MockUserServiceMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockUserService) Find(arg0 context.Context, arg1, arg2 string) (*core.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockUserServiceMockRecorder) Find(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockUserService)(nil).Find), arg0, arg1, arg2) -} - -// FindLogin mocks base method. -func (m *MockUserService) FindLogin(arg0 context.Context, arg1 *core.User, arg2 string) (*core.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindLogin", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindLogin indicates an expected call of FindLogin. -func (mr *MockUserServiceMockRecorder) FindLogin(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindLogin", reflect.TypeOf((*MockUserService)(nil).FindLogin), arg0, arg1, arg2) -} - -// MockRepositoryService is a mock of RepositoryService interface. -type MockRepositoryService struct { - ctrl *gomock.Controller - recorder *MockRepositoryServiceMockRecorder -} - -// MockRepositoryServiceMockRecorder is the mock recorder for MockRepositoryService. -type MockRepositoryServiceMockRecorder struct { - mock *MockRepositoryService -} - -// NewMockRepositoryService creates a new mock instance. -func NewMockRepositoryService(ctrl *gomock.Controller) *MockRepositoryService { - mock := &MockRepositoryService{ctrl: ctrl} - mock.recorder = &MockRepositoryServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRepositoryService) EXPECT() *MockRepositoryServiceMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockRepositoryService) Find(arg0 context.Context, arg1 *core.User, arg2 string) (*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockRepositoryServiceMockRecorder) Find(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockRepositoryService)(nil).Find), arg0, arg1, arg2) -} - -// FindPerm mocks base method. -func (m *MockRepositoryService) FindPerm(arg0 context.Context, arg1 *core.User, arg2 string) (*core.Perm, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindPerm", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Perm) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindPerm indicates an expected call of FindPerm. -func (mr *MockRepositoryServiceMockRecorder) FindPerm(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindPerm", reflect.TypeOf((*MockRepositoryService)(nil).FindPerm), arg0, arg1, arg2) -} - -// List mocks base method. -func (m *MockRepositoryService) List(arg0 context.Context, arg1 *core.User) ([]*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockRepositoryServiceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepositoryService)(nil).List), arg0, arg1) -} - -// MockCommitService is a mock of CommitService interface. -type MockCommitService struct { - ctrl *gomock.Controller - recorder *MockCommitServiceMockRecorder -} - -// MockCommitServiceMockRecorder is the mock recorder for MockCommitService. -type MockCommitServiceMockRecorder struct { - mock *MockCommitService -} - -// NewMockCommitService creates a new mock instance. -func NewMockCommitService(ctrl *gomock.Controller) *MockCommitService { - mock := &MockCommitService{ctrl: ctrl} - mock.recorder = &MockCommitServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockCommitService) EXPECT() *MockCommitServiceMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockCommitService) Find(arg0 context.Context, arg1 *core.User, arg2, arg3 string) (*core.Commit, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*core.Commit) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockCommitServiceMockRecorder) Find(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockCommitService)(nil).Find), arg0, arg1, arg2, arg3) -} - -// FindRef mocks base method. -func (m *MockCommitService) FindRef(arg0 context.Context, arg1 *core.User, arg2, arg3 string) (*core.Commit, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindRef", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*core.Commit) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindRef indicates an expected call of FindRef. -func (mr *MockCommitServiceMockRecorder) FindRef(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindRef", reflect.TypeOf((*MockCommitService)(nil).FindRef), arg0, arg1, arg2, arg3) -} - -// ListChanges mocks base method. -func (m *MockCommitService) ListChanges(arg0 context.Context, arg1 *core.User, arg2, arg3, arg4 string) ([]*core.Change, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListChanges", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].([]*core.Change) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListChanges indicates an expected call of ListChanges. -func (mr *MockCommitServiceMockRecorder) ListChanges(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListChanges", reflect.TypeOf((*MockCommitService)(nil).ListChanges), arg0, arg1, arg2, arg3, arg4) -} - -// MockStatusService is a mock of StatusService interface. -type MockStatusService struct { - ctrl *gomock.Controller - recorder *MockStatusServiceMockRecorder -} - -// MockStatusServiceMockRecorder is the mock recorder for MockStatusService. -type MockStatusServiceMockRecorder struct { - mock *MockStatusService -} - -// NewMockStatusService creates a new mock instance. -func NewMockStatusService(ctrl *gomock.Controller) *MockStatusService { - mock := &MockStatusService{ctrl: ctrl} - mock.recorder = &MockStatusServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockStatusService) EXPECT() *MockStatusServiceMockRecorder { - return m.recorder -} - -// Send mocks base method. -func (m *MockStatusService) Send(arg0 context.Context, arg1 *core.User, arg2 *core.StatusInput) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Send", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Send indicates an expected call of Send. -func (mr *MockStatusServiceMockRecorder) Send(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockStatusService)(nil).Send), arg0, arg1, arg2) -} - -// MockHookService is a mock of HookService interface. -type MockHookService struct { - ctrl *gomock.Controller - recorder *MockHookServiceMockRecorder -} - -// MockHookServiceMockRecorder is the mock recorder for MockHookService. -type MockHookServiceMockRecorder struct { - mock *MockHookService -} - -// NewMockHookService creates a new mock instance. -func NewMockHookService(ctrl *gomock.Controller) *MockHookService { - mock := &MockHookService{ctrl: ctrl} - mock.recorder = &MockHookServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockHookService) EXPECT() *MockHookServiceMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockHookService) Create(arg0 context.Context, arg1 *core.User, arg2 *core.Repository) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockHookServiceMockRecorder) Create(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockHookService)(nil).Create), arg0, arg1, arg2) -} - -// Delete mocks base method. -func (m *MockHookService) Delete(arg0 context.Context, arg1 *core.User, arg2 *core.Repository) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockHookServiceMockRecorder) Delete(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockHookService)(nil).Delete), arg0, arg1, arg2) -} - -// MockFileService is a mock of FileService interface. -type MockFileService struct { - ctrl *gomock.Controller - recorder *MockFileServiceMockRecorder -} - -// MockFileServiceMockRecorder is the mock recorder for MockFileService. -type MockFileServiceMockRecorder struct { - mock *MockFileService -} - -// NewMockFileService creates a new mock instance. -func NewMockFileService(ctrl *gomock.Controller) *MockFileService { - mock := &MockFileService{ctrl: ctrl} - mock.recorder = &MockFileServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockFileService) EXPECT() *MockFileServiceMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockFileService) Find(arg0 context.Context, arg1 *core.User, arg2, arg3, arg4, arg5 string) (*core.File, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(*core.File) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockFileServiceMockRecorder) Find(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockFileService)(nil).Find), arg0, arg1, arg2, arg3, arg4, arg5) -} - -// MockBatcher is a mock of Batcher interface. -type MockBatcher struct { - ctrl *gomock.Controller - recorder *MockBatcherMockRecorder -} - -// MockBatcherMockRecorder is the mock recorder for MockBatcher. -type MockBatcherMockRecorder struct { - mock *MockBatcher -} - -// NewMockBatcher creates a new mock instance. -func NewMockBatcher(ctrl *gomock.Controller) *MockBatcher { - mock := &MockBatcher{ctrl: ctrl} - mock.recorder = &MockBatcherMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBatcher) EXPECT() *MockBatcherMockRecorder { - return m.recorder -} - -// Batch mocks base method. -func (m *MockBatcher) Batch(arg0 context.Context, arg1 *core.User, arg2 *core.Batch) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Batch", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Batch indicates an expected call of Batch. -func (mr *MockBatcherMockRecorder) Batch(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Batch", reflect.TypeOf((*MockBatcher)(nil).Batch), arg0, arg1, arg2) -} - -// MockBuildStore is a mock of BuildStore interface. -type MockBuildStore struct { - ctrl *gomock.Controller - recorder *MockBuildStoreMockRecorder -} - -// MockBuildStoreMockRecorder is the mock recorder for MockBuildStore. -type MockBuildStoreMockRecorder struct { - mock *MockBuildStore -} - -// NewMockBuildStore creates a new mock instance. -func NewMockBuildStore(ctrl *gomock.Controller) *MockBuildStore { - mock := &MockBuildStore{ctrl: ctrl} - mock.recorder = &MockBuildStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBuildStore) EXPECT() *MockBuildStoreMockRecorder { - return m.recorder -} - -// Count mocks base method. -func (m *MockBuildStore) Count(arg0 context.Context) (int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Count", arg0) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Count indicates an expected call of Count. -func (mr *MockBuildStoreMockRecorder) Count(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockBuildStore)(nil).Count), arg0) -} - -// Create mocks base method. -func (m *MockBuildStore) Create(arg0 context.Context, arg1 *core.Build, arg2 []*core.Stage) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockBuildStoreMockRecorder) Create(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockBuildStore)(nil).Create), arg0, arg1, arg2) -} - -// Delete mocks base method. -func (m *MockBuildStore) Delete(arg0 context.Context, arg1 *core.Build) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockBuildStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockBuildStore)(nil).Delete), arg0, arg1) -} - -// DeleteBranch mocks base method. -func (m *MockBuildStore) DeleteBranch(arg0 context.Context, arg1 int64, arg2 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteBranch", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteBranch indicates an expected call of DeleteBranch. -func (mr *MockBuildStoreMockRecorder) DeleteBranch(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBranch", reflect.TypeOf((*MockBuildStore)(nil).DeleteBranch), arg0, arg1, arg2) -} - -// DeleteDeploy mocks base method. -func (m *MockBuildStore) DeleteDeploy(arg0 context.Context, arg1 int64, arg2 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteDeploy", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteDeploy indicates an expected call of DeleteDeploy. -func (mr *MockBuildStoreMockRecorder) DeleteDeploy(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDeploy", reflect.TypeOf((*MockBuildStore)(nil).DeleteDeploy), arg0, arg1, arg2) -} - -// DeletePull mocks base method. -func (m *MockBuildStore) DeletePull(arg0 context.Context, arg1 int64, arg2 int) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeletePull", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeletePull indicates an expected call of DeletePull. -func (mr *MockBuildStoreMockRecorder) DeletePull(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePull", reflect.TypeOf((*MockBuildStore)(nil).DeletePull), arg0, arg1, arg2) -} - -// Find mocks base method. -func (m *MockBuildStore) Find(arg0 context.Context, arg1 int64) (*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockBuildStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockBuildStore)(nil).Find), arg0, arg1) -} - -// FindNumber mocks base method. -func (m *MockBuildStore) FindNumber(arg0 context.Context, arg1, arg2 int64) (*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindNumber", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindNumber indicates an expected call of FindNumber. -func (mr *MockBuildStoreMockRecorder) FindNumber(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindNumber", reflect.TypeOf((*MockBuildStore)(nil).FindNumber), arg0, arg1, arg2) -} - -// FindRef mocks base method. -func (m *MockBuildStore) FindRef(arg0 context.Context, arg1 int64, arg2 string) (*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindRef", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindRef indicates an expected call of FindRef. -func (mr *MockBuildStoreMockRecorder) FindRef(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindRef", reflect.TypeOf((*MockBuildStore)(nil).FindRef), arg0, arg1, arg2) -} - -// LatestBranches mocks base method. -func (m *MockBuildStore) LatestBranches(arg0 context.Context, arg1 int64) ([]*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LatestBranches", arg0, arg1) - ret0, _ := ret[0].([]*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// LatestBranches indicates an expected call of LatestBranches. -func (mr *MockBuildStoreMockRecorder) LatestBranches(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestBranches", reflect.TypeOf((*MockBuildStore)(nil).LatestBranches), arg0, arg1) -} - -// LatestDeploys mocks base method. -func (m *MockBuildStore) LatestDeploys(arg0 context.Context, arg1 int64) ([]*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LatestDeploys", arg0, arg1) - ret0, _ := ret[0].([]*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// LatestDeploys indicates an expected call of LatestDeploys. -func (mr *MockBuildStoreMockRecorder) LatestDeploys(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestDeploys", reflect.TypeOf((*MockBuildStore)(nil).LatestDeploys), arg0, arg1) -} - -// LatestPulls mocks base method. -func (m *MockBuildStore) LatestPulls(arg0 context.Context, arg1 int64) ([]*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LatestPulls", arg0, arg1) - ret0, _ := ret[0].([]*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// LatestPulls indicates an expected call of LatestPulls. -func (mr *MockBuildStoreMockRecorder) LatestPulls(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestPulls", reflect.TypeOf((*MockBuildStore)(nil).LatestPulls), arg0, arg1) -} - -// List mocks base method. -func (m *MockBuildStore) List(arg0 context.Context, arg1 int64, arg2, arg3 int) ([]*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockBuildStoreMockRecorder) List(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockBuildStore)(nil).List), arg0, arg1, arg2, arg3) -} - -// ListRef mocks base method. -func (m *MockBuildStore) ListRef(arg0 context.Context, arg1 int64, arg2 string, arg3, arg4 int) ([]*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListRef", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].([]*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListRef indicates an expected call of ListRef. -func (mr *MockBuildStoreMockRecorder) ListRef(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRef", reflect.TypeOf((*MockBuildStore)(nil).ListRef), arg0, arg1, arg2, arg3, arg4) -} - -// Pending mocks base method. -func (m *MockBuildStore) Pending(arg0 context.Context) ([]*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Pending", arg0) - ret0, _ := ret[0].([]*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Pending indicates an expected call of Pending. -func (mr *MockBuildStoreMockRecorder) Pending(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pending", reflect.TypeOf((*MockBuildStore)(nil).Pending), arg0) -} - -// Purge mocks base method. -func (m *MockBuildStore) Purge(arg0 context.Context, arg1, arg2 int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Purge", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Purge indicates an expected call of Purge. -func (mr *MockBuildStoreMockRecorder) Purge(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Purge", reflect.TypeOf((*MockBuildStore)(nil).Purge), arg0, arg1, arg2) -} - -// Running mocks base method. -func (m *MockBuildStore) Running(arg0 context.Context) ([]*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Running", arg0) - ret0, _ := ret[0].([]*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Running indicates an expected call of Running. -func (mr *MockBuildStoreMockRecorder) Running(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Running", reflect.TypeOf((*MockBuildStore)(nil).Running), arg0) -} - -// Update mocks base method. -func (m *MockBuildStore) Update(arg0 context.Context, arg1 *core.Build) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockBuildStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockBuildStore)(nil).Update), arg0, arg1) -} - -// MockCronStore is a mock of CronStore interface. -type MockCronStore struct { - ctrl *gomock.Controller - recorder *MockCronStoreMockRecorder -} - -// MockCronStoreMockRecorder is the mock recorder for MockCronStore. -type MockCronStoreMockRecorder struct { - mock *MockCronStore -} - -// NewMockCronStore creates a new mock instance. -func NewMockCronStore(ctrl *gomock.Controller) *MockCronStore { - mock := &MockCronStore{ctrl: ctrl} - mock.recorder = &MockCronStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockCronStore) EXPECT() *MockCronStoreMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockCronStore) Create(arg0 context.Context, arg1 *core.Cron) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockCronStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockCronStore)(nil).Create), arg0, arg1) -} - -// Delete mocks base method. -func (m *MockCronStore) Delete(arg0 context.Context, arg1 *core.Cron) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockCronStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCronStore)(nil).Delete), arg0, arg1) -} - -// Find mocks base method. -func (m *MockCronStore) Find(arg0 context.Context, arg1 int64) (*core.Cron, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Cron) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockCronStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockCronStore)(nil).Find), arg0, arg1) -} - -// FindName mocks base method. -func (m *MockCronStore) FindName(arg0 context.Context, arg1 int64, arg2 string) (*core.Cron, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindName", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Cron) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindName indicates an expected call of FindName. -func (mr *MockCronStoreMockRecorder) FindName(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindName", reflect.TypeOf((*MockCronStore)(nil).FindName), arg0, arg1, arg2) -} - -// List mocks base method. -func (m *MockCronStore) List(arg0 context.Context, arg1 int64) ([]*core.Cron, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Cron) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockCronStoreMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockCronStore)(nil).List), arg0, arg1) -} - -// Ready mocks base method. -func (m *MockCronStore) Ready(arg0 context.Context, arg1 int64) ([]*core.Cron, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Ready", arg0, arg1) - ret0, _ := ret[0].([]*core.Cron) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Ready indicates an expected call of Ready. -func (mr *MockCronStoreMockRecorder) Ready(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ready", reflect.TypeOf((*MockCronStore)(nil).Ready), arg0, arg1) -} - -// Update mocks base method. -func (m *MockCronStore) Update(arg0 context.Context, arg1 *core.Cron) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockCronStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockCronStore)(nil).Update), arg0, arg1) -} - -// MockLogStore is a mock of LogStore interface. -type MockLogStore struct { - ctrl *gomock.Controller - recorder *MockLogStoreMockRecorder -} - -// MockLogStoreMockRecorder is the mock recorder for MockLogStore. -type MockLogStoreMockRecorder struct { - mock *MockLogStore -} - -// NewMockLogStore creates a new mock instance. -func NewMockLogStore(ctrl *gomock.Controller) *MockLogStore { - mock := &MockLogStore{ctrl: ctrl} - mock.recorder = &MockLogStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockLogStore) EXPECT() *MockLogStoreMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockLogStore) Create(arg0 context.Context, arg1 int64, arg2 io.Reader) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockLogStoreMockRecorder) Create(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockLogStore)(nil).Create), arg0, arg1, arg2) -} - -// Delete mocks base method. -func (m *MockLogStore) Delete(arg0 context.Context, arg1 int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockLogStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockLogStore)(nil).Delete), arg0, arg1) -} - -// Find mocks base method. -func (m *MockLogStore) Find(arg0 context.Context, arg1 int64) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockLogStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockLogStore)(nil).Find), arg0, arg1) -} - -// Update mocks base method. -func (m *MockLogStore) Update(arg0 context.Context, arg1 int64, arg2 io.Reader) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockLogStoreMockRecorder) Update(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockLogStore)(nil).Update), arg0, arg1, arg2) -} - -// MockPermStore is a mock of PermStore interface. -type MockPermStore struct { - ctrl *gomock.Controller - recorder *MockPermStoreMockRecorder -} - -// MockPermStoreMockRecorder is the mock recorder for MockPermStore. -type MockPermStoreMockRecorder struct { - mock *MockPermStore -} - -// NewMockPermStore creates a new mock instance. -func NewMockPermStore(ctrl *gomock.Controller) *MockPermStore { - mock := &MockPermStore{ctrl: ctrl} - mock.recorder = &MockPermStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockPermStore) EXPECT() *MockPermStoreMockRecorder { - return m.recorder -} - -// Delete mocks base method. -func (m *MockPermStore) Delete(arg0 context.Context, arg1 *core.Perm) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockPermStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockPermStore)(nil).Delete), arg0, arg1) -} - -// Find mocks base method. -func (m *MockPermStore) Find(arg0 context.Context, arg1 string, arg2 int64) (*core.Perm, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Perm) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockPermStoreMockRecorder) Find(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockPermStore)(nil).Find), arg0, arg1, arg2) -} - -// List mocks base method. -func (m *MockPermStore) List(arg0 context.Context, arg1 string) ([]*core.Collaborator, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Collaborator) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockPermStoreMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockPermStore)(nil).List), arg0, arg1) -} - -// Update mocks base method. -func (m *MockPermStore) Update(arg0 context.Context, arg1 *core.Perm) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockPermStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockPermStore)(nil).Update), arg0, arg1) -} - -// MockSecretStore is a mock of SecretStore interface. -type MockSecretStore struct { - ctrl *gomock.Controller - recorder *MockSecretStoreMockRecorder -} - -// MockSecretStoreMockRecorder is the mock recorder for MockSecretStore. -type MockSecretStoreMockRecorder struct { - mock *MockSecretStore -} - -// NewMockSecretStore creates a new mock instance. -func NewMockSecretStore(ctrl *gomock.Controller) *MockSecretStore { - mock := &MockSecretStore{ctrl: ctrl} - mock.recorder = &MockSecretStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSecretStore) EXPECT() *MockSecretStoreMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockSecretStore) Create(arg0 context.Context, arg1 *core.Secret) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockSecretStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockSecretStore)(nil).Create), arg0, arg1) -} - -// Delete mocks base method. -func (m *MockSecretStore) Delete(arg0 context.Context, arg1 *core.Secret) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockSecretStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSecretStore)(nil).Delete), arg0, arg1) -} - -// Find mocks base method. -func (m *MockSecretStore) Find(arg0 context.Context, arg1 int64) (*core.Secret, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Secret) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockSecretStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockSecretStore)(nil).Find), arg0, arg1) -} - -// FindName mocks base method. -func (m *MockSecretStore) FindName(arg0 context.Context, arg1 int64, arg2 string) (*core.Secret, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindName", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Secret) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindName indicates an expected call of FindName. -func (mr *MockSecretStoreMockRecorder) FindName(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindName", reflect.TypeOf((*MockSecretStore)(nil).FindName), arg0, arg1, arg2) -} - -// List mocks base method. -func (m *MockSecretStore) List(arg0 context.Context, arg1 int64) ([]*core.Secret, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Secret) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockSecretStoreMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockSecretStore)(nil).List), arg0, arg1) -} - -// Update mocks base method. -func (m *MockSecretStore) Update(arg0 context.Context, arg1 *core.Secret) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockSecretStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSecretStore)(nil).Update), arg0, arg1) -} - -// MockGlobalSecretStore is a mock of GlobalSecretStore interface. -type MockGlobalSecretStore struct { - ctrl *gomock.Controller - recorder *MockGlobalSecretStoreMockRecorder -} - -// MockGlobalSecretStoreMockRecorder is the mock recorder for MockGlobalSecretStore. -type MockGlobalSecretStoreMockRecorder struct { - mock *MockGlobalSecretStore -} - -// NewMockGlobalSecretStore creates a new mock instance. -func NewMockGlobalSecretStore(ctrl *gomock.Controller) *MockGlobalSecretStore { - mock := &MockGlobalSecretStore{ctrl: ctrl} - mock.recorder = &MockGlobalSecretStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockGlobalSecretStore) EXPECT() *MockGlobalSecretStoreMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockGlobalSecretStore) Create(arg0 context.Context, arg1 *core.Secret) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockGlobalSecretStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockGlobalSecretStore)(nil).Create), arg0, arg1) -} - -// Delete mocks base method. -func (m *MockGlobalSecretStore) Delete(arg0 context.Context, arg1 *core.Secret) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockGlobalSecretStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockGlobalSecretStore)(nil).Delete), arg0, arg1) -} - -// Find mocks base method. -func (m *MockGlobalSecretStore) Find(arg0 context.Context, arg1 int64) (*core.Secret, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Secret) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockGlobalSecretStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockGlobalSecretStore)(nil).Find), arg0, arg1) -} - -// FindName mocks base method. -func (m *MockGlobalSecretStore) FindName(arg0 context.Context, arg1, arg2 string) (*core.Secret, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindName", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Secret) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindName indicates an expected call of FindName. -func (mr *MockGlobalSecretStoreMockRecorder) FindName(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindName", reflect.TypeOf((*MockGlobalSecretStore)(nil).FindName), arg0, arg1, arg2) -} - -// List mocks base method. -func (m *MockGlobalSecretStore) List(arg0 context.Context, arg1 string) ([]*core.Secret, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Secret) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockGlobalSecretStoreMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockGlobalSecretStore)(nil).List), arg0, arg1) -} - -// ListAll mocks base method. -func (m *MockGlobalSecretStore) ListAll(arg0 context.Context) ([]*core.Secret, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAll", arg0) - ret0, _ := ret[0].([]*core.Secret) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListAll indicates an expected call of ListAll. -func (mr *MockGlobalSecretStoreMockRecorder) ListAll(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAll", reflect.TypeOf((*MockGlobalSecretStore)(nil).ListAll), arg0) -} - -// Update mocks base method. -func (m *MockGlobalSecretStore) Update(arg0 context.Context, arg1 *core.Secret) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockGlobalSecretStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockGlobalSecretStore)(nil).Update), arg0, arg1) -} - -// MockStageStore is a mock of StageStore interface. -type MockStageStore struct { - ctrl *gomock.Controller - recorder *MockStageStoreMockRecorder -} - -// MockStageStoreMockRecorder is the mock recorder for MockStageStore. -type MockStageStoreMockRecorder struct { - mock *MockStageStore -} - -// NewMockStageStore creates a new mock instance. -func NewMockStageStore(ctrl *gomock.Controller) *MockStageStore { - mock := &MockStageStore{ctrl: ctrl} - mock.recorder = &MockStageStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockStageStore) EXPECT() *MockStageStoreMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockStageStore) Create(arg0 context.Context, arg1 *core.Stage) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockStageStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockStageStore)(nil).Create), arg0, arg1) -} - -// Find mocks base method. -func (m *MockStageStore) Find(arg0 context.Context, arg1 int64) (*core.Stage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Stage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockStageStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockStageStore)(nil).Find), arg0, arg1) -} - -// FindNumber mocks base method. -func (m *MockStageStore) FindNumber(arg0 context.Context, arg1 int64, arg2 int) (*core.Stage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindNumber", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Stage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindNumber indicates an expected call of FindNumber. -func (mr *MockStageStoreMockRecorder) FindNumber(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindNumber", reflect.TypeOf((*MockStageStore)(nil).FindNumber), arg0, arg1, arg2) -} - -// List mocks base method. -func (m *MockStageStore) List(arg0 context.Context, arg1 int64) ([]*core.Stage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Stage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockStageStoreMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockStageStore)(nil).List), arg0, arg1) -} - -// ListIncomplete mocks base method. -func (m *MockStageStore) ListIncomplete(arg0 context.Context) ([]*core.Stage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListIncomplete", arg0) - ret0, _ := ret[0].([]*core.Stage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListIncomplete indicates an expected call of ListIncomplete. -func (mr *MockStageStoreMockRecorder) ListIncomplete(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListIncomplete", reflect.TypeOf((*MockStageStore)(nil).ListIncomplete), arg0) -} - -// ListState mocks base method. -func (m *MockStageStore) ListState(arg0 context.Context, arg1 string) ([]*core.Stage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListState", arg0, arg1) - ret0, _ := ret[0].([]*core.Stage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListState indicates an expected call of ListState. -func (mr *MockStageStoreMockRecorder) ListState(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListState", reflect.TypeOf((*MockStageStore)(nil).ListState), arg0, arg1) -} - -// ListSteps mocks base method. -func (m *MockStageStore) ListSteps(arg0 context.Context, arg1 int64) ([]*core.Stage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListSteps", arg0, arg1) - ret0, _ := ret[0].([]*core.Stage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListSteps indicates an expected call of ListSteps. -func (mr *MockStageStoreMockRecorder) ListSteps(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSteps", reflect.TypeOf((*MockStageStore)(nil).ListSteps), arg0, arg1) -} - -// Update mocks base method. -func (m *MockStageStore) Update(arg0 context.Context, arg1 *core.Stage) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockStageStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockStageStore)(nil).Update), arg0, arg1) -} - -// MockStepStore is a mock of StepStore interface. -type MockStepStore struct { - ctrl *gomock.Controller - recorder *MockStepStoreMockRecorder -} - -// MockStepStoreMockRecorder is the mock recorder for MockStepStore. -type MockStepStoreMockRecorder struct { - mock *MockStepStore -} - -// NewMockStepStore creates a new mock instance. -func NewMockStepStore(ctrl *gomock.Controller) *MockStepStore { - mock := &MockStepStore{ctrl: ctrl} - mock.recorder = &MockStepStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockStepStore) EXPECT() *MockStepStoreMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockStepStore) Create(arg0 context.Context, arg1 *core.Step) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockStepStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockStepStore)(nil).Create), arg0, arg1) -} - -// Find mocks base method. -func (m *MockStepStore) Find(arg0 context.Context, arg1 int64) (*core.Step, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Step) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockStepStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockStepStore)(nil).Find), arg0, arg1) -} - -// FindNumber mocks base method. -func (m *MockStepStore) FindNumber(arg0 context.Context, arg1 int64, arg2 int) (*core.Step, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindNumber", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Step) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindNumber indicates an expected call of FindNumber. -func (mr *MockStepStoreMockRecorder) FindNumber(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindNumber", reflect.TypeOf((*MockStepStore)(nil).FindNumber), arg0, arg1, arg2) -} - -// List mocks base method. -func (m *MockStepStore) List(arg0 context.Context, arg1 int64) ([]*core.Step, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Step) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockStepStoreMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockStepStore)(nil).List), arg0, arg1) -} - -// Update mocks base method. -func (m *MockStepStore) Update(arg0 context.Context, arg1 *core.Step) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockStepStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockStepStore)(nil).Update), arg0, arg1) -} - -// MockRepositoryStore is a mock of RepositoryStore interface. -type MockRepositoryStore struct { - ctrl *gomock.Controller - recorder *MockRepositoryStoreMockRecorder -} - -// MockRepositoryStoreMockRecorder is the mock recorder for MockRepositoryStore. -type MockRepositoryStoreMockRecorder struct { - mock *MockRepositoryStore -} - -// NewMockRepositoryStore creates a new mock instance. -func NewMockRepositoryStore(ctrl *gomock.Controller) *MockRepositoryStore { - mock := &MockRepositoryStore{ctrl: ctrl} - mock.recorder = &MockRepositoryStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRepositoryStore) EXPECT() *MockRepositoryStoreMockRecorder { - return m.recorder -} - -// Activate mocks base method. -func (m *MockRepositoryStore) Activate(arg0 context.Context, arg1 *core.Repository) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Activate", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Activate indicates an expected call of Activate. -func (mr *MockRepositoryStoreMockRecorder) Activate(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Activate", reflect.TypeOf((*MockRepositoryStore)(nil).Activate), arg0, arg1) -} - -// Count mocks base method. -func (m *MockRepositoryStore) Count(arg0 context.Context) (int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Count", arg0) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Count indicates an expected call of Count. -func (mr *MockRepositoryStoreMockRecorder) Count(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockRepositoryStore)(nil).Count), arg0) -} - -// Create mocks base method. -func (m *MockRepositoryStore) Create(arg0 context.Context, arg1 *core.Repository) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockRepositoryStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockRepositoryStore)(nil).Create), arg0, arg1) -} - -// Delete mocks base method. -func (m *MockRepositoryStore) Delete(arg0 context.Context, arg1 *core.Repository) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockRepositoryStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRepositoryStore)(nil).Delete), arg0, arg1) -} - -// Find mocks base method. -func (m *MockRepositoryStore) Find(arg0 context.Context, arg1 int64) (*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockRepositoryStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockRepositoryStore)(nil).Find), arg0, arg1) -} - -// FindName mocks base method. -func (m *MockRepositoryStore) FindName(arg0 context.Context, arg1, arg2 string) (*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindName", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindName indicates an expected call of FindName. -func (mr *MockRepositoryStoreMockRecorder) FindName(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindName", reflect.TypeOf((*MockRepositoryStore)(nil).FindName), arg0, arg1, arg2) -} - -// Increment mocks base method. -func (m *MockRepositoryStore) Increment(arg0 context.Context, arg1 *core.Repository) (*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Increment", arg0, arg1) - ret0, _ := ret[0].(*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Increment indicates an expected call of Increment. -func (mr *MockRepositoryStoreMockRecorder) Increment(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Increment", reflect.TypeOf((*MockRepositoryStore)(nil).Increment), arg0, arg1) -} - -// List mocks base method. -func (m *MockRepositoryStore) List(arg0 context.Context, arg1 int64) ([]*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockRepositoryStoreMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepositoryStore)(nil).List), arg0, arg1) -} - -// ListAll mocks base method. -func (m *MockRepositoryStore) ListAll(arg0 context.Context, arg1, arg2 int) ([]*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAll", arg0, arg1, arg2) - ret0, _ := ret[0].([]*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListAll indicates an expected call of ListAll. -func (mr *MockRepositoryStoreMockRecorder) ListAll(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAll", reflect.TypeOf((*MockRepositoryStore)(nil).ListAll), arg0, arg1, arg2) -} - -// ListIncomplete mocks base method. -func (m *MockRepositoryStore) ListIncomplete(arg0 context.Context) ([]*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListIncomplete", arg0) - ret0, _ := ret[0].([]*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListIncomplete indicates an expected call of ListIncomplete. -func (mr *MockRepositoryStoreMockRecorder) ListIncomplete(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListIncomplete", reflect.TypeOf((*MockRepositoryStore)(nil).ListIncomplete), arg0) -} - -// ListLatest mocks base method. -func (m *MockRepositoryStore) ListLatest(arg0 context.Context, arg1 int64) ([]*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListLatest", arg0, arg1) - ret0, _ := ret[0].([]*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListLatest indicates an expected call of ListLatest. -func (mr *MockRepositoryStoreMockRecorder) ListLatest(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListLatest", reflect.TypeOf((*MockRepositoryStore)(nil).ListLatest), arg0, arg1) -} - -// ListRecent mocks base method. -func (m *MockRepositoryStore) ListRecent(arg0 context.Context, arg1 int64) ([]*core.Repository, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListRecent", arg0, arg1) - ret0, _ := ret[0].([]*core.Repository) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListRecent indicates an expected call of ListRecent. -func (mr *MockRepositoryStoreMockRecorder) ListRecent(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRecent", reflect.TypeOf((*MockRepositoryStore)(nil).ListRecent), arg0, arg1) -} - -// ListRunningStatus mocks base method. -func (m *MockRepositoryStore) ListRunningStatus(arg0 context.Context) ([]*core.RepoBuildStage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListRunningStatus", arg0) - ret0, _ := ret[0].([]*core.RepoBuildStage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListRunningStatus indicates an expected call of ListRunningStatus. -func (mr *MockRepositoryStoreMockRecorder) ListRunningStatus(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRunningStatus", reflect.TypeOf((*MockRepositoryStore)(nil).ListRunningStatus), arg0) -} - -// Update mocks base method. -func (m *MockRepositoryStore) Update(arg0 context.Context, arg1 *core.Repository) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockRepositoryStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockRepositoryStore)(nil).Update), arg0, arg1) -} - -// MockUserStore is a mock of UserStore interface. -type MockUserStore struct { - ctrl *gomock.Controller - recorder *MockUserStoreMockRecorder -} - -// MockUserStoreMockRecorder is the mock recorder for MockUserStore. -type MockUserStoreMockRecorder struct { - mock *MockUserStore -} - -// NewMockUserStore creates a new mock instance. -func NewMockUserStore(ctrl *gomock.Controller) *MockUserStore { - mock := &MockUserStore{ctrl: ctrl} - mock.recorder = &MockUserStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockUserStore) EXPECT() *MockUserStoreMockRecorder { - return m.recorder -} - -// Count mocks base method. -func (m *MockUserStore) Count(arg0 context.Context) (int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Count", arg0) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Count indicates an expected call of Count. -func (mr *MockUserStoreMockRecorder) Count(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockUserStore)(nil).Count), arg0) -} - -// CountHuman mocks base method. -func (m *MockUserStore) CountHuman(arg0 context.Context) (int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CountHuman", arg0) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CountHuman indicates an expected call of CountHuman. -func (mr *MockUserStoreMockRecorder) CountHuman(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountHuman", reflect.TypeOf((*MockUserStore)(nil).CountHuman), arg0) -} - -// Create mocks base method. -func (m *MockUserStore) Create(arg0 context.Context, arg1 *core.User) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockUserStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockUserStore)(nil).Create), arg0, arg1) -} - -// Delete mocks base method. -func (m *MockUserStore) Delete(arg0 context.Context, arg1 *core.User) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockUserStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockUserStore)(nil).Delete), arg0, arg1) -} - -// Find mocks base method. -func (m *MockUserStore) Find(arg0 context.Context, arg1 int64) (*core.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockUserStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockUserStore)(nil).Find), arg0, arg1) -} - -// FindLogin mocks base method. -func (m *MockUserStore) FindLogin(arg0 context.Context, arg1 string) (*core.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindLogin", arg0, arg1) - ret0, _ := ret[0].(*core.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindLogin indicates an expected call of FindLogin. -func (mr *MockUserStoreMockRecorder) FindLogin(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindLogin", reflect.TypeOf((*MockUserStore)(nil).FindLogin), arg0, arg1) -} - -// FindToken mocks base method. -func (m *MockUserStore) FindToken(arg0 context.Context, arg1 string) (*core.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindToken", arg0, arg1) - ret0, _ := ret[0].(*core.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindToken indicates an expected call of FindToken. -func (mr *MockUserStoreMockRecorder) FindToken(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindToken", reflect.TypeOf((*MockUserStore)(nil).FindToken), arg0, arg1) -} - -// List mocks base method. -func (m *MockUserStore) List(arg0 context.Context) ([]*core.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0) - ret0, _ := ret[0].([]*core.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockUserStoreMockRecorder) List(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockUserStore)(nil).List), arg0) -} - -// ListRange mocks base method. -func (m *MockUserStore) ListRange(arg0 context.Context, arg1 core.UserParams) ([]*core.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListRange", arg0, arg1) - ret0, _ := ret[0].([]*core.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListRange indicates an expected call of ListRange. -func (mr *MockUserStoreMockRecorder) ListRange(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRange", reflect.TypeOf((*MockUserStore)(nil).ListRange), arg0, arg1) -} - -// Update mocks base method. -func (m *MockUserStore) Update(arg0 context.Context, arg1 *core.User) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockUserStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockUserStore)(nil).Update), arg0, arg1) -} - -// MockScheduler is a mock of Scheduler interface. -type MockScheduler struct { - ctrl *gomock.Controller - recorder *MockSchedulerMockRecorder -} - -// MockSchedulerMockRecorder is the mock recorder for MockScheduler. -type MockSchedulerMockRecorder struct { - mock *MockScheduler -} - -// NewMockScheduler creates a new mock instance. -func NewMockScheduler(ctrl *gomock.Controller) *MockScheduler { - mock := &MockScheduler{ctrl: ctrl} - mock.recorder = &MockSchedulerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockScheduler) EXPECT() *MockSchedulerMockRecorder { - return m.recorder -} - -// Cancel mocks base method. -func (m *MockScheduler) Cancel(arg0 context.Context, arg1 int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Cancel", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Cancel indicates an expected call of Cancel. -func (mr *MockSchedulerMockRecorder) Cancel(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cancel", reflect.TypeOf((*MockScheduler)(nil).Cancel), arg0, arg1) -} - -// Cancelled mocks base method. -func (m *MockScheduler) Cancelled(arg0 context.Context, arg1 int64) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Cancelled", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Cancelled indicates an expected call of Cancelled. -func (mr *MockSchedulerMockRecorder) Cancelled(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cancelled", reflect.TypeOf((*MockScheduler)(nil).Cancelled), arg0, arg1) -} - -// Pause mocks base method. -func (m *MockScheduler) Pause(arg0 context.Context) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Pause", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// Pause indicates an expected call of Pause. -func (mr *MockSchedulerMockRecorder) Pause(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pause", reflect.TypeOf((*MockScheduler)(nil).Pause), arg0) -} - -// Request mocks base method. -func (m *MockScheduler) Request(arg0 context.Context, arg1 core.Filter) (*core.Stage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Request", arg0, arg1) - ret0, _ := ret[0].(*core.Stage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Request indicates an expected call of Request. -func (mr *MockSchedulerMockRecorder) Request(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Request", reflect.TypeOf((*MockScheduler)(nil).Request), arg0, arg1) -} - -// Resume mocks base method. -func (m *MockScheduler) Resume(arg0 context.Context) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Resume", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// Resume indicates an expected call of Resume. -func (mr *MockSchedulerMockRecorder) Resume(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resume", reflect.TypeOf((*MockScheduler)(nil).Resume), arg0) -} - -// Schedule mocks base method. -func (m *MockScheduler) Schedule(arg0 context.Context, arg1 *core.Stage) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Schedule", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Schedule indicates an expected call of Schedule. -func (mr *MockSchedulerMockRecorder) Schedule(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Schedule", reflect.TypeOf((*MockScheduler)(nil).Schedule), arg0, arg1) -} - -// Stats mocks base method. -func (m *MockScheduler) Stats(arg0 context.Context) (interface{}, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Stats", arg0) - ret0, _ := ret[0].(interface{}) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Stats indicates an expected call of Stats. -func (mr *MockSchedulerMockRecorder) Stats(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stats", reflect.TypeOf((*MockScheduler)(nil).Stats), arg0) -} - -// MockSession is a mock of Session interface. -type MockSession struct { - ctrl *gomock.Controller - recorder *MockSessionMockRecorder -} - -// MockSessionMockRecorder is the mock recorder for MockSession. -type MockSessionMockRecorder struct { - mock *MockSession -} - -// NewMockSession creates a new mock instance. -func NewMockSession(ctrl *gomock.Controller) *MockSession { - mock := &MockSession{ctrl: ctrl} - mock.recorder = &MockSessionMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSession) EXPECT() *MockSessionMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockSession) Create(arg0 http.ResponseWriter, arg1 *core.User) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockSessionMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockSession)(nil).Create), arg0, arg1) -} - -// Delete mocks base method. -func (m *MockSession) Delete(arg0 http.ResponseWriter) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockSessionMockRecorder) Delete(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSession)(nil).Delete), arg0) -} - -// Get mocks base method. -func (m *MockSession) Get(arg0 *http.Request) (*core.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", arg0) - ret0, _ := ret[0].(*core.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get. -func (mr *MockSessionMockRecorder) Get(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockSession)(nil).Get), arg0) -} - -// MockOrganizationService is a mock of OrganizationService interface. -type MockOrganizationService struct { - ctrl *gomock.Controller - recorder *MockOrganizationServiceMockRecorder -} - -// MockOrganizationServiceMockRecorder is the mock recorder for MockOrganizationService. -type MockOrganizationServiceMockRecorder struct { - mock *MockOrganizationService -} - -// NewMockOrganizationService creates a new mock instance. -func NewMockOrganizationService(ctrl *gomock.Controller) *MockOrganizationService { - mock := &MockOrganizationService{ctrl: ctrl} - mock.recorder = &MockOrganizationServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockOrganizationService) EXPECT() *MockOrganizationServiceMockRecorder { - return m.recorder -} - -// List mocks base method. -func (m *MockOrganizationService) List(arg0 context.Context, arg1 *core.User) ([]*core.Organization, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Organization) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockOrganizationServiceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockOrganizationService)(nil).List), arg0, arg1) -} - -// Membership mocks base method. -func (m *MockOrganizationService) Membership(arg0 context.Context, arg1 *core.User, arg2 string) (bool, bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Membership", arg0, arg1, arg2) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(bool) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Membership indicates an expected call of Membership. -func (mr *MockOrganizationServiceMockRecorder) Membership(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Membership", reflect.TypeOf((*MockOrganizationService)(nil).Membership), arg0, arg1, arg2) -} - -// MockSecretService is a mock of SecretService interface. -type MockSecretService struct { - ctrl *gomock.Controller - recorder *MockSecretServiceMockRecorder -} - -// MockSecretServiceMockRecorder is the mock recorder for MockSecretService. -type MockSecretServiceMockRecorder struct { - mock *MockSecretService -} - -// NewMockSecretService creates a new mock instance. -func NewMockSecretService(ctrl *gomock.Controller) *MockSecretService { - mock := &MockSecretService{ctrl: ctrl} - mock.recorder = &MockSecretServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSecretService) EXPECT() *MockSecretServiceMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockSecretService) Find(arg0 context.Context, arg1 *core.SecretArgs) (*core.Secret, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Secret) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockSecretServiceMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockSecretService)(nil).Find), arg0, arg1) -} - -// MockRegistryService is a mock of RegistryService interface. -type MockRegistryService struct { - ctrl *gomock.Controller - recorder *MockRegistryServiceMockRecorder -} - -// MockRegistryServiceMockRecorder is the mock recorder for MockRegistryService. -type MockRegistryServiceMockRecorder struct { - mock *MockRegistryService -} - -// NewMockRegistryService creates a new mock instance. -func NewMockRegistryService(ctrl *gomock.Controller) *MockRegistryService { - mock := &MockRegistryService{ctrl: ctrl} - mock.recorder = &MockRegistryServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRegistryService) EXPECT() *MockRegistryServiceMockRecorder { - return m.recorder -} - -// List mocks base method. -func (m *MockRegistryService) List(arg0 context.Context, arg1 *core.RegistryArgs) ([]*core.Registry, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Registry) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockRegistryServiceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRegistryService)(nil).List), arg0, arg1) -} - -// MockConfigService is a mock of ConfigService interface. -type MockConfigService struct { - ctrl *gomock.Controller - recorder *MockConfigServiceMockRecorder -} - -// MockConfigServiceMockRecorder is the mock recorder for MockConfigService. -type MockConfigServiceMockRecorder struct { - mock *MockConfigService -} - -// NewMockConfigService creates a new mock instance. -func NewMockConfigService(ctrl *gomock.Controller) *MockConfigService { - mock := &MockConfigService{ctrl: ctrl} - mock.recorder = &MockConfigServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockConfigService) EXPECT() *MockConfigServiceMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockConfigService) Find(arg0 context.Context, arg1 *core.ConfigArgs) (*core.Config, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Config) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockConfigServiceMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockConfigService)(nil).Find), arg0, arg1) -} - -// MockTransferer is a mock of Transferer interface. -type MockTransferer struct { - ctrl *gomock.Controller - recorder *MockTransfererMockRecorder -} - -// MockTransfererMockRecorder is the mock recorder for MockTransferer. -type MockTransfererMockRecorder struct { - mock *MockTransferer -} - -// NewMockTransferer creates a new mock instance. -func NewMockTransferer(ctrl *gomock.Controller) *MockTransferer { - mock := &MockTransferer{ctrl: ctrl} - mock.recorder = &MockTransfererMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockTransferer) EXPECT() *MockTransfererMockRecorder { - return m.recorder -} - -// Transfer mocks base method. -func (m *MockTransferer) Transfer(arg0 context.Context, arg1 *core.User) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Transfer", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Transfer indicates an expected call of Transfer. -func (mr *MockTransfererMockRecorder) Transfer(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transfer", reflect.TypeOf((*MockTransferer)(nil).Transfer), arg0, arg1) -} - -// MockTriggerer is a mock of Triggerer interface. -type MockTriggerer struct { - ctrl *gomock.Controller - recorder *MockTriggererMockRecorder -} - -// MockTriggererMockRecorder is the mock recorder for MockTriggerer. -type MockTriggererMockRecorder struct { - mock *MockTriggerer -} - -// NewMockTriggerer creates a new mock instance. -func NewMockTriggerer(ctrl *gomock.Controller) *MockTriggerer { - mock := &MockTriggerer{ctrl: ctrl} - mock.recorder = &MockTriggererMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockTriggerer) EXPECT() *MockTriggererMockRecorder { - return m.recorder -} - -// Trigger mocks base method. -func (m *MockTriggerer) Trigger(arg0 context.Context, arg1 *core.Repository, arg2 *core.Hook) (*core.Build, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Trigger", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Build) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Trigger indicates an expected call of Trigger. -func (mr *MockTriggererMockRecorder) Trigger(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trigger", reflect.TypeOf((*MockTriggerer)(nil).Trigger), arg0, arg1, arg2) -} - -// MockSyncer is a mock of Syncer interface. -type MockSyncer struct { - ctrl *gomock.Controller - recorder *MockSyncerMockRecorder -} - -// MockSyncerMockRecorder is the mock recorder for MockSyncer. -type MockSyncerMockRecorder struct { - mock *MockSyncer -} - -// NewMockSyncer creates a new mock instance. -func NewMockSyncer(ctrl *gomock.Controller) *MockSyncer { - mock := &MockSyncer{ctrl: ctrl} - mock.recorder = &MockSyncerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSyncer) EXPECT() *MockSyncerMockRecorder { - return m.recorder -} - -// Sync mocks base method. -func (m *MockSyncer) Sync(arg0 context.Context, arg1 *core.User) (*core.Batch, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Sync", arg0, arg1) - ret0, _ := ret[0].(*core.Batch) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Sync indicates an expected call of Sync. -func (mr *MockSyncerMockRecorder) Sync(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sync", reflect.TypeOf((*MockSyncer)(nil).Sync), arg0, arg1) -} - -// MockLogStream is a mock of LogStream interface. -type MockLogStream struct { - ctrl *gomock.Controller - recorder *MockLogStreamMockRecorder -} - -// MockLogStreamMockRecorder is the mock recorder for MockLogStream. -type MockLogStreamMockRecorder struct { - mock *MockLogStream -} - -// NewMockLogStream creates a new mock instance. -func NewMockLogStream(ctrl *gomock.Controller) *MockLogStream { - mock := &MockLogStream{ctrl: ctrl} - mock.recorder = &MockLogStreamMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockLogStream) EXPECT() *MockLogStreamMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockLogStream) Create(arg0 context.Context, arg1 int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockLogStreamMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockLogStream)(nil).Create), arg0, arg1) -} - -// Delete mocks base method. -func (m *MockLogStream) Delete(arg0 context.Context, arg1 int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockLogStreamMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockLogStream)(nil).Delete), arg0, arg1) -} - -// Info mocks base method. -func (m *MockLogStream) Info(arg0 context.Context) *core.LogStreamInfo { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Info", arg0) - ret0, _ := ret[0].(*core.LogStreamInfo) - return ret0 -} - -// Info indicates an expected call of Info. -func (mr *MockLogStreamMockRecorder) Info(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogStream)(nil).Info), arg0) -} - -// Tail mocks base method. -func (m *MockLogStream) Tail(arg0 context.Context, arg1 int64) (<-chan *core.Line, <-chan error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Tail", arg0, arg1) - ret0, _ := ret[0].(<-chan *core.Line) - ret1, _ := ret[1].(<-chan error) - return ret0, ret1 -} - -// Tail indicates an expected call of Tail. -func (mr *MockLogStreamMockRecorder) Tail(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Tail", reflect.TypeOf((*MockLogStream)(nil).Tail), arg0, arg1) -} - -// Write mocks base method. -func (m *MockLogStream) Write(arg0 context.Context, arg1 int64, arg2 *core.Line) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Write", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Write indicates an expected call of Write. -func (mr *MockLogStreamMockRecorder) Write(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockLogStream)(nil).Write), arg0, arg1, arg2) -} - -// MockWebhookSender is a mock of WebhookSender interface. -type MockWebhookSender struct { - ctrl *gomock.Controller - recorder *MockWebhookSenderMockRecorder -} - -// MockWebhookSenderMockRecorder is the mock recorder for MockWebhookSender. -type MockWebhookSenderMockRecorder struct { - mock *MockWebhookSender -} - -// NewMockWebhookSender creates a new mock instance. -func NewMockWebhookSender(ctrl *gomock.Controller) *MockWebhookSender { - mock := &MockWebhookSender{ctrl: ctrl} - mock.recorder = &MockWebhookSenderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockWebhookSender) EXPECT() *MockWebhookSenderMockRecorder { - return m.recorder -} - -// Send mocks base method. -func (m *MockWebhookSender) Send(arg0 context.Context, arg1 *core.WebhookData) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Send", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Send indicates an expected call of Send. -func (mr *MockWebhookSenderMockRecorder) Send(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockWebhookSender)(nil).Send), arg0, arg1) -} - -// MockLicenseService is a mock of LicenseService interface. -type MockLicenseService struct { - ctrl *gomock.Controller - recorder *MockLicenseServiceMockRecorder -} - -// MockLicenseServiceMockRecorder is the mock recorder for MockLicenseService. -type MockLicenseServiceMockRecorder struct { - mock *MockLicenseService -} - -// NewMockLicenseService creates a new mock instance. -func NewMockLicenseService(ctrl *gomock.Controller) *MockLicenseService { - mock := &MockLicenseService{ctrl: ctrl} - mock.recorder = &MockLicenseServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockLicenseService) EXPECT() *MockLicenseServiceMockRecorder { - return m.recorder -} - -// Exceeded mocks base method. -func (m *MockLicenseService) Exceeded(arg0 context.Context) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Exceeded", arg0) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Exceeded indicates an expected call of Exceeded. -func (mr *MockLicenseServiceMockRecorder) Exceeded(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exceeded", reflect.TypeOf((*MockLicenseService)(nil).Exceeded), arg0) -} - -// Expired mocks base method. -func (m *MockLicenseService) Expired(arg0 context.Context) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Expired", arg0) - ret0, _ := ret[0].(bool) - return ret0 -} - -// Expired indicates an expected call of Expired. -func (mr *MockLicenseServiceMockRecorder) Expired(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Expired", reflect.TypeOf((*MockLicenseService)(nil).Expired), arg0) -} - -// MockTemplateStore is a mock of TemplateStore interface. -type MockTemplateStore struct { - ctrl *gomock.Controller - recorder *MockTemplateStoreMockRecorder -} - -// MockTemplateStoreMockRecorder is the mock recorder for MockTemplateStore. -type MockTemplateStoreMockRecorder struct { - mock *MockTemplateStore -} - -// NewMockTemplateStore creates a new mock instance. -func NewMockTemplateStore(ctrl *gomock.Controller) *MockTemplateStore { - mock := &MockTemplateStore{ctrl: ctrl} - mock.recorder = &MockTemplateStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockTemplateStore) EXPECT() *MockTemplateStoreMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockTemplateStore) Create(arg0 context.Context, arg1 *core.Template) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockTemplateStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockTemplateStore)(nil).Create), arg0, arg1) -} - -// Delete mocks base method. -func (m *MockTemplateStore) Delete(arg0 context.Context, arg1 *core.Template) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockTemplateStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockTemplateStore)(nil).Delete), arg0, arg1) -} - -// Find mocks base method. -func (m *MockTemplateStore) Find(arg0 context.Context, arg1 int64) (*core.Template, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*core.Template) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockTemplateStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockTemplateStore)(nil).Find), arg0, arg1) -} - -// FindName mocks base method. -func (m *MockTemplateStore) FindName(arg0 context.Context, arg1, arg2 string) (*core.Template, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindName", arg0, arg1, arg2) - ret0, _ := ret[0].(*core.Template) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindName indicates an expected call of FindName. -func (mr *MockTemplateStoreMockRecorder) FindName(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindName", reflect.TypeOf((*MockTemplateStore)(nil).FindName), arg0, arg1, arg2) -} - -// List mocks base method. -func (m *MockTemplateStore) List(arg0 context.Context, arg1 string) ([]*core.Template, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*core.Template) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List. -func (mr *MockTemplateStoreMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockTemplateStore)(nil).List), arg0, arg1) -} - -// ListAll mocks base method. -func (m *MockTemplateStore) ListAll(arg0 context.Context) ([]*core.Template, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAll", arg0) - ret0, _ := ret[0].([]*core.Template) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListAll indicates an expected call of ListAll. -func (mr *MockTemplateStoreMockRecorder) ListAll(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAll", reflect.TypeOf((*MockTemplateStore)(nil).ListAll), arg0) -} - -// Update mocks base method. -func (m *MockTemplateStore) Update(arg0 context.Context, arg1 *core.Template) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockTemplateStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockTemplateStore)(nil).Update), arg0, arg1) -} - -// MockCardStore is a mock of CardStore interface. -type MockCardStore struct { - ctrl *gomock.Controller - recorder *MockCardStoreMockRecorder -} - -// MockCardStoreMockRecorder is the mock recorder for MockCardStore. -type MockCardStoreMockRecorder struct { - mock *MockCardStore -} - -// NewMockCardStore creates a new mock instance. -func NewMockCardStore(ctrl *gomock.Controller) *MockCardStore { - mock := &MockCardStore{ctrl: ctrl} - mock.recorder = &MockCardStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockCardStore) EXPECT() *MockCardStoreMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockCardStore) Create(arg0 context.Context, arg1 int64, arg2 io.Reader) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create. -func (mr *MockCardStoreMockRecorder) Create(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockCardStore)(nil).Create), arg0, arg1, arg2) -} - -// Delete mocks base method. -func (m *MockCardStore) Delete(arg0 context.Context, arg1 int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockCardStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCardStore)(nil).Delete), arg0, arg1) -} - -// Find mocks base method. -func (m *MockCardStore) Find(arg0 context.Context, arg1 int64) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockCardStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockCardStore)(nil).Find), arg0, arg1) -} - -// Update mocks base method. -func (m *MockCardStore) Update(arg0 context.Context, arg1 int64, arg2 io.Reader) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockCardStoreMockRecorder) Update(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockCardStore)(nil).Update), arg0, arg1, arg2) -} diff --git a/mock/mockscm/mock.go b/mock/mockscm/mock.go deleted file mode 100644 index 3d25749bdd..0000000000 --- a/mock/mockscm/mock.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package mockscm - -//go:generate mockgen -package=mockscm -destination=mock_gen.go github.com/drone/go-scm/scm ContentService,GitService,OrganizationService,PullRequestService,RepositoryService,UserService diff --git a/mock/mockscm/mock_gen.go b/mock/mockscm/mock_gen.go deleted file mode 100644 index ab1ce294ea..0000000000 --- a/mock/mockscm/mock_gen.go +++ /dev/null @@ -1,815 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/drone/go-scm/scm (interfaces: ContentService,GitService,OrganizationService,PullRequestService,RepositoryService,UserService) - -// Package mockscm is a generated GoMock package. -package mockscm - -import ( - context "context" - reflect "reflect" - - scm "github.com/drone/go-scm/scm" - gomock "github.com/golang/mock/gomock" -) - -// MockContentService is a mock of ContentService interface. -type MockContentService struct { - ctrl *gomock.Controller - recorder *MockContentServiceMockRecorder -} - -// MockContentServiceMockRecorder is the mock recorder for MockContentService. -type MockContentServiceMockRecorder struct { - mock *MockContentService -} - -// NewMockContentService creates a new mock instance. -func NewMockContentService(ctrl *gomock.Controller) *MockContentService { - mock := &MockContentService{ctrl: ctrl} - mock.recorder = &MockContentServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockContentService) EXPECT() *MockContentServiceMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockContentService) Create(arg0 context.Context, arg1, arg2 string, arg3 *scm.ContentParams) (*scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*scm.Response) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create. -func (mr *MockContentServiceMockRecorder) Create(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockContentService)(nil).Create), arg0, arg1, arg2, arg3) -} - -// Delete mocks base method. -func (m *MockContentService) Delete(arg0 context.Context, arg1, arg2 string, arg3 *scm.ContentParams) (*scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*scm.Response) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Delete indicates an expected call of Delete. -func (mr *MockContentServiceMockRecorder) Delete(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockContentService)(nil).Delete), arg0, arg1, arg2, arg3) -} - -// Find mocks base method. -func (m *MockContentService) Find(arg0 context.Context, arg1, arg2, arg3 string) (*scm.Content, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*scm.Content) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Find indicates an expected call of Find. -func (mr *MockContentServiceMockRecorder) Find(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockContentService)(nil).Find), arg0, arg1, arg2, arg3) -} - -// List mocks base method. -func (m *MockContentService) List(arg0 context.Context, arg1, arg2, arg3 string, arg4 scm.ListOptions) ([]*scm.ContentInfo, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].([]*scm.ContentInfo) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// List indicates an expected call of List. -func (mr *MockContentServiceMockRecorder) List(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockContentService)(nil).List), arg0, arg1, arg2, arg3, arg4) -} - -// Update mocks base method. -func (m *MockContentService) Update(arg0 context.Context, arg1, arg2 string, arg3 *scm.ContentParams) (*scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*scm.Response) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Update indicates an expected call of Update. -func (mr *MockContentServiceMockRecorder) Update(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockContentService)(nil).Update), arg0, arg1, arg2, arg3) -} - -// MockGitService is a mock of GitService interface. -type MockGitService struct { - ctrl *gomock.Controller - recorder *MockGitServiceMockRecorder -} - -// MockGitServiceMockRecorder is the mock recorder for MockGitService. -type MockGitServiceMockRecorder struct { - mock *MockGitService -} - -// NewMockGitService creates a new mock instance. -func NewMockGitService(ctrl *gomock.Controller) *MockGitService { - mock := &MockGitService{ctrl: ctrl} - mock.recorder = &MockGitServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockGitService) EXPECT() *MockGitServiceMockRecorder { - return m.recorder -} - -// CompareChanges mocks base method. -func (m *MockGitService) CompareChanges(arg0 context.Context, arg1, arg2, arg3 string, arg4 scm.ListOptions) ([]*scm.Change, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CompareChanges", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].([]*scm.Change) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// CompareChanges indicates an expected call of CompareChanges. -func (mr *MockGitServiceMockRecorder) CompareChanges(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompareChanges", reflect.TypeOf((*MockGitService)(nil).CompareChanges), arg0, arg1, arg2, arg3, arg4) -} - -// CreateBranch mocks base method. -func (m *MockGitService) CreateBranch(arg0 context.Context, arg1 string, arg2 *scm.CreateBranch) (*scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateBranch", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Response) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateBranch indicates an expected call of CreateBranch. -func (mr *MockGitServiceMockRecorder) CreateBranch(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBranch", reflect.TypeOf((*MockGitService)(nil).CreateBranch), arg0, arg1, arg2) -} - -// FindBranch mocks base method. -func (m *MockGitService) FindBranch(arg0 context.Context, arg1, arg2 string) (*scm.Reference, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindBranch", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Reference) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FindBranch indicates an expected call of FindBranch. -func (mr *MockGitServiceMockRecorder) FindBranch(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindBranch", reflect.TypeOf((*MockGitService)(nil).FindBranch), arg0, arg1, arg2) -} - -// FindCommit mocks base method. -func (m *MockGitService) FindCommit(arg0 context.Context, arg1, arg2 string) (*scm.Commit, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindCommit", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Commit) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FindCommit indicates an expected call of FindCommit. -func (mr *MockGitServiceMockRecorder) FindCommit(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindCommit", reflect.TypeOf((*MockGitService)(nil).FindCommit), arg0, arg1, arg2) -} - -// FindTag mocks base method. -func (m *MockGitService) FindTag(arg0 context.Context, arg1, arg2 string) (*scm.Reference, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindTag", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Reference) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FindTag indicates an expected call of FindTag. -func (mr *MockGitServiceMockRecorder) FindTag(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindTag", reflect.TypeOf((*MockGitService)(nil).FindTag), arg0, arg1, arg2) -} - -// ListBranches mocks base method. -func (m *MockGitService) ListBranches(arg0 context.Context, arg1 string, arg2 scm.ListOptions) ([]*scm.Reference, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListBranches", arg0, arg1, arg2) - ret0, _ := ret[0].([]*scm.Reference) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListBranches indicates an expected call of ListBranches. -func (mr *MockGitServiceMockRecorder) ListBranches(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBranches", reflect.TypeOf((*MockGitService)(nil).ListBranches), arg0, arg1, arg2) -} - -// ListChanges mocks base method. -func (m *MockGitService) ListChanges(arg0 context.Context, arg1, arg2 string, arg3 scm.ListOptions) ([]*scm.Change, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListChanges", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]*scm.Change) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListChanges indicates an expected call of ListChanges. -func (mr *MockGitServiceMockRecorder) ListChanges(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListChanges", reflect.TypeOf((*MockGitService)(nil).ListChanges), arg0, arg1, arg2, arg3) -} - -// ListCommits mocks base method. -func (m *MockGitService) ListCommits(arg0 context.Context, arg1 string, arg2 scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListCommits", arg0, arg1, arg2) - ret0, _ := ret[0].([]*scm.Commit) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListCommits indicates an expected call of ListCommits. -func (mr *MockGitServiceMockRecorder) ListCommits(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCommits", reflect.TypeOf((*MockGitService)(nil).ListCommits), arg0, arg1, arg2) -} - -// ListTags mocks base method. -func (m *MockGitService) ListTags(arg0 context.Context, arg1 string, arg2 scm.ListOptions) ([]*scm.Reference, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListTags", arg0, arg1, arg2) - ret0, _ := ret[0].([]*scm.Reference) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListTags indicates an expected call of ListTags. -func (mr *MockGitServiceMockRecorder) ListTags(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTags", reflect.TypeOf((*MockGitService)(nil).ListTags), arg0, arg1, arg2) -} - -// MockOrganizationService is a mock of OrganizationService interface. -type MockOrganizationService struct { - ctrl *gomock.Controller - recorder *MockOrganizationServiceMockRecorder -} - -// MockOrganizationServiceMockRecorder is the mock recorder for MockOrganizationService. -type MockOrganizationServiceMockRecorder struct { - mock *MockOrganizationService -} - -// NewMockOrganizationService creates a new mock instance. -func NewMockOrganizationService(ctrl *gomock.Controller) *MockOrganizationService { - mock := &MockOrganizationService{ctrl: ctrl} - mock.recorder = &MockOrganizationServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockOrganizationService) EXPECT() *MockOrganizationServiceMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockOrganizationService) Find(arg0 context.Context, arg1 string) (*scm.Organization, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*scm.Organization) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Find indicates an expected call of Find. -func (mr *MockOrganizationServiceMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockOrganizationService)(nil).Find), arg0, arg1) -} - -// FindMembership mocks base method. -func (m *MockOrganizationService) FindMembership(arg0 context.Context, arg1, arg2 string) (*scm.Membership, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindMembership", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Membership) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FindMembership indicates an expected call of FindMembership. -func (mr *MockOrganizationServiceMockRecorder) FindMembership(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMembership", reflect.TypeOf((*MockOrganizationService)(nil).FindMembership), arg0, arg1, arg2) -} - -// List mocks base method. -func (m *MockOrganizationService) List(arg0 context.Context, arg1 scm.ListOptions) ([]*scm.Organization, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*scm.Organization) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// List indicates an expected call of List. -func (mr *MockOrganizationServiceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockOrganizationService)(nil).List), arg0, arg1) -} - -// MockPullRequestService is a mock of PullRequestService interface. -type MockPullRequestService struct { - ctrl *gomock.Controller - recorder *MockPullRequestServiceMockRecorder -} - -// MockPullRequestServiceMockRecorder is the mock recorder for MockPullRequestService. -type MockPullRequestServiceMockRecorder struct { - mock *MockPullRequestService -} - -// NewMockPullRequestService creates a new mock instance. -func NewMockPullRequestService(ctrl *gomock.Controller) *MockPullRequestService { - mock := &MockPullRequestService{ctrl: ctrl} - mock.recorder = &MockPullRequestServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockPullRequestService) EXPECT() *MockPullRequestServiceMockRecorder { - return m.recorder -} - -// Close mocks base method. -func (m *MockPullRequestService) Close(arg0 context.Context, arg1 string, arg2 int) (*scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Response) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Close indicates an expected call of Close. -func (mr *MockPullRequestServiceMockRecorder) Close(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockPullRequestService)(nil).Close), arg0, arg1, arg2) -} - -// Create mocks base method. -func (m *MockPullRequestService) Create(arg0 context.Context, arg1 string, arg2 *scm.PullRequestInput) (*scm.PullRequest, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.PullRequest) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Create indicates an expected call of Create. -func (mr *MockPullRequestServiceMockRecorder) Create(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockPullRequestService)(nil).Create), arg0, arg1, arg2) -} - -// CreateComment mocks base method. -func (m *MockPullRequestService) CreateComment(arg0 context.Context, arg1 string, arg2 int, arg3 *scm.CommentInput) (*scm.Comment, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateComment", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*scm.Comment) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// CreateComment indicates an expected call of CreateComment. -func (mr *MockPullRequestServiceMockRecorder) CreateComment(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateComment", reflect.TypeOf((*MockPullRequestService)(nil).CreateComment), arg0, arg1, arg2, arg3) -} - -// DeleteComment mocks base method. -func (m *MockPullRequestService) DeleteComment(arg0 context.Context, arg1 string, arg2, arg3 int) (*scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteComment", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*scm.Response) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DeleteComment indicates an expected call of DeleteComment. -func (mr *MockPullRequestServiceMockRecorder) DeleteComment(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteComment", reflect.TypeOf((*MockPullRequestService)(nil).DeleteComment), arg0, arg1, arg2, arg3) -} - -// Find mocks base method. -func (m *MockPullRequestService) Find(arg0 context.Context, arg1 string, arg2 int) (*scm.PullRequest, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.PullRequest) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Find indicates an expected call of Find. -func (mr *MockPullRequestServiceMockRecorder) Find(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockPullRequestService)(nil).Find), arg0, arg1, arg2) -} - -// FindComment mocks base method. -func (m *MockPullRequestService) FindComment(arg0 context.Context, arg1 string, arg2, arg3 int) (*scm.Comment, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindComment", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*scm.Comment) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FindComment indicates an expected call of FindComment. -func (mr *MockPullRequestServiceMockRecorder) FindComment(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindComment", reflect.TypeOf((*MockPullRequestService)(nil).FindComment), arg0, arg1, arg2, arg3) -} - -// List mocks base method. -func (m *MockPullRequestService) List(arg0 context.Context, arg1 string, arg2 scm.PullRequestListOptions) ([]*scm.PullRequest, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1, arg2) - ret0, _ := ret[0].([]*scm.PullRequest) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// List indicates an expected call of List. -func (mr *MockPullRequestServiceMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockPullRequestService)(nil).List), arg0, arg1, arg2) -} - -// ListChanges mocks base method. -func (m *MockPullRequestService) ListChanges(arg0 context.Context, arg1 string, arg2 int, arg3 scm.ListOptions) ([]*scm.Change, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListChanges", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]*scm.Change) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListChanges indicates an expected call of ListChanges. -func (mr *MockPullRequestServiceMockRecorder) ListChanges(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListChanges", reflect.TypeOf((*MockPullRequestService)(nil).ListChanges), arg0, arg1, arg2, arg3) -} - -// ListComments mocks base method. -func (m *MockPullRequestService) ListComments(arg0 context.Context, arg1 string, arg2 int, arg3 scm.ListOptions) ([]*scm.Comment, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListComments", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]*scm.Comment) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListComments indicates an expected call of ListComments. -func (mr *MockPullRequestServiceMockRecorder) ListComments(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListComments", reflect.TypeOf((*MockPullRequestService)(nil).ListComments), arg0, arg1, arg2, arg3) -} - -// ListCommits mocks base method. -func (m *MockPullRequestService) ListCommits(arg0 context.Context, arg1 string, arg2 int, arg3 scm.ListOptions) ([]*scm.Commit, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListCommits", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]*scm.Commit) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListCommits indicates an expected call of ListCommits. -func (mr *MockPullRequestServiceMockRecorder) ListCommits(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCommits", reflect.TypeOf((*MockPullRequestService)(nil).ListCommits), arg0, arg1, arg2, arg3) -} - -// Merge mocks base method. -func (m *MockPullRequestService) Merge(arg0 context.Context, arg1 string, arg2 int) (*scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Merge", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Response) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Merge indicates an expected call of Merge. -func (mr *MockPullRequestServiceMockRecorder) Merge(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Merge", reflect.TypeOf((*MockPullRequestService)(nil).Merge), arg0, arg1, arg2) -} - -// MockRepositoryService is a mock of RepositoryService interface. -type MockRepositoryService struct { - ctrl *gomock.Controller - recorder *MockRepositoryServiceMockRecorder -} - -// MockRepositoryServiceMockRecorder is the mock recorder for MockRepositoryService. -type MockRepositoryServiceMockRecorder struct { - mock *MockRepositoryService -} - -// NewMockRepositoryService creates a new mock instance. -func NewMockRepositoryService(ctrl *gomock.Controller) *MockRepositoryService { - mock := &MockRepositoryService{ctrl: ctrl} - mock.recorder = &MockRepositoryServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRepositoryService) EXPECT() *MockRepositoryServiceMockRecorder { - return m.recorder -} - -// CreateHook mocks base method. -func (m *MockRepositoryService) CreateHook(arg0 context.Context, arg1 string, arg2 *scm.HookInput) (*scm.Hook, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateHook", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Hook) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// CreateHook indicates an expected call of CreateHook. -func (mr *MockRepositoryServiceMockRecorder) CreateHook(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHook", reflect.TypeOf((*MockRepositoryService)(nil).CreateHook), arg0, arg1, arg2) -} - -// CreateStatus mocks base method. -func (m *MockRepositoryService) CreateStatus(arg0 context.Context, arg1, arg2 string, arg3 *scm.StatusInput) (*scm.Status, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateStatus", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*scm.Status) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// CreateStatus indicates an expected call of CreateStatus. -func (mr *MockRepositoryServiceMockRecorder) CreateStatus(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateStatus", reflect.TypeOf((*MockRepositoryService)(nil).CreateStatus), arg0, arg1, arg2, arg3) -} - -// DeleteHook mocks base method. -func (m *MockRepositoryService) DeleteHook(arg0 context.Context, arg1, arg2 string) (*scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteHook", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Response) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DeleteHook indicates an expected call of DeleteHook. -func (mr *MockRepositoryServiceMockRecorder) DeleteHook(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHook", reflect.TypeOf((*MockRepositoryService)(nil).DeleteHook), arg0, arg1, arg2) -} - -// Find mocks base method. -func (m *MockRepositoryService) Find(arg0 context.Context, arg1 string) (*scm.Repository, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0, arg1) - ret0, _ := ret[0].(*scm.Repository) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Find indicates an expected call of Find. -func (mr *MockRepositoryServiceMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockRepositoryService)(nil).Find), arg0, arg1) -} - -// FindHook mocks base method. -func (m *MockRepositoryService) FindHook(arg0 context.Context, arg1, arg2 string) (*scm.Hook, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindHook", arg0, arg1, arg2) - ret0, _ := ret[0].(*scm.Hook) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FindHook indicates an expected call of FindHook. -func (mr *MockRepositoryServiceMockRecorder) FindHook(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindHook", reflect.TypeOf((*MockRepositoryService)(nil).FindHook), arg0, arg1, arg2) -} - -// FindPerms mocks base method. -func (m *MockRepositoryService) FindPerms(arg0 context.Context, arg1 string) (*scm.Perm, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindPerms", arg0, arg1) - ret0, _ := ret[0].(*scm.Perm) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FindPerms indicates an expected call of FindPerms. -func (mr *MockRepositoryServiceMockRecorder) FindPerms(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindPerms", reflect.TypeOf((*MockRepositoryService)(nil).FindPerms), arg0, arg1) -} - -// List mocks base method. -func (m *MockRepositoryService) List(arg0 context.Context, arg1 scm.ListOptions) ([]*scm.Repository, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1) - ret0, _ := ret[0].([]*scm.Repository) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// List indicates an expected call of List. -func (mr *MockRepositoryServiceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepositoryService)(nil).List), arg0, arg1) -} - -// ListHooks mocks base method. -func (m *MockRepositoryService) ListHooks(arg0 context.Context, arg1 string, arg2 scm.ListOptions) ([]*scm.Hook, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListHooks", arg0, arg1, arg2) - ret0, _ := ret[0].([]*scm.Hook) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListHooks indicates an expected call of ListHooks. -func (mr *MockRepositoryServiceMockRecorder) ListHooks(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListHooks", reflect.TypeOf((*MockRepositoryService)(nil).ListHooks), arg0, arg1, arg2) -} - -// ListStatus mocks base method. -func (m *MockRepositoryService) ListStatus(arg0 context.Context, arg1, arg2 string, arg3 scm.ListOptions) ([]*scm.Status, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListStatus", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].([]*scm.Status) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListStatus indicates an expected call of ListStatus. -func (mr *MockRepositoryServiceMockRecorder) ListStatus(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListStatus", reflect.TypeOf((*MockRepositoryService)(nil).ListStatus), arg0, arg1, arg2, arg3) -} - -// UpdateHook mocks base method. -func (m *MockRepositoryService) UpdateHook(arg0 context.Context, arg1, arg2 string, arg3 *scm.HookInput) (*scm.Hook, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateHook", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*scm.Hook) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// UpdateHook indicates an expected call of UpdateHook. -func (mr *MockRepositoryServiceMockRecorder) UpdateHook(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHook", reflect.TypeOf((*MockRepositoryService)(nil).UpdateHook), arg0, arg1, arg2, arg3) -} - -// MockUserService is a mock of UserService interface. -type MockUserService struct { - ctrl *gomock.Controller - recorder *MockUserServiceMockRecorder -} - -// MockUserServiceMockRecorder is the mock recorder for MockUserService. -type MockUserServiceMockRecorder struct { - mock *MockUserService -} - -// NewMockUserService creates a new mock instance. -func NewMockUserService(ctrl *gomock.Controller) *MockUserService { - mock := &MockUserService{ctrl: ctrl} - mock.recorder = &MockUserServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockUserService) EXPECT() *MockUserServiceMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockUserService) Find(arg0 context.Context) (*scm.User, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Find", arg0) - ret0, _ := ret[0].(*scm.User) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Find indicates an expected call of Find. -func (mr *MockUserServiceMockRecorder) Find(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockUserService)(nil).Find), arg0) -} - -// FindEmail mocks base method. -func (m *MockUserService) FindEmail(arg0 context.Context) (string, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindEmail", arg0) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FindEmail indicates an expected call of FindEmail. -func (mr *MockUserServiceMockRecorder) FindEmail(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindEmail", reflect.TypeOf((*MockUserService)(nil).FindEmail), arg0) -} - -// FindLogin mocks base method. -func (m *MockUserService) FindLogin(arg0 context.Context, arg1 string) (*scm.User, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindLogin", arg0, arg1) - ret0, _ := ret[0].(*scm.User) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// FindLogin indicates an expected call of FindLogin. -func (mr *MockUserServiceMockRecorder) FindLogin(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindLogin", reflect.TypeOf((*MockUserService)(nil).FindLogin), arg0, arg1) -} - -// ListEmail mocks base method. -func (m *MockUserService) ListEmail(arg0 context.Context, arg1 scm.ListOptions) ([]*scm.Email, *scm.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListEmail", arg0, arg1) - ret0, _ := ret[0].([]*scm.Email) - ret1, _ := ret[1].(*scm.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListEmail indicates an expected call of ListEmail. -func (mr *MockUserServiceMockRecorder) ListEmail(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListEmail", reflect.TypeOf((*MockUserService)(nil).ListEmail), arg0, arg1) -} diff --git a/operator/manager/manager.go b/operator/manager/manager.go deleted file mode 100644 index f20e51e7a3..0000000000 --- a/operator/manager/manager.go +++ /dev/null @@ -1,557 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package manager - -import ( - "bytes" - "context" - "io" - "io/ioutil" - "time" - - "github.com/drone/drone-yaml/yaml/converter" - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - - "github.com/hashicorp/go-multierror" - "github.com/sirupsen/logrus" -) - -var noContext = context.Background() - -var _ BuildManager = (*Manager)(nil) - -type ( - // Context represents the minimum amount of information - // required by the runner to execute a build. - Context struct { - Repo *core.Repository `json:"repository"` - Build *core.Build `json:"build"` - Stage *core.Stage `json:"stage"` - Config *core.File `json:"config"` - Secrets []*core.Secret `json:"secrets"` - System *core.System `json:"system"` - } - - // BuildManager encapsulates complex build operations and provides - // a simplified interface for build runners. - BuildManager interface { - // Request requests the next available build stage for execution. - Request(ctx context.Context, args *Request) (*core.Stage, error) - - // Accept accepts the build stage for execution. - Accept(ctx context.Context, stage int64, machine string) (*core.Stage, error) - - // Netrc returns a valid netrc for execution. - Netrc(ctx context.Context, repo int64) (*core.Netrc, error) - - // Details fetches build details - Details(ctx context.Context, stage int64) (*Context, error) - - // Before signals the build step is about to start. - Before(ctx context.Context, step *core.Step) error - - // After signals the build step is complete. - After(ctx context.Context, step *core.Step) error - - // BeforeAll signals the build stage is about to start. - BeforeAll(ctx context.Context, stage *core.Stage) error - - // AfterAll signals the build stage is complete. - AfterAll(ctx context.Context, stage *core.Stage) error - - // Watch watches for build cancellation requests. - Watch(ctx context.Context, stage int64) (bool, error) - - // Write writes a line to the build logs - Write(ctx context.Context, step int64, line *core.Line) error - - // Upload uploads the full logs - Upload(ctx context.Context, step int64, r io.Reader) error - - // UploadBytes uploads the full logs - UploadBytes(ctx context.Context, step int64, b []byte) error - - // UploadCard creates a new card - UploadCard(ctx context.Context, step int64, input *core.CardInput) error - } - - // Request provides filters when requesting a pending - // build from the queue. This allows an agent, for example, - // to request a build that matches its architecture and kernel. - Request struct { - Kind string `json:"kind"` - Type string `json:"type"` - OS string `json:"os"` - Arch string `json:"arch"` - Variant string `json:"variant"` - Kernel string `json:"kernel"` - Labels map[string]string `json:"labels,omitempty"` - } -) - -// New returns a new Manager. -func New( - builds core.BuildStore, - cards core.CardStore, - config core.ConfigService, - converter core.ConvertService, - events core.Pubsub, - logs core.LogStore, - logz core.LogStream, - netrcs core.NetrcService, - repos core.RepositoryStore, - scheduler core.Scheduler, - secrets core.SecretStore, - globals core.GlobalSecretStore, - status core.StatusService, - stages core.StageStore, - steps core.StepStore, - system *core.System, - users core.UserStore, - webhook core.WebhookSender, -) BuildManager { - return &Manager{ - Builds: builds, - Cards: cards, - Config: config, - Converter: converter, - Events: events, - Globals: globals, - Logs: logs, - Logz: logz, - Netrcs: netrcs, - Repos: repos, - Scheduler: scheduler, - Secrets: secrets, - Status: status, - Stages: stages, - Steps: steps, - System: system, - Users: users, - Webhook: webhook, - } -} - -// Manager provides a simplified interface to the build runner so that it -// can more easily interact with the server. -type Manager struct { - Builds core.BuildStore - Cards core.CardStore - Config core.ConfigService - Converter core.ConvertService - Events core.Pubsub - Globals core.GlobalSecretStore - Logs core.LogStore - Logz core.LogStream - Netrcs core.NetrcService - Repos core.RepositoryStore - Scheduler core.Scheduler - Secrets core.SecretStore - Status core.StatusService - Stages core.StageStore - Steps core.StepStore - System *core.System - Users core.UserStore - Webhook core.WebhookSender -} - -// Request requests the next available build stage for execution. -func (m *Manager) Request(ctx context.Context, args *Request) (*core.Stage, error) { - logger := logrus.WithFields( - logrus.Fields{ - "kind": args.Kind, - "type": args.Type, - "os": args.OS, - "arch": args.Arch, - "kernel": args.Kernel, - "variant": args.Variant, - }, - ) - logger.Debugln("manager: request queue item") - - stage, err := m.Scheduler.Request(ctx, core.Filter{ - Kind: args.Kind, - Type: args.Type, - OS: args.OS, - Arch: args.Arch, - Kernel: args.Kernel, - Variant: args.Variant, - Labels: args.Labels, - }) - if err != nil && ctx.Err() != nil { - logger.Debugln("manager: context canceled") - return nil, err - } - if err != nil { - logger = logrus.WithError(err) - logger.Warnln("manager: request queue item error") - return nil, err - } - return stage, nil -} - -// Accept accepts the build stage for execution. It is possible for multiple -// agents to pull the same stage from the queue. The system uses optimistic -// locking at the database-level to prevent multiple agents from executing the -// same stage. -func (m *Manager) Accept(ctx context.Context, id int64, machine string) (*core.Stage, error) { - logger := logrus.WithFields( - logrus.Fields{ - "stage-id": id, - "machine": machine, - }, - ) - logger.Debugln("manager: accept stage") - - stage, err := m.Stages.Find(noContext, id) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot find stage") - return nil, err - } - if stage.Machine != "" { - logger.Debugln("manager: stage already assigned. abort.") - return nil, db.ErrOptimisticLock - } - - stage.Machine = machine - stage.Status = core.StatusPending - stage.Updated = time.Now().Unix() - - err = m.Stages.Update(noContext, stage) - if err == db.ErrOptimisticLock { - logger = logger.WithError(err) - logger.Debugln("manager: stage processed by another agent") - } else if err != nil { - logger = logger.WithError(err) - logger.Debugln("manager: cannot update stage") - } else { - logger.Debugln("manager: stage accepted") - } - return stage, err -} - -// Details fetches build details. -func (m *Manager) Details(ctx context.Context, id int64) (*Context, error) { - logger := logrus.WithField("step-id", id) - logger.Debugln("manager: fetching stage details") - - stage, err := m.Stages.Find(noContext, id) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot find stage") - return nil, err - } - build, err := m.Builds.Find(noContext, stage.BuildID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot find build") - return nil, err - } - stages, err := m.Stages.List(ctx, stage.BuildID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot list stages") - return nil, err - } - build.Stages = stages - repo, err := m.Repos.Find(noContext, build.RepoID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot find repository") - return nil, err - } - logger = logger.WithFields( - logrus.Fields{ - "build": build.Number, - "repo": repo.Slug, - }, - ) - user, err := m.Users.Find(noContext, repo.UserID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot find repository owner") - return nil, err - } - config, err := m.Config.Find(noContext, &core.ConfigArgs{ - User: user, - Repo: repo, - Build: build, - }) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot find configuration") - return nil, err - } - - // this code is temporarily in place to detect and convert - // the legacy yaml configuration file to the new format. - config.Data, _ = converter.ConvertString(config.Data, converter.Metadata{ - Filename: repo.Config, - URL: repo.Link, - Ref: build.Ref, - }) - - config, err = m.Converter.Convert(noContext, &core.ConvertArgs{ - Build: build, - Config: config, - Repo: repo, - User: user, - }) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot convert configuration") - return nil, err - } - var secrets []*core.Secret - tmpSecrets, err := m.Secrets.List(noContext, repo.ID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot list secrets") - return nil, err - } - tmpGlobalSecrets, err := m.Globals.List(noContext, repo.Namespace) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot list global secrets") - return nil, err - } - // TODO(bradrydzewski) can we delegate filtering - // secrets to the agent? If not, we should add - // unit tests. - for _, secret := range tmpSecrets { - if secret.PullRequest == false && - build.Event == core.EventPullRequest { - continue - } - secrets = append(secrets, secret) - } - for _, secret := range tmpGlobalSecrets { - if secret.PullRequest == false && - build.Event == core.EventPullRequest { - continue - } - secrets = append(secrets, secret) - } - return &Context{ - Repo: repo, - Build: build, - Stage: stage, - Secrets: secrets, - System: m.System, - Config: &core.File{Data: []byte(config.Data)}, - }, nil -} - -// Before signals the build step is about to start. -func (m *Manager) Before(ctx context.Context, step *core.Step) error { - logger := logrus.WithFields( - logrus.Fields{ - "step.status": step.Status, - "step.name": step.Name, - "step.id": step.ID, - }, - ) - logger.Debugln("manager: updating step status") - - err := m.Logz.Create(noContext, step.ID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot create log stream") - return err - } - updater := &updater{ - Builds: m.Builds, - Events: m.Events, - Repos: m.Repos, - Steps: m.Steps, - Stages: m.Stages, - Webhook: m.Webhook, - } - return updater.do(ctx, step) -} - -// After signals the build step is complete. -func (m *Manager) After(ctx context.Context, step *core.Step) error { - logger := logrus.WithFields( - logrus.Fields{ - "step.status": step.Status, - "step.name": step.Name, - "step.id": step.ID, - }, - ) - logger.Debugln("manager: updating step status") - - var errs error - updater := &updater{ - Builds: m.Builds, - Events: m.Events, - Repos: m.Repos, - Steps: m.Steps, - Stages: m.Stages, - Webhook: m.Webhook, - } - - if err := updater.do(ctx, step); err != nil { - errs = multierror.Append(errs, err) - logger = logger.WithError(err) - logger.Warnln("manager: cannot update step") - } - - if err := m.Logz.Delete(noContext, step.ID); err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot teardown log stream") - } - return errs -} - -// BeforeAll signals the build stage is about to start. -func (m *Manager) BeforeAll(ctx context.Context, stage *core.Stage) error { - s := &setup{ - Builds: m.Builds, - Events: m.Events, - Repos: m.Repos, - Steps: m.Steps, - Stages: m.Stages, - Status: m.Status, - Users: m.Users, - } - return s.do(ctx, stage) -} - -// AfterAll signals the build stage is complete. -func (m *Manager) AfterAll(ctx context.Context, stage *core.Stage) error { - t := &teardown{ - Builds: m.Builds, - Events: m.Events, - Logs: m.Logz, - Repos: m.Repos, - Scheduler: m.Scheduler, - Steps: m.Steps, - Stages: m.Stages, - Status: m.Status, - Users: m.Users, - Webhook: m.Webhook, - } - return t.do(ctx, stage) -} - -// Netrc returns netrc file with a valid, non-expired token -// that can be used to clone the repository. -func (m *Manager) Netrc(ctx context.Context, id int64) (*core.Netrc, error) { - logger := logrus.WithField("repo.id", id) - - repo, err := m.Repos.Find(ctx, id) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot find repository") - return nil, err - } - - user, err := m.Users.Find(ctx, repo.UserID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("manager: cannot find repository owner") - return nil, err - } - - netrc, err := m.Netrcs.Create(ctx, user, repo) - if err != nil { - logger = logger.WithError(err) - logger = logger.WithField("repo.name", repo.Slug) - logger.Warnln("manager: cannot generate netrc") - } - return netrc, err -} - -// Watch watches for build cancellation requests. -func (m *Manager) Watch(ctx context.Context, id int64) (bool, error) { - ok, err := m.Scheduler.Cancelled(ctx, id) - // we expect a context cancel error here which - // indicates a polling timeout. The subscribing - // client should look for the context cancel error - // and resume polling. - if err != nil { - return ok, err - } - - // // TODO (bradrydzewski) we should be able to return - // // immediately if Cancelled returns true. This requires - // // some more testing but would avoid the extra database - // // call. - // if ok { - // return ok, err - // } - - // if no error is returned we should check - // the database to see if the build is complete. If - // complete, return true. - build, err := m.Builds.Find(ctx, id) - if err != nil { - logger := logrus.WithError(err) - logger = logger.WithField("build-id", id) - logger.Warnln("manager: cannot find build") - return ok, err - } - return build.IsDone(), nil -} - -// Write writes a line to the build logs. -func (m *Manager) Write(ctx context.Context, step int64, line *core.Line) error { - err := m.Logz.Write(ctx, step, line) - if err != nil { - logger := logrus.WithError(err) - logger = logger.WithField("step-id", step) - logger.Warnln("manager: cannot write to log stream") - } - return err -} - -// Upload uploads the full logs. -func (m *Manager) Upload(ctx context.Context, step int64, r io.Reader) error { - err := m.Logs.Create(ctx, step, r) - if err != nil { - logger := logrus.WithError(err) - logger = logger.WithField("step-id", step) - logger.Warnln("manager: cannot upload complete logs") - } - return err -} - -// UploadBytes uploads the full logs. -func (m *Manager) UploadBytes(ctx context.Context, step int64, data []byte) error { - buf := bytes.NewBuffer(data) - err := m.Logs.Create(ctx, step, buf) - if err != nil { - logger := logrus.WithError(err) - logger = logger.WithField("step-id", step) - logger.Warnln("manager: cannot upload complete logs") - } - return err -} - -// UploadCard creates card for step. -func (m *Manager) UploadCard(ctx context.Context, stepId int64, input *core.CardInput) error { - data := ioutil.NopCloser( - bytes.NewBuffer(input.Data), - ) - err := m.Cards.Create(ctx, stepId, data) - if err != nil { - logger := logrus.WithError(err) - logger.Warnln("manager: cannot create card") - } - return nil -} diff --git a/operator/manager/manager_test.go b/operator/manager/manager_test.go deleted file mode 100644 index afa0ba2adf..0000000000 --- a/operator/manager/manager_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package manager - -import ( - "io/ioutil" - - "github.com/sirupsen/logrus" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} diff --git a/operator/manager/rpc/client.go b/operator/manager/rpc/client.go deleted file mode 100644 index 0cf5f118f7..0000000000 --- a/operator/manager/rpc/client.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package rpc - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "strings" - "time" - - "github.com/drone/drone/operator/manager" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - - "github.com/hashicorp/go-retryablehttp" - "github.com/oxtoacart/bpool" -) - -var _ manager.BuildManager = (*Client)(nil) - -var bufpool = bpool.NewBufferPool(64) - -// Client defines an RPC client. -type Client struct { - token string - server string - client *retryablehttp.Client -} - -// NewClient returns a new rpc client that is able to -// interact with a remote build controller using the -// http transport. -func NewClient(server, token string) *Client { - client := retryablehttp.NewClient() - client.RetryMax = 30 - client.RetryWaitMax = time.Second * 10 - client.RetryWaitMin = time.Second * 1 - client.Logger = nil - return &Client{ - client: client, - server: strings.TrimSuffix(server, "/"), - token: token, - } -} - -// SetDebug enabled debug-level logging within the retryable -// http.Client. This can be useful if you are debugging network -// connectivity issues and want to monitor disconnects, -// reconnects, and retries. -func (s *Client) SetDebug(debug bool) { - if debug == true { - s.client.Logger = log.New(os.Stderr, "", log.LstdFlags) - } else { - s.client.Logger = nil - } -} - -// Request requests the next available build stage for execution. -func (s *Client) Request(ctx context.Context, args *manager.Request) (*core.Stage, error) { - timeout, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - - in := &requestRequest{Request: args} - out := &core.Stage{} - err := s.send(timeout, "/rpc/v1/request", in, out) - - // The request is performing long polling and is subject - // to a client-side and server-side timeout. The timeout - // error is therefore expected behavior, and is not - // considered an error by the system. - if err == context.DeadlineExceeded { - return nil, nil // no error - } - return out, err -} - -// Accept accepts the build stage for execution. -func (s *Client) Accept(ctx context.Context, stage int64, machine string) (*core.Stage, error) { - in := &acceptRequest{Stage: stage, Machine: machine} - return nil, s.send(noContext, "/rpc/v1/accept", in, nil) -} - -// Netrc returns a valid netrc for execution. -func (s *Client) Netrc(ctx context.Context, repo int64) (*core.Netrc, error) { - in := &netrcRequest{repo} - out := &core.Netrc{} - err := s.send(noContext, "/rpc/v1/netrc", in, out) - return out, err -} - -// Details fetches build details -func (s *Client) Details(ctx context.Context, stage int64) (*manager.Context, error) { - in := &detailsRequest{Stage: stage} - out := &buildContextToken{} - err := s.send(noContext, "/rpc/v1/details", in, out) - if err != nil { - return nil, err - } - // the repository token is excluded from the json encoding - // by default. this workaround ensures it is available to - // the remote build agent. - out.Context.Repo.Secret = out.Secret - return out.Context, nil -} - -// Before signals the build step is about to start. -func (s *Client) Before(ctx context.Context, step *core.Step) error { - in := &stepRequest{Step: step} - out := &core.Step{} - err := s.send(noContext, "/rpc/v1/before", in, out) - if err != nil { - return err - } - // the step ID and version (optimistic locking) are - // updated when the step is created. Copy the updated - // values back to the original step object. - step.ID = out.ID - step.Version = out.Version - return err -} - -// After signals the build step is complete. -func (s *Client) After(ctx context.Context, step *core.Step) error { - in := &stepRequest{Step: step} - out := &core.Step{} - err := s.send(noContext, "/rpc/v1/after", in, out) - if err != nil { - return err - } - // the step version (optimistic locking) is updated - // when the step is created. Copy the updated values - // back to the original step object. - step.Version = out.Version - return err -} - -// BeforeAll signals the build stage is about to start. -func (s *Client) BeforeAll(ctx context.Context, stage *core.Stage) error { - in := &stageRequest{Stage: stage} - out := &core.Stage{} - err := s.send(noContext, "/rpc/v1/beforeAll", in, out) - if err != nil { - return err - } - stage.Version = out.Version - stage.Updated = out.Updated - stage.Created = out.Created - // TODO(bradrydzewski) clean this code to prevent possible - // index-out-of-bounds exceptions. - for i, step := range stage.Steps { - step.ID = out.Steps[i].ID - step.Version = out.Steps[i].Version - } - return err -} - -// AfterAll signals the build stage is complete. -func (s *Client) AfterAll(ctx context.Context, stage *core.Stage) error { - in := &stageRequest{Stage: stage} - out := &core.Stage{} - err := s.send(noContext, "/rpc/v1/afterAll", in, out) - if err != nil { - return err - } - // the stage timestamps and version (optimistic locking) - // are updated when the step is created. Copy the updated - // values back to the original step object. - stage.Version = out.Version - stage.Updated = out.Updated - stage.Created = out.Created - return err -} - -func (s *Client) Watch(ctx context.Context, build int64) (bool, error) { - in := &watchRequest{build} - out := &watchResponse{} - err := s.send(ctx, "/rpc/v1/watch", in, out) - return out.Done, err -} - -func (s *Client) Write(ctx context.Context, step int64, line *core.Line) error { - in := writePool.Get().(*writeRequest) - in.Step = step - in.Line = line - err := s.send(noContext, "/rpc/v1/write", in, nil) - writePool.Put(in) - return err -} - -func (s *Client) Upload(ctx context.Context, step int64, r io.Reader) error { - endpoint := "/rpc/v1/upload?id=" + fmt.Sprint(step) - return s.upload(noContext, endpoint, r) -} - -func (s *Client) UploadBytes(ctx context.Context, step int64, data []byte) error { - endpoint := "/rpc/v1/upload?id=" + fmt.Sprint(step) - return s.upload(noContext, endpoint, data) -} - -func (s *Client) UploadCard(ctx context.Context, step int64, input *core.CardInput) error { - return errors.New("rpc upload card not supported") -} - -func (s *Client) send(ctx context.Context, path string, in, out interface{}) error { - // Source a buffer from a pool. The agent may generate a - // large number of small requests for log entries. This will - // help reduce pressure on the garbage collector. - buf := bufpool.Get() - defer bufpool.Put(buf) - - err := json.NewEncoder(buf).Encode(in) - if err != nil { - return err - } - - url := s.server + path - req, err := retryablehttp.NewRequest("POST", url, buf) - if err != nil { - return err - } - req = req.WithContext(ctx) - req.Header.Set("X-Drone-Token", s.token) - - res, err := s.client.Do(req) - if res != nil { - defer res.Body.Close() - } - if err != nil { - return err - } - - // Check the response for a 409 conflict. This indicates an - // optimistic lock error, in which case multiple clients may - // be attempting to update the same record. Convert this error - // code to a proper error. - if res.StatusCode == 409 { - return db.ErrOptimisticLock - } - - // Check the response for a 524 deadline exceeded. This is a - // custom status code that indicates the server canceled the - // request due to an internal polling timeout (this is normal). - if res.StatusCode == 524 { - return context.DeadlineExceeded - } - - if res.StatusCode > 299 { - body, _ := ioutil.ReadAll(res.Body) - return &serverError{ - Status: res.StatusCode, - Message: string(body), - } - } - - // Check the response for a 204 no content. This indicates - // the response body is empty and should be discarded. - if res.StatusCode == 204 || out == nil { - return nil - } - - return json.NewDecoder(res.Body).Decode(out) -} - -func (s *Client) upload(ctx context.Context, path string, body interface{}) error { - url := s.server + path - req, err := retryablehttp.NewRequest("POST", url, body) - if err != nil { - return err - } - req = req.WithContext(ctx) - req.Header.Set("X-Drone-Token", s.token) - - res, err := s.client.Do(req) - if err != nil { - return err - } - defer res.Body.Close() - - if res.StatusCode > 299 { - body, _ := ioutil.ReadAll(res.Body) - return &serverError{ - Status: res.StatusCode, - Message: string(body), - } - } - return nil -} - -// helper function returns true if the http.Request should be -// retried based on error and http status code. This function -// is used by the retryablehttp.Client. -func retryFunc(ctx context.Context, resp *http.Response, err error) (bool, error) { - // do not retry on context.Canceled or context.DeadlineExceeded - if ctx.Err() != nil { - return false, ctx.Err() - } - if resp != nil { - // Check the path to prevent retries when writing to the log - // stream. This stream is temporary and ephemeral, and losing - // log lines will not negatively impact the final persisted - // log entries. - if resp.Request.URL.Path == "/rpc/v1/write" { - return false, err - } - // Check the response code. We retry on 500-range responses - // to allow the server time to recover, as 500's are typically - // not permanent errors and may relate to outages on the - // server side. - if resp.StatusCode >= 500 { - return true, nil - } - } - if err != nil { - return true, err - } - return false, nil -} diff --git a/operator/manager/rpc/client_test.go b/operator/manager/rpc/client_test.go deleted file mode 100644 index 3cb4a468da..0000000000 --- a/operator/manager/rpc/client_test.go +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package rpc - -import ( - "bytes" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/operator/manager" - "github.com/drone/drone/store/shared/db" - - "github.com/google/go-cmp/cmp" - "github.com/h2non/gock" -) - -func TestRequest(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/request"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"Request":{"kind":"","type":"","os":"linux","arch":"amd64","variant":"","kernel":""}}`). - Reply(200). - Type("application/json"). - BodyString(`{"id":1,"build_id":2,"number":3,"name":"build","status":"pending","errignore":false,"exit_code":0,"machine":"localhost","os":"linux","arch":"amd64","started":0,"stopped":0,"created":0,"updated":0,"version":1,"on_success":false,"on_failure":false}`) - - want := &core.Stage{ - ID: 1, - BuildID: 2, - Number: 3, - Name: "build", - Machine: "localhost", - OS: "linux", - Arch: "amd64", - Status: core.StatusPending, - ExitCode: 0, - Version: 1, - } - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - got, err := client.Request(noContext, &manager.Request{OS: "linux", Arch: "amd64"}) - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf(diff) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestAccept(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/accept"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"Stage":1,"Machine":"localhost"}`). - Reply(204) - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - _, err := client.Accept(noContext, 1, "localhost") - if err != nil { - t.Error(err) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestNetrc(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/netrc"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"Repo":1}`). - Reply(200). - Type("application/json"). - BodyString(`{"machine":"github.com","login":"octocat","password":"12345"}`) - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - got, err := client.Netrc(noContext, 1) - if err != nil { - t.Error(err) - } - - want := &core.Netrc{ - Password: "12345", - Login: "octocat", - Machine: "github.com", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestDetails(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/details"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"Stage":1}`). - Reply(200). - Type("application/json"). - BodyString(`{"Secret":"passphrase", "Context":{"repository":{}}}`) - - // TODO(bradrydzewski) return a mock core.BuildContext - // and validate the unmarshaled results. - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - out, err := client.Details(noContext, 1) - if err != nil { - t.Error(err) - return - } - - if out.Repo.Secret != "passphrase" { - t.Errorf("Expect repository passphrase encoded in json response") - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestBefore(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/before"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"id":1,"step_id":2,"number":3,"name":"build","status":"pending","exit_code":0,"version":1}`). - Reply(200). - Type("application/json"). - BodyString(`{"id":1,"step_id":2,"number":3,"name":"build","status":"pending","exit_code":0,"version":2}`) - - before := &core.Step{ - ID: 1, - StageID: 2, - Number: 3, - Name: "build", - Status: core.StatusPending, - ExitCode: 0, - Version: 1, - } - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.Before(noContext, before) - if err != nil { - t.Error(err) - } - - after := &core.Step{ - ID: 1, - StageID: 2, - Number: 3, - Name: "build", - Status: core.StatusPending, - ExitCode: 0, - Version: 2, - } - - if diff := cmp.Diff(before, after); diff != "" { - t.Errorf(diff) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestAfter(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/after"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"id":1,"step_id":2,"number":3,"name":"build","status":"failure","exit_code":1,"version":2}`). - Reply(200). - Type("application/json"). - BodyString(`{"id":1,"step_id":2,"number":3,"name":"build","status":"failure","exit_code":1,"version":3}`) - - before := &core.Step{ - ID: 1, - StageID: 2, - Number: 3, - Name: "build", - Status: core.StatusFailing, - ExitCode: 1, - Version: 2, - } - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.After(noContext, before) - if err != nil { - t.Error(err) - } - - after := &core.Step{ - ID: 1, - StageID: 2, - Number: 3, - Name: "build", - Status: core.StatusFailing, - ExitCode: 1, - Version: 3, - } - - if diff := cmp.Diff(before, after); diff != "" { - t.Errorf(diff) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestBeforeAll(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/beforeAll"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"id":1,"repo_id":0,"build_id":2,"number":3,"name":"build","status":"pending","errignore":false,"exit_code":0,"machine":"localhost","os":"linux","arch":"amd64","started":0,"stopped":0,"created":0,"updated":0,"version":1,"on_success":false,"on_failure":false}`). - Reply(200). - Type("application/json"). - BodyString(`{"id":1,"repo_id":0,"build_id":2,"number":3,"name":"build","status":"pending","errignore":false,"exit_code":0,"machine":"localhost","os":"linux","arch":"amd64","started":0,"stopped":0,"created":0,"updated":0,"version":2,"on_success":false,"on_failure":false}`) - - before := &core.Stage{ - ID: 1, - BuildID: 2, - Number: 3, - Name: "build", - Machine: "localhost", - OS: "linux", - Arch: "amd64", - Status: core.StatusPending, - ExitCode: 0, - Version: 1, - } - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.BeforeAll(noContext, before) - if err != nil { - t.Error(err) - } - - after := &core.Stage{ - ID: 1, - BuildID: 2, - Number: 3, - Name: "build", - Machine: "localhost", - OS: "linux", - Arch: "amd64", - Status: core.StatusPending, - ExitCode: 0, - Version: 2, - } - - if diff := cmp.Diff(before, after); diff != "" { - t.Errorf(diff) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestAfterAll(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/afterAll"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"id":1,"repo_id":0,"build_id":2,"number":3,"name":"build","status":"pending","errignore":false,"exit_code":0,"machine":"localhost","os":"linux","arch":"amd64","started":0,"stopped":0,"created":0,"updated":0,"version":1,"on_success":false,"on_failure":false}`). - Reply(200). - Type("application/json"). - BodyString(`{"id":1,"repo_id":0,"build_id":2,"number":3,"name":"build","status":"pending","errignore":false,"exit_code":0,"machine":"localhost","os":"linux","arch":"amd64","started":0,"stopped":0,"created":0,"updated":0,"version":2,"on_success":false,"on_failure":false}`) - - before := &core.Stage{ - ID: 1, - BuildID: 2, - Number: 3, - Name: "build", - Machine: "localhost", - OS: "linux", - Arch: "amd64", - Status: core.StatusPending, - ExitCode: 0, - Version: 1, - } - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.AfterAll(noContext, before) - if err != nil { - t.Error(err) - } - - after := &core.Stage{ - ID: 1, - BuildID: 2, - Number: 3, - Name: "build", - Machine: "localhost", - OS: "linux", - Arch: "amd64", - Status: core.StatusPending, - ExitCode: 0, - Version: 2, - } - - if diff := cmp.Diff(before, after); diff != "" { - t.Errorf(diff) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestBefore_OptimisticLock(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/before"). - Reply(409) - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.Before(noContext, new(core.Step)) - if err != db.ErrOptimisticLock { - t.Errorf("Want optimistic lock error") - } - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestAfter_OptimisticLock(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/after"). - Reply(409) - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.After(noContext, new(core.Step)) - if err != db.ErrOptimisticLock { - t.Errorf("Want optimistic lock error") - } - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestBeforeAll_OptimisticLock(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/beforeAll"). - Reply(409) - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.BeforeAll(noContext, new(core.Stage)) - if err != db.ErrOptimisticLock { - t.Errorf("Want optimistic lock error") - } - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestAfterAll_OptimisticLock(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/afterAll"). - Reply(409) - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.AfterAll(noContext, new(core.Stage)) - if err != db.ErrOptimisticLock { - t.Errorf("Want optimistic lock error") - } - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestWatch(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/watch"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"Build":1}`). - Reply(200). - Type("application/json"). - BodyString(`{"Done":true}`) - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - done, err := client.Watch(noContext, 1) - if err != nil { - t.Error(err) - } - - if !done { - t.Errorf("Want done=true, got false") - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestWrite(t *testing.T) { - defer gock.Off() - - gock.New("http://drone.company.com"). - Post("/rpc/v1/write"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`{"pos":1,"out":"whoami","time":0}`). - Reply(204) - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.Write(noContext, 1, &core.Line{Number: 1, Message: "whoami", Timestamp: 0}) - if err != nil { - t.Error(err) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestUpload(t *testing.T) { - defer gock.Off() - - buf := bytes.NewBufferString(`[{"pos":1,"out":"whoami","time":0}]`) - - gock.New("http://drone.company.com"). - Post("/rpc/v1/upload"). - MatchParam("id", "1"). - MatchHeader("X-Drone-Token", "correct-horse-battery-staple"). - BodyString(`[{"pos":1,"out":"whoami","time":0}]`). - Reply(200) - - client := NewClient("http://drone.company.com", "correct-horse-battery-staple") - gock.InterceptClient(client.client.HTTPClient) - err := client.Upload(noContext, 1, buf) - if err != nil { - t.Error(err) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -// func xTestRetrySend(t *testing.T) { -// defer gock.Off() - -// gock.New("http://drone.company.com"). -// Post("/rpc/v1/write"). -// Times(5). -// Reply(http.StatusBadGateway) - -// client := NewClient("http://drone.company.com", "correct-horse-battery-staple").(*Client) -// err := client.retrySend("http://drone.company.com/rpc/v1/write", nil, nil) -// if serr, ok := err.(*serverError); !ok || serr.Status != http.StatusBadGateway { -// t.Errorf("Want bad gateway error, got %d", serr.Status) -// } - -// if gock.IsPending() { -// t.Errorf("Unfinished requests") -// } -// } diff --git a/operator/manager/rpc/error.go b/operator/manager/rpc/error.go deleted file mode 100644 index c742b53165..0000000000 --- a/operator/manager/rpc/error.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package rpc - -type serverError struct { - Status int - Message string -} - -func (s *serverError) Error() string { - return s.Message -} diff --git a/operator/manager/rpc/server.go b/operator/manager/rpc/server.go deleted file mode 100644 index d228dcbbb7..0000000000 --- a/operator/manager/rpc/server.go +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package rpc - -import ( - "context" - "encoding/json" - "io" - "net/http" - "strconv" - "time" - - "github.com/drone/drone/operator/manager" - "github.com/drone/drone/store/shared/db" -) - -// default http request timeout -var defaultTimeout = time.Second * 30 - -var noContext = context.Background() - -// Server is an rpc handler that enables remote interaction -// between the server and controller using the http transport. -type Server struct { - manager manager.BuildManager - secret string -} - -// NewServer returns a new rpc server that enables remote -// interaction with the build controller using the http transport. -func NewServer(manager manager.BuildManager, secret string) *Server { - return &Server{ - manager: manager, - secret: secret, - } -} - -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if s.secret == "" { - w.WriteHeader(401) // not found - return - } - if r.Header.Get("X-Drone-Token") != s.secret { - w.WriteHeader(401) // not authorized - return - } - switch r.URL.Path { - case "/rpc/v1/write": - s.handleWrite(w, r) - case "/rpc/v1/request": - s.handleRequest(w, r) - case "/rpc/v1/accept": - s.handleAccept(w, r) - case "/rpc/v1/netrc": - s.handleNetrc(w, r) - case "/rpc/v1/details": - s.handleDetails(w, r) - case "/rpc/v1/before": - s.handleBefore(w, r) - case "/rpc/v1/after": - s.handleAfter(w, r) - case "/rpc/v1/beforeAll": - s.handleBeforeAll(w, r) - case "/rpc/v1/afterAll": - s.handleAfterAll(w, r) - case "/rpc/v1/watch": - s.handleWatch(w, r) - case "/rpc/v1/upload": - s.handleUpload(w, r) - default: - w.WriteHeader(404) - } -} - -func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - ctx, cancel := context.WithTimeout(ctx, defaultTimeout) - defer cancel() - - in := &requestRequest{} - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - stage, err := s.manager.Request(ctx, in.Request) - if err != nil { - writeError(w, err) - return - } - json.NewEncoder(w).Encode(stage) -} - -func (s *Server) handleAccept(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - in := &acceptRequest{} - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - out, err := s.manager.Accept(ctx, in.Stage, in.Machine) - if err != nil { - writeError(w, err) - return - } - json.NewEncoder(w).Encode(out) -} - -func (s *Server) handleNetrc(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - in := &netrcRequest{} - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - netrc, err := s.manager.Netrc(ctx, in.Repo) - if err != nil { - writeError(w, err) - return - } - json.NewEncoder(w).Encode(netrc) -} - -func (s *Server) handleDetails(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - in := &detailsRequest{} - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - build, err := s.manager.Details(ctx, in.Stage) - if err != nil { - writeError(w, err) - return - } - out := &buildContextToken{ - Secret: build.Repo.Secret, - Context: build, - } - json.NewEncoder(w).Encode(out) -} - -func (s *Server) handleBefore(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - in := &stepRequest{} - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - err = s.manager.Before(ctx, in.Step) - if err != nil { - writeError(w, err) - return - } - json.NewEncoder(w).Encode(in.Step) -} - -func (s *Server) handleAfter(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - in := &stepRequest{} - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - err = s.manager.After(ctx, in.Step) - if err != nil { - writeError(w, err) - return - } - json.NewEncoder(w).Encode(in.Step) -} - -func (s *Server) handleBeforeAll(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - in := &stageRequest{} - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - err = s.manager.BeforeAll(ctx, in.Stage) - if err != nil { - writeError(w, err) - return - } - json.NewEncoder(w).Encode(in.Stage) -} - -func (s *Server) handleAfterAll(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - in := &stageRequest{} - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - err = s.manager.AfterAll(ctx, in.Stage) - if err != nil { - writeError(w, err) - return - } - json.NewEncoder(w).Encode(in.Stage) -} - -func (s *Server) handleWrite(w http.ResponseWriter, r *http.Request) { - in := writePool.Get().(*writeRequest) - in.Line = nil - in.Step = 0 - - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - err = s.manager.Write(noContext, in.Step, in.Line) - if err != nil { - writeError(w, err) - return - } - w.WriteHeader(http.StatusNoContent) - writePool.Put(in) -} - -func (s *Server) handleUpload(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - in := r.FormValue("id") - id, err := strconv.ParseInt(in, 10, 64) - if err != nil { - writeBadRequest(w, err) - return - } - err = s.manager.Upload(ctx, id, r.Body) - if err != nil { - writeError(w, err) - return - } - w.WriteHeader(http.StatusNoContent) -} - -func (s *Server) handleWatch(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - ctx, cancel := context.WithTimeout(ctx, defaultTimeout) - defer cancel() - - in := &watchRequest{} - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - writeBadRequest(w, err) - return - } - done, err := s.manager.Watch(ctx, in.Build) - if err != nil { - writeError(w, err) - return - } - json.NewEncoder(w).Encode(&watchResponse{ - Done: done, - }) -} - -func writeBadRequest(w http.ResponseWriter, err error) { - w.WriteHeader(500) // should retry - io.WriteString(w, err.Error()) -} - -func writeError(w http.ResponseWriter, err error) { - if err == context.DeadlineExceeded { - w.WriteHeader(524) // should retry - } else if err == context.Canceled { - w.WriteHeader(524) // should retry - } else if err == db.ErrOptimisticLock { - w.WriteHeader(409) // should abort - } else { - w.WriteHeader(400) // should fail - } - io.WriteString(w, err.Error()) -} diff --git a/operator/manager/rpc/server_oss.go b/operator/manager/rpc/server_oss.go deleted file mode 100644 index 6d068b6f46..0000000000 --- a/operator/manager/rpc/server_oss.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package rpc - -import ( - "context" - "errors" - "io" - "net/http" - - "github.com/drone/drone/core" - "github.com/drone/drone/operator/manager" -) - -// Server is a no-op rpc server. -type Server struct { - manager manager.BuildManager - secret string -} - -// NewServer returns a no-op rpc server. -func NewServer(manager.BuildManager, string) *Server { - return &Server{} -} - -// Request requests the next available build stage for execution. -func (Server) Request(ctx context.Context, args *manager.Request) (*core.Stage, error) { - return nil, errors.New("not implemented") -} - -// Accept accepts the build stage for execution. -func (Server) Accept(ctx context.Context, stage int64, machine string) error { - return errors.New("not implemented") -} - -// Netrc returns a valid netrc for execution. -func (Server) Netrc(ctx context.Context, repo int64) (*core.Netrc, error) { - return nil, errors.New("not implemented") -} - -// Details fetches build details -func (Server) Details(ctx context.Context, stage int64) (*manager.Context, error) { - return nil, errors.New("not implemented") -} - -// Before signals the build step is about to start. -func (Server) Before(ctx context.Context, step *core.Step) error { - return errors.New("not implemented") -} - -// After signals the build step is complete. -func (Server) After(ctx context.Context, step *core.Step) error { - return errors.New("not implemented") -} - -// Before signals the build stage is about to start. -func (Server) BeforeAll(ctx context.Context, stage *core.Stage) error { - return errors.New("not implemented") -} - -// After signals the build stage is complete. -func (Server) AfterAll(ctx context.Context, stage *core.Stage) error { - return errors.New("not implemented") -} - -// Watch watches for build cancellation requests. -func (Server) Watch(ctx context.Context, stage int64) (bool, error) { - return false, errors.New("not implemented") -} - -// Write writes a line to the build logs -func (Server) Write(ctx context.Context, step int64, line *core.Line) error { - return errors.New("not implemented") -} - -// Upload uploads the full logs -func (Server) Upload(ctx context.Context, step int64, r io.Reader) error { - return errors.New("not implemented") -} - -// UploadBytes uploads the full logs -func (Server) UploadBytes(ctx context.Context, step int64, b []byte) error { - return errors.New("not implemented") -} - -// ServeHTTP is an empty handler. -func (Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {} diff --git a/operator/manager/rpc/server_test.go b/operator/manager/rpc/server_test.go deleted file mode 100644 index 008899758a..0000000000 --- a/operator/manager/rpc/server_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package rpc diff --git a/operator/manager/rpc/types.go b/operator/manager/rpc/types.go deleted file mode 100644 index 1ca8d97451..0000000000 --- a/operator/manager/rpc/types.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package rpc - -import ( - "sync" - - "github.com/drone/drone/core" - "github.com/drone/drone/operator/manager" -) - -type requestRequest struct { - Request *manager.Request -} - -type acceptRequest struct { - Stage int64 - Machine string -} - -type netrcRequest struct { - Repo int64 -} - -type detailsRequest struct { - Stage int64 -} - -type stageRequest struct { - Stage *core.Stage -} - -type stepRequest struct { - Step *core.Step -} - -type writeRequest struct { - Step int64 - Line *core.Line -} - -type watchRequest struct { - Build int64 -} - -type watchResponse struct { - Done bool -} - -type buildContextToken struct { - Secret string - Context *manager.Context -} - -type errorWrapper struct { - Message string -} - -var writePool = sync.Pool{ - New: func() interface{} { - return &writeRequest{} - }, -} diff --git a/operator/manager/rpc2/client.go b/operator/manager/rpc2/client.go deleted file mode 100644 index cf1892a300..0000000000 --- a/operator/manager/rpc2/client.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package rpc2 diff --git a/operator/manager/rpc2/handler.go b/operator/manager/rpc2/handler.go deleted file mode 100644 index e015615d4c..0000000000 --- a/operator/manager/rpc2/handler.go +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -/* - -/rpc/v2/stage POST (request) -/rpc/v2/stage/{stage}?machine= POST (accept, details) -/rpc/v2/stage/{stage} PUT (beforeAll, afterAll) -/rpc/v2/stage/{stage}/steps/{step} PUT (before, after) -/rpc/v2/build/{build}/watch POST (watch) -/rpc/v2/stage/{stage}/logs/batch POST (batch) -/rpc/v2/stage/{stage}/logs/upload POST (upload) - -*/ - -package rpc2 - -import ( - "context" - "encoding/json" - "io" - "net/http" - "strconv" - "time" - - "github.com/go-chi/chi" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/render" - "github.com/drone/drone/operator/manager" - "github.com/drone/drone/store/shared/db" -) - -// default http request timeout -var defaultTimeout = time.Second * 30 - -var noContext = context.Background() - -// HandleJoin returns an http.HandlerFunc that makes an -// http.Request to join the cluster. -// -// POST /rpc/v2/nodes/:machine -func HandleJoin() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - writeOK(w) // this is a no-op - } -} - -// HandleLeave returns an http.HandlerFunc that makes an -// http.Request to leave the cluster. -// -// DELETE /rpc/v2/nodes/:machine -func HandleLeave() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - writeOK(w) // this is a no-op - } -} - -// HandlePing returns an http.HandlerFunc that makes an -// http.Request to ping the server and confirm connectivity. -// -// GET /rpc/v2/ping -func HandlePing() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - writeOK(w) // this is a no-op - } -} - -// HandleRequest returns an http.HandlerFunc that processes an -// http.Request to request a stage from the queue for execution. -// -// POST /rpc/v2/stage -func HandleRequest(m manager.BuildManager) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - ctx, cancel := context.WithTimeout(ctx, defaultTimeout) - defer cancel() - - req := new(manager.Request) - err := json.NewDecoder(r.Body).Decode(req) - if err != nil { - writeError(w, err) - return - } - stage, err := m.Request(ctx, req) - if err != nil { - writeError(w, err) - } else { - writeJSON(w, stage) - } - } -} - -// HandleAccept returns an http.HandlerFunc that processes an -// http.Request to accept ownership of the stage. -// -// POST /rpc/v2/stage/{stage}?machine= -func HandleAccept(m manager.BuildManager) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - stage, _ := strconv.ParseInt( - chi.URLParam(r, "stage"), 10, 64) - - out, err := m.Accept(noContext, stage, r.FormValue("machine")) - if err != nil { - writeError(w, err) - } else { - writeJSON(w, out) - } - } -} - -// HandleInfo returns an http.HandlerFunc that processes an -// http.Request to get the build details. -// -// POST /rpc/v2/build/{build} -func HandleInfo(m manager.BuildManager) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - stage, _ := strconv.ParseInt( - chi.URLParam(r, "stage"), 10, 64) - - res, err := m.Details(noContext, stage) - if err != nil { - writeError(w, err) - return - } - - netrc, err := m.Netrc(noContext, res.Repo.ID) - if err != nil { - writeError(w, err) - return - } - - writeJSON(w, &details{ - Context: res, - Netrc: netrc, - Repo: &repository{ - Repository: res.Repo, - Secret: res.Repo.Secret, - }, - }) - } -} - -// HandleUpdateStage returns an http.HandlerFunc that processes -// an http.Request to update a stage. -// -// PUT /rpc/v2/stage/{stage} -func HandleUpdateStage(m manager.BuildManager) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - dst := new(core.Stage) - err := json.NewDecoder(r.Body).Decode(dst) - if err != nil { - writeError(w, err) - return - } - - if dst.Status == core.StatusPending || - dst.Status == core.StatusRunning { - err = m.BeforeAll(noContext, dst) - } else { - err = m.AfterAll(noContext, dst) - } - - if err != nil { - writeError(w, err) - } else { - writeJSON(w, dst) - } - } -} - -// HandleUpdateStep returns an http.HandlerFunc that processes -// an http.Request to update a step. -// -// POST /rpc/v2/step/{step} -func HandleUpdateStep(m manager.BuildManager) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - dst := new(core.Step) - err := json.NewDecoder(r.Body).Decode(dst) - if err != nil { - writeError(w, err) - return - } - - if dst.Status == core.StatusPending || - dst.Status == core.StatusRunning { - err = m.Before(noContext, dst) - } else { - err = m.After(noContext, dst) - } - - if err != nil { - writeError(w, err) - } else { - writeJSON(w, dst) - } - } -} - -// HandleWatch returns an http.HandlerFunc that accepts a -// blocking http.Request that watches a build for cancellation -// events. -// -// GET /rpc/v2/build/{build}/watch -func HandleWatch(m manager.BuildManager) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - ctx, cancel := context.WithTimeout(ctx, defaultTimeout) - defer cancel() - - build, _ := strconv.ParseInt( - chi.URLParamFromCtx(ctx, "build"), 10, 64) - - _, err := m.Watch(ctx, build) - if err != nil { - writeError(w, err) - } else { - writeOK(w) - } - } -} - -// HandleLogBatch returns an http.HandlerFunc that accepts an -// http.Request to submit a stream of logs to the system. -// -// POST /rpc/v2/step/{step}/logs/batch -func HandleLogBatch(m manager.BuildManager) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - step, _ := strconv.ParseInt( - chi.URLParam(r, "step"), 10, 64) - - lines := []*core.Line{} - err := json.NewDecoder(r.Body).Decode(&lines) - if err != nil { - writeError(w, err) - return - } - - // TODO(bradrydzewski) modify the write function to - // accept a slice of lines. - for _, line := range lines { - err := m.Write(noContext, step, line) - if err != nil { - writeError(w, err) - return - } - } - - writeOK(w) - } -} - -// HandleLogUpload returns an http.HandlerFunc that accepts an -// http.Request to upload and persist logs for a pipeline stage. -// -// POST /rpc/v2/step/{step}/logs/upload -func HandleLogUpload(m manager.BuildManager) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - step, _ := strconv.ParseInt( - chi.URLParam(r, "step"), 10, 64) - - err := m.Upload(noContext, step, r.Body) - if err != nil { - writeError(w, err) - } else { - writeOK(w) - } - } -} - -// HandleCardUpload returns an http.HandlerFunc that accepts an -// http.Request to upload and persist a card for a pipeline step. -// -// POST /rpc/v2/step/{step}/card -func HandleCardUpload(m manager.BuildManager) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - step, _ := strconv.ParseInt( - chi.URLParam(r, "step"), 10, 64) - - in := new(core.CardInput) - err := json.NewDecoder(r.Body).Decode(in) - if err != nil { - render.BadRequest(w, err) - return - } - - err = m.UploadCard(noContext, step, in) - if err != nil { - writeError(w, err) - } else { - writeOK(w) - } - } -} - -// write a 200 Status OK to the response body. -func writeJSON(w http.ResponseWriter, v interface{}) { - json.NewEncoder(w).Encode(v) -} - -// write a 200 Status OK to the response body. -func writeOK(w http.ResponseWriter) { - w.WriteHeader(http.StatusOK) -} - -// write an error message to the response body. -func writeError(w http.ResponseWriter, err error) { - if err == context.DeadlineExceeded { - w.WriteHeader(204) // should retry - } else if err == context.Canceled { - w.WriteHeader(204) // should retry - } else if err == db.ErrOptimisticLock { - w.WriteHeader(409) // should abort - } else { - w.WriteHeader(500) // should fail - } - io.WriteString(w, err.Error()) -} diff --git a/operator/manager/rpc2/server.go b/operator/manager/rpc2/server.go deleted file mode 100644 index f5d587f346..0000000000 --- a/operator/manager/rpc2/server.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package rpc2 - -import ( - "net/http" - - "github.com/drone/drone/operator/manager" - - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" -) - -// Server wraps the chi Router in a custom type for wire -// injection purposes. -type Server http.Handler - -// NewServer returns a new rpc server that enables remote -// interaction with the build controller using the http transport. -func NewServer(manager manager.BuildManager, secret string) Server { - r := chi.NewRouter() - r.Use(middleware.Recoverer) - r.Use(middleware.NoCache) - r.Use(authorization(secret)) - r.Post("/nodes/:machine", HandleJoin()) - r.Delete("/nodes/:machine", HandleLeave()) - r.Post("/ping", HandlePing()) - r.Post("/stage", HandleRequest(manager)) - r.Post("/stage/{stage}", HandleAccept(manager)) - r.Get("/stage/{stage}", HandleInfo(manager)) - r.Put("/stage/{stage}", HandleUpdateStage(manager)) - r.Put("/step/{step}", HandleUpdateStep(manager)) - r.Post("/build/{build}/watch", HandleWatch(manager)) - r.Post("/step/{step}/logs/batch", HandleLogBatch(manager)) - r.Post("/step/{step}/logs/upload", HandleLogUpload(manager)) - r.Post("/step/{step}/card", HandleCardUpload(manager)) - return Server(r) -} - -func authorization(token string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // prevents system administrators from accidentally - // exposing drone without credentials. - if token == "" { - w.WriteHeader(403) - } else if token == r.Header.Get("X-Drone-Token") { - next.ServeHTTP(w, r) - } else { - w.WriteHeader(401) - } - }) - } -} diff --git a/operator/manager/rpc2/server_oss.go b/operator/manager/rpc2/server_oss.go deleted file mode 100644 index 6a225f50ab..0000000000 --- a/operator/manager/rpc2/server_oss.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package rpc2 - -import ( - "net/http" - - "github.com/drone/drone/operator/manager" -) - -// Server wraps the chi Router in a custom type for wire -// injection purposes. -type Server http.Handler - -// NewServer returns a new rpc server that enables remote -// interaction with the build controller using the http transport. -func NewServer(manager manager.BuildManager, secret string) Server { - return Server(http.NotFoundHandler()) -} diff --git a/operator/manager/rpc2/types.go b/operator/manager/rpc2/types.go deleted file mode 100644 index f06749b766..0000000000 --- a/operator/manager/rpc2/types.go +++ /dev/null @@ -1,27 +0,0 @@ -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package rpc2 - -// Copyright 2019 Drone.IO Inc. All rights reserved. -import ( - "github.com/drone/drone/core" - "github.com/drone/drone/operator/manager" -) - -// details provides the runner with the build details and -// includes all environment data required to execute the build. -type details struct { - *manager.Context - Netrc *core.Netrc `json:"netrc"` - Repo *repository `json:"repository"` -} - -// repository wraps a repository object to include the secret -// when the repository is marshaled to json. -type repository struct { - *core.Repository - Secret string `json:"secret"` -} diff --git a/operator/manager/setup.go b/operator/manager/setup.go deleted file mode 100644 index 3e81555b15..0000000000 --- a/operator/manager/setup.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package manager - -import ( - "context" - "encoding/json" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - - "github.com/hashicorp/go-multierror" - "github.com/sirupsen/logrus" -) - -type setup struct { - Builds core.BuildStore - Events core.Pubsub - Repos core.RepositoryStore - Steps core.StepStore - Stages core.StageStore - Status core.StatusService - Users core.UserStore -} - -func (s *setup) do(ctx context.Context, stage *core.Stage) error { - logger := logrus.WithField("stage.id", stage.ID) - - build, err := s.Builds.Find(noContext, stage.BuildID) - if err != nil { - logger.WithError(err).Warnln("manager: cannot find the build") - return err - } - - repo, err := s.Repos.Find(noContext, build.RepoID) - if err != nil { - logger.WithError(err).WithFields( - logrus.Fields{ - "build.number": build.Number, - "build.id": build.ID, - "stage.id": stage.ID, - "repo.id": build.RepoID, - }, - ).Warnln("manager: cannot find the repository") - return err - } - - logger = logger.WithFields( - logrus.Fields{ - "build.number": build.Number, - "build.id": build.ID, - "stage.id": stage.ID, - "repo.id": build.RepoID, - }, - ) - - // // note that if multiple stages run concurrently it will attempt - // // to create the watcher multiple times. The watcher is responsible - // // for handling multiple concurrent requests and preventing duplication. - // err = s.Watcher.Register(noContext, build.ID) - // if err != nil { - // logger.WithError(err).Warnln("manager: cannot create the watcher") - // return err - // } - - if len(stage.Error) > 500 { - stage.Error = stage.Error[:500] - } - stage.Updated = time.Now().Unix() - err = s.Stages.Update(noContext, stage) - if err != nil { - logger.WithError(err). - WithField("stage.status", stage.Status). - Warnln("manager: cannot update the stage") - return err - } - - for _, step := range stage.Steps { - if len(step.Error) > 500 { - step.Error = step.Error[:500] - } - err := s.Steps.Create(noContext, step) - if err != nil { - logger.WithError(err). - WithField("stage.status", stage.Status). - WithField("step.name", step.Name). - WithField("step.id", step.ID). - Warnln("manager: cannot persist the step") - return err - } - } - - updated, err := s.updateBuild(ctx, build) - if err != nil { - logger.WithError(err).Warnln("manager: cannot update the build") - return err - } - - stages, err := s.Stages.ListSteps(noContext, build.ID) - if err != nil { - logger.WithError(err).Warnln("manager: cannot query build stages") - return err - } - - repo.Build = build - repo.Build.Stages = stages - data, _ := json.Marshal(repo) - err = s.Events.Publish(noContext, &core.Message{ - Repository: repo.Slug, - Visibility: repo.Visibility, - Data: data, - }) - if err != nil { - logger.Warnln("manager: cannot publish build event") - } - - if updated { - user, err := s.Users.Find(noContext, repo.UserID) - if err != nil { - logger.WithError(err). - Warnln("manager: cannot find repository owner") - return err - } - - req := &core.StatusInput{ - Repo: repo, - Build: build, - } - err = s.Status.Send(noContext, user, req) - if err != nil { - logger.WithError(err). - Warnln("manager: cannot publish status") - } - } - - return nil -} - -// TODO(bradrydzewski) this should really be encapsulated into a single -// function call that internally uses a database transaction so that we -// can rollback if any operations fail. -func (s *setup) createSteps(ctx context.Context, stage *core.Stage) error { - var errs error - for _, step := range stage.Steps { - err := s.Steps.Create(ctx, step) - if err != nil { - errs = multierror.Append(errs, err) - } - } - return errs -} - -// helper function that updates the build status from pending to running. -// This accounts for the fact that another agent may have already updated -// the build status, which may happen if two stages execute concurrently. -func (s *setup) updateBuild(ctx context.Context, build *core.Build) (bool, error) { - if build.Status != core.StatusPending { - return false, nil - } - build.Started = time.Now().Unix() - build.Updated = time.Now().Unix() - build.Status = core.StatusRunning - err := s.Builds.Update(noContext, build) - if err == db.ErrOptimisticLock { - return false, nil - } - if err != nil { - return false, err - } - return true, nil -} diff --git a/operator/manager/setup_test.go b/operator/manager/setup_test.go deleted file mode 100644 index 553d474810..0000000000 --- a/operator/manager/setup_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package manager diff --git a/operator/manager/teardown.go b/operator/manager/teardown.go deleted file mode 100644 index ea7fd07180..0000000000 --- a/operator/manager/teardown.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package manager - -import ( - "context" - "encoding/json" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/go-scm/scm" - - "github.com/hashicorp/go-multierror" - "github.com/sirupsen/logrus" -) - -type teardown struct { - Builds core.BuildStore - Events core.Pubsub - Logs core.LogStream - Scheduler core.Scheduler - Repos core.RepositoryStore - Steps core.StepStore - Status core.StatusService - Stages core.StageStore - Users core.UserStore - Webhook core.WebhookSender -} - -func (t *teardown) do(ctx context.Context, stage *core.Stage) error { - logger := logrus.WithField("stage.id", stage.ID) - logger.Debugln("manager: stage is complete. teardown") - - build, err := t.Builds.Find(noContext, stage.BuildID) - if err != nil { - logger.WithError(err).Warnln("manager: cannot find the build") - return err - } - - logger = logger.WithFields( - logrus.Fields{ - "build.number": build.Number, - "build.id": build.ID, - "repo.id": build.RepoID, - }, - ) - - repo, err := t.Repos.Find(noContext, build.RepoID) - if err != nil { - logger.WithError(err).Warnln("manager: cannot find the repository") - return err - } - - for _, step := range stage.Steps { - if len(step.Error) > 500 { - step.Error = step.Error[:500] - } - err := t.Steps.Update(noContext, step) - if err != nil { - logger.WithError(err). - WithField("stage.status", stage.Status). - WithField("step.name", step.Name). - WithField("step.id", step.ID). - Warnln("manager: cannot persist the step") - return err - } - } - - if len(stage.Error) > 500 { - stage.Error = stage.Error[:500] - } - - stage.Updated = time.Now().Unix() - err = t.Stages.Update(noContext, stage) - if err != nil { - logger.WithError(err). - Warnln("manager: cannot update the stage") - return err - } - - for _, step := range stage.Steps { - t.Logs.Delete(noContext, step.ID) - } - - stages, err := t.Stages.ListSteps(noContext, build.ID) - if err != nil { - logger.WithError(err).Warnln("manager: cannot get stages") - return err - } - - // - // - // - - err = t.cancelDownstream(ctx, stages) - if err != nil { - logger.WithError(err). - Errorln("manager: cannot cancel downstream builds") - return err - } - - err = t.scheduleDownstream(ctx, stage, stages) - if err != nil { - logger.WithError(err). - Errorln("manager: cannot schedule downstream builds") - return err - } - - // - // - // - - if isBuildComplete(stages) == false { - logger.Debugln("manager: build pending completion of additional stages") - return nil - } - - logger.Debugln("manager: build is finished, teardown") - - build.Status = core.StatusPassing - build.Finished = time.Now().Unix() - for _, sibling := range stages { - if sibling.Status == core.StatusKilled { - build.Status = core.StatusKilled - break - } - if sibling.Status == core.StatusFailing { - build.Status = core.StatusFailing - break - } - if sibling.Status == core.StatusError { - build.Status = core.StatusError - break - } - } - if build.Started == 0 { - build.Started = build.Finished - } - - err = t.Builds.Update(noContext, build) - if err == db.ErrOptimisticLock { - logger.WithError(err). - Warnln("manager: build updated by another goroutine") - return nil - } - if err != nil { - logger.WithError(err). - Warnln("manager: cannot update the build") - return err - } - - repo.Build = build - repo.Build.Stages = stages - data, _ := json.Marshal(repo) - err = t.Events.Publish(noContext, &core.Message{ - Repository: repo.Slug, - Visibility: repo.Visibility, - Data: data, - }) - if err != nil { - logger.WithError(err). - Warnln("manager: cannot publish build event") - } - - payload := &core.WebhookData{ - Event: core.WebhookEventBuild, - Action: core.WebhookActionUpdated, - Repo: repo, - Build: build, - } - err = t.Webhook.Send(noContext, payload) - if err != nil { - logger.WithError(err).Warnln("manager: cannot send global webhook") - } - - user, err := t.Users.Find(noContext, repo.UserID) - if err != nil { - logger.WithError(err). - Warnln("manager: cannot find repository owner") - - // this error is insufficient to fail the function, - // however, execution of the function should be halted - // to prevent a nil pointer in subsequent operations. - return nil - } - - req := &core.StatusInput{ - Repo: repo, - Build: build, - } - err = t.Status.Send(noContext, user, req) - if err != nil && err != scm.ErrNotSupported { - logger.WithError(err). - Warnln("manager: cannot publish status") - } - return nil -} - -// cancelDownstream is a helper function that tests for -// downstream stages and cancels them based on the overall -// pipeline state. -func (t *teardown) cancelDownstream( - ctx context.Context, - stages []*core.Stage, -) error { - failed := false - for _, s := range stages { - if s.IsFailed() { - failed = true - } - } - - var errs error - for _, s := range stages { - if s.Status != core.StatusWaiting { - continue - } - - var skip bool - if failed == true && s.OnFailure == false { - skip = true - } - if failed == false && s.OnSuccess == false { - skip = true - } - if skip == false { - continue - } - - if areDepsComplete(s, stages) == false { - continue - } - - logger := logrus.WithFields( - logrus.Fields{ - "stage.id": s.ID, - "stage.on_success": s.OnSuccess, - "stage.on_failure": s.OnFailure, - "stage.is_failure": failed, - "stage.depends_on": s.DependsOn, - }, - ) - logger.Debugln("manager: skipping step") - - s.Status = core.StatusSkipped - s.Started = time.Now().Unix() - s.Stopped = time.Now().Unix() - err := t.Stages.Update(noContext, s) - if err == db.ErrOptimisticLock { - t.resync(ctx, s) - continue - } - if err != nil { - logger.WithError(err). - Warnln("manager: cannot update stage status") - errs = multierror.Append(errs, err) - } - } - return errs -} - -// scheduleDownstream is a helper function that tests for -// downstream stages and schedules stages if all dependencies -// and execution requirements are met. -func (t *teardown) scheduleDownstream( - ctx context.Context, - stage *core.Stage, - stages []*core.Stage, -) error { - - var errs error - for _, sibling := range stages { - if sibling.Status == core.StatusWaiting { - if len(sibling.DependsOn) == 0 { - continue - } - - // PROBLEM: isDep only checks the direct parent - // i think .... - // if isDep(stage, sibling) == false { - // continue - // } - if areDepsComplete(sibling, stages) == false { - continue - } - // if isLastDep(stage, sibling, stages) == false { - // continue - // } - - logger := logrus.WithFields( - logrus.Fields{ - "stage.id": sibling.ID, - "stage.name": sibling.Name, - "stage.depends_on": sibling.DependsOn, - }, - ) - logger.Debugln("manager: schedule next stage") - - sibling.Status = core.StatusPending - sibling.Updated = time.Now().Unix() - err := t.Stages.Update(noContext, sibling) - if err == db.ErrOptimisticLock { - t.resync(ctx, sibling) - continue - } - if err != nil { - logger.WithError(err). - Warnln("manager: cannot update stage status") - errs = multierror.Append(errs, err) - } - - err = t.Scheduler.Schedule(noContext, sibling) - if err != nil { - logger.WithError(err). - Warnln("manager: cannot schedule stage") - errs = multierror.Append(errs, err) - } - } - } - return errs -} - -// resync updates the stage from the database. Note that it does -// not update the Version field. This is by design. It prevents -// the current go routine from updating a stage that has been -// updated by another go routine. -func (t *teardown) resync(ctx context.Context, stage *core.Stage) error { - updated, err := t.Stages.Find(ctx, stage.ID) - if err != nil { - return err - } - stage.Status = updated.Status - stage.Error = updated.Error - stage.ExitCode = updated.ExitCode - stage.Machine = updated.Machine - stage.Started = updated.Started - stage.Stopped = updated.Stopped - stage.Created = updated.Created - stage.Updated = updated.Updated - return nil -} diff --git a/operator/manager/teardown_test.go b/operator/manager/teardown_test.go deleted file mode 100644 index 553d474810..0000000000 --- a/operator/manager/teardown_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package manager diff --git a/operator/manager/updater.go b/operator/manager/updater.go deleted file mode 100644 index c6a6dde71c..0000000000 --- a/operator/manager/updater.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package manager - -import ( - "context" - "encoding/json" - - "github.com/drone/drone/core" - - "github.com/sirupsen/logrus" -) - -type updater struct { - Builds core.BuildStore - Events core.Pubsub - Repos core.RepositoryStore - Steps core.StepStore - Stages core.StageStore - Webhook core.WebhookSender -} - -func (u *updater) do(ctx context.Context, step *core.Step) error { - logger := logrus.WithFields( - logrus.Fields{ - "step.status": step.Status, - "step.name": step.Name, - "step.id": step.ID, - }, - ) - - if len(step.Error) > 500 { - step.Error = step.Error[:500] - } - err := u.Steps.Update(noContext, step) - if err != nil { - logger.WithError(err).Warnln("manager: cannot update step") - return err - } - - stage, err := u.Stages.Find(noContext, step.StageID) - if err != nil { - logger.WithError(err).Warnln("manager: cannot find stage") - return nil - } - - build, err := u.Builds.Find(noContext, stage.BuildID) - if err != nil { - logger.WithError(err).Warnln("manager: cannot find build") - return nil - } - - repo, err := u.Repos.Find(noContext, build.RepoID) - if err != nil { - logger.WithError(err).Warnln("manager: cannot find repo") - return nil - } - - stages, err := u.Stages.ListSteps(noContext, build.ID) - if err != nil { - logger.WithError(err).Warnln("manager: cannot list stages") - return nil - } - - repo.Build = build - repo.Build.Stages = stages - data, _ := json.Marshal(repo) - err = u.Events.Publish(noContext, &core.Message{ - Repository: repo.Slug, - Visibility: repo.Visibility, - Data: data, - }) - if err != nil { - logger.WithError(err).Warnln("manager: cannot publish build event") - } - - payload := &core.WebhookData{ - Event: core.WebhookEventBuild, - Action: core.WebhookActionUpdated, - Repo: repo, - Build: build, - } - err = u.Webhook.Send(noContext, payload) - if err != nil { - logger.WithError(err).Warnln("manager: cannot send global webhook") - } - return nil -} diff --git a/operator/manager/util.go b/operator/manager/util.go deleted file mode 100644 index 3c17b8db7c..0000000000 --- a/operator/manager/util.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package manager - -import ( - "github.com/drone/drone/core" -) - -func isBuildComplete(stages []*core.Stage) bool { - for _, stage := range stages { - switch stage.Status { - case core.StatusPending, - core.StatusRunning, - core.StatusWaiting, - core.StatusDeclined, - core.StatusBlocked: - return false - } - } - return true -} - -func isLastStage(stage *core.Stage, stages []*core.Stage) bool { - for _, sibling := range stages { - if stage.Number == sibling.Number { - continue - } - if sibling.Updated > stage.Updated { - return false - } else if sibling.Updated == stage.Updated && - sibling.Number > stage.Number { - return false - } - } - return true -} - -func isDep(a *core.Stage, b *core.Stage) bool { - for _, name := range b.DependsOn { - if name == a.Name { - return true - } - } - return false -} - -func areDepsComplete(stage *core.Stage, stages []*core.Stage) bool { - deps := map[string]struct{}{} - for _, dep := range stage.DependsOn { - deps[dep] = struct{}{} - } - for _, sibling := range stages { - if _, ok := deps[sibling.Name]; !ok { - continue - } - if !sibling.IsDone() { - return false - } - } - return true -} - -// helper function returns true if the current stage is the last -// dependency in the tree. -func isLastDep(curr, next *core.Stage, stages []*core.Stage) bool { - deps := map[string]struct{}{} - for _, dep := range next.DependsOn { - deps[dep] = struct{}{} - } - for _, sibling := range stages { - if _, ok := deps[sibling.Name]; !ok { - continue - } - if sibling.Updated > curr.Updated { - return false - } else if sibling.Updated == curr.Updated && - sibling.Number > curr.Number { - return false - } - } - return true -} - -// helper function returns true if all dependencies are complete. -func depsComplete(stage *core.Stage, siblings []*core.Stage) bool { - for _, dep := range stage.DependsOn { - found := false - inner: - for _, sibling := range siblings { - if sibling.Name == dep { - found = true - break inner - } - } - if !found { - return false - } - } - return true -} diff --git a/operator/manager/util_test.go b/operator/manager/util_test.go deleted file mode 100644 index 553d474810..0000000000 --- a/operator/manager/util_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package manager diff --git a/operator/runner/after.go b/operator/runner/after.go deleted file mode 100644 index 25308ea5a3..0000000000 --- a/operator/runner/after.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runner diff --git a/operator/runner/after_test.go b/operator/runner/after_test.go deleted file mode 100644 index f4b8566d05..0000000000 --- a/operator/runner/after_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package runner diff --git a/operator/runner/before.go b/operator/runner/before.go deleted file mode 100644 index 25308ea5a3..0000000000 --- a/operator/runner/before.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runner diff --git a/operator/runner/before_test.go b/operator/runner/before_test.go deleted file mode 100644 index f4b8566d05..0000000000 --- a/operator/runner/before_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package runner diff --git a/operator/runner/convert.go b/operator/runner/convert.go deleted file mode 100644 index 918e33749c..0000000000 --- a/operator/runner/convert.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runner - -import ( - "strings" - - "github.com/drone/drone-runtime/engine" - "github.com/drone/drone-runtime/runtime" - "github.com/drone/drone/core" -) - -func convertVolumes(from []string) map[string]string { - to := map[string]string{} - for _, s := range from { - parts := strings.Split(s, ":") - if len(parts) != 2 { - continue - } - key := parts[0] - val := parts[1] - to[key] = val - } - return to -} - -func convertSecrets(from []*core.Secret) map[string]string { - to := map[string]string{} - for _, secret := range from { - to[secret.Name] = secret.Data - } - return to -} - -func convertRegistry(from []*core.Registry) []*engine.DockerAuth { - var to []*engine.DockerAuth - for _, registry := range from { - to = append(to, &engine.DockerAuth{ - Address: registry.Address, - Username: registry.Username, - Password: registry.Password, - }) - } - return to -} - -func convertLines(from []*runtime.Line) []*core.Line { - var to []*core.Line - for _, v := range from { - to = append(to, &core.Line{ - Number: v.Number, - Message: v.Message, - Timestamp: v.Timestamp, - }) - } - return to -} - -func convertLine(from *runtime.Line) *core.Line { - return &core.Line{ - Number: from.Number, - Message: from.Message, - Timestamp: from.Timestamp, - } -} diff --git a/operator/runner/convert_test.go b/operator/runner/convert_test.go deleted file mode 100644 index 2fdcebfdab..0000000000 --- a/operator/runner/convert_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package runner - -import ( - "testing" - - "github.com/drone/drone-runtime/engine" - "github.com/drone/drone-runtime/runtime" - "github.com/drone/drone/core" - "github.com/google/go-cmp/cmp" -) - -// func Test_convertSecrets(t *testing.T) { -// secrets := []*core.Secret{ -// {Name: "docker_username", Data: "octocat"}, -// {Name: "docker_password", Data: "password"}, -// } -// got := convertSecrets(secrets) - -// want := []compiler.Secret{ -// {Name: "docker_username", Value: "octocat"}, -// {Name: "docker_password", Value: "password"}, -// } - -// if diff := cmp.Diff(got, want); len(diff) != 0 { -// t.Errorf(diff) -// } -// } - -func Test_convertRegistry(t *testing.T) { - list := []*core.Registry{ - { - Address: "docker.io", - Username: "octocat", - Password: "password", - }, - } - got := convertRegistry(list) - want := []*engine.DockerAuth{ - { - Address: "docker.io", - Username: "octocat", - Password: "password", - }, - } - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func Test_convertLines(t *testing.T) { - lines := []*runtime.Line{ - { - Number: 1, - Message: "ping google.com", - Timestamp: 1257894000, - }, - { - Number: 1, - Message: "PING google.com (1.2.3.4): 56 data bytes", - Timestamp: 1257894000, - }, - } - got := convertLines(lines) - want := []*core.Line{ - { - Number: 1, - Message: "ping google.com", - Timestamp: 1257894000, - }, - { - Number: 1, - Message: "PING google.com (1.2.3.4): 56 data bytes", - Timestamp: 1257894000, - }, - } - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func Test_convertLine(t *testing.T) { - line := &runtime.Line{ - Number: 1, - Message: "ping google.com", - Timestamp: 1257894000, - } - got := convertLine(line) - want := &core.Line{ - Number: 1, - Message: "ping google.com", - Timestamp: 1257894000, - } - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/operator/runner/env.go b/operator/runner/env.go deleted file mode 100644 index 627143a5b1..0000000000 --- a/operator/runner/env.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runner - -import ( - "fmt" - "regexp" - "strings" - - "github.com/drone/drone/core" -) - -func systemEnviron(system *core.System) map[string]string { - return map[string]string{ - "CI": "true", - "DRONE": "true", - "DRONE_SYSTEM_PROTO": system.Proto, - "DRONE_SYSTEM_HOST": system.Host, - "DRONE_SYSTEM_HOSTNAME": system.Host, - "DRONE_SYSTEM_VERSION": fmt.Sprint(system.Version), - } -} - -func agentEnviron(runner *Runner) map[string]string { - return map[string]string{ - "DRONE_MACHINE": runner.Machine, - "DRONE_RUNNER_HOST": runner.Machine, - "DRONE_RUNNER_HOSTNAME": runner.Machine, - "DRONE_RUNNER_PLATFORM": runner.Platform, - } -} - -func repoEnviron(repo *core.Repository) map[string]string { - return map[string]string{ - "DRONE_REPO": repo.Slug, - "DRONE_REPO_SCM": repo.SCM, - "DRONE_REPO_OWNER": repo.Namespace, - "DRONE_REPO_NAMESPACE": repo.Namespace, - "DRONE_REPO_NAME": repo.Name, - "DRONE_REPO_LINK": repo.Link, - "DRONE_REPO_BRANCH": repo.Branch, - "DRONE_REMOTE_URL": repo.HTTPURL, - "DRONE_GIT_HTTP_URL": repo.HTTPURL, - "DRONE_GIT_SSH_URL": repo.SSHURL, - "DRONE_REPO_VISIBILITY": repo.Visibility, - "DRONE_REPO_PRIVATE": fmt.Sprint(repo.Private), - - // - // these are legacy configuration parameters for backward - // compatibility with drone 0.8. - // - "CI_REPO": repo.Slug, - "CI_REPO_NAME": repo.Slug, - "CI_REPO_LINK": repo.Link, - "CI_REPO_REMOTE": repo.HTTPURL, - "CI_REMOTE_URL": repo.HTTPURL, - "CI_REPO_PRIVATE": fmt.Sprint(repo.Private), - } -} - -func stageEnviron(stage *core.Stage) map[string]string { - return map[string]string{ - "DRONE_STAGE_KIND": "pipeline", - "DRONE_STAGE_NAME": stage.Name, - "DRONE_STAGE_NUMBER": fmt.Sprint(stage.Number), - "DRONE_STAGE_MACHINE": stage.Machine, - "DRONE_STAGE_OS": stage.OS, - "DRONE_STAGE_ARCH": stage.Arch, - "DRONE_STAGE_VARIANT": stage.Variant, - "DRONE_STAGE_DEPENDS_ON": strings.Join(stage.DependsOn, ","), - } -} - -func buildEnviron(build *core.Build) map[string]string { - env := map[string]string{ - "DRONE_BRANCH": build.Target, - "DRONE_SOURCE_BRANCH": build.Source, - "DRONE_TARGET_BRANCH": build.Target, - "DRONE_COMMIT": build.After, - "DRONE_COMMIT_SHA": build.After, - "DRONE_COMMIT_BEFORE": build.Before, - "DRONE_COMMIT_AFTER": build.After, - "DRONE_COMMIT_REF": build.Ref, - "DRONE_COMMIT_BRANCH": build.Target, - "DRONE_COMMIT_LINK": build.Link, - "DRONE_COMMIT_MESSAGE": build.Message, - "DRONE_COMMIT_AUTHOR": build.Author, - "DRONE_COMMIT_AUTHOR_EMAIL": build.AuthorEmail, - "DRONE_COMMIT_AUTHOR_AVATAR": build.AuthorAvatar, - "DRONE_COMMIT_AUTHOR_NAME": build.AuthorName, - "DRONE_BUILD_NUMBER": fmt.Sprint(build.Number), - "DRONE_BUILD_EVENT": build.Event, - "DRONE_BUILD_ACTION": build.Action, - "DRONE_BUILD_CREATED": fmt.Sprint(build.Created), - "DRONE_BUILD_STARTED": fmt.Sprint(build.Started), - "DRONE_BUILD_FINISHED": fmt.Sprint(build.Finished), - "DRONE_DEPLOY_TO": build.Deploy, - - // - // these are legacy configuration parameters for backward - // compatibility with drone 0.8. - // - "CI_BUILD_NUMBER": fmt.Sprint(build.Number), - "CI_PARENT_BUILD_NUMBER": fmt.Sprint(build.Parent), - "CI_BUILD_CREATED": fmt.Sprint(build.Created), - "CI_BUILD_STARTED": fmt.Sprint(build.Started), - "CI_BUILD_FINISHED": fmt.Sprint(build.Finished), - "CI_BUILD_STATUS": build.Status, - "CI_BUILD_EVENT": build.Event, - "CI_BUILD_LINK": build.Link, - "CI_BUILD_TARGET": build.Deploy, - "CI_COMMIT_SHA": build.After, - "CI_COMMIT_REF": build.Ref, - "CI_COMMIT_BRANCH": build.Target, - "CI_COMMIT_MESSAGE": build.Message, - "CI_COMMIT_AUTHOR": build.Author, - "CI_COMMIT_AUTHOR_NAME": build.AuthorName, - "CI_COMMIT_AUTHOR_EMAIL": build.AuthorEmail, - "CI_COMMIT_AUTHOR_AVATAR": build.AuthorAvatar, - } - if strings.HasPrefix(build.Ref, "refs/tags/") { - env["DRONE_TAG"] = strings.TrimPrefix(build.Ref, "refs/tags/") - } - if build.Event == core.EventPullRequest { - env["DRONE_PULL_REQUEST"] = re.FindString(build.Ref) - } - return env -} - -func linkEnviron(repo *core.Repository, build *core.Build, system *core.System) map[string]string { - return map[string]string{ - "DRONE_BUILD_LINK": fmt.Sprintf( - "%s://%s/%s/%d", - system.Proto, - system.Host, - repo.Slug, - build.Number, - ), - } -} - -// regular expression to extract the pull request number -// from the git ref (e.g. refs/pulls/{d}/head) -var re = regexp.MustCompile("\\d+") - -// helper function combines one or more maps of environment -// variables into a single map. -func combineEnviron(env ...map[string]string) map[string]string { - c := map[string]string{} - for _, e := range env { - for k, v := range e { - c[k] = v - } - } - return c -} diff --git a/operator/runner/env_test.go b/operator/runner/env_test.go deleted file mode 100644 index 38ba37ce44..0000000000 --- a/operator/runner/env_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package runner - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/google/go-cmp/cmp" -) - -func Test_systemEnviron(t *testing.T) { - system := &core.System{ - Proto: "https", - Host: "meta.drone.io", - Link: "https://meta.drone.io", - Version: "v1.0.0", - } - got := systemEnviron(system) - want := map[string]string{ - "CI": "true", - "DRONE": "true", - "DRONE_SYSTEM_PROTO": "https", - "DRONE_SYSTEM_HOST": "meta.drone.io", - "DRONE_SYSTEM_HOSTNAME": "meta.drone.io", - "DRONE_SYSTEM_VERSION": "v1.0.0", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func Test_runnerEnviron(t *testing.T) { - runner := &Runner{ - Machine: "ip-12-34-56-78.us-west-2.compute.internal", - Platform: "linux/amd64", - } - got := agentEnviron(runner) - want := map[string]string{ - "DRONE_MACHINE": "ip-12-34-56-78.us-west-2.compute.internal", - "DRONE_RUNNER_HOST": "ip-12-34-56-78.us-west-2.compute.internal", - "DRONE_RUNNER_HOSTNAME": "ip-12-34-56-78.us-west-2.compute.internal", - "DRONE_RUNNER_PLATFORM": "linux/amd64", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} diff --git a/operator/runner/machine/client.go b/operator/runner/machine/client.go deleted file mode 100644 index 5e896f0ebd..0000000000 --- a/operator/runner/machine/client.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package machine - -// import ( -// "io/ioutil" -// "net/http" -// "path/filepath" - -// "docker.io/go-docker" -// "docker.io/go-docker/api" -// "github.com/docker/go-connections/tlsconfig" -// ) - -// // Client returns a new Docker client from the -// // machine directory. -// func Client(path string) (docker.APIClient, error) { -// // read the docker-machine configuration file from -// // the local machine directory. -// configPath, err := := filepath.Join(path, "config.json") -// if err != nil { -// return nil, err -// } -// config := - -// options := tlsconfig.Options{ -// CAFile: filepath.Join(path, "ca.pem"), -// CertFile: filepath.Join(path, "cert.pem"), -// KeyFile: filepath.Join(path, "key.pem"), -// InsecureSkipVerify: false, -// } -// tlsc, err := tlsconfig.Client(options) -// if err != nil { -// return nil, err -// } -// client = &http.Client{ -// Transport: &http.Transport{ -// TLSClientConfig: tlsc, -// }, -// CheckRedirect: docker.CheckRedirect, -// } -// return docker.NewClient(host, api.DefaultVersion, client, nil) -// } diff --git a/operator/runner/machine/config.go b/operator/runner/machine/config.go deleted file mode 100644 index 1fbe5fa36e..0000000000 --- a/operator/runner/machine/config.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package machine - -import ( - "bytes" - "encoding/json" - "io" - "io/ioutil" - "strings" -) - -// Config provides the Docker machine configuration. -type Config struct { - Name string - Driver struct { - IPAddress string - MachineName string - } - HostOptions struct { - EngineOptions struct { - TLSVerify bool `json:"TlsVerify"` - } - AuthOptions struct { - CertDir string - CaCertPath string - CaPrivateKeyPath string - ServerCertPath string - ServerKeyPath string - ClientKeyPath string - ClientCertPath string - StorePath string - } - } -} - -// helper function reads and unmarshalls the docker-machine -// configuration from a reader. -func parseReader(r io.Reader) (*Config, error) { - out := new(Config) - err := json.NewDecoder(r).Decode(out) - return out, err -} - -// heper function parses the docker-machine configuration -// from a json string. -func parseString(s string) (*Config, error) { - r := strings.NewReader(s) - return parseReader(r) -} - -// heper function parses the docker-machine configuration -// from a json file. -func parseFile(path string) (*Config, error) { - d, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - r := bytes.NewReader(d) - return parseReader(r) -} diff --git a/operator/runner/machine/config_test.go b/operator/runner/machine/config_test.go deleted file mode 100644 index cfd1a6686d..0000000000 --- a/operator/runner/machine/config_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package machine diff --git a/operator/runner/machine/machine.go b/operator/runner/machine/machine.go deleted file mode 100644 index e25faf8404..0000000000 --- a/operator/runner/machine/machine.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package machine - -import ( - "errors" - "io/ioutil" - "path/filepath" -) - -// ErrNoMachines is returned when no valid or matching -// docker machines are found in the docker-machine home -// directory. -var ErrNoMachines = errors.New("No Docker Machines found") - -// Load loads the docker-machine runners. -func Load(home, match string) ([]*Config, error) { - path := filepath.Join(home, "machines") - entries, err := ioutil.ReadDir(path) - if err != nil { - return nil, err - } - // loop through the list of docker-machine home - // and capture a list of matching subdirectories. - var machines []*Config - for _, entry := range entries { - if entry.IsDir() == false { - continue - } - name := entry.Name() - confPath := filepath.Join(path, name, "config.json") - conf, err := parseFile(confPath) - if err != nil { - return nil, err - } - // If no match logic is defined, the machine is - // automatically used as a build machine. - if match == "" { - machines = append(machines, conf) - continue - } - // Else verify the machine matches the user-defined - // pattern. Use as a build machine if a match exists - match, _ := filepath.Match(match, conf.Name) - if match { - machines = append(machines, conf) - } - } - if len(machines) == 0 { - return nil, ErrNoMachines - } - return machines, nil -} diff --git a/operator/runner/machine/machine_test.go b/operator/runner/machine/machine_test.go deleted file mode 100644 index dbbb1e6fdd..0000000000 --- a/operator/runner/machine/machine_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package machine - -import ( - "testing" -) - -func TestLoad(t *testing.T) { - t.Skip() -} diff --git a/operator/runner/runner.go b/operator/runner/runner.go deleted file mode 100644 index 25c97a7f18..0000000000 --- a/operator/runner/runner.go +++ /dev/null @@ -1,611 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runner - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "runtime/debug" - "strconv" - "strings" - "sync" - "time" - - "github.com/drone/drone-runtime/engine" - "github.com/drone/drone-runtime/runtime" - "github.com/drone/drone-yaml/yaml" - "github.com/drone/drone-yaml/yaml/compiler" - "github.com/drone/drone-yaml/yaml/compiler/transform" - "github.com/drone/drone-yaml/yaml/converter" - "github.com/drone/drone-yaml/yaml/linter" - "github.com/drone/drone/core" - "github.com/drone/drone/operator/manager" - "github.com/drone/drone/plugin/registry" - "github.com/drone/drone/plugin/secret" - "github.com/drone/drone/store/shared/db" - "github.com/drone/envsubst" - "golang.org/x/sync/errgroup" - - "github.com/sirupsen/logrus" -) - -// Limits defines runtime container limits. -type Limits struct { - MemSwapLimit int64 - MemLimit int64 - ShmSize int64 - CPUQuota int64 - CPUShares int64 - CPUSet string -} - -// Runner is responsible for retrieving and executing builds, and -// reporting back their status to the central server. -type Runner struct { - sync.Mutex - - Engine engine.Engine - Manager manager.BuildManager - Registry core.RegistryService - Secrets core.SecretService - Limits Limits - Volumes []string - Networks []string - Devices []string - Privileged []string - Environ map[string]string - Machine string - Labels map[string]string - - Kind string - Type string - Platform string - OS string - Arch string - Kernel string - Variant string -} - -func (r *Runner) handleError(ctx context.Context, stage *core.Stage, err error) error { - switch stage.Status { - case core.StatusPending, - core.StatusRunning: - default: - } - for _, step := range stage.Steps { - if step.Status == core.StatusPending { - step.Status = core.StatusSkipped - } - if step.Status == core.StatusRunning { - step.Status = core.StatusPassing - step.Stopped = time.Now().Unix() - } - } - stage.Status = core.StatusError - stage.Error = err.Error() - stage.Stopped = time.Now().Unix() - switch v := err.(type) { - case *runtime.ExitError: - stage.Error = "" - stage.Status = core.StatusFailing - stage.ExitCode = v.Code - case *runtime.OomError: - stage.Error = "OOM kill signaled by host operating system" - } - return r.Manager.AfterAll(ctx, stage) -} - -// -// this is a quick copy-paste duplicate of above that -// removes some code. this is for testing purposes only. -// - -func (r *Runner) Run(ctx context.Context, id int64) error { - logger := logrus.WithFields( - logrus.Fields{ - "machine": r.Machine, - "os": r.OS, - "arch": r.Arch, - "stage-id": id, - }, - ) - - logger.Debug("runner: get stage details from server") - - defer func() { - // taking the paranoid approach to recover from - // a panic that should absolutely never happen. - if r := recover(); r != nil { - logger.Errorf("runner: unexpected panic: %s", r) - debug.PrintStack() - } - }() - - m, err := r.Manager.Details(ctx, id) - if err != nil { - logger.WithError(err).Warnln("runner: cannot get stage details") - return err - } - - logger = logger.WithFields( - logrus.Fields{ - "repo": m.Repo.Slug, - "build": m.Build.Number, - "stage": m.Stage.Number, - }, - ) - - netrc, err := r.Manager.Netrc(ctx, m.Repo.ID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("runner: cannot get netrc file") - return r.handleError(ctx, m.Stage, err) - } - if netrc == nil { - netrc = new(core.Netrc) - } - - if m.Build.Status == core.StatusKilled || m.Build.Status == core.StatusSkipped { - logger = logger.WithError(err) - logger.Infoln("runner: cannot run a canceled build") - return nil - } - - environ := combineEnviron( - agentEnviron(r), - buildEnviron(m.Build), - repoEnviron(m.Repo), - stageEnviron(m.Stage), - systemEnviron(m.System), - linkEnviron(m.Repo, m.Build, m.System), - m.Build.Params, - ) - - // - // parse configuration file - // - - // - // TODO extract the yaml document by index - // TODO mutate the yaml - // - - // this code is temporarily in place to detect and convert - // the legacy yaml configuration file to the new format. - y, err := converter.ConvertString(string(m.Config.Data), converter.Metadata{ - Filename: m.Repo.Config, - URL: m.Repo.Link, - Ref: m.Build.Ref, - }) - - if err != nil { - return err - } - - y, err = envsubst.Eval(y, func(name string) string { - env := environ[name] - if strings.Contains(env, "\n") { - env = fmt.Sprintf("%q", env) - } - return env - }) - if err != nil { - return r.handleError(ctx, m.Stage, err) - } - - manifest, err := yaml.ParseString(y) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("runner: cannot parse yaml") - return r.handleError(ctx, m.Stage, err) - } - - var pipeline *yaml.Pipeline - for _, resource := range manifest.Resources { - v, ok := resource.(*yaml.Pipeline) - if !ok { - continue - } - if v.Name == m.Stage.Name { - pipeline = v - break - } - } - if pipeline == nil { - logger = logger.WithError(err) - logger.Errorln("runner: cannot find named pipeline") - return r.handleError(ctx, m.Stage, - errors.New("cannot find named pipeline"), - ) - } - - logger = logger.WithField("pipeline", pipeline.Name) - - err = linter.Lint(pipeline, m.Repo.Trusted) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("runner: yaml lint errors") - return r.handleError(ctx, m.Stage, err) - } - - secretService := secret.Combine( - secret.Encrypted(), - secret.Static(m.Secrets), - r.Secrets, - ) - registryService := registry.Combine( - registry.Static(m.Secrets), - r.Registry, - ) - - comp := new(compiler.Compiler) - comp.PrivilegedFunc = compiler.DindFunc( - append( - r.Privileged, - "plugins/docker", - "plugins/acr", - "plugins/ecr", - "plugins/gcr", - "plugins/heroku", - ), - ) - comp.SkipFunc = compiler.SkipFunc( - compiler.SkipData{ - Action: m.Build.Action, - Branch: m.Build.Target, - Cron: m.Build.Cron, - Event: m.Build.Event, - Instance: m.System.Host, - Ref: m.Build.Ref, - Repo: m.Repo.Slug, - Target: m.Build.Deploy, - }, - ) - comp.TransformFunc = transform.Combine( - // transform.Include(), - // transform.Exclude(), - // transform.ResumeAt(), - transform.WithAuthsFunc( - func() []*engine.DockerAuth { - in := &core.RegistryArgs{ - Build: m.Build, - Repo: m.Repo, - Conf: manifest, - Pipeline: pipeline, - } - out, err := registryService.List(ctx, in) - if err != nil { - return nil - } - return convertRegistry(out) - }, - ), - transform.WithEnviron(environ), - transform.WithEnviron(r.Environ), - transform.WithLables( - map[string]string{ - "io.drone": "true", - "io.drone.build.number": fmt.Sprint(m.Build.Number), - "io.drone.repo.namespace": m.Repo.Namespace, - "io.drone.repo.name": m.Repo.Name, - "io.drone.stage.name": m.Stage.Name, - "io.drone.stage.number": fmt.Sprint(m.Stage.Number), - "io.drone.ttl": fmt.Sprint(time.Duration(m.Repo.Timeout) * time.Minute), - "io.drone.expires": fmt.Sprint(time.Now().Add(time.Duration(m.Repo.Timeout)*time.Minute + time.Hour).Unix()), - "io.drone.created": fmt.Sprint(time.Now().Unix()), - "io.drone.protected": "false", - }, - ), // TODO append labels here - transform.WithLimits( - r.Limits.MemLimit, - 0, // no clue how to apply the docker cpu limit - ), - transform.WithNetrc( - netrc.Machine, - netrc.Login, - netrc.Password, - ), - transform.WithNetworks(r.Networks), - transform.WithProxy(), - transform.WithSecretFunc( - func(name string) *engine.Secret { - in := &core.SecretArgs{ - Name: name, - Build: m.Build, - Repo: m.Repo, - Conf: manifest, - } - out, err := secretService.Find(ctx, in) - if err != nil { - return nil - } - if out == nil { - return nil - } - return &engine.Secret{ - Metadata: engine.Metadata{Name: name}, - Data: out.Data, - } - }, - ), - transform.WithVolumes( - convertVolumes(r.Volumes), - ), - ) - ir := comp.Compile(pipeline) - - steps := map[string]*core.Step{} - i := 0 - for _, s := range ir.Steps { - if s.RunPolicy == engine.RunNever { - continue - } - i++ - dst := &core.Step{ - Number: i, - Name: s.Metadata.Name, - StageID: m.Stage.ID, - Status: core.StatusPending, - ErrIgnore: s.IgnoreErr, - } - steps[dst.Name] = dst - m.Stage.Steps = append(m.Stage.Steps, dst) - } - - hooks := &runtime.Hook{ - BeforeEach: func(s *runtime.State) error { - r.Lock() - s.Step.Envs["DRONE_MACHINE"] = r.Machine - s.Step.Envs["CI_BUILD_STATUS"] = "success" - s.Step.Envs["CI_BUILD_STARTED"] = strconv.FormatInt(s.Runtime.Time, 10) - s.Step.Envs["CI_BUILD_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10) - s.Step.Envs["DRONE_BUILD_STATUS"] = "success" - s.Step.Envs["DRONE_BUILD_STARTED"] = strconv.FormatInt(s.Runtime.Time, 10) - s.Step.Envs["DRONE_BUILD_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10) - - s.Step.Envs["CI_JOB_STATUS"] = "success" - s.Step.Envs["CI_JOB_STARTED"] = strconv.FormatInt(s.Runtime.Time, 10) - s.Step.Envs["CI_JOB_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10) - s.Step.Envs["DRONE_JOB_STATUS"] = "success" - s.Step.Envs["DRONE_JOB_STARTED"] = strconv.FormatInt(s.Runtime.Time, 10) - s.Step.Envs["DRONE_JOB_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10) - s.Step.Envs["DRONE_STAGE_STATUS"] = "success" - s.Step.Envs["DRONE_STAGE_STARTED"] = strconv.FormatInt(s.Runtime.Time, 10) - s.Step.Envs["DRONE_STAGE_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10) - - if s.Runtime.Error != nil { - s.Step.Envs["CI_BUILD_STATUS"] = "failure" - s.Step.Envs["CI_JOB_STATUS"] = "failure" - s.Step.Envs["DRONE_BUILD_STATUS"] = "failure" - s.Step.Envs["DRONE_STAGE_STATUS"] = "failure" - s.Step.Envs["DRONE_JOB_STATUS"] = "failure" - } - for _, stage := range m.Build.Stages { - if stage.IsFailed() { - s.Step.Envs["DRONE_BUILD_STATUS"] = "failure" - break - } - } - - step, ok := steps[s.Step.Metadata.Name] - if ok { - step.Status = core.StatusRunning - step.Started = time.Now().Unix() - - s.Step.Envs["DRONE_STEP_NAME"] = step.Name - s.Step.Envs["DRONE_STEP_NUMBER"] = fmt.Sprint(step.Number) - } - - stepClone := new(core.Step) - *stepClone = *step - r.Unlock() - - err := r.Manager.Before(ctx, stepClone) - if err != nil { - return err - } - - r.Lock() - step.ID = stepClone.ID - step.Version = stepClone.Version - r.Unlock() - return nil - }, - - AfterEach: func(s *runtime.State) error { - r.Lock() - step, ok := steps[s.Step.Metadata.Name] - if ok { - step.Status = core.StatusPassing - step.Stopped = time.Now().Unix() - step.ExitCode = s.State.ExitCode - if s.State.ExitCode != 0 && s.State.ExitCode != 78 { - step.Status = core.StatusFailing - } - } - stepClone := new(core.Step) - *stepClone = *step - r.Unlock() - - err := r.Manager.After(ctx, stepClone) - if err != nil { - return err - } - - r.Lock() - step.Version = stepClone.Version - r.Unlock() - - return nil - }, - - GotLine: func(s *runtime.State, line *runtime.Line) error { - r.Lock() - step, ok := steps[s.Step.Metadata.Name] - r.Unlock() - if !ok { - // TODO log error - return nil - } - return r.Manager.Write(ctx, step.ID, convertLine(line)) - }, - - GotLogs: func(s *runtime.State, lines []*runtime.Line) error { - r.Lock() - step, ok := steps[s.Step.Metadata.Name] - r.Unlock() - if !ok { - // TODO log error - return nil - } - raw, _ := json.Marshal( - convertLines(lines), - ) - return r.Manager.UploadBytes(ctx, step.ID, raw) - }, - } - - runner := runtime.New( - runtime.WithEngine(r.Engine), - runtime.WithConfig(ir), - runtime.WithHooks(hooks), - ) - - m.Stage.Status = core.StatusRunning - m.Stage.Started = time.Now().Unix() - m.Stage.Machine = r.Machine - err = r.Manager.BeforeAll(ctx, m.Stage) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("runner: cannot initialize pipeline") - return r.handleError(ctx, m.Stage, err) - } - - timeout, cancel := context.WithTimeout(ctx, time.Duration(m.Repo.Timeout)*time.Minute) - defer cancel() - - logger.Infoln("runner: start execution") - - err = runner.Run(timeout) - if err != nil && err != runtime.ErrInterrupt { - logger = logger.WithError(err) - logger.Infoln("runner: execution failed") - return r.handleError(ctx, m.Stage, err) - } - logger = logger.WithError(err) - logger.Infoln("runner: execution complete") - - m.Stage.Status = core.StatusPassing - m.Stage.Stopped = time.Now().Unix() - for _, step := range m.Stage.Steps { - if step.Status == core.StatusPending { - step.Status = core.StatusSkipped - } - if step.Status == core.StatusRunning { - step.Status = core.StatusPassing - step.Stopped = time.Now().Unix() - } - } - - return r.Manager.AfterAll(ctx, m.Stage) -} - -// Start starts N build runner processes. Each process polls -// the server for pending builds to execute. -func (r *Runner) Start(ctx context.Context, n int) error { - var g errgroup.Group - for i := 0; i < n; i++ { - g.Go(func() error { - return r.start(ctx) - }) - } - return g.Wait() -} - -func (r *Runner) start(ctx context.Context) error { - for { - select { - case <-ctx.Done(): - return nil - default: - // This error is ignored on purpose. The system - // should not exit the runner on error. The run - // function logs all errors, which should be enough - // to surface potential issues to an administrator. - r.poll(ctx) - } - } -} - -func (r *Runner) poll(ctx context.Context) error { - logger := logrus.WithFields( - logrus.Fields{ - "machine": r.Machine, - "os": r.OS, - "arch": r.Arch, - }, - ) - - logger.Debugln("runner: polling queue") - p, err := r.Manager.Request(ctx, &manager.Request{ - Kind: "pipeline", - Type: "docker", - OS: r.OS, - Arch: r.Arch, - Kernel: r.Kernel, - Variant: r.Variant, - Labels: r.Labels, - }) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("runner: cannot get queue item") - return err - } - if p == nil || p.ID == 0 { - return nil - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err = r.Manager.Accept(ctx, p.ID, r.Machine) - if err == db.ErrOptimisticLock { - return nil - } else if err != nil { - logger.WithError(err). - WithFields( - logrus.Fields{ - "stage-id": p.ID, - "build-id": p.BuildID, - "repo-id": p.RepoID, - }).Warnln("runner: cannot ack stage") - return err - } - - go func() { - logger.Debugln("runner: watch for cancel signal") - done, _ := r.Manager.Watch(ctx, p.BuildID) - if done { - cancel() - logger.Debugln("runner: received cancel signal") - } else { - logger.Debugln("runner: done listening for cancel signals") - } - }() - - return r.Run(ctx, p.ID) -} diff --git a/operator/runner/runner_test.go b/operator/runner/runner_test.go deleted file mode 100644 index d58755fe99..0000000000 --- a/operator/runner/runner_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package runner - -import ( - "io/ioutil" - - "github.com/sirupsen/logrus" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} diff --git a/operator/runner/secrets.go b/operator/runner/secrets.go deleted file mode 100644 index d373ef9bb7..0000000000 --- a/operator/runner/secrets.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package runner - -import "github.com/drone/drone/core" - -func toSecretMap(secrets []*core.Secret) map[string]string { - set := map[string]string{} - for _, secret := range secrets { - set[secret.Name] = secret.Data - } - return set -} diff --git a/plugin/admission/account.go b/plugin/admission/account.go deleted file mode 100644 index 0461523499..0000000000 --- a/plugin/admission/account.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package admission - -import ( - "context" - "errors" - "strings" - - "github.com/drone/drone/core" -) - -// ErrMembership is returned when attempting to create a new -// user account for a user that is not a member of an approved -// organization. -var ErrMembership = errors.New("User must be a member of an approved organization") - -// Membership limits user access by organization membership. -func Membership(service core.OrganizationService, accounts []string) core.AdmissionService { - lookup := map[string]struct{}{} - for _, account := range accounts { - account = strings.TrimSpace(account) - account = strings.ToLower(account) - lookup[account] = struct{}{} - } - return &membership{service: service, account: lookup} -} - -type membership struct { - service core.OrganizationService - account map[string]struct{} -} - -func (s *membership) Admit(ctx context.Context, user *core.User) error { - // this admission policy is only enforced for - // new users. Existing users are always admitted. - if user.ID != 0 { - return nil - } - - // if the membership whitelist is empty assume the system - // is open admission. - if len(s.account) == 0 { - return nil - } - // if the username is in the whitelist when can admin - // the user without making an API call to fetch the - // organization list. - _, ok := s.account[strings.ToLower(user.Login)] - if ok { - return nil - } - // make an API call to retrive the list of organizations - // to which the user belongs. - orgs, err := s.service.List(ctx, user) - if err != nil { - return err - } - // if the user is a member of an organization in the - // account whitelist we can admit the user. - for _, org := range orgs { - _, ok := s.account[strings.ToLower(org.Name)] - if ok { - return nil - } - } - // else deny access - return ErrMembership -} diff --git a/plugin/admission/account_oss.go b/plugin/admission/account_oss.go deleted file mode 100644 index ec27eb7d0b..0000000000 --- a/plugin/admission/account_oss.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package admission - -import "github.com/drone/drone/core" - -// Membership is a no-op admission controller -func Membership(core.OrganizationService, []string) core.AdmissionService { - return new(noop) -} diff --git a/plugin/admission/account_test.go b/plugin/admission/account_test.go deleted file mode 100644 index e80cac3071..0000000000 --- a/plugin/admission/account_test.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package admission - -import ( - "context" - "errors" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -var noContext = context.TODO() - -func TestMembership_MatchOrg(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - } - - orgs := mock.NewMockOrganizationService(controller) - orgs.EXPECT().List(gomock.Any(), dummyUser).Return([]*core.Organization{ - {Name: "bar"}, {Name: "baz"}, {Name: "GiThUb"}, - }, nil) - - service := Membership(orgs, []string{"GithuB"}) - err := service.Admit(noContext, dummyUser) - if err != nil { - t.Error(err) - } -} - -func TestOrganization_MatchUser(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - } - - service := Membership(nil, []string{"octocat"}) - err := service.Admit(noContext, dummyUser) - if err != nil { - t.Error(err) - } -} - -func TestOrganization_MembershipError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - } - - orgs := mock.NewMockOrganizationService(controller) - orgs.EXPECT().List(gomock.Any(), dummyUser).Return([]*core.Organization{ - {Name: "foo"}, {Name: "bar"}, - }, nil) - - service := Membership(orgs, []string{"baz"}) - err := service.Admit(noContext, dummyUser) - if err != ErrMembership { - t.Errorf("Expect ErrMembership") - } -} - -func TestOrganization_OrganizationListError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - } - - orgs := mock.NewMockOrganizationService(controller) - orgs.EXPECT().List(gomock.Any(), dummyUser).Return(nil, errors.New("")) - - service := Membership(orgs, []string{"GithuB"}) - err := service.Admit(noContext, dummyUser) - if err == nil { - t.Errorf("Expected error") - } -} - -func TestOrganization_EmptyWhitelist(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - } - - service := Membership(nil, []string{}) - err := service.Admit(noContext, dummyUser) - if err != nil { - t.Error(err) - } -} diff --git a/plugin/admission/combine.go b/plugin/admission/combine.go deleted file mode 100644 index 608971bb02..0000000000 --- a/plugin/admission/combine.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package admission - -import ( - "context" - - "github.com/drone/drone/core" -) - -// Combine combines admission services. -func Combine(service ...core.AdmissionService) core.AdmissionService { - return &combined{services: service} -} - -type combined struct { - services []core.AdmissionService -} - -func (s *combined) Admit(ctx context.Context, user *core.User) error { - for _, service := range s.services { - if err := service.Admit(ctx, user); err != nil { - return err - } - } - return nil -} diff --git a/plugin/admission/combine_test.go b/plugin/admission/combine_test.go deleted file mode 100644 index b043c1c225..0000000000 --- a/plugin/admission/combine_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package admission - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/golang/mock/gomock" -) - -func TestCombineAdmit(t *testing.T) { - user := &core.User{Login: "octocat"} - err := Combine( - Membership(nil, nil), - Membership(nil, nil), - ).Admit(noContext, user) - if err != nil { - t.Error(err) - } -} - -func TestCombineAdmit_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{Login: "octocat"} - - orgs := mock.NewMockOrganizationService(controller) - orgs.EXPECT().List(gomock.Any(), user).Return(nil, nil) - - service1 := Membership(orgs, nil) - service2 := Membership(orgs, []string{"github"}) - err := Combine(service1, service2).Admit(noContext, user) - if err != ErrMembership { - t.Errorf("expect ErrMembership") - } -} diff --git a/plugin/admission/external.go b/plugin/admission/external.go deleted file mode 100644 index bade9c568f..0000000000 --- a/plugin/admission/external.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package admission - -import ( - "context" - "time" - - "github.com/drone/drone-go/drone" - "github.com/drone/drone-go/plugin/admission" - "github.com/drone/drone/core" -) - -// External returns a new external Admission controller. -func External(endpoint, secret string, skipVerify bool) core.AdmissionService { - return &external{ - endpoint: endpoint, - secret: secret, - skipVerify: skipVerify, - } -} - -type external struct { - endpoint string - secret string - skipVerify bool -} - -func (c *external) Admit(ctx context.Context, user *core.User) error { - if c.endpoint == "" { - return nil - } - - // include a timeout to prevent an API call from - // hanging the build process indefinitely. The - // external service must return a request within - // one minute. - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - - req := &admission.Request{ - Event: admission.EventLogin, - User: toUser(user), - } - if user.ID == 0 { - req.Event = admission.EventRegister - } - client := admission.Client(c.endpoint, c.secret, c.skipVerify) - result, err := client.Admit(ctx, req) - if result != nil { - user.Admin = result.Admin - } - return err -} - -func toUser(from *core.User) drone.User { - return drone.User{ - ID: from.ID, - Login: from.Login, - Email: from.Email, - Avatar: from.Avatar, - Active: from.Active, - Admin: from.Admin, - Machine: from.Machine, - Syncing: from.Syncing, - Synced: from.Synced, - Created: from.Created, - Updated: from.Updated, - LastLogin: from.LastLogin, - } -} diff --git a/plugin/admission/external_oss.go b/plugin/admission/external_oss.go deleted file mode 100644 index 8497ebb92b..0000000000 --- a/plugin/admission/external_oss.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package admission - -import "github.com/drone/drone/core" - -// External is a no-op admission controller -func External(string, string, bool) core.AdmissionService { - return new(noop) -} diff --git a/plugin/admission/external_test.go b/plugin/admission/external_test.go deleted file mode 100644 index 5af7309a91..0000000000 --- a/plugin/admission/external_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package admission diff --git a/plugin/admission/nobot.go b/plugin/admission/nobot.go deleted file mode 100644 index 62eb3636b2..0000000000 --- a/plugin/admission/nobot.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package admission - -import ( - "context" - "errors" - "time" - - "github.com/drone/drone/core" -) - -// ErrCannotVerify is returned when attempting to verify the -// user is a human being. -var ErrCannotVerify = errors.New("Cannot verify user authenticity") - -// Nobot enforces an admission policy that restricts access to -// users accounts that were recently created and may be bots. -// The policy expects the source control management system will -// identify and remove the bot accounts before they would be -// eligible to use the system. -func Nobot(service core.UserService, age time.Duration) core.AdmissionService { - return &nobot{service: service, age: age} -} - -type nobot struct { - age time.Duration - service core.UserService -} - -func (s *nobot) Admit(ctx context.Context, user *core.User) error { - // this admission policy is only enforced for - // new users. Existing users are always admitted. - if user.ID != 0 { - return nil - } - - // if the minimum required age is not specified the check - // is skipped. - if s.age == 0 { - return nil - } - account, err := s.service.Find(ctx, user.Token, user.Refresh) - if err != nil { - return err - } - if account.Created == 0 { - return nil - } - now := time.Now() - if time.Unix(account.Created, 0).Add(s.age).After(now) { - return ErrCannotVerify - } - return nil -} diff --git a/plugin/admission/nobot_oss.go b/plugin/admission/nobot_oss.go deleted file mode 100644 index 0f06b20e9b..0000000000 --- a/plugin/admission/nobot_oss.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package admission - -import ( - "time" - - "github.com/drone/drone/core" -) - -// Nobot is a no-op admission controller -func Nobot(core.UserService, time.Duration) core.AdmissionService { - return new(noop) -} diff --git a/plugin/admission/nobot_test.go b/plugin/admission/nobot_test.go deleted file mode 100644 index bb0ac2f3f1..0000000000 --- a/plugin/admission/nobot_test.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package admission - -import ( - "errors" - "testing" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/golang/mock/gomock" -) - -func TestNobot(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - localUser := &core.User{Login: "octocat"} - remoteUser := &core.User{Login: "octocat", Created: time.Now().Unix() - 120} // 120 seconds - users := mock.NewMockUserService(controller) - users.EXPECT().Find(gomock.Any(), gomock.Any(), gomock.Any()).Return(remoteUser, nil) - - admission := Nobot(users, time.Minute) // 60 seconds - err := admission.Admit(noContext, localUser) - if err != nil { - t.Error(err) - } -} - -func TestNobot_AccountTooNew(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - localUser := &core.User{Login: "octocat"} - remoteUser := &core.User{Login: "octocat", Created: time.Now().Unix()} - users := mock.NewMockUserService(controller) - users.EXPECT().Find(gomock.Any(), gomock.Any(), gomock.Any()).Return(remoteUser, nil) - - admission := Nobot(users, time.Hour) - err := admission.Admit(noContext, localUser) - if err != ErrCannotVerify { - t.Errorf("Expect ErrCannotVerify error") - } -} - -func TestNobot_ZeroDate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - localUser := &core.User{Login: "octocat"} - remoteUser := &core.User{Login: "octocat", Created: 0} - users := mock.NewMockUserService(controller) - users.EXPECT().Find(gomock.Any(), gomock.Any(), gomock.Any()).Return(remoteUser, nil) - - admission := Nobot(users, time.Minute) - err := admission.Admit(noContext, localUser) - if err != nil { - t.Error(err) - } -} - -func TestNobot_RemoteError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - want := errors.New("") - users := mock.NewMockUserService(controller) - users.EXPECT().Find(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, want) - - admission := Nobot(users, time.Minute) - got := admission.Admit(noContext, new(core.User)) - if got != want { - t.Errorf("Expect error from source control management system returned") - } -} - -func TestNobot_SkipCheck(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - dummyUser := &core.User{ - Login: "octocat", - } - - admission := Nobot(nil, 0) - err := admission.Admit(noContext, dummyUser) - if err != nil { - t.Error(err) - } -} diff --git a/plugin/admission/noop.go b/plugin/admission/noop.go deleted file mode 100644 index cc60c856b1..0000000000 --- a/plugin/admission/noop.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package admission - -import ( - "context" - - "github.com/drone/drone/core" -) - -// noop is a stub admission controller. -type noop struct{} - -func (noop) Admit(context.Context, *core.User) error { - return nil -} diff --git a/plugin/admission/open.go b/plugin/admission/open.go deleted file mode 100644 index 80acf47893..0000000000 --- a/plugin/admission/open.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package admission - -import ( - "context" - "errors" - - "github.com/drone/drone/core" -) - -// ErrClosed is returned when attempting to create a new -// user account and admissions are closed. -var ErrClosed = errors.New("User registration is disabled") - -// Open enforces an open admission policy by default unless -// disabled. -func Open(disabled bool) core.AdmissionService { - return &closed{disabled: disabled} -} - -type closed struct { - disabled bool -} - -func (s *closed) Admit(ctx context.Context, user *core.User) error { - // this admission policy is only enforced for - // new users. Existing users are always admitted. - if user.ID != 0 { - return nil - } - - if s.disabled { - return ErrClosed - } - return nil -} diff --git a/plugin/admission/open_test.go b/plugin/admission/open_test.go deleted file mode 100644 index 1724854476..0000000000 --- a/plugin/admission/open_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package admission - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/golang/mock/gomock" -) - -func TestOpen(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{Login: "octocat"} - err := Open(false).Admit(noContext, user) - if err != nil { - t.Error(err) - } - - err = Open(true).Admit(noContext, user) - if err == nil { - t.Errorf("Expect error when open admission is closed") - } - - user.ID = 1 - err = Open(true).Admit(noContext, user) - if err != nil { - t.Error(err) - } -} diff --git a/plugin/config/combine.go b/plugin/config/combine.go deleted file mode 100644 index da506ee77c..0000000000 --- a/plugin/config/combine.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "context" - "errors" - - "github.com/drone/drone/core" -) - -// error returned when no configured found. -var errNotFound = errors.New("configuration: not found") - -// Combine combines the config services, allowing the system -// to source pipeline configuration from multiple sources. -func Combine(services ...core.ConfigService) core.ConfigService { - return &combined{services} -} - -type combined struct { - sources []core.ConfigService -} - -func (c *combined) Find(ctx context.Context, req *core.ConfigArgs) (*core.Config, error) { - for _, source := range c.sources { - config, err := source.Find(ctx, req) - if err != nil { - return nil, err - } - if config == nil { - continue - } - if config.Data == "" { - continue - } - return config, nil - } - return nil, errNotFound -} diff --git a/plugin/config/combine_test.go b/plugin/config/combine_test.go deleted file mode 100644 index 1d35280476..0000000000 --- a/plugin/config/combine_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package config - -import ( - "errors" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -func TestCombine(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConfigArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - } - - resp := &core.Config{Data: string(mockFile)} - - service := mock.NewMockConfigService(controller) - service.EXPECT().Find(noContext, args).Return(resp, nil) - - result, err := Combine(service).Find(noContext, args) - if err != nil { - t.Error(err) - return - } - - if result.Data != string(resp.Data) { - t.Errorf("unexpected file contents") - } -} - -func TestCombineErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - resp := errors.New("") - service := mock.NewMockConfigService(controller) - service.EXPECT().Find(noContext, nil).Return(nil, resp) - - _, err := Combine(service).Find(noContext, nil) - if err != resp { - t.Errorf("expected config service error") - } -} - -func TestCombineNoConfig(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConfigArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - } - - resp := &core.Config{Data: string(mockFile)} - - service1 := mock.NewMockConfigService(controller) - service1.EXPECT().Find(noContext, args).Return(nil, nil) - - service2 := mock.NewMockConfigService(controller) - service2.EXPECT().Find(noContext, args).Return(resp, nil) - - result, err := Combine(service1, service2).Find(noContext, args) - if err != nil { - t.Error(err) - return - } - - if result.Data != string(resp.Data) { - t.Errorf("unexpected file contents") - } -} - -func TestCombineEmptyConfig(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConfigArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - } - - resp1 := &core.Config{} - resp2 := &core.Config{Data: string(mockFile)} - - service1 := mock.NewMockConfigService(controller) - service1.EXPECT().Find(noContext, args).Return(resp1, nil) - - service2 := mock.NewMockConfigService(controller) - service2.EXPECT().Find(noContext, args).Return(resp2, nil) - - result, err := Combine(service1, service2).Find(noContext, args) - if err != nil { - t.Error(err) - return - } - - if result.Data != string(resp2.Data) { - t.Errorf("unexpected file contents") - } -} - -func TestCombineNoConfigErr(t *testing.T) { - // args := &core.ConfigArgs{ - // User: &core.User{Login: "octocat"}, - // Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - // Build: &core.Build{After: "6d144de7"}, - // } - - service := Combine() - _, err := service.Find(noContext, nil) - if err != errNotFound { - t.Errorf("Expect not found error") - } -} diff --git a/plugin/config/global.go b/plugin/config/global.go deleted file mode 100644 index 73d05518d1..0000000000 --- a/plugin/config/global.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package config - -import ( - "context" - "time" - - "github.com/drone/drone-go/drone" - "github.com/drone/drone-go/plugin/config" - - "github.com/drone/drone/core" -) - -// Global returns a configuration service that fetches the yaml -// configuration from a remote endpoint. -func Global(endpoint, signer string, skipVerify bool, timeout time.Duration) core.ConfigService { - if endpoint == "" { - return new(global) - } - return &global{ - client: config.Client( - endpoint, - signer, - skipVerify, - ), - timeout: timeout, - } -} - -type global struct { - client config.Plugin - timeout time.Duration -} - -func (g *global) Find(ctx context.Context, in *core.ConfigArgs) (*core.Config, error) { - if g.client == nil { - return nil, nil - } - // include a timeout to prevent an API call from - // hanging the build process indefinitely. The - // external service must return a response within - // the configured timeout (default 1m). - ctx, cancel := context.WithTimeout(ctx, g.timeout) - defer cancel() - - req := &config.Request{ - Repo: toRepo(in.Repo), - Build: toBuild(in.Build), - Token: drone.Token{ - Access: in.User.Token, - Refresh: in.User.Refresh, - }, - } - - res, err := g.client.Find(ctx, req) - if err != nil { - return nil, err - } - - // if no error is returned and the secret is empty, - // this indicates the client returned No Content, - // and we should exit with no secret, but no error. - if res.Data == "" { - return nil, nil - } - - return &core.Config{ - Kind: res.Kind, - Data: res.Data, - }, nil -} - -func toRepo(from *core.Repository) drone.Repo { - return drone.Repo{ - ID: from.ID, - UID: from.UID, - UserID: from.UserID, - Namespace: from.Namespace, - Name: from.Name, - Slug: from.Slug, - SCM: from.SCM, - HTTPURL: from.HTTPURL, - SSHURL: from.SSHURL, - Link: from.Link, - Branch: from.Branch, - Private: from.Private, - Visibility: from.Visibility, - Active: from.Active, - Config: from.Config, - Trusted: from.Trusted, - Protected: from.Protected, - Timeout: from.Timeout, - } -} - -func toBuild(from *core.Build) drone.Build { - return drone.Build{ - ID: from.ID, - RepoID: from.RepoID, - Trigger: from.Trigger, - Number: from.Number, - Parent: from.Parent, - Status: from.Status, - Error: from.Error, - Event: from.Event, - Action: from.Action, - Link: from.Link, - Timestamp: from.Timestamp, - Title: from.Title, - Message: from.Message, - Before: from.Before, - After: from.After, - Ref: from.Ref, - Fork: from.Fork, - Source: from.Source, - Target: from.Target, - Author: from.Author, - AuthorName: from.AuthorName, - AuthorEmail: from.AuthorEmail, - AuthorAvatar: from.AuthorAvatar, - Sender: from.Sender, - Params: from.Params, - Deploy: from.Deploy, - Started: from.Started, - Finished: from.Finished, - Created: from.Created, - Updated: from.Updated, - Version: from.Version, - } -} diff --git a/plugin/config/global_oss.go b/plugin/config/global_oss.go deleted file mode 100644 index b75a2b3a92..0000000000 --- a/plugin/config/global_oss.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package config - -import ( - "context" - "time" - - "github.com/drone/drone/core" -) - -// Global returns a no-op configuration service. -func Global(string, string, bool, time.Duration) core.ConfigService { - return new(noop) -} - -type noop struct{} - -func (noop) Find(context.Context, *core.ConfigArgs) (*core.Config, error) { - return nil, nil -} diff --git a/plugin/config/global_test.go b/plugin/config/global_test.go deleted file mode 100644 index 6b2d858698..0000000000 --- a/plugin/config/global_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package config - -import ( - "testing" - "time" - - "github.com/drone/drone/core" - "github.com/h2non/gock" -) - -func TestGlobal(t *testing.T) { - defer gock.Off() - - gock.New("https://company.com"). - Post("/config"). - MatchHeader("Accept", "application/vnd.drone.config.v1\\+json"). - MatchHeader("Accept-Encoding", "identity"). - MatchHeader("Content-Type", "application/json"). - Reply(200). - BodyString(`{"data": "{ kind: pipeline, name: default }"}`). - Done() - - args := &core.ConfigArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - } - - service := Global("https://company.com/config", "GMEuUHQfmrMRsseWxi9YlIeBtn9lm6im", - false, time.Minute) - result, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - - if result.Data != "{ kind: pipeline, name: default }" { - t.Errorf("unexpected file contents") - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - return - } -} - -func TestGlobalErr(t *testing.T) { - defer gock.Off() - - gock.New("https://company.com"). - Post("/config"). - MatchHeader("Accept", "application/vnd.drone.config.v1\\+json"). - MatchHeader("Accept-Encoding", "identity"). - MatchHeader("Content-Type", "application/json"). - Reply(404). - Done() - - args := &core.ConfigArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - } - - service := Global("https://company.com/config", "GMEuUHQfmrMRsseWxi9YlIeBtn9lm6im", - false, time.Minute) - _, err := service.Find(noContext, args) - if err == nil { - t.Errorf("Expect http.Response error") - } else if err.Error() != "Not Found" { - t.Errorf("Expect Not Found error") - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestGlobalEmpty(t *testing.T) { - defer gock.Off() - - gock.New("https://company.com"). - Post("/config"). - MatchHeader("Accept", "application/vnd.drone.config.v1\\+json"). - MatchHeader("Accept-Encoding", "identity"). - MatchHeader("Content-Type", "application/json"). - Reply(204). - Done() - - args := &core.ConfigArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - } - - service := Global("https://company.com/config", "GMEuUHQfmrMRsseWxi9YlIeBtn9lm6im", - false, time.Minute) - result, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - if result != nil { - t.Errorf("Expect empty data") - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - return - } -} - -func TestGlobalDisabled(t *testing.T) { - res, err := Global("", "", false, time.Minute).Find(noContext, nil) - if err != nil { - t.Error(err) - } - if res != nil { - t.Errorf("expect nil config when disabled") - } -} diff --git a/plugin/config/jsonnet.go b/plugin/config/jsonnet.go deleted file mode 100644 index b55cf835ac..0000000000 --- a/plugin/config/jsonnet.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package config - -import ( - "bytes" - "context" - "strings" - - "github.com/drone/drone/core" - - "github.com/google/go-jsonnet" -) - -// Jsonnet returns a configuration service that fetches the -// jsonnet file directly from the source code management (scm) -// system and converts to a yaml file. -func Jsonnet(service core.FileService, enabled bool) core.ConfigService { - return &jsonnetPlugin{ - enabled: enabled, - repos: &repo{files: service}, - } -} - -type jsonnetPlugin struct { - enabled bool - repos *repo -} - -func (p *jsonnetPlugin) Find(ctx context.Context, req *core.ConfigArgs) (*core.Config, error) { - if p.enabled == false { - return nil, nil - } - - // if the file extension is not jsonnet we can - // skip this plugin by returning zero values. - if strings.HasSuffix(req.Repo.Config, ".jsonnet") == false { - return nil, nil - } - - // get the file contents. - config, err := p.repos.Find(ctx, req) - if err != nil { - return nil, err - } - - // TODO(bradrydzewski) temporarily disable file imports - // TODO(bradrydzewski) handle object vs array output - - // create the jsonnet vm - vm := jsonnet.MakeVM() - vm.MaxStack = 500 - vm.StringOutput = false - vm.ErrorFormatter.SetMaxStackTraceSize(20) - - // convert the jsonnet file to yaml - buf := new(bytes.Buffer) - docs, err := vm.EvaluateSnippetStream(req.Repo.Config, config.Data) - if err != nil { - return nil, err - } - - // the jsonnet vm returns a stream of yaml documents - // that need to be combined into a single yaml file. - for _, doc := range docs { - buf.WriteString("---") - buf.WriteString("\n") - buf.WriteString(doc) - } - - config.Data = buf.String() - return config, nil -} diff --git a/plugin/config/jsonnet_oss.go b/plugin/config/jsonnet_oss.go deleted file mode 100644 index dc38165deb..0000000000 --- a/plugin/config/jsonnet_oss.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package config - -import "github.com/drone/drone/core" - -// Jsonnet returns a no-op configuration service. -func Jsonnet(service core.FileService, enabled bool) core.ConfigService { - return new(noop) -} diff --git a/plugin/config/jsonnet_test.go b/plugin/config/jsonnet_test.go deleted file mode 100644 index 9d26a590cf..0000000000 --- a/plugin/config/jsonnet_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package config diff --git a/plugin/config/memoize.go b/plugin/config/memoize.go deleted file mode 100644 index f46520324e..0000000000 --- a/plugin/config/memoize.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package config - -import ( - "context" - "fmt" - - "github.com/drone/drone/core" - - lru "github.com/hashicorp/golang-lru" - "github.com/sirupsen/logrus" -) - -// cache key pattern used in the cache, comprised of the -// repository slug and commit sha. -const keyf = "%d|%d|%s|%s|%s|%s|%s" - -// Memoize caches the conversion results for subsequent calls. -// This micro-optimization is intended for multi-pipeline -// projects that would otherwise covert the file for each -// pipeline execution. -func Memoize(base core.ConfigService) core.ConfigService { - // simple cache prevents the same yaml file from being - // requested multiple times in a short period. - cache, _ := lru.New(10) - return &memoize{base: base, cache: cache} -} - -type memoize struct { - base core.ConfigService - cache *lru.Cache -} - -func (c *memoize) Find(ctx context.Context, req *core.ConfigArgs) (*core.Config, error) { - // this is a minor optimization that prevents caching if the - // base converter is a global config service and is disabled. - if global, ok := c.base.(*global); ok == true && global.client == nil { - return nil, nil - } - - // generate the key used to cache the converted file. - key := fmt.Sprintf(keyf, - req.Repo.ID, - req.Build.Created, - req.Build.Event, - req.Build.Action, - req.Build.Ref, - req.Build.After, - req.Repo.Config, - ) - - logger := logrus.WithField("repo", req.Repo.Slug). - WithField("build", req.Build.Event). - WithField("action", req.Build.Action). - WithField("ref", req.Build.Ref). - WithField("rev", req.Build.After). - WithField("config", req.Repo.Config) - - logger.Trace("extension: configuration: check cache") - - // check the cache for the file and return if exists. - cached, ok := c.cache.Get(key) - if ok { - logger.Trace("extension: configuration: cache hit") - return cached.(*core.Config), nil - } - - logger.Trace("extension: configuration: cache miss") - - // else find the configuration file. - config, err := c.base.Find(ctx, req) - if err != nil { - return nil, err - } - - if config == nil { - return nil, nil - } - if config.Data == "" { - return nil, nil - } - - // if the configuration file was retrieved - // it is temporarily cached. Note that we do - // not cache if the commit sha is empty (gogs). - if req.Build.After != "" { - c.cache.Add(key, config) - } - - return config, nil -} diff --git a/plugin/config/memoize_oss.go b/plugin/config/memoize_oss.go deleted file mode 100644 index d901b7bc3c..0000000000 --- a/plugin/config/memoize_oss.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package config - -import ( - "github.com/drone/drone/core" -) - -// Memoize caches the conversion results for subsequent calls. -// This micro-optimization is intended for multi-pipeline -// projects that would otherwise covert the file for each -// pipeline execution. -func Memoize(base core.ConfigService) core.ConfigService { - return new(noop) -} diff --git a/plugin/config/memoize_test.go b/plugin/config/memoize_test.go deleted file mode 100644 index 74975776f0..0000000000 --- a/plugin/config/memoize_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package config - -import ( - "errors" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -func TestMemoize(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - conf := &core.Config{Data: "{kind: pipeline, type: docker, steps: []}"} - args := &core.ConfigArgs{ - Build: &core.Build{After: "3950521325d4744760a96c18e3d0c67d86495af3"}, - Repo: &core.Repository{ID: 42}, - Config: conf, - } - - base := mock.NewMockConfigService(controller) - base.EXPECT().Find(gomock.Any(), gomock.Any()).Return(args.Config, nil) - - service := Memoize(base).(*memoize) - _, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - - if got, want := service.cache.Len(), 1; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } - - args.Config = nil // set to nil to prove we get the cached value - res, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - if res != conf { - t.Errorf("Expect result from cache") - } - - if got, want := service.cache.Len(), 1; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } -} - -func TestMemoize_Tag(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConfigArgs{ - Build: &core.Build{Ref: "refs/tags/v1.0.0"}, - Repo: &core.Repository{ID: 42}, - Config: &core.Config{Data: "{kind: pipeline, type: docker, steps: []}"}, - } - - base := mock.NewMockConfigService(controller) - base.EXPECT().Find(gomock.Any(), gomock.Any()).Return(args.Config, nil) - - service := Memoize(base).(*memoize) - res, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - if res != args.Config { - t.Errorf("Expect result from cache") - } -} - -func TestMemoize_Empty(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConfigArgs{ - Build: &core.Build{After: "3950521325d4744760a96c18e3d0c67d86495af3"}, - Repo: &core.Repository{ID: 42}, - Config: &core.Config{Data: ""}, // empty - } - - base := mock.NewMockConfigService(controller) - base.EXPECT().Find(gomock.Any(), gomock.Any()).Return(args.Config, nil) - - service := Memoize(base).(*memoize) - res, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - if res != nil { - t.Errorf("Expect nil response") - } - if got, want := service.cache.Len(), 0; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } -} - -func TestMemoize_Nil(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConfigArgs{ - Build: &core.Build{After: "3950521325d4744760a96c18e3d0c67d86495af3"}, - Repo: &core.Repository{ID: 42}, - Config: nil, - } - - base := mock.NewMockConfigService(controller) - base.EXPECT().Find(gomock.Any(), gomock.Any()).Return(args.Config, nil) - - service := Memoize(base).(*memoize) - res, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - if res != nil { - t.Errorf("Expect nil response") - } - if got, want := service.cache.Len(), 0; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } -} - -func TestMemoize_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConfigArgs{ - Build: &core.Build{After: "3950521325d4744760a96c18e3d0c67d86495af3"}, - Repo: &core.Repository{ID: 42}, - } - - want := errors.New("not found") - base := mock.NewMockConfigService(controller) - base.EXPECT().Find(gomock.Any(), gomock.Any()).Return(nil, want) - - service := Memoize(base).(*memoize) - _, err := service.Find(noContext, args) - if err == nil { - t.Errorf("Expect error from base returned to caller") - return - } - if got, want := service.cache.Len(), 0; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } -} diff --git a/plugin/config/repo.go b/plugin/config/repo.go deleted file mode 100644 index d6e8eb0b51..0000000000 --- a/plugin/config/repo.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config - -import ( - "context" - - "github.com/drone/drone/core" -) - -// Repository returns a configuration service that fetches the yaml -// directly from the source code management (scm) system. -func Repository(service core.FileService) core.ConfigService { - return &repo{files: service} -} - -type repo struct { - files core.FileService -} - -func (r *repo) Find(ctx context.Context, req *core.ConfigArgs) (*core.Config, error) { - raw, err := r.files.Find(ctx, req.User, req.Repo.Slug, req.Build.After, req.Build.Ref, req.Repo.Config) - if err != nil { - return nil, err - } - return &core.Config{ - Data: string(raw.Data), - }, err -} diff --git a/plugin/config/repo_test.go b/plugin/config/repo_test.go deleted file mode 100644 index 9b89705394..0000000000 --- a/plugin/config/repo_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package config - -import ( - "context" - "errors" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -var noContext = context.TODO() - -var mockFile = []byte(` -kind: pipeline -name: default - -steps: [] -`) - -func TestRepository(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConfigArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - Config: nil, - } - - resp := &core.File{Data: mockFile} - - files := mock.NewMockFileService(controller) - files.EXPECT().Find(noContext, args.User, args.Repo.Slug, args.Build.After, args.Build.Ref, args.Repo.Config).Return(resp, nil) - - service := Repository(files) - result, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - } - - if result.Data != string(resp.Data) { - t.Errorf("unexpected file contents") - } -} - -func TestRepositoryErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConfigArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - Config: nil, - } - - resp := errors.New("") - - files := mock.NewMockFileService(controller) - files.EXPECT().Find(noContext, args.User, args.Repo.Slug, args.Build.After, args.Build.Ref, args.Repo.Config).Return(nil, resp) - - service := Repository(files) - _, err := service.Find(noContext, args) - if err != resp { - t.Errorf("expect error returned from file service") - } -} diff --git a/plugin/converter/combine.go b/plugin/converter/combine.go deleted file mode 100644 index 05144807d8..0000000000 --- a/plugin/converter/combine.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package converter - -import ( - "context" - - "github.com/drone/drone/core" -) - -// Combine combines the conversion services, provision support -// for multiple conversion utilities. -func Combine(multi bool, services ...core.ConvertService) core.ConvertService { - return &combined{multi: multi, sources: services} -} - -type combined struct { - sources []core.ConvertService - - // this feature flag can be removed once we solve for - // https://github.com/harness/drone/pull/2994#issuecomment-795955312 - multi bool -} - -func (c *combined) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) { - for _, source := range c.sources { - config, err := source.Convert(ctx, req) - if err != nil { - return nil, err - } - if config == nil { - continue - } - if config.Data == "" { - continue - } - if c.multi { - req.Config = config - } else { - return config, nil - } - } - return req.Config, nil -} diff --git a/plugin/converter/combine_test.go b/plugin/converter/combine_test.go deleted file mode 100644 index 28d839ef8e..0000000000 --- a/plugin/converter/combine_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package converter - -import ( - "context" - "errors" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -var noContext = context.Background() - -var mockFile = ` -kind: pipeline -type: docker -name: testing -` - -func TestCombine(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConvertArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - Config: &core.Config{}, - } - - resp := &core.Config{Data: string(mockFile)} - - service := mock.NewMockConvertService(controller) - service.EXPECT().Convert(noContext, args).Return(resp, nil) - - result, err := Combine(false, service).Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - - if result.Data != string(resp.Data) { - t.Errorf("unexpected file contents") - } -} - -func TestCombineErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - resp := errors.New("") - service := mock.NewMockConvertService(controller) - service.EXPECT().Convert(noContext, nil).Return(nil, resp) - - _, err := Combine(false, service).Convert(noContext, nil) - if err != resp { - t.Errorf("expected convert service error") - } -} - -func TestCombineNoConfig(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConvertArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - } - - resp := &core.Config{Data: string(mockFile)} - - service1 := mock.NewMockConvertService(controller) - service1.EXPECT().Convert(noContext, args).Return(nil, nil) - - service2 := mock.NewMockConvertService(controller) - service2.EXPECT().Convert(noContext, args).Return(&core.Config{}, nil) - - service3 := mock.NewMockConvertService(controller) - service3.EXPECT().Convert(noContext, args).Return(resp, nil) - - result, err := Combine(false, service1, service2, service3).Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - - if result.Data != string(resp.Data) { - t.Errorf("unexpected file contents") - } -} - -func TestCombineEmptyConfig(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConvertArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - Config: &core.Config{Data: string(mockFile)}, - } - - service1 := mock.NewMockConvertService(controller) - service1.EXPECT().Convert(noContext, args).Return(nil, nil) - - result, err := Combine(false, service1).Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - - if result != args.Config { - t.Errorf("unexpected file contents") - } -} diff --git a/plugin/converter/jsonnet.go b/plugin/converter/jsonnet.go deleted file mode 100644 index a0060d5ef4..0000000000 --- a/plugin/converter/jsonnet.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package converter - -import ( - "context" - "strings" - - "github.com/drone/drone/core" - "github.com/drone/drone/plugin/converter/jsonnet" -) - -// TODO(bradrydzewski) handle jsonnet imports -// TODO(bradrydzewski) handle jsonnet object vs array output - -// Jsonnet returns a conversion service that converts the -// jsonnet file to a yaml file. -func Jsonnet(enabled bool, limit int, fileService core.FileService) core.ConvertService { - return &jsonnetPlugin{ - enabled: enabled, - limit: limit, - fileService: fileService, - } -} - -type jsonnetPlugin struct { - enabled bool - limit int - fileService core.FileService -} - -func (p *jsonnetPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) { - if p.enabled == false { - return nil, nil - } - - // if the file extension is not jsonnet we can - // skip this plugin by returning zero values. - if strings.HasSuffix(req.Repo.Config, ".jsonnet") == false { - return nil, nil - } - - file, err := jsonnet.Parse(req, p.fileService, p.limit, nil, nil) - - if err != nil { - return nil, err - } - return &core.Config{ - Data: file, - }, nil -} diff --git a/plugin/converter/jsonnet/jsonnet.go b/plugin/converter/jsonnet/jsonnet.go deleted file mode 100644 index ee6642cd5b..0000000000 --- a/plugin/converter/jsonnet/jsonnet.go +++ /dev/null @@ -1,202 +0,0 @@ -package jsonnet - -import ( - "bytes" - "context" - "fmt" - "path" - "strconv" - "strings" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - - "github.com/google/go-jsonnet" -) - -const repo = "repo." -const build = "build." -const param = "param." - -var noContext = context.Background() - -type importer struct { - repo *core.Repository - build *core.Build - - // jsonnet does not cache file imports and may request - // the same file multiple times. We cache the files to - // duplicate API calls. - cache map[string]jsonnet.Contents - - // limit the number of outbound requests. github limits - // the number of api requests per hour, so we should - // make sure that a single build does not abuse the api - // by importing dozens of files. - limit int - - // counts the number of outbound requests. if the count - // exceeds the limit, the importer will return errors. - count int - - fileService core.FileService - user *core.User -} - -func (i *importer) Import(importedFrom, importedPath string) (contents jsonnet.Contents, foundAt string, err error) { - if i.cache == nil { - i.cache = map[string]jsonnet.Contents{} - } - - // the import is relative to the imported from path. the - // imported path must resolve to a filepath relative to - // the root of the repository. - importedPath = path.Join( - path.Dir(importedFrom), - importedPath, - ) - - if strings.HasPrefix(importedFrom, "../") { - err = fmt.Errorf("jsonnet: cannot resolve import: %s", importedPath) - return contents, foundAt, err - } - - // if the contents exist in the cache, return the - // cached item. - if contents, ok := i.cache[importedPath]; ok { - return contents, importedPath, nil - } - - defer func() { - i.count++ - }() - - // if the import limit is exceeded log an error message. - if i.limit > 0 && i.count >= i.limit { - return contents, foundAt, errors.New("jsonnet: import limit exceeded") - } - - find, err := i.fileService.Find(noContext, i.user, i.repo.Slug, i.build.After, i.build.Ref, importedPath) - - if err != nil { - return contents, foundAt, err - } - - i.cache[importedPath] = jsonnet.MakeContents(string(find.Data)) - - return i.cache[importedPath], importedPath, err -} - -func Parse(req *core.ConvertArgs, fileService core.FileService, limit int, template *core.Template, templateData map[string]interface{}) (string, error) { - vm := jsonnet.MakeVM() - vm.MaxStack = 500 - vm.StringOutput = false - vm.ErrorFormatter.SetMaxStackTraceSize(20) - if fileService != nil && limit > 0 { - vm.Importer( - &importer{ - repo: req.Repo, - build: req.Build, - limit: limit, - user: req.User, - fileService: fileService, - }, - ) - } - - //map build/repo parameters - if req.Build != nil { - mapBuild(req.Build, vm) - } - if req.Repo != nil { - mapRepo(req.Repo, vm) - } - - var jsonnetFile string - var jsonnetFileName string - if template != nil { - jsonnetFile = template.Data - jsonnetFileName = template.Name - } else { - jsonnetFile = req.Config.Data - jsonnetFileName = req.Repo.Config - } - // map external inputs - if len(templateData) != 0 { - for k, v := range templateData { - key := fmt.Sprintf("input." + k) - val := fmt.Sprint(v) - vm.ExtVar(key, val) - } - } - - // convert the jsonnet file to yaml - buf := new(bytes.Buffer) - docs, err := vm.EvaluateAnonymousSnippetStream(jsonnetFileName, jsonnetFile) - if err != nil { - doc, err2 := vm.EvaluateAnonymousSnippet(jsonnetFileName, jsonnetFile) - if err2 != nil { - return "", err - } - docs = append(docs, doc) - } - - // the jsonnet vm returns a stream of yaml documents - // that need to be combined into a single yaml file. - for _, doc := range docs { - buf.WriteString("---") - buf.WriteString("\n") - buf.WriteString(doc) - } - - return buf.String(), nil -} - -func mapBuild(v *core.Build, vm *jsonnet.VM) { - vm.ExtVar(build+"event", v.Event) - vm.ExtVar(build+"action", v.Action) - vm.ExtVar(build+"environment", v.Deploy) - vm.ExtVar(build+"link", v.Link) - vm.ExtVar(build+"branch", v.Target) - vm.ExtVar(build+"source", v.Source) - vm.ExtVar(build+"before", v.Before) - vm.ExtVar(build+"after", v.After) - vm.ExtVar(build+"target", v.Target) - vm.ExtVar(build+"ref", v.Ref) - vm.ExtVar(build+"commit", v.After) - vm.ExtVar(build+"ref", v.Ref) - vm.ExtVar(build+"title", v.Title) - vm.ExtVar(build+"message", v.Message) - vm.ExtVar(build+"source_repo", v.Fork) - vm.ExtVar(build+"author_login", v.Author) - vm.ExtVar(build+"author_name", v.AuthorName) - vm.ExtVar(build+"author_email", v.AuthorEmail) - vm.ExtVar(build+"author_avatar", v.AuthorAvatar) - vm.ExtVar(build+"sender", v.Sender) - fromMap(v.Params, vm) -} - -func mapRepo(v *core.Repository, vm *jsonnet.VM) { - vm.ExtVar(repo+"uid", v.UID) - vm.ExtVar(repo+"name", v.Name) - vm.ExtVar(repo+"namespace", v.Namespace) - vm.ExtVar(repo+"slug", v.Slug) - vm.ExtVar(repo+"git_http_url", v.HTTPURL) - vm.ExtVar(repo+"git_ssh_url", v.SSHURL) - vm.ExtVar(repo+"link", v.Link) - vm.ExtVar(repo+"branch", v.Branch) - vm.ExtVar(repo+"config", v.Config) - vm.ExtVar(repo+"private", strconv.FormatBool(v.Private)) - vm.ExtVar(repo+"visibility", v.Visibility) - vm.ExtVar(repo+"active", strconv.FormatBool(v.Active)) - vm.ExtVar(repo+"trusted", strconv.FormatBool(v.Trusted)) - vm.ExtVar(repo+"protected", strconv.FormatBool(v.Protected)) - vm.ExtVar(repo+"ignore_forks", strconv.FormatBool(v.IgnoreForks)) - vm.ExtVar(repo+"ignore_pull_requests", strconv.FormatBool(v.IgnorePulls)) -} - -func fromMap(m map[string]string, vm *jsonnet.VM) { - for k, v := range m { - vm.ExtVar(build+param+k, v) - } -} diff --git a/plugin/converter/jsonnet/jsonnet_test.go b/plugin/converter/jsonnet/jsonnet_test.go deleted file mode 100644 index 94a661bbf1..0000000000 --- a/plugin/converter/jsonnet/jsonnet_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package jsonnet - -import ( - "io/ioutil" - "runtime" - "strings" - "testing" - - "github.com/drone/drone/core" -) - -func TestParse(t *testing.T) { - before, err := ioutil.ReadFile("../testdata/input.jsonnet") - if err != nil { - t.Error(err) - return - } - - after, err := ioutil.ReadFile("../testdata/input.jsonnet.golden") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - }, - Config: &core.Config{}, - } - - template := &core.Template{ - Name: "my_template.jsonnet", - Data: string(before), - } - - templateData := map[string]interface{}{ - "stepName": "my_step", - "image": "my_image", - "commands": "my_command", - } - - req.Config.Data = string(before) - - got, err := Parse(req, nil, 0, template, templateData) - if err != nil { - t.Error(err) - return - } - - want := string(after) - // on windows line endings are \r\n, lets change them to linux for comparison - if runtime.GOOS == "windows" { - want = strings.Replace(want, "\r\n", "\n", -1) - } - - if want != got { - t.Errorf("Want %q got %q", want, got) - } -} - -func TestParseJsonnetNotTemplateFile(t *testing.T) { - before, err := ioutil.ReadFile("../testdata/single.jsonnet") - if err != nil { - t.Error(err) - return - } - - after, err := ioutil.ReadFile("../testdata/input.jsonnet.golden") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.jsonnet", - }, - Config: &core.Config{}, - } - - req.Repo.Config = "plugin.jsonnet" - req.Config.Data = string(before) - - got, err := Parse(req, nil, 0, nil, nil) - if err != nil { - t.Error(err) - return - } - - want := string(after) - // on windows line endings are \r\n, lets change them to linux for comparison - if runtime.GOOS == "windows" { - want = strings.Replace(want, "\r\n", "\n", -1) - } - - if want != got { - t.Errorf("Want %q got %q", want, got) - } -} diff --git a/plugin/converter/jsonnet_oss.go b/plugin/converter/jsonnet_oss.go deleted file mode 100644 index 67614961fa..0000000000 --- a/plugin/converter/jsonnet_oss.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package converter - -import ( - "github.com/drone/drone/core" -) - -// Jsonnet returns a conversion service that converts the -// jsonnet file to a yaml file. -func Jsonnet(enabled bool, limit int, fileService core.FileService) core.ConvertService { - return new(noop) -} diff --git a/plugin/converter/jsonnet_test.go b/plugin/converter/jsonnet_test.go deleted file mode 100644 index 243ad4537b..0000000000 --- a/plugin/converter/jsonnet_test.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package converter - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -const jsonnetFile = `{"foo": "bar"}` -const jsonnetFileAfter = `--- -{ - "foo": "bar" -} -` - -const jsonnetStream = `[{"foo": "bar"}]` -const jsonnetStreamAfter = `--- -{ - "foo": "bar" -} -` - -const jsonnetFileImport = `local step = import '.step.libsonnet'; -{"foo": ["bar"], "steps": [step]}` -const jsonnetFileImportLib = `{"image": "app"}` -const jsonnetFileImportAfter = `--- -{ - "foo": [ - "bar" - ], - "steps": [ - { - "image": "app" - } - ] -} -` - -const jsonnetFileMultipleImports = `local step = import '.step.libsonnet'; -local step2 = import '.step2.jsonnet'; -{"foo": ["bar"], "steps": [step, step2]}` - -func TestJsonnet_Stream(t *testing.T) { - args := &core.ConvertArgs{ - Repo: &core.Repository{Config: ".drone.jsonnet"}, - Config: &core.Config{Data: jsonnetStream}, - } - service := Jsonnet(true, 0, nil) - res, err := service.Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - if res == nil { - t.Errorf("Expected a converted file, got nil") - return - } - if got, want := res.Data, jsonnetStreamAfter; got != want { - t.Errorf("Want converted file %q, got %q", want, got) - } -} - -func TestJsonnet_Snippet(t *testing.T) { - args := &core.ConvertArgs{ - Repo: &core.Repository{Config: ".drone.jsonnet"}, - Config: &core.Config{Data: jsonnetFile}, - } - service := Jsonnet(true, 0, nil) - res, err := service.Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - if res == nil { - t.Errorf("Expected a converted file, got nil") - return - } - if got, want := res.Data, jsonnetFileAfter; got != want { - t.Errorf("Want converted file %q, got %q", want, got) - } -} - -func TestJsonnet_Error(t *testing.T) { - args := &core.ConvertArgs{ - Repo: &core.Repository{Config: ".drone.jsonnet"}, - Config: &core.Config{Data: "\\"}, // invalid jsonnet - } - service := Jsonnet(true, 0, nil) - _, err := service.Convert(noContext, args) - if err == nil { - t.Errorf("Expect jsonnet parsing error, got nil") - } -} - -func TestJsonnet_Disabled(t *testing.T) { - service := Jsonnet(false, 0, nil) - res, err := service.Convert(noContext, nil) - if err != nil { - t.Error(err) - } - if res != nil { - t.Errorf("Expect nil response when disabled") - } -} - -func TestJsonnet_NotJsonnet(t *testing.T) { - args := &core.ConvertArgs{ - Repo: &core.Repository{Config: ".drone.yml"}, - } - service := Jsonnet(true, 0, nil) - res, err := service.Convert(noContext, args) - if err != nil { - t.Error(err) - } - if res != nil { - t.Errorf("Expect nil response when not jsonnet") - } -} - -func TestJsonnet_Import(t *testing.T) { - args := &core.ConvertArgs{ - Build: &core.Build{ - Ref: "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", - After: "542ed565d03dab86f079798f937663ec1f05360b", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.jsonnet"}, - Config: &core.Config{Data: jsonnetFileImport}, - User: &core.User{ - Token: "foobar", - }, - } - importedContent := &core.File{ - Data: []byte(jsonnetFileImportLib), - } - controller := gomock.NewController(t) - mockFileService := mock.NewMockFileService(controller) - mockFileService.EXPECT().Find(gomock.Any(), &core.User{Token: "foobar"}, "octocat/hello-world", "542ed565d03dab86f079798f937663ec1f05360b", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", ".step.libsonnet").Return(importedContent, nil).Times(2) - service := Jsonnet(true, 1, mockFileService) - res, err := service.Convert(noContext, args) - if err != nil { - t.Error(err) - } - if got, want := res.Data, jsonnetFileImportAfter; got != want { - t.Errorf("Want converted file:\n%q\ngot\n%q", want, got) - } -} - -func TestJsonnet_ImportLimit(t *testing.T) { - args := &core.ConvertArgs{ - Build: &core.Build{ - Ref: "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", - After: "542ed565d03dab86f079798f937663ec1f05360b", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.jsonnet"}, - Config: &core.Config{Data: jsonnetFileMultipleImports}, - User: &core.User{ - Token: "foobar", - }, - } - importedContent := &core.File{ - Data: []byte(jsonnetFileImportLib), - } - controller := gomock.NewController(t) - mockFileService := mock.NewMockFileService(controller) - mockFileService.EXPECT().Find(gomock.Any(), &core.User{Token: "foobar"}, "octocat/hello-world", "542ed565d03dab86f079798f937663ec1f05360b", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", ".step.libsonnet").Return(importedContent, nil).Times(2) - - service := Jsonnet(true, 1, mockFileService) - _, err := service.Convert(noContext, args) - if err == nil { - t.Errorf("Expect nil response when jsonnet import limit is exceeded") - } -} - -func TestJsonnet_LimitZero(t *testing.T) { - args := &core.ConvertArgs{ - Build: &core.Build{ - Ref: "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", - After: "542ed565d03dab86f079798f937663ec1f05360b", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.jsonnet"}, - Config: &core.Config{Data: jsonnetFile}, - User: &core.User{ - Token: "foobar", - }, - } - - controller := gomock.NewController(t) - mockFileService := mock.NewMockFileService(controller) - mockFileService.EXPECT().Find(gomock.Any(), &core.User{Token: "foobar"}, "octocat/hello-world", "542ed565d03dab86f079798f937663ec1f05360b", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", ".step.libsonnet").Times(0) - - service := Jsonnet(true, 0, mockFileService) - res, err := service.Convert(noContext, args) - - if err != nil { - t.Error(err) - return - } - if got, want := res.Data, jsonnetFileAfter; got != want { - t.Errorf("Want converted file %q, got %q", want, got) - } -} - -func TestJsonnet_ImportLimitZero(t *testing.T) { - args := &core.ConvertArgs{ - Build: &core.Build{ - Ref: "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", - After: "542ed565d03dab86f079798f937663ec1f05360b", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.jsonnet"}, - Config: &core.Config{Data: jsonnetFileImport}, - User: &core.User{ - Token: "foobar", - }, - } - importedContent := &core.File{ - Data: []byte(jsonnetFileImportLib), - } - controller := gomock.NewController(t) - mockFileService := mock.NewMockFileService(controller) - mockFileService.EXPECT().Find(gomock.Any(), &core.User{Token: "foobar"}, "octocat/hello-world", "542ed565d03dab86f079798f937663ec1f05360b", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", ".step.libsonnet").Return(importedContent, nil).Times(2) - - service := Jsonnet(true, 0, mockFileService) - _, err := service.Convert(noContext, args) - if err == nil { - t.Errorf("Expect nil response when jsonnet import limit is exceeded") - } -} - -func TestJsonnet_ImportFileServiceNil(t *testing.T) { - args := &core.ConvertArgs{ - Build: &core.Build{ - Ref: "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", - After: "542ed565d03dab86f079798f937663ec1f05360b", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.jsonnet"}, - Config: &core.Config{Data: jsonnetFileMultipleImports}, - User: &core.User{ - Token: "foobar", - }, - } - - service := Jsonnet(true, 1, nil) - _, err := service.Convert(noContext, args) - if err == nil { - t.Errorf("Expect nil response when jsonnet import limit is exceeded") - } -} - -func TestJsonnet_FileServiceNil(t *testing.T) { - args := &core.ConvertArgs{ - Build: &core.Build{ - Ref: "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", - After: "542ed565d03dab86f079798f937663ec1f05360b", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.jsonnet"}, - Config: &core.Config{Data: jsonnetFile}, - User: &core.User{ - Token: "foobar", - }, - } - - service := Jsonnet(true, 1, nil) - res, err := service.Convert(noContext, args) - - if err != nil { - t.Error(err) - return - } - if got, want := res.Data, jsonnetFileAfter; got != want { - t.Errorf("Want converted file %q, got %q", want, got) - } -} \ No newline at end of file diff --git a/plugin/converter/legacy.go b/plugin/converter/legacy.go deleted file mode 100644 index 271caa8e05..0000000000 --- a/plugin/converter/legacy.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package converter - -import ( - "context" - - "github.com/drone/drone/core" -) - -// Legacy returns a conversion service that converts a -// legacy 0.8 yaml file to a yaml file. -func Legacy(enabled bool) core.ConvertService { - return &legacyPlugin{ - enabled: enabled, - } -} - -type legacyPlugin struct { - enabled bool -} - -func (p *legacyPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) { - if p.enabled == false { - return nil, nil - } - return &core.Config{ - Data: req.Config.Data, - }, nil -} diff --git a/plugin/converter/legacy_oss.go b/plugin/converter/legacy_oss.go deleted file mode 100644 index e5aa43e948..0000000000 --- a/plugin/converter/legacy_oss.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package converter - -import ( - "github.com/drone/drone/core" -) - -// Legacy returns a conversion service that converts the -// legacy 0.8 file to a yaml file. -func Legacy(enabled bool) core.ConvertService { - return new(noop) -} diff --git a/plugin/converter/memoize.go b/plugin/converter/memoize.go deleted file mode 100644 index ea78b168a6..0000000000 --- a/plugin/converter/memoize.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package converter - -import ( - "context" - "fmt" - - "github.com/drone/drone/core" - - lru "github.com/hashicorp/golang-lru" - "github.com/sirupsen/logrus" -) - -// cache key pattern used in the cache, comprised of the -// repository slug and commit sha. -const keyf = "%d|%s|%s|%s|%s|%s" - -// Memoize caches the conversion results for subsequent calls. -// This micro-optimization is intended for multi-pipeline -// projects that would otherwise covert the file for each -// pipeline execution. -func Memoize(base core.ConvertService, size int) core.ConvertService { - // simple cache prevents the same yaml file from being - // requested multiple times in a short period. - cache, _ := lru.New(10) - return &memoize{base: base, cache: cache, size: size} -} - -type memoize struct { - base core.ConvertService - cache *lru.Cache - size int -} - -func (c *memoize) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) { - // this is a minor optimization that prevents caching if the - // base converter is a remote converter and is disabled. - if remote, ok := c.base.(*remote); ok == true && remote.client == nil { - return nil, nil - } - - // the client can optionally disable cacheing. - if c.size == 0 { - return c.base.Convert(ctx, req) - } - - // generate the key used to cache the converted file. - key := fmt.Sprintf(keyf, - req.Repo.ID, - req.Build.Event, - req.Build.Action, - req.Build.Ref, - req.Build.After, - req.Repo.Config, - ) - - logger := logrus.WithField("repo", req.Repo.Slug). - WithField("build", req.Build.Event). - WithField("action", req.Build.Action). - WithField("ref", req.Build.Ref). - WithField("rev", req.Build.After). - WithField("config", req.Repo.Config) - - logger.Trace("extension: conversion: check cache") - - // check the cache for the file and return if exists. - cached, ok := c.cache.Get(key) - if ok { - logger.Trace("extension: conversion: cache hit") - return cached.(*core.Config), nil - } - - logger.Trace("extension: conversion: cache miss") - - // else convert the configuration file. - config, err := c.base.Convert(ctx, req) - if err != nil { - return nil, err - } - - if config == nil { - return nil, nil - } - if config.Data == "" { - return nil, nil - } - - // if the configuration file was converted - // it is temporarily cached. Note that we do - // not cache if the commit sha is empty (gogs). - if req.Build.After != "" { - c.cache.Add(key, config) - } - - return config, nil -} diff --git a/plugin/converter/memoize_oss.go b/plugin/converter/memoize_oss.go deleted file mode 100644 index 08d4aae53d..0000000000 --- a/plugin/converter/memoize_oss.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package converter - -import ( - "github.com/drone/drone/core" -) - -// Memoize caches the conversion results for subsequent calls. -// This micro-optimization is intended for multi-pipeline -// projects that would otherwise covert the file for each -// pipeline execution. -func Memoize(base core.ConvertService, size int) core.ConvertService { - return new(noop) -} diff --git a/plugin/converter/memoize_test.go b/plugin/converter/memoize_test.go deleted file mode 100644 index f700734d11..0000000000 --- a/plugin/converter/memoize_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package converter - -import ( - "errors" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -func TestMemoize(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - conf := &core.Config{Data: "{kind: pipeline, type: docker, steps: []}"} - args := &core.ConvertArgs{ - Build: &core.Build{After: "3950521325d4744760a96c18e3d0c67d86495af3"}, - Repo: &core.Repository{ID: 42}, - Config: conf, - } - - base := mock.NewMockConvertService(controller) - base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(args.Config, nil) - - service := Memoize(base, 10).(*memoize) - _, err := service.Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - - if got, want := service.cache.Len(), 1; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } - - args.Config = nil // set to nil to prove we get the cached value - res, err := service.Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - if res != conf { - t.Errorf("Expect result from cache") - } - - if got, want := service.cache.Len(), 1; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } -} - -func TestMemoize_Tag(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConvertArgs{ - Build: &core.Build{Ref: "refs/tags/v1.0.0"}, - Repo: &core.Repository{ID: 42}, - Config: &core.Config{Data: "{kind: pipeline, type: docker, steps: []}"}, - } - - base := mock.NewMockConvertService(controller) - base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(args.Config, nil) - - service := Memoize(base, 10).(*memoize) - res, err := service.Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - if res != args.Config { - t.Errorf("Expect result from cache") - } -} - -func TestMemoize_Empty(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConvertArgs{ - Build: &core.Build{After: "3950521325d4744760a96c18e3d0c67d86495af3"}, - Repo: &core.Repository{ID: 42}, - Config: &core.Config{Data: ""}, // empty - } - - base := mock.NewMockConvertService(controller) - base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(args.Config, nil) - - service := Memoize(base, 10).(*memoize) - res, err := service.Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - if res != nil { - t.Errorf("Expect nil response") - } - if got, want := service.cache.Len(), 0; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } -} - -func TestMemoize_Nil(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConvertArgs{ - Build: &core.Build{After: "3950521325d4744760a96c18e3d0c67d86495af3"}, - Repo: &core.Repository{ID: 42}, - Config: nil, - } - - base := mock.NewMockConvertService(controller) - base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(args.Config, nil) - - service := Memoize(base, 10).(*memoize) - res, err := service.Convert(noContext, args) - if err != nil { - t.Error(err) - return - } - if res != nil { - t.Errorf("Expect nil response") - } - if got, want := service.cache.Len(), 0; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } -} - -func TestMemoize_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ConvertArgs{ - Build: &core.Build{After: "3950521325d4744760a96c18e3d0c67d86495af3"}, - Repo: &core.Repository{ID: 42}, - } - - want := errors.New("not found") - base := mock.NewMockConvertService(controller) - base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(nil, want) - - service := Memoize(base, 10).(*memoize) - _, err := service.Convert(noContext, args) - if err == nil { - t.Errorf("Expect error from base returned to caller") - return - } - if got, want := service.cache.Len(), 0; got != want { - t.Errorf("Expect %d items in cache, got %d", want, got) - } -} diff --git a/plugin/converter/noop.go b/plugin/converter/noop.go deleted file mode 100644 index 422fb6c1f3..0000000000 --- a/plugin/converter/noop.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package converter - -import ( - "context" - - "github.com/drone/drone/core" -) - -type noop struct{} - -func (noop) Convert(context.Context, *core.ConvertArgs) (*core.Config, error) { - return nil, nil -} diff --git a/plugin/converter/remote.go b/plugin/converter/remote.go deleted file mode 100644 index 41d59ace64..0000000000 --- a/plugin/converter/remote.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package converter - -import ( - "context" - "strings" - "time" - - "github.com/drone/drone-go/drone" - "github.com/drone/drone-go/plugin/converter" - "github.com/drone/drone/core" -) - -// Remote returns a conversion service that converts the -// configuration file using a remote http service. -func Remote(endpoint, signer, extension string, skipVerify bool, timeout time.Duration) core.ConvertService { - if endpoint == "" { - return new(remote) - } - return &remote{ - extension: extension, - client: converter.Client( - endpoint, - signer, - skipVerify, - ), - timeout: timeout, - } -} - -type remote struct { - client converter.Plugin - extension string - timeout time.Duration -} - -func (g *remote) Convert(ctx context.Context, in *core.ConvertArgs) (*core.Config, error) { - if g.client == nil { - return nil, nil - } - if g.extension != "" { - if !strings.HasSuffix(in.Repo.Config, g.extension) { - return nil, nil - } - } - // include a timeout to prevent an API call from - // hanging the build process indefinitely. The - // external service must return a response within - // the configured timeout (default 1m). - ctx, cancel := context.WithTimeout(ctx, g.timeout) - defer cancel() - - req := &converter.Request{ - Repo: toRepo(in.Repo), - Build: toBuild(in.Build), - Config: drone.Config{ - Data: in.Config.Data, - }, - Token: drone.Token{ - Access: in.User.Token, - Refresh: in.User.Refresh, - }, - } - - res, err := g.client.Convert(ctx, req) - if err != nil { - return nil, err - } - if res == nil { - return nil, nil - } - - // if no error is returned and the secret is empty, - // this indicates the client returned No Content, - // and we should exit with no secret, but no error. - if res.Data == "" { - return nil, nil - } - - return &core.Config{ - Kind: res.Kind, - Data: res.Data, - }, nil -} - -func toRepo(from *core.Repository) drone.Repo { - return drone.Repo{ - ID: from.ID, - UID: from.UID, - UserID: from.UserID, - Namespace: from.Namespace, - Name: from.Name, - Slug: from.Slug, - SCM: from.SCM, - HTTPURL: from.HTTPURL, - SSHURL: from.SSHURL, - Link: from.Link, - Branch: from.Branch, - Private: from.Private, - Visibility: from.Visibility, - Active: from.Active, - Config: from.Config, - Trusted: from.Trusted, - Protected: from.Protected, - Timeout: from.Timeout, - } -} - -func toBuild(from *core.Build) drone.Build { - return drone.Build{ - ID: from.ID, - RepoID: from.RepoID, - Trigger: from.Trigger, - Number: from.Number, - Parent: from.Parent, - Status: from.Status, - Error: from.Error, - Event: from.Event, - Action: from.Action, - Link: from.Link, - Timestamp: from.Timestamp, - Title: from.Title, - Message: from.Message, - Before: from.Before, - After: from.After, - Ref: from.Ref, - Fork: from.Fork, - Source: from.Source, - Target: from.Target, - Author: from.Author, - AuthorName: from.AuthorName, - AuthorEmail: from.AuthorEmail, - AuthorAvatar: from.AuthorAvatar, - Sender: from.Sender, - Params: from.Params, - Deploy: from.Deploy, - Started: from.Started, - Finished: from.Finished, - Created: from.Created, - Updated: from.Updated, - Version: from.Version, - } -} diff --git a/plugin/converter/remote_oss.go b/plugin/converter/remote_oss.go deleted file mode 100644 index 6a9fc9f2fc..0000000000 --- a/plugin/converter/remote_oss.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package converter - -import ( - "time" - - "github.com/drone/drone/core" -) - -// Remote returns a conversion service that converts the -// configuration file using a remote http service. -func Remote(endpoint, signer, extension string, skipVerify bool, timeout time.Duration) core.ConvertService { - return new(noop) -} diff --git a/plugin/converter/remote_test.go b/plugin/converter/remote_test.go deleted file mode 100644 index b5bf4c99ab..0000000000 --- a/plugin/converter/remote_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package converter - -import ( - "context" - "testing" - "time" - - "github.com/drone/drone/core" - "github.com/h2non/gock" -) - -func TestConvert(t *testing.T) { - defer gock.Off() - - gock.New("https://company.com"). - Post("/convert"). - MatchHeader("Accept", "application/vnd.drone.convert.v1\\+json"). - MatchHeader("Accept-Encoding", "identity"). - MatchHeader("Content-Type", "application/json"). - Reply(200). - BodyString(`{"data": "{ kind: pipeline, type: docker, name: default }"}`). - Done() - - args := &core.ConvertArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - Config: &core.Config{ - Data: "{ kind: pipeline, name: default }", - }, - } - - service := Remote("https://company.com/convert", "GMEuUHQfmrMRsseWxi9YlIeBtn9lm6im", "", - false, time.Minute) - result, err := service.Convert(context.Background(), args) - if err != nil { - t.Error(err) - return - } - - if result.Data != "{ kind: pipeline, type: docker, name: default }" { - t.Errorf("unexpected file contents") - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - return - } -} diff --git a/plugin/converter/starlark.go b/plugin/converter/starlark.go deleted file mode 100644 index 67659254be..0000000000 --- a/plugin/converter/starlark.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package converter - -import ( - "context" - "strings" - - "github.com/drone/drone/core" - "github.com/drone/drone/plugin/converter/starlark" -) - -// Starlark returns a conversion service that converts the -// starlark file to a yaml file. -func Starlark(enabled bool, stepLimit uint64, sizeLimit uint64) core.ConvertService { - return &starlarkPlugin{ - enabled: enabled, - stepLimit: stepLimit, - sizeLimit: sizeLimit, - } -} - -type starlarkPlugin struct { - enabled bool - stepLimit uint64 - sizeLimit uint64 -} - -func (p *starlarkPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) { - if p.enabled == false { - return nil, nil - } - - // if the file extension is not jsonnet we can - // skip this plugin by returning zero values. - switch { - case strings.HasSuffix(req.Repo.Config, ".script"): - case strings.HasSuffix(req.Repo.Config, ".star"): - case strings.HasSuffix(req.Repo.Config, ".starlark"): - default: - return nil, nil - } - - file, err := starlark.Parse(req, nil, nil, p.stepLimit, p.sizeLimit) - if err != nil { - return nil, err - } - return &core.Config{ - Data: file, - }, nil -} diff --git a/plugin/converter/starlark/args.go b/plugin/converter/starlark/args.go deleted file mode 100644 index edc8bf2b95..0000000000 --- a/plugin/converter/starlark/args.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package starlark - -import ( - "fmt" - "reflect" - - "github.com/drone/drone/core" - - "go.starlark.net/starlark" - "go.starlark.net/starlarkstruct" -) - -// TODO(bradrydzewski) add repository id -// TODO(bradrydzewski) add repository timeout -// TODO(bradrydzewski) add repository counter -// TODO(bradrydzewski) add repository created -// TODO(bradrydzewski) add repository updated -// TODO(bradrydzewski) add repository synced -// TODO(bradrydzewski) add repository version - -// TODO(bradrydzewski) add build id, will always be zero value -// TODO(bradrydzewski) add build number, will always be zero value -// TODO(bradrydzewski) add build started, will always be zero value -// TODO(bradrydzewski) add build finished, will always be zero value -// TODO(bradrydzewski) add build created, will always be zero value -// TODO(bradrydzewski) add build updated, will always be zero value -// TODO(bradrydzewski) add build parent -// TODO(bradrydzewski) add build timestamp - -func createArgs(repo *core.Repository, build *core.Build, input map[string]interface{}) ([]starlark.Value, error) { - inputArgs, err := fromInput(input) - if err != nil { - return nil, err - } - args := []starlark.Value{ - starlarkstruct.FromStringDict( - starlark.String("context"), - starlark.StringDict{ - "repo": starlarkstruct.FromStringDict(starlark.String("repo"), fromRepo(repo)), - "build": starlarkstruct.FromStringDict(starlark.String("build"), fromBuild(build)), - "input": starlarkstruct.FromStringDict(starlark.String("input"), inputArgs), - }, - ), - } - return args, nil -} - -func fromInput(input map[string]interface{}) (starlark.StringDict, error) { - out := map[string]starlark.Value{} - for key, value := range input { - v := reflect.ValueOf(value) - result, err := toValue(v) - if err != nil { - return nil, err - } - out[key] = result - } - return out, nil -} - -func toValue(val reflect.Value) (starlark.Value, error) { - kind := val.Kind() - if kind == reflect.Ptr { - kind = val.Elem().Kind() - } - switch kind { - case reflect.Bool: - return starlark.Bool(val.Bool()), nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return starlark.MakeInt64(val.Int()), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return starlark.MakeUint64(val.Uint()), nil - case reflect.Float32, reflect.Float64: - return starlark.Float(val.Float()), nil - case reflect.Map: - dict := new(starlark.Dict) - for _, key := range val.MapKeys() { - value := val.MapIndex(key) - getValue, err := toValue(reflect.ValueOf(value.Interface())) - if err != nil { - return nil, err - } - dict.SetKey( - starlark.String(fmt.Sprint(key)), - getValue, - ) - } - return dict, nil - case reflect.String: - return starlark.String(val.String()), nil - case reflect.Slice, reflect.Array: - list := new(starlark.List) - for i := 0; i < val.Len(); i++ { - keyValue := val.Index(i).Interface() - vOf := reflect.ValueOf(keyValue) - result, err := toValue(vOf) - if err != nil { - return nil, err - } - list.Append(result) - } - return list, nil - } - - return nil, fmt.Errorf("type %T is not a supported starlark type", val.Interface()) -} - -func fromBuild(v *core.Build) starlark.StringDict { - return starlark.StringDict{ - "event": starlark.String(v.Event), - "action": starlark.String(v.Action), - "cron": starlark.String(v.Cron), - "environment": starlark.String(v.Deploy), - "link": starlark.String(v.Link), - "branch": starlark.String(v.Target), - "source": starlark.String(v.Source), - "before": starlark.String(v.Before), - "after": starlark.String(v.After), - "target": starlark.String(v.Target), - "ref": starlark.String(v.Ref), - "commit": starlark.String(v.After), - "title": starlark.String(v.Title), - "message": starlark.String(v.Message), - "source_repo": starlark.String(v.Fork), - "author_login": starlark.String(v.Author), - "author_name": starlark.String(v.AuthorName), - "author_email": starlark.String(v.AuthorEmail), - "author_avatar": starlark.String(v.AuthorAvatar), - "sender": starlark.String(v.Sender), - "debug": starlark.Bool(v.Debug), - "params": fromMap(v.Params), - } -} - -func fromRepo(v *core.Repository) starlark.StringDict { - return starlark.StringDict{ - "uid": starlark.String(v.UID), - "name": starlark.String(v.Name), - "namespace": starlark.String(v.Namespace), - "slug": starlark.String(v.Slug), - "git_http_url": starlark.String(v.HTTPURL), - "git_ssh_url": starlark.String(v.SSHURL), - "link": starlark.String(v.Link), - "branch": starlark.String(v.Branch), - "config": starlark.String(v.Config), - "private": starlark.Bool(v.Private), - "visibility": starlark.String(v.Visibility), - "active": starlark.Bool(v.Active), - "trusted": starlark.Bool(v.Trusted), - "protected": starlark.Bool(v.Protected), - "ignore_forks": starlark.Bool(v.IgnoreForks), - "ignore_pull_requests": starlark.Bool(v.IgnorePulls), - } -} - -func fromMap(m map[string]string) *starlark.Dict { - dict := new(starlark.Dict) - for k, v := range m { - dict.SetKey( - starlark.String(k), - starlark.String(v), - ) - } - return dict -} diff --git a/plugin/converter/starlark/starlark.go b/plugin/converter/starlark/starlark.go deleted file mode 100644 index 5e7a79904a..0000000000 --- a/plugin/converter/starlark/starlark.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package starlark - -import ( - "bytes" - - "github.com/drone/drone/core" - "github.com/drone/drone/handler/api/errors" - - "github.com/sirupsen/logrus" - "go.starlark.net/starlark" -) - -const ( - separator = "---" - newline = "\n" -) - -// default limit for generated configuration file size. -const defaultSizeLimit = 1000000 - -var ( - // ErrMainMissing indicates the starlark script is missing - // the main method. - ErrMainMissing = errors.New("starlark: missing main function") - - // ErrMainInvalid indicates the starlark script defines a - // global variable named main, however, it is not callable. - ErrMainInvalid = errors.New("starlark: main must be a function") - - // ErrMainReturn indicates the starlark script's main method - // returns an invalid or unexpected type. - ErrMainReturn = errors.New("starlark: main returns an invalid type") - - // ErrMaximumSize indicates the starlark script generated a - // file that exceeds the maximum allowed file size. - ErrMaximumSize = errors.New("starlark: maximum file size exceeded") - - // ErrCannotLoad indicates the starlark script is attempting to - // load an external file which is currently restricted. - ErrCannotLoad = errors.New("starlark: cannot load external scripts") -) - -func Parse(req *core.ConvertArgs, template *core.Template, templateData map[string]interface{}, stepLimit uint64, sizeLimit uint64) (string, error) { - thread := &starlark.Thread{ - Name: "drone", - Load: noLoad, - Print: func(_ *starlark.Thread, msg string) { - logrus.WithFields(logrus.Fields{ - "namespace": req.Repo.Namespace, - "name": req.Repo.Name, - }).Traceln(msg) - }, - } - var starlarkFile string - var starlarkFileName string - if template != nil { - starlarkFile = template.Data - starlarkFileName = template.Name - } else { - starlarkFile = req.Config.Data - starlarkFileName = req.Repo.Config - } - - globals, err := starlark.ExecFile(thread, starlarkFileName, starlarkFile, nil) - if err != nil { - return "", err - } - - // find the main method in the starlark script and - // cast to a callable type. If not callable the script - // is invalid. - mainVal, ok := globals["main"] - if !ok { - return "", ErrMainMissing - } - main, ok := mainVal.(starlark.Callable) - if !ok { - return "", ErrMainInvalid - } - - // create the input args and invoke the main method - // using the input args. - args, err := createArgs(req.Repo, req.Build, templateData) - if err != nil { - return "", err - } - - // set the maximum number of operations in the script. this - // mitigates long running scripts. - if stepLimit == 0 { - stepLimit = 50000 - } - thread.SetMaxExecutionSteps(stepLimit) - - // execute the main method in the script. - mainVal, err = starlark.Call(thread, main, args, nil) - if err != nil { - return "", err - } - - buf := new(bytes.Buffer) - switch v := mainVal.(type) { - case *starlark.List: - for i := 0; i < v.Len(); i++ { - item := v.Index(i) - buf.WriteString(separator) - buf.WriteString(newline) - if err := write(buf, item); err != nil { - return "", err - } - buf.WriteString(newline) - } - case *starlark.Dict: - if err := write(buf, v); err != nil { - return "", err - } - default: - return "", ErrMainReturn - } - - if sizeLimit == 0 { - sizeLimit = defaultSizeLimit - } - - // this is a temporary workaround until we - // implement a LimitWriter. - if b := buf.Bytes(); uint64(len(b)) > sizeLimit { - return "", ErrMaximumSize - } - return buf.String(), nil -} - -func noLoad(_ *starlark.Thread, _ string) (starlark.StringDict, error) { - return nil, ErrCannotLoad -} diff --git a/plugin/converter/starlark/starlark_test.go b/plugin/converter/starlark/starlark_test.go deleted file mode 100644 index 8e7871c974..0000000000 --- a/plugin/converter/starlark/starlark_test.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package starlark - -import ( - "io/ioutil" - "testing" - - "github.com/drone/drone/core" -) - -func TestParseStarlark(t *testing.T) { - before, err := ioutil.ReadFile("../testdata/starlark.input.star") - if err != nil { - t.Error(err) - return - } - - after, err := ioutil.ReadFile("../testdata/starlark.input.star.golden") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - }, - Config: &core.Config{}, - } - template := &core.Template{ - Name: "my_template.star", - Data: string(before), - } - - templateData := map[string]interface{}{ - "stepName": "my_step", - "image": "my_image", - "commands": "my_command", - } - - req.Config.Data = string(before) - - parsedFile, err := Parse(req, template, templateData, 0, 0) - if err != nil { - t.Error(err) - return - } - - if want, got := parsedFile, string(after); want != got { - t.Errorf("Want %q got %q", want, got) - } -} - -func TestParseStarlarkNotTemplateFile(t *testing.T) { - before, err := ioutil.ReadFile("../testdata/single.star") - if err != nil { - t.Error(err) - return - } - - after, err := ioutil.ReadFile("../testdata/single.star.golden") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.star", - }, - Config: &core.Config{}, - } - - req.Repo.Config = "plugin.starlark.star" - req.Config.Data = string(before) - - parsedFile, err := Parse(req, nil, nil, 0, 0) - if err != nil { - t.Error(err) - return - } - - if want, got := parsedFile, string(after); want != got { - t.Errorf("Want %q got %q", want, got) - } -} diff --git a/plugin/converter/starlark/write.go b/plugin/converter/starlark/write.go deleted file mode 100644 index 5b5f5f04f0..0000000000 --- a/plugin/converter/starlark/write.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package starlark - -import ( - "encoding/json" - "fmt" - "io" - - "go.starlark.net/starlark" -) - -type writer interface { - io.Writer - io.ByteWriter - io.StringWriter -} - -func write(out writer, v starlark.Value) error { - if marshaler, ok := v.(json.Marshaler); ok { - jsonData, err := marshaler.MarshalJSON() - if err != nil { - return err - } - out.Write(jsonData) - return nil - } - - switch v := v.(type) { - case starlark.NoneType: - out.WriteString("null") - case starlark.Bool: - fmt.Fprintf(out, "%t", v) - case starlark.Int: - out.WriteString(v.String()) - case starlark.Float: - fmt.Fprintf(out, "%g", v) - case starlark.String: - s := string(v) - if isQuoteSafe(s) { - fmt.Fprintf(out, "%q", s) - } else { - data, _ := json.Marshal(s) - out.Write(data) - } - case starlark.Indexable: - out.WriteByte('[') - for i, n := 0, starlark.Len(v); i < n; i++ { - if i > 0 { - out.WriteString(", ") - } - if err := write(out, v.Index(i)); err != nil { - return err - } - } - out.WriteByte(']') - case *starlark.Dict: - out.WriteByte('{') - for i, itemPair := range v.Items() { - key := itemPair[0] - value := itemPair[1] - if i > 0 { - out.WriteString(", ") - } - if err := write(out, key); err != nil { - return err - } - out.WriteString(": ") - if err := write(out, value); err != nil { - return err - } - } - out.WriteByte('}') - default: - return fmt.Errorf("value %s (type `%s') can't be converted to JSON", v.String(), v.Type()) - } - return nil -} - -func isQuoteSafe(s string) bool { - for _, r := range s { - if r < 0x20 || r >= 0x10000 { - return false - } - } - return true -} diff --git a/plugin/converter/starlark_oss.go b/plugin/converter/starlark_oss.go deleted file mode 100644 index 0444be33d5..0000000000 --- a/plugin/converter/starlark_oss.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package converter - -import "github.com/drone/drone/core" - -func Starlark(enabled bool, stepLimit uint64, sizeLimit uint64) core.ConvertService { - return new(noop) -} diff --git a/plugin/converter/starlark_test.go b/plugin/converter/starlark_test.go deleted file mode 100644 index 6f3c8fd36f..0000000000 --- a/plugin/converter/starlark_test.go +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package converter - -import ( - "io/ioutil" - "runtime" - "strings" - "testing" - - "github.com/drone/drone/core" -) - -func TestStarlarkConvert(t *testing.T) { - plugin := Starlark(true, 0, 0) - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - }, - Config: &core.Config{}, - } - - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - if config != nil { - t.Error("Want nil config when configuration is not starlark file") - return - } - - before, err := ioutil.ReadFile("testdata/single.star") - if err != nil { - t.Error(err) - return - } - after, err := ioutil.ReadFile("testdata/single.star.golden") - if err != nil { - t.Error(err) - return - } - - req.Repo.Config = "single.star" - req.Config.Data = string(before) - config, err = plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - if config == nil { - t.Error("Want non-nil configuration") - return - } - - if want, got := config.Data, string(after); want != got { - t.Errorf("Want %q got %q", want, got) - } -} - -// this test verifies the starlark file can generate a multi-document -// yaml file that defines multiple pipelines. -func TestConvert_Multi(t *testing.T) { - before, err := ioutil.ReadFile("testdata/multi.star") - if err != nil { - t.Error(err) - return - } - after, err := ioutil.ReadFile("testdata/multi.star.golden") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.star", - }, - Config: &core.Config{ - Data: string(before), - }, - } - - plugin := Starlark(true, 0, 0) - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - - config, err = plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - if config == nil { - t.Error("Want non-nil configuration") - return - } - - want := string(after) - // on windows line endings are \r\n, lets change them to linux for comparison - if runtime.GOOS == "windows" { - want = strings.Replace(want, "\r\n", "\n", -1) - } - - got := config.Data - if want != got { - t.Errorf("Want %q got %q", want, got) - } -} - -// this test verifies the plugin is skipped when it has -// not been explicitly enabled. -func TestConvert_Skip(t *testing.T) { - plugin := Starlark(false, 0, 0) - config, err := plugin.Convert(noContext, nil) - if err != nil { - t.Error(err) - return - } - if config != nil { - t.Errorf("Expect nil config returned when plugin disabled") - } -} - -// this test verifies the plugin is skipped when the config -// file extension is not a starlark extension. -func TestConvert_SkipYaml(t *testing.T) { - req := &core.ConvertArgs{ - Repo: &core.Repository{ - Config: ".drone.yaml", - }, - } - - plugin := Starlark(true, 0, 0) - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - if config != nil { - t.Errorf("Expect nil config returned for non-starlark files") - } -} - -// this test verifies the plugin returns error -// if the generated file size is exceeded. -func TestConvert_SizeLimit(t *testing.T) { - smallFileSizeLimit := uint64(1) - plugin := Starlark(true, 0, smallFileSizeLimit) - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - }, - Config: &core.Config{}, - } - - before, err := ioutil.ReadFile("testdata/single.star") - if err != nil { - t.Error(err) - return - } - - req.Repo.Config = "single.star" - req.Config.Data = string(before) - _, expectedError := plugin.Convert(noContext, req) - if expectedError == nil { - t.Error("Expected 'starlark: maximum file size exceeded' error") - return - } -} \ No newline at end of file diff --git a/plugin/converter/template.go b/plugin/converter/template.go deleted file mode 100644 index a8d2ab595b..0000000000 --- a/plugin/converter/template.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package converter - -import ( - "bytes" - "context" - "database/sql" - "errors" - "path/filepath" - "regexp" - templating "text/template" - - "github.com/drone/drone/core" - "github.com/drone/drone/plugin/converter/jsonnet" - "github.com/drone/drone/plugin/converter/starlark" - "github.com/drone/funcmap" - - "gopkg.in/yaml.v2" -) - -var ( - // templateFileRE regex to verifying kind is template. - templateFileRE = regexp.MustCompilePOSIX("^kind:[[:space:]]+template[[:space:]]?+$") - errTemplateNotFound = errors.New("template converter: template name given not found") - errTemplateSyntaxErrors = errors.New("template converter: there is a problem with the yaml file provided") - errTemplateExtensionInvalid = errors.New("template extension invalid. must be yaml, starlark or jsonnet") -) - -func Template(templateStore core.TemplateStore, stepLimit uint64, sizeLimit uint64) core.ConvertService { - return &templatePlugin{ - templateStore: templateStore, - stepLimit: stepLimit, - sizeLimit: sizeLimit, - } -} - -type templatePlugin struct { - templateStore core.TemplateStore - stepLimit uint64 - sizeLimit uint64 -} - -func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) { - // check type is yaml - configExt := filepath.Ext(req.Repo.Config) - - if configExt != ".yml" && configExt != ".yaml" { - return nil, nil - } - - // check kind is template - if templateFileRE.MatchString(req.Config.Data) == false { - return nil, nil - } - // map to templateArgs - var templateArgs core.TemplateArgs - err := yaml.Unmarshal([]byte(req.Config.Data), &templateArgs) - if err != nil { - return nil, errTemplateSyntaxErrors - } - // get template from db - template, err := p.templateStore.FindName(ctx, templateArgs.Load, req.Repo.Namespace) - if err == sql.ErrNoRows { - return nil, errTemplateNotFound - } - if err != nil { - return nil, err - } - - switch filepath.Ext(templateArgs.Load) { - case ".yml", ".yaml": - return parseYaml(req, template, templateArgs) - case ".star", ".starlark", ".script": - return parseStarlark(req, template, templateArgs, p.stepLimit, p.sizeLimit) - case ".jsonnet": - return parseJsonnet(req, template, templateArgs) - default: - return nil, errTemplateExtensionInvalid - } -} - -func parseYaml(req *core.ConvertArgs, template *core.Template, templateArgs core.TemplateArgs) (*core.Config, error) { - data := map[string]interface{}{ - "build": toBuild(req.Build), - "repo": toRepo(req.Repo), - "input": templateArgs.Data, - } - tmpl, err := templating.New(template.Name).Funcs(funcmap.SafeFuncs).Parse(template.Data) - if err != nil { - return nil, err - } - var out bytes.Buffer - err = tmpl.Execute(&out, data) - if err != nil { - return nil, err - } - return &core.Config{ - Data: out.String(), - }, nil -} - -func parseJsonnet(req *core.ConvertArgs, template *core.Template, templateArgs core.TemplateArgs) (*core.Config, error) { - file, err := jsonnet.Parse(req, nil, 0, template, templateArgs.Data) - if err != nil { - return nil, err - } - return &core.Config{ - Data: file, - }, nil -} - -func parseStarlark(req *core.ConvertArgs, template *core.Template, templateArgs core.TemplateArgs, stepLimit uint64, sizeLimit uint64) (*core.Config, error) { - file, err := starlark.Parse(req, template, templateArgs.Data, stepLimit, sizeLimit) - if err != nil { - return nil, err - } - return &core.Config{ - Data: file, - }, nil -} diff --git a/plugin/converter/template_oss.go b/plugin/converter/template_oss.go deleted file mode 100644 index 29ca662e87..0000000000 --- a/plugin/converter/template_oss.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package converter - -import ( - "context" - - "github.com/drone/drone/core" -) - -func Template(templateStore core.TemplateStore, stepLimit uint64, sizeLimit uint64) core.ConvertService { - return &templatePlugin{ - templateStore: templateStore, - } -} - -type templatePlugin struct { - templateStore core.TemplateStore -} - -func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) { - return nil, nil -} diff --git a/plugin/converter/template_test.go b/plugin/converter/template_test.go deleted file mode 100644 index f78d2a86ca..0000000000 --- a/plugin/converter/template_test.go +++ /dev/null @@ -1,553 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package converter - -import ( - "encoding/json" - "errors" - "io/ioutil" - "runtime" - "strings" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -func TestTemplatePluginConvertStarlark(t *testing.T) { - templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - Namespace: "octocat", - }, - Config: &core.Config{ - Data: string(templateArgs), - }, - } - - beforeInput, err := ioutil.ReadFile("testdata/starlark.input.star") - if err != nil { - t.Error(err) - return - } - - after, err := ioutil.ReadFile("testdata/starlark.input.star.golden") - if err != nil { - t.Error(err) - return - } - - template := &core.Template{ - Name: "plugin.starlark", - Data: string(beforeInput), - Namespace: "octocat", - } - - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil) - - plugin := Template(templates, 0, 0) - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - - if config == nil { - t.Error("Want non-nil configuration") - return - } - - if want, got := config.Data, string(after); want != got { - t.Errorf("Want %q got %q", want, got) - } -} - -func TestTemplatePluginConvertNotYamlFile(t *testing.T) { - - plugin := Template(nil, 0, 0) - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.star", - }, - Config: &core.Config{}, - } - - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - if config != nil { - t.Errorf("Expect nil config returned for non-starlark files") - } -} - -func TestTemplatePluginConvertDroneFileTypePipeline(t *testing.T) { - args, err := ioutil.ReadFile("testdata/drone.yml") - if err != nil { - t.Error(err) - return - } - plugin := Template(nil, 0, 0) - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - }, - Config: &core.Config{Data: string(args)}, - } - - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - if config != nil { - t.Errorf("Expect nil config returned for non-starlark files") - } -} - -// Test makes sure that we don't skip templating for neither the "yml" or "yaml" extension. -func TestTemplatePluginConvertDroneFileYamlExtensions(t *testing.T) { - extensions := []string{"yml", "yaml"} - dummyErr := errors.New("dummy-error") - - for _, extension := range extensions { - t.Run(extension, func(t *testing.T) { - args, err := ioutil.ReadFile("testdata/yaml.template.yml") - if err != nil { - t.Error(err) - return - } - - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().FindName(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, dummyErr) - - plugin := Template(templates, 0, 0) - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone." + extension, - }, - Config: &core.Config{Data: string(args)}, - } - - _, err = plugin.Convert(noContext, req) - if err != nil && err != dummyErr { - t.Error(err) - } - if err == nil { - t.Errorf("Templating was skipped") - } - }) - } -} - -func TestTemplatePluginConvertTemplateNotFound(t *testing.T) { - templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - Namespace: "octocat", - }, - Config: &core.Config{Data: string(templateArgs)}, - } - - controller := gomock.NewController(t) - defer controller.Finish() - - template := &core.Template{ - Name: "plugin.starlark", - Data: "", - } - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(nil, nil) - - plugin := Template(templates, 0, 0) - - config, err := plugin.Convert(noContext, req) - if config != nil { - t.Errorf("template converter: template name given not found") - } -} - -func TestTemplatePluginConvertJsonnet(t *testing.T) { - templateArgs, err := ioutil.ReadFile("testdata/jsonnet.template.yml") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - Namespace: "octocat", - }, - Config: &core.Config{ - Data: string(templateArgs), - }, - } - - beforeInput, err := ioutil.ReadFile("testdata/input.jsonnet") - if err != nil { - t.Error(err) - return - } - - after, err := ioutil.ReadFile("testdata/input.jsonnet.golden") - if err != nil { - t.Error(err) - return - } - - template := &core.Template{ - Name: "plugin.jsonnet", - Data: string(beforeInput), - Namespace: "octocat", - } - - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil) - - plugin := Template(templates, 0, 0) - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - - if config == nil { - t.Error("Want non-nil configuration") - return - } - - want := string(after) - // on windows line endings are \r\n, lets change them to linux for comparison - if runtime.GOOS == "windows" { - want = strings.Replace(want, "\r\n", "\n", -1) - } - - got := config.Data - if want != got { - t.Errorf("Want %q got %q", want, got) - } -} - -func TestTemplateNestedValuesPluginConvertStarlark(t *testing.T) { - type Pipeline struct { - Kind string `json:"kind"` - Name string `json:"name"` - Steps []struct { - Name string `json:"name"` - Image string `json:"image"` - Commands []string `json:"commands"` - } `json:"steps"` - } - - templateArgs, err := ioutil.ReadFile("testdata/starlark-nested.template.yml") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - Namespace: "octocat", - }, - Config: &core.Config{ - Data: string(templateArgs), - }, - } - - beforeInput, err := ioutil.ReadFile("testdata/starlark.input-nested.star") - if err != nil { - t.Error(err) - return - } - - after, err := ioutil.ReadFile("testdata/starlark.input-nested.star.golden") - if err != nil { - t.Error(err) - return - } - - template := &core.Template{ - Name: "test.nested.starlark", - Data: string(beforeInput), - Namespace: "octocat", - } - - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil) - - plugin := Template(templates, 0, 0) - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - - if config == nil { - t.Error("Want non-nil configuration") - return - } - result := Pipeline{} - err = json.Unmarshal(after, &result) - beforeConfig := Pipeline{} - err = json.Unmarshal([]byte(config.Data), &beforeConfig) - - if want, got := beforeConfig.Name, result.Name; want != got { - t.Errorf("Want %q got %q", want, got) - } - if want, got := beforeConfig.Kind, result.Kind; want != got { - t.Errorf("Want %q got %q", want, got) - } - if want, got := beforeConfig.Steps[0].Name, result.Steps[0].Name; want != got { - t.Errorf("Want %q got %q", want, got) - } - if want, got := beforeConfig.Steps[0].Commands[0], result.Steps[0].Commands[0]; want != got { - t.Errorf("Want %q got %q", want, got) - } - if want, got := beforeConfig.Steps[0].Image, result.Steps[0].Image; want != got { - t.Errorf("Want %q got %q", want, got) - } -} - -func TestTemplatePluginConvertYaml(t *testing.T) { - templateArgs, err := ioutil.ReadFile("testdata/yaml.template.yml") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - Namespace: "octocat", - }, - Config: &core.Config{ - Data: string(templateArgs), - }, - } - - beforeInput, err := ioutil.ReadFile("testdata/yaml.input.yml") - if err != nil { - t.Error(err) - return - } - - after, err := ioutil.ReadFile("testdata/yaml.input.golden") - if err != nil { - t.Error(err) - return - } - - template := &core.Template{ - Name: "plugin.yaml", - Data: string(beforeInput), - Namespace: "octocat", - } - - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil) - - plugin := Template(templates, 0, 0) - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - - if config == nil { - t.Error("Want non-nil configuration") - return - } - - if want, got := config.Data, string(after); want != got { - t.Errorf("Want %q got %q", want, got) - } -} - -// tests to check error is thrown if user has already loaded a template file of invalid extension -// and refers to it in the drone.yml file -func TestTemplatePluginConvertInvalidTemplateExtension(t *testing.T) { - // reads yml input file which refers to a template file of invalid extensions - templateArgs, err := ioutil.ReadFile("testdata/yaml.template.invalid.yml") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - Namespace: "octocat", - }, - Config: &core.Config{ - Data: string(templateArgs), - }, - } - // reads the template drone.yml - beforeInput, err := ioutil.ReadFile("testdata/yaml.input.yml") - if err != nil { - t.Error(err) - return - } - - template := &core.Template{ - Name: "plugin.txt", - Data: string(beforeInput), - Namespace: "octocat", - } - - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil) - - plugin := Template(templates, 0, 0) - config, err := plugin.Convert(noContext, req) - if config != nil { - t.Errorf("template extension invalid. must be yaml, starlark or jsonnet") - } -} - -func TestTemplatePluginConvertYamlWithComment(t *testing.T) { - templateArgs, err := ioutil.ReadFile("testdata/yaml.template.comment.yml") - if err != nil { - t.Error(err) - return - } - - req := &core.ConvertArgs{ - Build: &core.Build{ - After: "3d21ec53a331a6f037a91c368710b99387d012c1", - }, - Repo: &core.Repository{ - Slug: "octocat/hello-world", - Config: ".drone.yml", - Namespace: "octocat", - }, - Config: &core.Config{ - Data: string(templateArgs), - }, - } - - beforeInput, err := ioutil.ReadFile("testdata/yaml.input.yml") - if err != nil { - t.Error(err) - return - } - - after, err := ioutil.ReadFile("testdata/yaml.input.golden") - if err != nil { - t.Error(err) - return - } - - template := &core.Template{ - Name: "plugin.yaml", - Data: string(beforeInput), - Namespace: "octocat", - } - - controller := gomock.NewController(t) - defer controller.Finish() - - templates := mock.NewMockTemplateStore(controller) - templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil) - - plugin := Template(templates, 0, 0) - config, err := plugin.Convert(noContext, req) - if err != nil { - t.Error(err) - return - } - - if config == nil { - t.Error("Want non-nil configuration") - return - } - - if want, got := config.Data, string(after); want != got { - t.Errorf("Want %q got %q", want, got) - } -} diff --git a/plugin/converter/testdata/drone.yml b/plugin/converter/testdata/drone.yml deleted file mode 100644 index a451f7bb49..0000000000 --- a/plugin/converter/testdata/drone.yml +++ /dev/null @@ -1,6 +0,0 @@ -kind: pipeline -load: plugin.starlark -data: - stepName: my_step - image: my_image - commands: my_command diff --git a/plugin/converter/testdata/input.jsonnet b/plugin/converter/testdata/input.jsonnet deleted file mode 100644 index 25f586354a..0000000000 --- a/plugin/converter/testdata/input.jsonnet +++ /dev/null @@ -1,18 +0,0 @@ -local stepName = std.extVar("input.stepName"); -local image = std.extVar("input.image"); -local commands = std.extVar("input.commands"); - -{ - "kind": "pipeline", - "type": "docker", - "name": "default", - "steps": [ - { - "name": stepName, - "image": image, - "commands": [ - commands - ] - } - ] -} diff --git a/plugin/converter/testdata/input.jsonnet.golden b/plugin/converter/testdata/input.jsonnet.golden deleted file mode 100644 index ba8143b76a..0000000000 --- a/plugin/converter/testdata/input.jsonnet.golden +++ /dev/null @@ -1,15 +0,0 @@ ---- -{ - "kind": "pipeline", - "name": "default", - "steps": [ - { - "commands": [ - "my_command" - ], - "image": "my_image", - "name": "my_step" - } - ], - "type": "docker" -} diff --git a/plugin/converter/testdata/jsonnet.template.yml b/plugin/converter/testdata/jsonnet.template.yml deleted file mode 100644 index f9ab53474c..0000000000 --- a/plugin/converter/testdata/jsonnet.template.yml +++ /dev/null @@ -1,6 +0,0 @@ -kind: template -load: plugin.jsonnet -data: - stepName: my_step - image: my_image - commands: my_command diff --git a/plugin/converter/testdata/multi.star b/plugin/converter/testdata/multi.star deleted file mode 100644 index 0ea00f920d..0000000000 --- a/plugin/converter/testdata/multi.star +++ /dev/null @@ -1,6 +0,0 @@ -def main(ctx): - return [{ - 'kind': 'pipeline', - 'type': 'docker', - 'name': 'default' - }] \ No newline at end of file diff --git a/plugin/converter/testdata/multi.star.golden b/plugin/converter/testdata/multi.star.golden deleted file mode 100644 index 4c81db209e..0000000000 --- a/plugin/converter/testdata/multi.star.golden +++ /dev/null @@ -1,2 +0,0 @@ ---- -{"kind": "pipeline", "type": "docker", "name": "default"} diff --git a/plugin/converter/testdata/single.jsonnet b/plugin/converter/testdata/single.jsonnet deleted file mode 100644 index 8640885f8d..0000000000 --- a/plugin/converter/testdata/single.jsonnet +++ /dev/null @@ -1,14 +0,0 @@ -{ - "kind": "pipeline", - "name": "default", - "steps": [ - { - "commands": [ - "my_command" - ], - "image": "my_image", - "name": "my_step" - } - ], - "type": "docker" -} diff --git a/plugin/converter/testdata/single.star b/plugin/converter/testdata/single.star deleted file mode 100644 index 52be305852..0000000000 --- a/plugin/converter/testdata/single.star +++ /dev/null @@ -1,11 +0,0 @@ -def main(ctx): - print(ctx.build) - print(ctx.build.commit) - print(ctx.repo) - print(ctx.repo.namespace) - print(ctx.repo.name) - return { - 'kind': 'pipeline', - 'type': 'docker', - 'name': 'default' - } diff --git a/plugin/converter/testdata/single.star.golden b/plugin/converter/testdata/single.star.golden deleted file mode 100644 index 33a6f44001..0000000000 --- a/plugin/converter/testdata/single.star.golden +++ /dev/null @@ -1 +0,0 @@ -{"kind": "pipeline", "type": "docker", "name": "default"} \ No newline at end of file diff --git a/plugin/converter/testdata/starlark-nested.template.yml b/plugin/converter/testdata/starlark-nested.template.yml deleted file mode 100644 index 401ebab957..0000000000 --- a/plugin/converter/testdata/starlark-nested.template.yml +++ /dev/null @@ -1,8 +0,0 @@ -kind: template -load: test.nested.starlark -data: - builds: - name: build - image: mcr.microsoft.com/dotnet/sdk:5.0 - commands: - - dotnet build \ No newline at end of file diff --git a/plugin/converter/testdata/starlark.input-nested.star b/plugin/converter/testdata/starlark.input-nested.star deleted file mode 100644 index b29fe69d28..0000000000 --- a/plugin/converter/testdata/starlark.input-nested.star +++ /dev/null @@ -1,8 +0,0 @@ -def main(ctx): - return { - "kind": "pipeline", - "name": "default", - "steps": [ - ctx.input.builds - ] - } \ No newline at end of file diff --git a/plugin/converter/testdata/starlark.input-nested.star.golden b/plugin/converter/testdata/starlark.input-nested.star.golden deleted file mode 100644 index 2e8eeeaa9f..0000000000 --- a/plugin/converter/testdata/starlark.input-nested.star.golden +++ /dev/null @@ -1 +0,0 @@ -{"kind": "pipeline", "name": "default", "steps": [{"name": "build", "image": "mcr.microsoft.com/dotnet/sdk:5.0", "commands": ["dotnet build"]}]} \ No newline at end of file diff --git a/plugin/converter/testdata/starlark.input.star b/plugin/converter/testdata/starlark.input.star deleted file mode 100644 index 19ad92b816..0000000000 --- a/plugin/converter/testdata/starlark.input.star +++ /dev/null @@ -1,14 +0,0 @@ -def main(ctx): - return { - "kind": "pipeline", - "name": "build", - "steps": [ - { - "name": ctx.input.stepName, - "image": ctx.input.image, - "commands": [ - ctx.input.commands - ] - } - ] - } diff --git a/plugin/converter/testdata/starlark.input.star.golden b/plugin/converter/testdata/starlark.input.star.golden deleted file mode 100644 index 28a4bbb1bb..0000000000 --- a/plugin/converter/testdata/starlark.input.star.golden +++ /dev/null @@ -1 +0,0 @@ -{"kind": "pipeline", "name": "build", "steps": [{"name": "my_step", "image": "my_image", "commands": ["my_command"]}]} \ No newline at end of file diff --git a/plugin/converter/testdata/starlark.template.yml b/plugin/converter/testdata/starlark.template.yml deleted file mode 100644 index d11f5b3a37..0000000000 --- a/plugin/converter/testdata/starlark.template.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -kind: template -load: plugin.starlark -data: - stepName: my_step - image: my_image - commands: my_command diff --git a/plugin/converter/testdata/yaml.input.golden b/plugin/converter/testdata/yaml.input.golden deleted file mode 100644 index 20b7e9a59b..0000000000 --- a/plugin/converter/testdata/yaml.input.golden +++ /dev/null @@ -1,8 +0,0 @@ -kind: pipeline -type: docker -name: defaults -steps: - - name: MY_STEP - image: my_image - commands: - - my_command \ No newline at end of file diff --git a/plugin/converter/testdata/yaml.input.yml b/plugin/converter/testdata/yaml.input.yml deleted file mode 100644 index 954d25d39e..0000000000 --- a/plugin/converter/testdata/yaml.input.yml +++ /dev/null @@ -1,8 +0,0 @@ -kind: pipeline -type: docker -name: defaults -steps: - - name: {{ upper .input.stepName }} - image: {{ .input.image }} - commands: - - {{ .input.commands }} \ No newline at end of file diff --git a/plugin/converter/testdata/yaml.template.comment.yml b/plugin/converter/testdata/yaml.template.comment.yml deleted file mode 100644 index eba9a589f5..0000000000 --- a/plugin/converter/testdata/yaml.template.comment.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -# this is a comment -kind: template -load: plugin.yaml -data: - stepName: my_step - image: my_image - commands: my_command diff --git a/plugin/converter/testdata/yaml.template.invalid.yml b/plugin/converter/testdata/yaml.template.invalid.yml deleted file mode 100644 index fe06494688..0000000000 --- a/plugin/converter/testdata/yaml.template.invalid.yml +++ /dev/null @@ -1,6 +0,0 @@ -kind: template -load: plugin.txt -data: - stepName: my_step - image: my_image - commands: my_command diff --git a/plugin/converter/testdata/yaml.template.yml b/plugin/converter/testdata/yaml.template.yml deleted file mode 100644 index eb2cbce0fa..0000000000 --- a/plugin/converter/testdata/yaml.template.yml +++ /dev/null @@ -1,6 +0,0 @@ -kind: template -load: plugin.yaml -data: - stepName: my_step - image: my_image - commands: my_command diff --git a/plugin/registry/auths/auth.go b/plugin/registry/auths/auth.go deleted file mode 100644 index b99bb0ae95..0000000000 --- a/plugin/registry/auths/auth.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package auths - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "io" - "os" - "strings" - - "github.com/drone/drone/core" -) - -// config represents the Docker client configuration, -// typically located at ~/.docker/config.json -type config struct { - Auths map[string]struct { - Auth string `json:"auth"` - } `json:"auths"` -} - -// Parse parses the registry credential from the reader. -func Parse(r io.Reader) ([]*core.Registry, error) { - c := new(config) - err := json.NewDecoder(r).Decode(c) - if err != nil { - return nil, err - } - var auths []*core.Registry - for k, v := range c.Auths { - username, password := decode(v.Auth) - auths = append(auths, &core.Registry{ - Address: k, - Username: username, - Password: password, - }) - } - return auths, nil -} - -// ParseFile parses the registry credential file. -func ParseFile(filepath string) ([]*core.Registry, error) { - f, err := os.Open(filepath) - if err != nil { - return nil, err - } - defer f.Close() - return Parse(f) -} - -// ParseString parses the registry credential file. -func ParseString(s string) ([]*core.Registry, error) { - return Parse(strings.NewReader(s)) -} - -// ParseBytes parses the registry credential file. -func ParseBytes(b []byte) ([]*core.Registry, error) { - return Parse(bytes.NewReader(b)) -} - -// encode returns the encoded credentials. -func encode(username, password string) string { - return base64.StdEncoding.EncodeToString( - []byte(username + ":" + password), - ) -} - -// decode returns the decoded credentials. -func decode(s string) (username, password string) { - d, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return - } - parts := strings.SplitN(string(d), ":", 2) - if len(parts) > 0 { - username = parts[0] - } - if len(parts) > 1 { - password = parts[1] - } - return -} diff --git a/plugin/registry/auths/auth_test.go b/plugin/registry/auths/auth_test.go deleted file mode 100644 index 7f1467a4a9..0000000000 --- a/plugin/registry/auths/auth_test.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package auths - -import ( - "os" - "testing" - - "github.com/drone/drone/core" - "github.com/google/go-cmp/cmp" -) - -func TestParse(t *testing.T) { - got, err := ParseString(sample) - if err != nil { - t.Error(err) - return - } - want := []*core.Registry{ - { - Address: "https://index.docker.io/v1/", - Username: "octocat", - Password: "correct-horse-battery-staple", - }, - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestParseBytes(t *testing.T) { - got, err := ParseBytes([]byte(sample)) - if err != nil { - t.Error(err) - return - } - want := []*core.Registry{ - { - Address: "https://index.docker.io/v1/", - Username: "octocat", - Password: "correct-horse-battery-staple", - }, - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestParseErr(t *testing.T) { - _, err := ParseString("") - if err == nil { - t.Errorf("Expect unmarshal error") - } -} - -func TestParseFile(t *testing.T) { - got, err := ParseFile("./testdata/config.json") - if err != nil { - t.Error(err) - return - } - want := []*core.Registry{ - { - Address: "https://index.docker.io/v1/", - Username: "octocat", - Password: "correct-horse-battery-staple", - }, - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestParseFileErr(t *testing.T) { - _, err := ParseFile("./testdata/x.json") - if _, ok := err.(*os.PathError); !ok { - t.Errorf("Expect error when file does not exist") - } -} - -func TestEncodeDecode(t *testing.T) { - username := "octocat" - password := "correct-horse-battery-staple" - - encoded := encode(username, password) - decodedUsername, decodedPassword := decode(encoded) - if got, want := decodedUsername, username; got != want { - t.Errorf("Want decoded username %s, got %s", want, got) - } - if got, want := decodedPassword, password; got != want { - t.Errorf("Want decoded password %s, got %s", want, got) - } -} - -func TestDecodeInvalid(t *testing.T) { - username, password := decode("b2N0b2NhdDp==") - if username != "" || password != "" { - t.Errorf("Expect decoding error") - } -} - -var sample = `{ - "auths": { - "https://index.docker.io/v1/": { - "auth": "b2N0b2NhdDpjb3JyZWN0LWhvcnNlLWJhdHRlcnktc3RhcGxl" - } - } -}` diff --git a/plugin/registry/auths/testdata/config.json b/plugin/registry/auths/testdata/config.json deleted file mode 100644 index 382c6908b1..0000000000 --- a/plugin/registry/auths/testdata/config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "auths": { - "https://index.docker.io/v1/": { - "auth": "b2N0b2NhdDpjb3JyZWN0LWhvcnNlLWJhdHRlcnktc3RhcGxl" - } - } -} \ No newline at end of file diff --git a/plugin/registry/auths/testdata/config2.json b/plugin/registry/auths/testdata/config2.json deleted file mode 100644 index 4ce3413de0..0000000000 --- a/plugin/registry/auths/testdata/config2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "auths": { - "https://gcr.io": { - "auth": "b2N0b2NhdDpjb3JyZWN0LWhvcnNlLWJhdHRlcnktc3RhcGxl" - } - } -} \ No newline at end of file diff --git a/plugin/registry/combine.go b/plugin/registry/combine.go deleted file mode 100644 index bb5da54670..0000000000 --- a/plugin/registry/combine.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package registry - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/logger" - - "github.com/sirupsen/logrus" -) - -// Combine combines the registry services, allowing the -// system to source registry credential from multiple sources. -func Combine(services ...core.RegistryService) core.RegistryService { - return &combined{services} -} - -type combined struct { - sources []core.RegistryService -} - -func (c *combined) List(ctx context.Context, req *core.RegistryArgs) ([]*core.Registry, error) { - var all []*core.Registry - for _, source := range c.sources { - list, err := source.List(ctx, req) - if err != nil { - return all, err - } - all = append(all, list...) - } - // if trace level debugging is enabled we print - // all registry credentials retrieved from the - // various registry sources. - logger := logger.FromContext(ctx) - if logrus.IsLevelEnabled(logrus.TraceLevel) { - if len(all) == 0 { - logger.Traceln("registry: no registry credentials loaded") - } - for _, registry := range all { - logger.WithField("address", registry.Address). - Traceln("registry: registry credentials loaded") - } - } - return all, nil -} diff --git a/plugin/registry/combine_test.go b/plugin/registry/combine_test.go deleted file mode 100644 index e7c95f1014..0000000000 --- a/plugin/registry/combine_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package registry - -import ( - "os" - "testing" - - "github.com/drone/drone/core" - "github.com/google/go-cmp/cmp" -) - -func TestCombineSources(t *testing.T) { - source := Combine( - FileSource("./auths/testdata/config.json"), - FileSource("./auths/testdata/config2.json"), - FileSource(""), // no source file, must not error - ) - got, err := source.List(noContext, &core.RegistryArgs{}) - if err != nil { - t.Error(err) - return - } - want := []*core.Registry{ - { - Address: "https://index.docker.io/v1/", - Username: "octocat", - Password: "correct-horse-battery-staple", - }, - { - Address: "https://gcr.io", - Username: "octocat", - Password: "correct-horse-battery-staple", - }, - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestCombineSources_Err(t *testing.T) { - source := Combine( - FileSource("./auths/testdata/config.json"), - FileSource("./auths/testdata/x.json"), - ) - _, err := source.List(noContext, &core.RegistryArgs{}) - if _, ok := err.(*os.PathError); !ok { - t.Errorf("Expect error when file does not exist") - } -} diff --git a/plugin/registry/encrypted.go b/plugin/registry/encrypted.go deleted file mode 100644 index f7a76a9331..0000000000 --- a/plugin/registry/encrypted.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package registry - -import ( - "context" - "crypto/aes" - "crypto/cipher" - "encoding/base64" - "errors" - - "github.com/drone/drone-yaml/yaml" - "github.com/drone/drone/core" - "github.com/drone/drone/logger" - "github.com/drone/drone/plugin/registry/auths" -) - -// Encrypted returns a new encrypted registry credentials -// provider that sources credentials from the encrypted strings -// in the yaml file. -func Encrypted() core.RegistryService { - return new(encrypted) -} - -type encrypted struct { -} - -func (c *encrypted) List(ctx context.Context, in *core.RegistryArgs) ([]*core.Registry, error) { - var results []*core.Registry - - for _, match := range in.Pipeline.PullSecrets { - logger := logger.FromContext(ctx). - WithField("name", match). - WithField("kind", "secret") - logger.Trace("image_pull_secrets: find encrypted secret") - - // lookup the named secret in the manifest. If the - // secret does not exist, return a nil variable, - // allowing the next secret controller in the chain - // to be invoked. - data, ok := getEncrypted(in.Conf, match) - if !ok { - logger.Trace("image_pull_secrets: no matching encrypted secret in yaml") - return nil, nil - } - - decoded, err := base64.StdEncoding.DecodeString(string(data)) - if err != nil { - logger.WithError(err).Trace("image_pull_secrets: cannot decode secret") - return nil, err - } - - decrypted, err := decrypt(decoded, []byte(in.Repo.Secret)) - if err != nil { - logger.WithError(err).Trace("image_pull_secrets: cannot decrypt secret") - return nil, err - } - - parsed, err := auths.ParseBytes(decrypted) - if err != nil { - logger.WithError(err).Trace("image_pull_secrets: cannot parse decrypted secret") - return nil, err - } - - logger.Trace("image_pull_secrets: found encrypted secret") - results = append(results, parsed...) - } - - return results, nil -} - -func getEncrypted(manifest *yaml.Manifest, match string) (data string, ok bool) { - for _, resource := range manifest.Resources { - secret, ok := resource.(*yaml.Secret) - if !ok { - continue - } - if secret.Name != match { - continue - } - if secret.Data == "" { - continue - } - return secret.Data, true - } - return -} - -func decrypt(ciphertext []byte, key []byte) (plaintext []byte, err error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - if len(ciphertext) < gcm.NonceSize() { - return nil, errors.New("malformed ciphertext") - } - - return gcm.Open(nil, - ciphertext[:gcm.NonceSize()], - ciphertext[gcm.NonceSize():], - nil, - ) -} diff --git a/plugin/registry/endpoint.go b/plugin/registry/endpoint.go deleted file mode 100644 index f69c9c4112..0000000000 --- a/plugin/registry/endpoint.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package registry - -import ( - "context" - - "github.com/drone/drone-go/plugin/registry" - "github.com/drone/drone/core" - "github.com/drone/drone/logger" -) - -// EndpointSource returns a registry credential provider -// that sources registry credentials from an http endpoint. -func EndpointSource(endpoint, secret string, skipVerify bool) core.RegistryService { - return &service{ - endpoint: endpoint, - secret: secret, - skipVerify: skipVerify, - } -} - -type service struct { - endpoint string - secret string - skipVerify bool -} - -func (c *service) List(ctx context.Context, in *core.RegistryArgs) ([]*core.Registry, error) { - if c.endpoint == "" { - return nil, nil - } - logger := logger.FromContext(ctx) - logger.Trace("registry: plugin: get credentials") - - req := ®istry.Request{ - Repo: toRepo(in.Repo), - Build: toBuild(in.Build), - } - client := registry.Client(c.endpoint, c.secret, c.skipVerify) - res, err := client.List(ctx, req) - if err != nil { - logger.WithError(err).Warn("registry: plugin: cannot get credentials") - return nil, err - } - - var registries []*core.Registry - for _, registry := range res { - registries = append(registries, &core.Registry{ - Address: registry.Address, - Username: registry.Username, - Password: registry.Password, - }) - logger.WithField("address", registry.Address). - Trace("registry: plugin: found credentials") - } - return registries, nil -} diff --git a/plugin/registry/endpoint_oss.go b/plugin/registry/endpoint_oss.go deleted file mode 100644 index f94c3892c1..0000000000 --- a/plugin/registry/endpoint_oss.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package registry - -import "github.com/drone/drone/core" - -// EndpointSource returns a no-op registry credential provider. -func EndpointSource(string, string, bool) core.RegistryService { - return new(noop) -} diff --git a/plugin/registry/endpoint_test.go b/plugin/registry/endpoint_test.go deleted file mode 100644 index e5bbde55e2..0000000000 --- a/plugin/registry/endpoint_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package registry - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/google/go-cmp/cmp" - "github.com/h2non/gock" -) - -var noContext = context.TODO() - -func TestEndpointSource(t *testing.T) { - defer gock.Off() - - gock.New("https://company.com"). - Post("/auths"). - MatchHeader("Accept", "application/vnd.drone.registry.v1\\+json"). - MatchHeader("Accept-Encoding", "identity"). - MatchHeader("Content-Type", "application/json"). - Reply(200). - BodyString(`[{"address":"index.docker.io","username":"octocat","password":"pa55word"}]`). - Done() - - service := EndpointSource("https://company.com/auths", "GMEuUHQfmrMRsseWxi9YlIeBtn9lm6im", false) - got, err := service.List(noContext, &core.RegistryArgs{Repo: &core.Repository{}, Build: &core.Build{}}) - if err != nil { - t.Error(err) - return - } - - want := []*core.Registry{ - { - Address: "index.docker.io", - Username: "octocat", - Password: "pa55word", - }, - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - return - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - return - } -} - -func TestEndpointSource_Err(t *testing.T) { - defer gock.Off() - - gock.New("https://company.com"). - Post("/auths"). - MatchHeader("Accept", "application/vnd.drone.registry.v1\\+json"). - MatchHeader("Accept-Encoding", "identity"). - MatchHeader("Content-Type", "application/json"). - Reply(404) - - service := EndpointSource("https://company.com/auths", "GMEuUHQfmrMRsseWxi9YlIeBtn9lm6im", false) - _, err := service.List(noContext, &core.RegistryArgs{Repo: &core.Repository{}, Build: &core.Build{}}) - if err == nil { - t.Errorf("Expect http.Response error") - } else if err.Error() != "Not Found" { - t.Errorf("Expect Not Found error") - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestNotConfigured(t *testing.T) { - service := EndpointSource("", "", false) - registry, err := service.List(noContext, &core.RegistryArgs{}) - if err != nil { - t.Error(err) - } - if registry != nil { - t.Errorf("Expect nil registry") - } -} diff --git a/plugin/registry/external.go b/plugin/registry/external.go deleted file mode 100644 index a3d2d51816..0000000000 --- a/plugin/registry/external.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package registry - -import ( - "context" - "time" - - "github.com/drone/drone-go/plugin/secret" - "github.com/drone/drone-yaml/yaml" - "github.com/drone/drone/core" - "github.com/drone/drone/logger" - "github.com/drone/drone/plugin/registry/auths" - - droneapi "github.com/drone/drone-go/drone" -) - -// External returns a new external Secret controller. -func External(endpoint, secret string, skipVerify bool) core.RegistryService { - return &externalController{ - endpoint: endpoint, - secret: secret, - skipVerify: skipVerify, - } -} - -type externalController struct { - endpoint string - secret string - skipVerify bool -} - -func (c *externalController) List(ctx context.Context, in *core.RegistryArgs) ([]*core.Registry, error) { - var results []*core.Registry - - for _, match := range in.Pipeline.PullSecrets { - logger := logger.FromContext(ctx). - WithField("name", match). - WithField("kind", "secret"). - WithField("secret", c.endpoint) - logger.Trace("image_pull_secrets: find secret") - - // lookup the named secret in the manifest. If the - // secret does not exist, return a nil variable, - // allowing the next secret controller in the chain - // to be invoked. - path, name, ok := getExternal(in.Conf, match) - if !ok { - logger.Trace("image_pull_secrets: no matching secret resource in yaml") - return nil, nil - } - - logger = logger. - WithField("get.path", path). - WithField("get.name", name) - - // include a timeout to prevent an API call from - // hanging the build process indefinitely. The - // external service must return a request within - // one minute. - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - - req := &secret.Request{ - Name: name, - Path: path, - Repo: toRepo(in.Repo), - Build: toBuild(in.Build), - } - client := secret.Client(c.endpoint, c.secret, c.skipVerify) - res, err := client.Find(ctx, req) - if err != nil { - logger.WithError(err).Trace("image_pull_secrets: cannot get secret") - return nil, err - } - - // if no error is returned and the secret is empty, - // this indicates the client returned No Content, - // and we should exit with no secret, but no error. - if res.Data == "" { - return nil, nil - } - - // The secret can be restricted to non-pull request - // events. If the secret is restricted, return - // empty results. - if (res.Pull == false && res.PullRequest == false) && - in.Build.Event == core.EventPullRequest { - logger.WithError(err).Trace("image_pull_secrets: pull_request access denied") - return nil, nil - } - - parsed, err := auths.ParseString(res.Data) - if err != nil { - return nil, err - } - - logger.Trace("image_pull_secrets: found secret") - results = append(results, parsed...) - } - - return results, nil -} - -func getExternal(manifest *yaml.Manifest, match string) (path, name string, ok bool) { - for _, resource := range manifest.Resources { - secret, ok := resource.(*yaml.Secret) - if !ok { - continue - } - if secret.Name != match { - continue - } - if secret.Get.Name == "" && secret.Get.Path == "" { - continue - } - return secret.Get.Path, secret.Get.Name, true - } - return -} - -func toRepo(from *core.Repository) droneapi.Repo { - return droneapi.Repo{ - ID: from.ID, - UID: from.UID, - UserID: from.UserID, - Namespace: from.Namespace, - Name: from.Name, - Slug: from.Slug, - SCM: from.SCM, - HTTPURL: from.HTTPURL, - SSHURL: from.SSHURL, - Link: from.Link, - Branch: from.Branch, - Private: from.Private, - Visibility: from.Visibility, - Active: from.Active, - Config: from.Config, - Trusted: from.Trusted, - Protected: from.Protected, - Timeout: from.Timeout, - } -} - -func toBuild(from *core.Build) droneapi.Build { - return droneapi.Build{ - ID: from.ID, - RepoID: from.RepoID, - Trigger: from.Trigger, - Number: from.Number, - Parent: from.Parent, - Status: from.Status, - Error: from.Error, - Event: from.Event, - Action: from.Action, - Link: from.Link, - Timestamp: from.Timestamp, - Title: from.Title, - Message: from.Message, - Before: from.Before, - After: from.After, - Ref: from.Ref, - Fork: from.Fork, - Source: from.Source, - Target: from.Target, - Author: from.Author, - AuthorName: from.AuthorName, - AuthorEmail: from.AuthorEmail, - AuthorAvatar: from.AuthorAvatar, - Sender: from.Sender, - Params: from.Params, - Deploy: from.Deploy, - Started: from.Started, - Finished: from.Finished, - Created: from.Created, - Updated: from.Updated, - Version: from.Version, - } -} diff --git a/plugin/registry/external_oss.go b/plugin/registry/external_oss.go deleted file mode 100644 index 683782066a..0000000000 --- a/plugin/registry/external_oss.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package registry - -import "github.com/drone/drone/core" - -// External returns a no-op registry credential provider. -func External(string, string, bool) core.RegistryService { - return new(noop) -} diff --git a/plugin/registry/external_test.go b/plugin/registry/external_test.go deleted file mode 100644 index 1bfb397da2..0000000000 --- a/plugin/registry/external_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package registry diff --git a/plugin/registry/file.go b/plugin/registry/file.go deleted file mode 100644 index 4b7755aa76..0000000000 --- a/plugin/registry/file.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package registry - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/plugin/registry/auths" - - "github.com/sirupsen/logrus" -) - -// FileSource returns a registry credential provider that -// sources registry credentials from a .docker/config.json file. -func FileSource(path string) core.RegistryService { - return ®istryConfig{ - path: path, - } -} - -type registryConfig struct { - path string -} - -func (r *registryConfig) List(ctx context.Context, req *core.RegistryArgs) ([]*core.Registry, error) { - // configuration of the .docker/config.json file path - // is optional. Ignore if empty string. - if r.path == "" { - return nil, nil - } - - logger := logrus.WithField("config", r.path) - logger.Traceln("registry: parsing docker config.json file") - - regs, err := auths.ParseFile(r.path) - if err != nil { - logger.WithError(err).Errorln("registry: cannot parse docker config.json file") - return nil, err - } - - return regs, err -} diff --git a/plugin/registry/file_oss.go b/plugin/registry/file_oss.go deleted file mode 100644 index fef39cb084..0000000000 --- a/plugin/registry/file_oss.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package registry - -import "github.com/drone/drone/core" - -// FileSource returns a no-op registry credential provider. -func FileSource(string) core.RegistryService { - return new(noop) -} diff --git a/plugin/registry/file_test.go b/plugin/registry/file_test.go deleted file mode 100644 index 40f9eb310e..0000000000 --- a/plugin/registry/file_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package registry - -import ( - "os" - "testing" - - "github.com/drone/drone/core" - "github.com/google/go-cmp/cmp" -) - -func TestFileSource(t *testing.T) { - source := FileSource("./auths/testdata/config.json") - got, err := source.List(noContext, &core.RegistryArgs{}) - if err != nil { - t.Error(err) - } - want := []*core.Registry{ - { - Address: "https://index.docker.io/v1/", - Username: "octocat", - Password: "correct-horse-battery-staple", - }, - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestFileSourceErr(t *testing.T) { - source := FileSource("./auths/testdata/x.json") - _, err := source.List(noContext, &core.RegistryArgs{}) - if _, ok := err.(*os.PathError); !ok { - t.Errorf("Expect error when file does not exist") - } -} diff --git a/plugin/registry/noop.go b/plugin/registry/noop.go deleted file mode 100644 index 8e2e77e241..0000000000 --- a/plugin/registry/noop.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package registry - -import ( - "context" - - "github.com/drone/drone/core" -) - -type noop struct{} - -func (noop) List(context.Context, *core.RegistryArgs) ([]*core.Registry, error) { - return nil, nil -} diff --git a/plugin/registry/static.go b/plugin/registry/static.go deleted file mode 100644 index 5f734fd2f1..0000000000 --- a/plugin/registry/static.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package registry - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/logger" - "github.com/drone/drone/plugin/registry/auths" -) - -// Static returns a new static credentials controller. -func Static(secrets []*core.Secret) core.RegistryService { - return &staticController{secrets: secrets} -} - -type staticController struct { - secrets []*core.Secret -} - -func (c *staticController) List(ctx context.Context, in *core.RegistryArgs) ([]*core.Registry, error) { - static := map[string]*core.Secret{} - for _, secret := range c.secrets { - static[secret.Name] = secret - } - - var results []*core.Registry - for _, name := range in.Pipeline.PullSecrets { - logger := logger.FromContext(ctx).WithField("name", name) - logger.Trace("registry: database: find secret") - - secret, ok := static[name] - if !ok { - logger.Trace("registry: database: cannot find secret") - continue - } - - // The secret can be restricted to non-pull request - // events. If the secret is restricted, return - // empty results. - if secret.PullRequest == false && - in.Build.Event == core.EventPullRequest { - logger.Trace("registry: database: pull_request access denied") - continue - } - - logger.Trace("registry: database: secret found") - parsed, err := auths.ParseString(secret.Data) - if err != nil { - logger.WithError(err).Error("registry: database: parsing error") - return nil, err - } - - results = append(results, parsed...) - } - return results, nil -} diff --git a/plugin/registry/static_test.go b/plugin/registry/static_test.go deleted file mode 100644 index 2eb8b2cdc6..0000000000 --- a/plugin/registry/static_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package registry - -import ( - "testing" - - "github.com/drone/drone-yaml/yaml" - "github.com/drone/drone/core" - "github.com/google/go-cmp/cmp" -) - -var mockDockerAuthConfig = `{ - "auths": { - "https://index.docker.io/v1/": { - "auth": "b2N0b2NhdDpjb3JyZWN0LWhvcnNlLWJhdHRlcnktc3RhcGxl" - } - } -}` - -func TestStatic(t *testing.T) { - secrets := []*core.Secret{ - { - Name: "dockerhub", - Data: mockDockerAuthConfig, - }, - } - - manifest, err := yaml.ParseString("kind: pipeline\nimage_pull_secrets: [ dockerhub ]") - if err != nil { - t.Error(err) - return - } - - args := &core.RegistryArgs{ - Build: &core.Build{Event: core.EventPush}, - Conf: manifest, - Pipeline: manifest.Resources[0].(*yaml.Pipeline), - } - service := Static(secrets) - got, err := service.List(noContext, args) - if err != nil { - t.Error(err) - return - } - - want := []*core.Registry{ - { - Address: "https://index.docker.io/v1/", - Username: "octocat", - Password: "correct-horse-battery-staple", - }, - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - return - } -} - -func TestStatic_NoMatch(t *testing.T) { - secrets := []*core.Secret{ - { - Name: "dockerhub", - Data: mockDockerAuthConfig, - }, - } - - manifest, err := yaml.ParseString("kind: pipeline\nimage_pull_secrets: [ unknown ]") - if err != nil { - t.Error(err) - return - } - - args := &core.RegistryArgs{ - Build: &core.Build{Event: core.EventPush}, - Conf: manifest, - Pipeline: manifest.Resources[0].(*yaml.Pipeline), - } - service := Static(secrets) - got, err := service.List(noContext, args) - if err != nil { - t.Error(err) - return - } - if len(got) != 0 { - t.Errorf("Expect no results") - } -} - -func TestStatic_DisablePullRequest(t *testing.T) { - secrets := []*core.Secret{ - { - Name: "dockerhub", - Data: mockDockerAuthConfig, - PullRequest: false, - }, - } - - manifest, err := yaml.ParseString("kind: pipeline\nimage_pull_secrets: [ dockerhub ]") - if err != nil { - t.Error(err) - return - } - - args := &core.RegistryArgs{ - Build: &core.Build{Event: core.EventPullRequest}, - Conf: manifest, - Pipeline: manifest.Resources[0].(*yaml.Pipeline), - } - service := Static(secrets) - got, err := service.List(noContext, args) - if err != nil { - t.Error(err) - return - } - if len(got) != 0 { - t.Errorf("Expect no results") - } -} diff --git a/plugin/secret/combine.go b/plugin/secret/combine.go deleted file mode 100644 index 9c85ce8017..0000000000 --- a/plugin/secret/combine.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package secret - -import ( - "context" - "strings" - - "github.com/drone/drone/core" -) - -// Combine combines the secret services, allowing the system -// to get pipeline secrets from multiple sources. -func Combine(services ...core.SecretService) core.SecretService { - return &combined{services} -} - -type combined struct { - sources []core.SecretService -} - -func (c *combined) Find(ctx context.Context, in *core.SecretArgs) (*core.Secret, error) { - // Ignore any requests for the .docker/config.json file. - // This file is reserved for internal use only, and is - // never exposed to the build environment. - if isDockerConfig(in.Name) { - return nil, nil - } - - for _, source := range c.sources { - secret, err := source.Find(ctx, in) - if err != nil { - return nil, err - } - if secret == nil { - continue - } - // if the secret object is not nil, but is empty - // we should assume the secret service returned a - // 204 no content, and proceed to the next service - // in the chain. - if secret.Data == "" { - continue - } - return secret, nil - } - return nil, nil -} - -// helper function returns true if the build event matches the -// docker_auth_config variable name. -func isDockerConfig(name string) bool { - return strings.EqualFold(name, "docker_auth_config") || - strings.EqualFold(name, ".dockerconfigjson") || - strings.EqualFold(name, ".dockerconfig") -} diff --git a/plugin/secret/combine_test.go b/plugin/secret/combine_test.go deleted file mode 100644 index fb44c68461..0000000000 --- a/plugin/secret/combine_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package secret diff --git a/plugin/secret/encrypted.go b/plugin/secret/encrypted.go deleted file mode 100644 index fb65feaeae..0000000000 --- a/plugin/secret/encrypted.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package secret - -import ( - "context" - "crypto/aes" - "crypto/cipher" - "encoding/base64" - "errors" - - "github.com/drone/drone-yaml/yaml" - "github.com/drone/drone/core" - "github.com/drone/drone/logger" -) - -// Encrypted returns a new encrypted Secret controller. -func Encrypted() core.SecretService { - return new(encrypted) -} - -type encrypted struct { -} - -func (c *encrypted) Find(ctx context.Context, in *core.SecretArgs) (*core.Secret, error) { - logger := logger.FromContext(ctx). - WithField("name", in.Name). - WithField("kind", "secret") - - // lookup the named secret in the manifest. If the - // secret does not exist, return a nil variable, - // allowing the next secret controller in the chain - // to be invoked. - data, ok := getEncrypted(in.Conf, in.Name) - if !ok { - logger.Trace("secret: encrypted: no matching secret") - return nil, nil - } - - // if the build event is a pull request and the source - // repository is a fork, the secret is not exposed to - // the pipeline, for security reasons. - if in.Repo.Private == false && - in.Build.Event == core.EventPullRequest && - in.Build.Fork != "" { - logger.Trace("secret: encrypted: restricted from forks") - return nil, nil - } - - decoded, err := base64.StdEncoding.DecodeString(string(data)) - if err != nil { - logger.WithError(err).Trace("secret: encrypted: cannot decode") - return nil, err - } - - decrypted, err := decrypt(decoded, []byte(in.Repo.Secret)) - if err != nil { - logger.WithError(err).Trace("secret: encrypted: cannot decrypt") - return nil, err - } - - logger.Trace("secret: encrypted: found matching secret") - - return &core.Secret{ - Name: in.Name, - Data: string(decrypted), - }, nil -} - -func getEncrypted(manifest *yaml.Manifest, match string) (data string, ok bool) { - for _, resource := range manifest.Resources { - secret, ok := resource.(*yaml.Secret) - if !ok { - continue - } - if secret.Name != match { - continue - } - if secret.Data == "" { - continue - } - return secret.Data, true - } - return -} - -func decrypt(ciphertext []byte, key []byte) (plaintext []byte, err error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - if len(ciphertext) < gcm.NonceSize() { - return nil, errors.New("malformed ciphertext") - } - - return gcm.Open(nil, - ciphertext[:gcm.NonceSize()], - ciphertext[gcm.NonceSize():], - nil, - ) -} diff --git a/plugin/secret/encrypted_test.go b/plugin/secret/encrypted_test.go deleted file mode 100644 index 69a03023f9..0000000000 --- a/plugin/secret/encrypted_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package secret diff --git a/plugin/secret/external.go b/plugin/secret/external.go deleted file mode 100644 index e74dbc13d8..0000000000 --- a/plugin/secret/external.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secret - -import ( - "context" - "time" - - "github.com/drone/drone-yaml/yaml" - "github.com/drone/drone/core" - "github.com/drone/drone/logger" - - "github.com/drone/drone-go/drone" - "github.com/drone/drone-go/plugin/secret" -) - -// External returns a new external Secret controller. -func External(endpoint, secret string, skipVerify bool) core.SecretService { - return &externalController{ - endpoint: endpoint, - secret: secret, - skipVerify: skipVerify, - } -} - -type externalController struct { - endpoint string - secret string - skipVerify bool -} - -func (c *externalController) Find(ctx context.Context, in *core.SecretArgs) (*core.Secret, error) { - if c.endpoint == "" { - return nil, nil - } - - logger := logger.FromContext(ctx). - WithField("name", in.Name). - WithField("kind", "secret") - - // lookup the named secret in the manifest. If the - // secret does not exist, return a nil variable, - // allowing the next secret controller in the chain - // to be invoked. - path, name, ok := getExternal(in.Conf, in.Name) - if !ok { - logger.Trace("secret: external: no matching secret") - return nil, nil - } - - // include a timeout to prevent an API call from - // hanging the build process indefinitely. The - // external service must return a request within - // one minute. - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - - req := &secret.Request{ - Name: name, - Path: path, - Repo: toRepo(in.Repo), - Build: toBuild(in.Build), - } - client := secret.Client(c.endpoint, c.secret, c.skipVerify) - res, err := client.Find(ctx, req) - if err != nil { - logger.WithError(err).Trace("secret: external: cannot get secret") - return nil, err - } - - // if no error is returned and the secret is empty, - // this indicates the client returned No Content, - // and we should exit with no secret, but no error. - if res.Data == "" { - logger.Trace("secret: external: secret disabled for pull requests") - return nil, nil - } - - // the secret can be restricted to non-pull request - // events. If the secret is restricted, return - // empty results. - if (res.Pull == false && res.PullRequest == false) && - in.Build.Event == core.EventPullRequest { - logger.Trace("secret: external: restricted from forks") - return nil, nil - } - - logger.Trace("secret: external: found matching secret") - - return &core.Secret{ - Name: in.Name, - Data: res.Data, - PullRequest: res.Pull, - }, nil -} - -func getExternal(manifest *yaml.Manifest, match string) (path, name string, ok bool) { - for _, resource := range manifest.Resources { - secret, ok := resource.(*yaml.Secret) - if !ok { - continue - } - if secret.Name != match { - continue - } - if secret.Get.Name == "" && secret.Get.Path == "" { - continue - } - return secret.Get.Path, secret.Get.Name, true - } - return -} - -func toRepo(from *core.Repository) drone.Repo { - return drone.Repo{ - ID: from.ID, - UID: from.UID, - UserID: from.UserID, - Namespace: from.Namespace, - Name: from.Name, - Slug: from.Slug, - SCM: from.SCM, - HTTPURL: from.HTTPURL, - SSHURL: from.SSHURL, - Link: from.Link, - Branch: from.Branch, - Private: from.Private, - Visibility: from.Visibility, - Active: from.Active, - Config: from.Config, - Trusted: from.Trusted, - Protected: from.Protected, - Timeout: from.Timeout, - } -} - -func toBuild(from *core.Build) drone.Build { - return drone.Build{ - ID: from.ID, - RepoID: from.RepoID, - Trigger: from.Trigger, - Number: from.Number, - Parent: from.Parent, - Status: from.Status, - Error: from.Error, - Event: from.Event, - Action: from.Action, - Link: from.Link, - Timestamp: from.Timestamp, - Title: from.Title, - Message: from.Message, - Before: from.Before, - After: from.After, - Ref: from.Ref, - Fork: from.Fork, - Source: from.Source, - Target: from.Target, - Author: from.Author, - AuthorName: from.AuthorName, - AuthorEmail: from.AuthorEmail, - AuthorAvatar: from.AuthorAvatar, - Sender: from.Sender, - Params: from.Params, - Deploy: from.Deploy, - Started: from.Started, - Finished: from.Finished, - Created: from.Created, - Updated: from.Updated, - Version: from.Version, - } -} diff --git a/plugin/secret/external_oss.go b/plugin/secret/external_oss.go deleted file mode 100644 index 04500f0275..0000000000 --- a/plugin/secret/external_oss.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package secret - -import ( - "context" - - "github.com/drone/drone/core" -) - -// External returns a no-op registry secret provider. -func External(string, string, bool) core.SecretService { - return new(noop) -} - -type noop struct{} - -func (noop) Find(context.Context, *core.SecretArgs) (*core.Secret, error) { - return nil, nil -} diff --git a/plugin/secret/external_test.go b/plugin/secret/external_test.go deleted file mode 100644 index fb44c68461..0000000000 --- a/plugin/secret/external_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package secret diff --git a/plugin/secret/static.go b/plugin/secret/static.go deleted file mode 100644 index b531e44b08..0000000000 --- a/plugin/secret/static.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package secret - -import ( - "context" - "strings" - - "github.com/drone/drone/core" -) - -// Static returns a new static Secret controller. -func Static(secrets []*core.Secret) core.SecretService { - return &staticController{secrets: secrets} -} - -type staticController struct { - secrets []*core.Secret -} - -func (c *staticController) Find(ctx context.Context, in *core.SecretArgs) (*core.Secret, error) { - for _, secret := range c.secrets { - if !strings.EqualFold(secret.Name, in.Name) { - continue - } - // The secret can be restricted to non-pull request - // events. If the secret is restricted, return - // empty results. - if secret.PullRequest == false && - in.Build.Event == core.EventPullRequest { - continue - } - return secret, nil - } - return nil, nil -} diff --git a/plugin/secret/static_test.go b/plugin/secret/static_test.go deleted file mode 100644 index 6c6f430620..0000000000 --- a/plugin/secret/static_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package secret - -import ( - "context" - "testing" - - "github.com/drone/drone/core" -) - -var noContext = context.Background() - -func TestStatic(t *testing.T) { - secrets := []*core.Secret{ - {Name: "docker_username"}, - {Name: "docker_password"}, - } - args := &core.SecretArgs{ - Name: "docker_password", - Build: &core.Build{Event: core.EventPush}, - } - service := Static(secrets) - secret, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - if secret != secrets[1] { - t.Errorf("expect docker_password") - } -} - -func TestStaticNotFound(t *testing.T) { - secrets := []*core.Secret{ - {Name: "docker_username"}, - {Name: "docker_password"}, - } - args := &core.SecretArgs{ - Name: "slack_token", - Build: &core.Build{Event: core.EventPush}, - } - service := Static(secrets) - secret, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - if secret != nil { - t.Errorf("Expect secret not found") - } -} - -func TestStaticPullRequestDisabled(t *testing.T) { - secrets := []*core.Secret{ - {Name: "docker_username"}, - {Name: "docker_password", PullRequest: false}, - } - args := &core.SecretArgs{ - Name: "docker_password", - Build: &core.Build{Event: core.EventPullRequest}, - } - service := Static(secrets) - secret, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - if secret != nil { - t.Errorf("Expect secret not found") - } -} - -func TestStaticPullRequestEnabled(t *testing.T) { - secrets := []*core.Secret{ - {Name: "docker_username"}, - {Name: "docker_password", PullRequest: true}, - } - args := &core.SecretArgs{ - Name: "docker_password", - Build: &core.Build{Event: core.EventPullRequest}, - } - service := Static(secrets) - secret, err := service.Find(noContext, args) - if err != nil { - t.Error(err) - return - } - if err != nil { - t.Error(err) - return - } - if secret != secrets[1] { - t.Errorf("expect docker_username") - } -} diff --git a/plugin/validator/combine.go b/plugin/validator/combine.go deleted file mode 100644 index c771c88eb8..0000000000 --- a/plugin/validator/combine.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validator - -import ( - "context" - - "github.com/drone/drone/core" -) - -// Combine combines the conversion services, provision support -// for multiple conversion utilities. -func Combine(services ...core.ValidateService) core.ValidateService { - return &combined{services} -} - -type combined struct { - sources []core.ValidateService -} - -func (c *combined) Validate(ctx context.Context, req *core.ValidateArgs) error { - for _, source := range c.sources { - if err := source.Validate(ctx, req); err != nil { - return err - } - } - return nil -} diff --git a/plugin/validator/combine_test.go b/plugin/validator/combine_test.go deleted file mode 100644 index 22627c7a38..0000000000 --- a/plugin/validator/combine_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package validator - -import ( - "context" - "errors" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -var noContext = context.Background() - -var mockFile = ` -kind: pipeline -type: docker -name: testing -` - -func TestCombine(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - args := &core.ValidateArgs{ - User: &core.User{Login: "octocat"}, - Repo: &core.Repository{Slug: "octocat/hello-world", Config: ".drone.yml"}, - Build: &core.Build{After: "6d144de7"}, - Config: &core.Config{}, - } - - service := mock.NewMockValidateService(controller) - service.EXPECT().Validate(noContext, args).Return(nil) - - err := Combine(service).Validate(noContext, args) - if err != nil { - t.Error(err) - } -} - -func TestCombineErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - resp := errors.New("") - service := mock.NewMockValidateService(controller) - service.EXPECT().Validate(noContext, nil).Return(resp) - - err := Combine(service).Validate(noContext, nil) - if err != resp { - t.Errorf("expected convert service error") - } -} diff --git a/plugin/validator/filter.go b/plugin/validator/filter.go deleted file mode 100644 index 0a1090511f..0000000000 --- a/plugin/validator/filter.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validator - -import ( - "context" - "path/filepath" - - "github.com/drone/drone/core" -) - -// Filter returns a validation service that skips -// pipelines that do not match the filter criteria. -func Filter(include, exclude []string) core.ValidateService { - return &filter{ - include: include, - exclude: exclude, - } -} - -type filter struct { - include []string - exclude []string -} - -func (f *filter) Validate(ctx context.Context, in *core.ValidateArgs) error { - if len(f.include) > 0 { - for _, pattern := range f.include { - ok, _ := filepath.Match(pattern, in.Repo.Slug) - if ok { - return nil - } - } - - // if the include list is specified, and the - // repository does not match any patterns in - // the include list, it should be skipped. - return core.ErrValidatorSkip - } - - if len(f.exclude) > 0 { - for _, pattern := range f.exclude { - ok, _ := filepath.Match(pattern, in.Repo.Slug) - if ok { - // if the exclude list is specified, and - // the repository matches a pattern in the - // exclude list, it should be skipped. - return core.ErrValidatorSkip - } - } - } - - return nil -} diff --git a/plugin/validator/filter_test.go b/plugin/validator/filter_test.go deleted file mode 100644 index 990cc9a0c6..0000000000 --- a/plugin/validator/filter_test.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validator - -import ( - "testing" - - "github.com/drone/drone/core" -) - -func TestFilter_None(t *testing.T) { - f := Filter(nil, nil) - if err := f.Validate(noContext, nil); err != nil { - t.Error(err) - } -} - -func TestFilter_Include(t *testing.T) { - args := &core.ValidateArgs{ - Repo: &core.Repository{Slug: "octocat/hello-world"}, - } - - f := Filter([]string{"octocat/hello-world"}, nil) - if err := f.Validate(noContext, args); err != nil { - t.Error(err) - } - - f = Filter([]string{"octocat/*"}, nil) - if err := f.Validate(noContext, args); err != nil { - t.Error(err) - } - - f = Filter([]string{"spaceghost/*"}, nil) - if err := f.Validate(noContext, args); err != core.ErrValidatorSkip { - t.Errorf("Expect ErrValidatorSkip, got %s", err) - } -} - -func TestFilter_Exclude(t *testing.T) { - args := &core.ValidateArgs{ - Repo: &core.Repository{Slug: "octocat/hello-world"}, - } - - f := Filter(nil, []string{"octocat/hello-world"}) - if err := f.Validate(noContext, args); err != core.ErrValidatorSkip { - t.Errorf("Expect ErrValidatorSkip, got %s", err) - } - - f = Filter(nil, []string{"octocat/*"}) - if err := f.Validate(noContext, args); err != core.ErrValidatorSkip { - t.Errorf("Expect ErrValidatorSkip, got %s", err) - } - - f = Filter(nil, []string{"spaceghost/*"}) - if err := f.Validate(noContext, args); err != nil { - t.Error(err) - } -} diff --git a/plugin/validator/noop.go b/plugin/validator/noop.go deleted file mode 100644 index 5010301051..0000000000 --- a/plugin/validator/noop.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package validator - -import ( - "context" - - "github.com/drone/drone/core" -) - -type noop struct{} - -func (noop) Validate(context.Context, *core.ValidateArgs) error { return nil } diff --git a/plugin/validator/remote.go b/plugin/validator/remote.go deleted file mode 100644 index 1bdfb43910..0000000000 --- a/plugin/validator/remote.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package validator - -import ( - "context" - "time" - - "github.com/drone/drone-go/drone" - "github.com/drone/drone-go/plugin/validator" - "github.com/drone/drone/core" -) - -// Remote returns a conversion service that converts the -// configuration file using a remote http service. -func Remote(endpoint, signer string, skipVerify bool, timeout time.Duration) core.ValidateService { - return &remote{ - endpoint: endpoint, - secret: signer, - skipVerify: skipVerify, - timeout: timeout, - } -} - -type remote struct { - endpoint string - secret string - skipVerify bool - timeout time.Duration -} - -func (g *remote) Validate(ctx context.Context, in *core.ValidateArgs) error { - if g.endpoint == "" { - return nil - } - // include a timeout to prevent an API call from - // hanging the build process indefinitely. The - // external service must return a response within - // the configured timeout (default 1m). - ctx, cancel := context.WithTimeout(ctx, g.timeout) - defer cancel() - - req := &validator.Request{ - Repo: toRepo(in.Repo), - Build: toBuild(in.Build), - Config: drone.Config{ - Data: in.Config.Data, - }, - } - client := validator.Client(g.endpoint, g.secret, g.skipVerify) - err := client.Validate(ctx, req) - switch err { - case validator.ErrBlock: - return core.ErrValidatorBlock - case validator.ErrSkip: - return core.ErrValidatorSkip - default: - return err - } -} - -func toRepo(from *core.Repository) drone.Repo { - return drone.Repo{ - ID: from.ID, - UID: from.UID, - UserID: from.UserID, - Namespace: from.Namespace, - Name: from.Name, - Slug: from.Slug, - SCM: from.SCM, - HTTPURL: from.HTTPURL, - SSHURL: from.SSHURL, - Link: from.Link, - Branch: from.Branch, - Private: from.Private, - Visibility: from.Visibility, - Active: from.Active, - Config: from.Config, - Trusted: from.Trusted, - Protected: from.Protected, - Timeout: from.Timeout, - } -} - -func toBuild(from *core.Build) drone.Build { - return drone.Build{ - ID: from.ID, - RepoID: from.RepoID, - Trigger: from.Trigger, - Number: from.Number, - Parent: from.Parent, - Status: from.Status, - Error: from.Error, - Event: from.Event, - Action: from.Action, - Link: from.Link, - Timestamp: from.Timestamp, - Title: from.Title, - Message: from.Message, - Before: from.Before, - After: from.After, - Ref: from.Ref, - Fork: from.Fork, - Source: from.Source, - Target: from.Target, - Author: from.Author, - AuthorName: from.AuthorName, - AuthorEmail: from.AuthorEmail, - AuthorAvatar: from.AuthorAvatar, - Sender: from.Sender, - Params: from.Params, - Deploy: from.Deploy, - Started: from.Started, - Finished: from.Finished, - Created: from.Created, - Updated: from.Updated, - Version: from.Version, - } -} diff --git a/plugin/validator/remote_oss.go b/plugin/validator/remote_oss.go deleted file mode 100644 index c6a9d4c169..0000000000 --- a/plugin/validator/remote_oss.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package validator - -import ( - "time" - - "github.com/drone/drone/core" -) - -// Remote returns a conversion service that converts the -// configuration file using a remote http service. -func Remote(endpoint, signer string, skipVerify bool, timeout time.Duration) core.ValidateService { - return new(noop) -} diff --git a/plugin/validator/remote_test.go b/plugin/validator/remote_test.go deleted file mode 100644 index 518de4719c..0000000000 --- a/plugin/validator/remote_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package validator diff --git a/plugin/webhook/config.go b/plugin/webhook/config.go deleted file mode 100644 index b712a041da..0000000000 --- a/plugin/webhook/config.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package webhook - -import "github.com/drone/drone/core" - -// Config provides the webhook configuration. -type Config struct { - Events []string - Endpoint []string - Secret string - System *core.System -} diff --git a/plugin/webhook/webhook.go b/plugin/webhook/webhook.go deleted file mode 100644 index 60eff999d3..0000000000 --- a/plugin/webhook/webhook.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package webhook - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "net/http" - "path/filepath" - "time" - - "github.com/drone/drone/core" - - "github.com/99designs/httpsignatures-go" -) - -// required http headers -var headers = []string{ - "date", - "digest", -} - -var signer = httpsignatures.NewSigner( - httpsignatures.AlgorithmHmacSha256, - headers..., -) - -// New returns a new Webhook sender. -func New(config Config) core.WebhookSender { - return &sender{ - Events: config.Events, - Endpoints: config.Endpoint, - Secret: config.Secret, - System: config.System, - } -} - -type payload struct { - *core.WebhookData - System *core.System `json:"system,omitempty"` -} - -type sender struct { - Client *http.Client - Events []string - Endpoints []string - Secret string - System *core.System -} - -// Send sends the JSON encoded webhook to the global -// HTTP endpoints. -func (s *sender) Send(ctx context.Context, in *core.WebhookData) error { - if len(s.Endpoints) == 0 { - return nil - } - if s.match(in.Event, in.Action) == false { - return nil - } - wrapper := payload{ - WebhookData: in, - System: s.System, - } - data, _ := json.Marshal(wrapper) - for _, endpoint := range s.Endpoints { - err := s.send(endpoint, s.Secret, in.Event, data) - if err != nil { - return err - } - } - return nil -} - -func (s *sender) send(endpoint, secret, event string, data []byte) error { - ctx := context.Background() - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - - buf := bytes.NewBuffer(data) - req, err := http.NewRequest("POST", endpoint, buf) - if err != nil { - return err - } - - req = req.WithContext(ctx) - req.Header.Add("X-Drone-Event", event) - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Digest", "SHA-256="+digest(data)) - req.Header.Add("Date", time.Now().UTC().Format(http.TimeFormat)) - err = signer.SignRequest("hmac-key", s.Secret, req) - if err != nil { - return err - } - res, err := s.client().Do(req) - if res != nil { - res.Body.Close() - } - return err -} - -func (s *sender) match(event, action string) bool { - if len(s.Events) == 0 { - return true - } - var name string - switch { - case action == "": - name = event - case action != "": - name = event + ":" + action - } - for _, pattern := range s.Events { - if ok, _ := filepath.Match(pattern, name); ok { - return true - } - } - return false -} - -func (s *sender) client() *http.Client { - if s.Client == nil { - return http.DefaultClient - } - return s.Client -} - -func digest(data []byte) string { - h := sha256.New() - h.Write(data) - return base64.StdEncoding.EncodeToString(h.Sum(nil)) -} diff --git a/plugin/webhook/webhook_oss.go b/plugin/webhook/webhook_oss.go deleted file mode 100644 index 4bba616869..0000000000 --- a/plugin/webhook/webhook_oss.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package webhook - -import ( - "context" - - "github.com/drone/drone/core" -) - -// New returns a no-op Webhook sender. -func New(Config) core.WebhookSender { - return new(noop) -} - -type noop struct{} - -func (noop) Send(context.Context, *core.WebhookData) error { - return nil -} diff --git a/plugin/webhook/webhook_test.go b/plugin/webhook/webhook_test.go deleted file mode 100644 index 4e4b7d7fcf..0000000000 --- a/plugin/webhook/webhook_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package webhook - -import ( - "context" - "net/http" - "testing" - - "github.com/drone/drone/core" - - "github.com/99designs/httpsignatures-go" - "github.com/h2non/gock" -) - -var noContext = context.Background() - -func TestWebhook(t *testing.T) { - defer gock.Off() - - webhook := &core.WebhookData{ - Event: core.WebhookEventUser, - Action: core.WebhookActionCreated, - User: &core.User{Login: "octocat"}, - } - - matchSignature := func(r *http.Request, _ *gock.Request) (bool, error) { - signature, err := httpsignatures.FromRequest(r) - if err != nil { - return false, err - } - return signature.IsValid("GMEuUHQfmrMRsseWxi9YlIeBtn9lm6im", r), nil - } - - gock.New("https://company.com"). - Post("/hooks"). - AddMatcher(matchSignature). - MatchHeader("X-Drone-Event", "user"). - MatchHeader("Content-Type", "application/json"). - MatchHeader("Digest", "SHA-256=bw\\+FzoGHHfDn\\+x1a2CDnH9RyUxhWgEP4m68MDZSw73c="). - JSON(webhook). - Reply(200). - Type("application/json") - - config := Config{ - Endpoint: []string{"https://company.com/hooks"}, - Secret: "GMEuUHQfmrMRsseWxi9YlIeBtn9lm6im", - } - sender := New(config) - err := sender.Send(noContext, webhook) - if err != nil { - t.Error(err) - } - - if gock.IsPending() { - t.Errorf("Unfinished requests") - } -} - -func TestWebhook_CustomClient(t *testing.T) { - sender := new(sender) - if sender.client() != http.DefaultClient { - t.Errorf("Expect default http client") - } - - custom := &http.Client{} - sender.Client = custom - if sender.client() != custom { - t.Errorf("Expect custom http client") - } -} - -func TestWebhook_NoEndpoints(t *testing.T) { - webhook := &core.WebhookData{ - Event: core.WebhookEventUser, - Action: core.WebhookActionCreated, - User: &core.User{Login: "octocat"}, - } - - config := Config{ - Endpoint: []string{}, - Secret: "correct-horse-battery-staple", - } - sender := New(config) - err := sender.Send(noContext, webhook) - if err != nil { - t.Error(err) - } -} - -func TestWebhook_NoMatch(t *testing.T) { - webhook := &core.WebhookData{ - Event: core.WebhookEventUser, - Action: core.WebhookActionCreated, - User: &core.User{Login: "octocat"}, - } - - config := Config{ - Events: []string{"repo:disabled"}, - Endpoint: []string{"https://localhost:1234"}, - Secret: "correct-horse-battery-staple", - } - sender := New(config) - err := sender.Send(noContext, webhook) - if err != nil { - t.Error(err) - } -} - -func TestWebhook_Match(t *testing.T) { - tests := []struct { - events []string - event string - action string - matched bool - }{ - { - event: "repo", - action: "enabled", - matched: true, - }, - { - events: []string{"user", "repo"}, - event: "repo", - matched: true, - }, - { - events: []string{"repo:disabled", "repo:enabled"}, - event: "repo", - action: "enabled", - matched: true, - }, - { - events: []string{"repo:disabled", "repo:*"}, - event: "repo", - action: "enabled", - matched: true, - }, - { - events: []string{"repo:disabled", "user:created"}, - event: "repo", - action: "enabled", - matched: false, - }, - { - events: []string{"repo", "user"}, - event: "repo", - action: "enabled", - matched: false, - }, - } - for i, test := range tests { - s := new(sender) - s.Events = test.events - if s.match(test.event, test.action) != test.matched { - t.Errorf("Expect matched %v at index %d", test.matched, i) - } - } -} diff --git a/pubsub/doc.go b/pubsub/doc.go deleted file mode 100644 index 4253db8daa..0000000000 --- a/pubsub/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package pubsub implements a thread safe publish subscriber, -// allowing multiple publishers to distribute messages to -// multiple subscribers. -package pubsub diff --git a/pubsub/hub.go b/pubsub/hub.go deleted file mode 100644 index 42143974d0..0000000000 --- a/pubsub/hub.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pubsub - -import ( - "context" - "sync" - - "github.com/drone/drone/core" -) - -type hub struct { - sync.Mutex - - subs map[*subscriber]struct{} -} - -// newHub creates a new publish subscriber. -func newHub() core.Pubsub { - return &hub{ - subs: map[*subscriber]struct{}{}, - } -} - -func (h *hub) Publish(ctx context.Context, e *core.Message) error { - h.Lock() - for s := range h.subs { - s.publish(e) - } - h.Unlock() - return nil -} - -func (h *hub) Subscribe(ctx context.Context) (<-chan *core.Message, <-chan error) { - h.Lock() - s := &subscriber{ - handler: make(chan *core.Message, 100), - quit: make(chan struct{}), - } - h.subs[s] = struct{}{} - h.Unlock() - errc := make(chan error) - go func() { - defer close(errc) - select { - case <-ctx.Done(): - h.Lock() - delete(h.subs, s) - h.Unlock() - s.close() - } - }() - return s.handler, errc -} - -func (h *hub) Subscribers() (int, error) { - h.Lock() - c := len(h.subs) - h.Unlock() - return c, nil -} diff --git a/pubsub/hub_redis.go b/pubsub/hub_redis.go deleted file mode 100644 index 8b546d6e69..0000000000 --- a/pubsub/hub_redis.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package pubsub - -import ( - "context" - "encoding/json" - "fmt" - "os" - "sync" - - "github.com/drone/drone/core" - "github.com/drone/drone/service/redisdb" -) - -const ( - redisPubSubEvents = "drone-events" - redisPubSubCapacity = 100 -) - -func newHubRedis(r redisdb.RedisDB) core.Pubsub { - h := &hubRedis{ - rdb: r, - subscribers: make(map[chan<- *core.Message]struct{}), - } - - go r.Subscribe(context.Background(), redisPubSubEvents, redisPubSubCapacity, h) - - return h -} - -type hubRedis struct { - sync.Mutex - rdb redisdb.RedisDB - subscribers map[chan<- *core.Message]struct{} -} - -// Publish publishes a new message. All subscribers will get it. -func (h *hubRedis) Publish(ctx context.Context, e *core.Message) (err error) { - client := h.rdb.Client() - - data, err := json.Marshal(e) - if err != nil { - return - } - - _, err = client.Publish(ctx, redisPubSubEvents, data).Result() - if err != nil { - return - } - - return -} - -// Subscribe add a new subscriber. The subscriber gets event until its context is not finished. -func (h *hubRedis) Subscribe(ctx context.Context) (<-chan *core.Message, <-chan error) { - chMessage := make(chan *core.Message, redisPubSubCapacity) - chErr := make(chan error) - - h.Lock() - h.subscribers[chMessage] = struct{}{} - h.Unlock() - - go func() { - <-ctx.Done() - - h.Lock() - delete(h.subscribers, chMessage) - h.Unlock() - - close(chMessage) - close(chErr) - }() - - return chMessage, chErr -} - -// Subscribers returns number of subscribers. -func (h *hubRedis) Subscribers() (int, error) { - h.Lock() - n := len(h.subscribers) - h.Unlock() - - return n, nil -} - -// ProcessMessage relays the message to all subscribers listening to drone events. -// It is a part of redisdb.PubSubProcessor implementation and it's called internally by redisdb.Subscribe. -func (h *hubRedis) ProcessMessage(s string) { - message := &core.Message{} - err := json.Unmarshal([]byte(s), message) - if err != nil { - // Ignore invalid messages. This is a "should not happen" situation, - // because messages are encoded as json in Publish(). - _, _ = fmt.Fprintf(os.Stderr, "pubsub/redis: failed to unmarshal a message. %s\n", err) - return - } - - h.Lock() - for ss := range h.subscribers { - select { - case ss <- message: - default: // messages are lost if a subscriber channel reaches its capacity - } - } - h.Unlock() -} - -// ProcessError is a part of redisdb.PubSubProcessor implementation. -func (h *hubRedis) ProcessError(error) {} diff --git a/pubsub/hub_test.go b/pubsub/hub_test.go deleted file mode 100644 index 866eb888de..0000000000 --- a/pubsub/hub_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package pubsub - -import ( - "context" - "sync" - "testing" - - "github.com/drone/drone/core" -) - -func TestBus(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - p := newHub() - events, errc := p.Subscribe(ctx) - - got, err := p.Subscribers() - if err != nil { - t.Errorf("Test failed with an error: %s", err.Error()) - return - } - - if want := 1; got != want { - t.Errorf("Want %d subscribers, got %d", want, got) - } - - w := sync.WaitGroup{} - w.Add(1) - go func() { - p.Publish(ctx, new(core.Message)) - p.Publish(ctx, new(core.Message)) - p.Publish(ctx, new(core.Message)) - w.Done() - }() - w.Wait() - - w.Add(3) - go func() { - for { - select { - case <-errc: - return - case <-events: - w.Done() - } - } - }() - w.Wait() - - cancel() -} diff --git a/pubsub/pubsub.go b/pubsub/pubsub.go deleted file mode 100644 index f4f2db160e..0000000000 --- a/pubsub/pubsub.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package pubsub - -import ( - "github.com/drone/drone/core" - "github.com/drone/drone/service/redisdb" -) - -// New creates a new publish subscriber. If Redis client passed as parameter is not nil it uses -// a Redis implementation, otherwise it uses an in-memory implementation. -func New(r redisdb.RedisDB) core.Pubsub { - if r != nil { - return newHubRedis(r) - } - - return newHub() -} diff --git a/pubsub/pubsub_oss.go b/pubsub/pubsub_oss.go deleted file mode 100644 index 85a01fd69f..0000000000 --- a/pubsub/pubsub_oss.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package pubsub - -import ( - "github.com/drone/drone/core" - "github.com/drone/drone/service/redisdb" -) - -// New creates a new in-memory publish subscriber. -func New(r redisdb.RedisDB) core.Pubsub { - return newHub() -} diff --git a/pubsub/sub.go b/pubsub/sub.go deleted file mode 100644 index 00f835c7b1..0000000000 --- a/pubsub/sub.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pubsub - -import ( - "sync" - - "github.com/drone/drone/core" -) - -type subscriber struct { - sync.Mutex - - handler chan *core.Message - quit chan struct{} - done bool -} - -func (s *subscriber) publish(event *core.Message) { - select { - case <-s.quit: - case s.handler <- event: - default: - // events are sent on a buffered channel. If there - // is a slow consumer that is not processing events, - // the buffered channel will fill and newer messages - // are ignored. - } -} - -func (s *subscriber) close() { - s.Lock() - if s.done == false { - close(s.quit) - s.done = true - } - s.Unlock() -} diff --git a/pubsub/sub_test.go b/pubsub/sub_test.go deleted file mode 100644 index 1ac4f88e71..0000000000 --- a/pubsub/sub_test.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package pubsub - -import ( - "testing" - - "github.com/drone/drone/core" -) - -func nop(*core.Message) {} - -func TestSubscription_publish(t *testing.T) { - s := &subscriber{ - handler: make(chan *core.Message, 5), - quit: make(chan struct{}), - } - - e := new(core.Message) - s.publish(e) - - if got, want := len(s.handler), 1; got != want { - t.Errorf("Want buffered channel size %d, got %d", want, got) - } - if got, want := <-s.handler, e; got != want { - t.Errorf("Want event received from channel") - } - if got, want := len(s.handler), 0; got != want { - t.Errorf("Want buffered channel size %d, got %d", want, got) - } -} - -func TestSubscription_buffer(t *testing.T) { - s := &subscriber{ - handler: make(chan *core.Message, 1), - quit: make(chan struct{}), - } - - // the buffer size is 1 to simulate what happens - // if the subscriber cannot keep up with processing - // and the buffer fills up. In this case, events - // should be ignored until pending events are - // processed. - - e := new(core.Message) - s.publish(e) - s.publish(e) - s.publish(e) - s.publish(e) - s.publish(e) - - if got, want := len(s.handler), 1; got != want { - t.Errorf("Want buffered channel size %d, got %d", want, got) - } -} - -func TestSubscription_stop(t *testing.T) { - s := &subscriber{ - handler: make(chan *core.Message, 1), - quit: make(chan struct{}), - } - - if got, want := s.done, false; got != want { - t.Errorf("Want subscription open") - } - - s.close() - if got, want := s.done, true; got != want { - t.Errorf("Want subscription closed") - } - - // if the subscription is closed we should - // ignore any new events being published. - - e := new(core.Message) - s.publish(e) - s.publish(e) - s.publish(e) - s.publish(e) - s.publish(e) -} diff --git a/scheduler/queue/canceller.go b/scheduler/queue/canceller.go deleted file mode 100644 index 458076cdbb..0000000000 --- a/scheduler/queue/canceller.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package queue - -import ( - "context" - "sync" - "time" -) - -type canceller struct { - sync.Mutex - - subscribers map[chan struct{}]int64 - cancelled map[int64]time.Time -} - -func newCanceller() *canceller { - return &canceller{ - subscribers: make(map[chan struct{}]int64), - cancelled: make(map[int64]time.Time), - } -} - -func (c *canceller) Cancel(ctx context.Context, id int64) error { - c.Lock() - c.cancelled[id] = time.Now().Add(time.Minute * 5) - for subscriber, build := range c.subscribers { - if id == build { - close(subscriber) - } - } - c.collect() - c.Unlock() - return nil -} - -func (c *canceller) Cancelled(ctx context.Context, id int64) (bool, error) { - subscriber := make(chan struct{}) - c.Lock() - c.subscribers[subscriber] = id - c.Unlock() - - defer func() { - c.Lock() - delete(c.subscribers, subscriber) - c.Unlock() - }() - - for { - select { - case <-ctx.Done(): - return false, ctx.Err() - case <-time.After(time.Minute): - c.Lock() - _, ok := c.cancelled[id] - c.Unlock() - if ok { - return true, nil - } - case <-subscriber: - return true, nil - } - } -} - -func (c *canceller) collect() { - // the list of cancelled builds is stored with a ttl, and - // is not removed until the ttl is reached. This provides - // adequate window for clients with connectivity issues to - // reconnect and receive notification of cancel events. - now := time.Now() - for build, timestamp := range c.cancelled { - if now.After(timestamp) { - delete(c.cancelled, build) - } - } -} diff --git a/scheduler/queue/canceller_redis.go b/scheduler/queue/canceller_redis.go deleted file mode 100644 index d43a976f87..0000000000 --- a/scheduler/queue/canceller_redis.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package queue - -import ( - "context" - "fmt" - "os" - "strconv" - "sync" - "time" - - "github.com/drone/drone/service/redisdb" - - "github.com/go-redis/redis/v8" -) - -const ( - redisPubSubCancel = "drone-cancel" - redisCancelValuePrefix = "drone-cancel-" - redisCancelValueTimeout = 5 * time.Minute - redisCancelValue = "canceled" -) - -func newCancellerRedis(r redisdb.RedisDB) *cancellerRedis { - h := &cancellerRedis{ - rdb: r, - subscribers: make(map[*cancelSubscriber]struct{}), - } - - go r.Subscribe(context.Background(), redisPubSubCancel, 1, h) - - return h -} - -type cancellerRedis struct { - rdb redisdb.RedisDB - subscribers map[*cancelSubscriber]struct{} - sync.Mutex -} - -type cancelSubscriber struct { - id int64 - ch chan<- error -} - -// Cancel informs all subscribers that a build with the provided id is cancelled. -func (c *cancellerRedis) Cancel(ctx context.Context, id int64) (err error) { - client := c.rdb.Client() - - ids := strconv.FormatInt(id, 10) - - // publish a cancel event to all subscribers (runners) waiting to - _, err = client.Publish(ctx, redisPubSubCancel, ids).Result() - if err != nil { - return - } - - // put a limited duration value in case a runner isn't listening currently. - _, err = client.Set(ctx, redisCancelValuePrefix+ids, redisCancelValue, redisCancelValueTimeout).Result() - if err != nil { - return - } - - return -} - -// Cancelled waits until it gets info that a build with the provided id is cancelled. -// The waiting is aborted when the provided context is done. -func (c *cancellerRedis) Cancelled(ctx context.Context, id int64) (isCancelled bool, err error) { - client := c.rdb.Client() - - ids := strconv.FormatInt(id, 10) - - // first check if the build is already cancelled - - result, err := client.Get(ctx, redisCancelValuePrefix+ids).Result() - if err != nil && err != redis.Nil { - return - } - - isCancelled = err != redis.Nil && result == redisCancelValue - if isCancelled { - return - } - - // if it is not cancelled, subscribe and listen to cancel build events - // until the context is cancelled or until the build is cancelled. - - ch := make(chan error) - sub := &cancelSubscriber{id: id, ch: ch} - - c.Lock() - c.subscribers[sub] = struct{}{} - c.Unlock() - - select { - case err = <-ch: - // If the build is cancelled or an error happened, - // than the subscriber is removed from the set by other go routine - isCancelled = err != nil - case <-ctx.Done(): - // If the context is cancelled then the subscriber must be be removed here. - c.Lock() - delete(c.subscribers, sub) - c.Unlock() - } - - return -} - -// ProcessMessage informs all subscribers listening to cancellation that the build with this id is cancelled. -// It is a part of redisdb.PubSubProcessor implementation and it's called internally by Subscribe. -func (c *cancellerRedis) ProcessMessage(s string) { - id, err := strconv.ParseInt(s, 10, 64) - if err != nil { - // Ignore invalid messages. This is a "should not happen" situation, - // because all messages are integers as strings in method Cancel(). - _, _ = fmt.Fprintf(os.Stderr, "canceller/redis: message is not an integer: %s\n", s) - return - } - - c.Lock() - for ss := range c.subscribers { - if ss.id == id { - ss.ch <- nil - close(ss.ch) - delete(c.subscribers, ss) - } - } - c.Unlock() -} - -// ProcessError informs all subscribers that an error happened and clears the set of subscribers. -// The set of subscribers is cleared because each subscriber receives only one message, -// so an error could cause that the message is missed - it's safer to return an error. -// It is a part of redisdb.PubSubProcessor implementation and it's called internally by Subscribe. -func (c *cancellerRedis) ProcessError(err error) { - c.Lock() - for ss := range c.subscribers { - ss.ch <- err - close(ss.ch) - delete(c.subscribers, ss) - } - c.Unlock() -} diff --git a/scheduler/queue/canceller_test.go b/scheduler/queue/canceller_test.go deleted file mode 100644 index d09df161fe..0000000000 --- a/scheduler/queue/canceller_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package queue - -import ( - "context" - "testing" - "time" -) - -var noContext = context.Background() - -func TestCollect(t *testing.T) { - c := newCanceller() - c.Cancel(noContext, 1) - c.Cancel(noContext, 2) - c.Cancel(noContext, 3) - c.Cancel(noContext, 4) - c.Cancel(noContext, 5) - c.cancelled[3] = c.cancelled[3].Add(time.Minute * -1) - c.cancelled[4] = time.Now().Add(time.Second * -1) - c.cancelled[5] = time.Now().Add(time.Second * -1) - c.collect() - - if got, want := len(c.cancelled), 3; got != want { - t.Errorf("Want 3 cancelled builds in the cache, got %d", got) - } - if _, ok := c.cancelled[4]; ok { - t.Errorf("Expect build id [4] removed") - } - if _, ok := c.cancelled[5]; ok { - t.Errorf("Expect build id [5] removed") - } -} diff --git a/scheduler/queue/queue.go b/scheduler/queue/queue.go deleted file mode 100644 index 255130c179..0000000000 --- a/scheduler/queue/queue.go +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package queue - -import ( - "context" - "sync" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/service/redisdb" - - "github.com/drone/drone-go/drone" -) - -type queue struct { - sync.Mutex - globMx redisdb.LockErr - - ready chan struct{} - paused bool - interval time.Duration - throttle int - store core.StageStore - workers map[*worker]struct{} - ctx context.Context -} - -// newQueue returns a new Queue backed by the build datastore. -func newQueue(ctx context.Context, store core.StageStore) *queue { - q := &queue{ - store: store, - globMx: redisdb.LockErrNoOp{}, - ready: make(chan struct{}, 1), - workers: map[*worker]struct{}{}, - interval: time.Minute, - ctx: ctx, - } - go q.start() - return q -} - -func (q *queue) Schedule(ctx context.Context, stage *core.Stage) error { - select { - case q.ready <- struct{}{}: - default: - } - return nil -} - -func (q *queue) Pause(ctx context.Context) error { - q.Lock() - q.paused = true - q.Unlock() - return nil -} - -func (q *queue) Paused(ctx context.Context) (bool, error) { - q.Lock() - paused := q.paused - q.Unlock() - return paused, nil -} - -func (q *queue) Resume(ctx context.Context) error { - q.Lock() - q.paused = false - q.Unlock() - - select { - case q.ready <- struct{}{}: - default: - } - return nil -} - -func (q *queue) Request(ctx context.Context, params core.Filter) (*core.Stage, error) { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - w := &worker{ - kind: params.Kind, - typ: params.Type, - os: params.OS, - arch: params.Arch, - kernel: params.Kernel, - variant: params.Variant, - labels: params.Labels, - channel: make(chan *core.Stage), - done: ctx.Done(), - } - q.Lock() - q.workers[w] = struct{}{} - q.Unlock() - - select { - case q.ready <- struct{}{}: - default: - } - - select { - case <-ctx.Done(): - q.Lock() - delete(q.workers, w) - q.Unlock() - return nil, ctx.Err() - case b := <-w.channel: - return b, nil - } -} - -func (q *queue) signal(ctx context.Context) error { - if err := q.globMx.LockContext(ctx); err != nil { - return err - } - defer q.globMx.UnlockContext(ctx) - - q.Lock() - count := len(q.workers) - pause := q.paused - q.Unlock() - if pause { - return nil - } - if count == 0 { - return nil - } - items, err := q.store.ListIncomplete(ctx) - if err != nil { - return err - } - - q.Lock() - defer q.Unlock() - for _, item := range items { - if item.Status == core.StatusRunning { - continue - } - if item.Machine != "" { - continue - } - - // if the stage defines concurrency limits we - // need to make sure those limits are not exceeded - // before proceeding. - if withinLimits(item, items) == false { - continue - } - - // if the system defines concurrency limits - // per repository we need to make sure those limits - // are not exceeded before proceeding. - if shouldThrottle(item, items, item.LimitRepo) == true { - continue - } - - loop: - for w := range q.workers { - // the worker must match the resource kind and type - if !matchResource(w.kind, w.typ, item.Kind, item.Type) { - continue - } - - if w.os != "" || w.arch != "" || w.variant != "" || w.kernel != "" { - // the worker is platform-specific. check to ensure - // the queue item matches the worker platform. - if w.os != item.OS { - continue - } - if w.arch != item.Arch { - continue - } - // if the pipeline defines a variant it must match - // the worker variant (e.g. arm6, arm7, etc). - if item.Variant != "" && item.Variant != w.variant { - continue - } - // if the pipeline defines a kernel version it must match - // the worker kernel version (e.g. 1709, 1803). - if item.Kernel != "" && item.Kernel != w.kernel { - continue - } - } - - if len(item.Labels) > 0 || len(w.labels) > 0 { - if !checkLabels(item.Labels, w.labels) { - continue - } - } - - // // the queue has 60 seconds to ack the item, otherwise - // // it is eligible for processing by another worker. - // // item.Expires = time.Now().Add(time.Minute).Unix() - // err := q.store.Update(ctx, item) - - // if err != nil { - // log.Ctx(ctx).Warn(). - // Err(err). - // Int64("build_id", item.BuildID). - // Int64("stage_id", item.ID). - // Msg("cannot update queue item") - // continue - // } - - // TODO: refactor to its own unexported method - sendWork := func() bool { - select { - case w.channel <- item: - return true - case <-w.done: - // Worker will exit when we call the deferred q.Unlock() - case <-time.After(q.interval): - // Worker failed to ack before timeout - } - return false - } - if sendWork() { - delete(q.workers, w) - break loop - } - } - } - return nil -} - -func (q *queue) start() error { - for { - select { - case <-q.ctx.Done(): - return q.ctx.Err() - case <-q.ready: - q.signal(q.ctx) - case <-time.After(q.interval): - q.signal(q.ctx) - } - } -} - -type worker struct { - kind string - typ string - os string - arch string - kernel string - variant string - labels map[string]string - channel chan *core.Stage - done <-chan struct{} -} - -type counter struct { - counts map[string]int -} - -func checkLabels(a, b map[string]string) bool { - if len(a) != len(b) { - return false - } - for k, v := range a { - if w, ok := b[k]; !ok || v != w { - return false - } - } - return true -} - -func withinLimits(stage *core.Stage, siblings []*core.Stage) bool { - if stage.Limit == 0 { - return true - } - count := 0 - for _, sibling := range siblings { - if sibling.RepoID != stage.RepoID { - continue - } - if sibling.ID == stage.ID { - continue - } - if sibling.Name != stage.Name { - continue - } - if sibling.ID < stage.ID || - sibling.Status == core.StatusRunning { - count++ - } - } - return count < stage.Limit -} - -func shouldThrottle(stage *core.Stage, siblings []*core.Stage, limit int) bool { - // if no throttle limit is defined (default) then - // return false to indicate no throttling is needed. - if limit == 0 { - return false - } - // if the repository is running it is too late - // to skip and we can exit - if stage.Status == drone.StatusRunning { - return false - } - - count := 0 - // loop through running stages to count number of - // running stages for the parent repository. - for _, sibling := range siblings { - // ignore stages from other repository. - if sibling.RepoID != stage.RepoID { - continue - } - // ignore this stage and stages that were - // scheduled after this stage. - if sibling.ID >= stage.ID { - continue - } - count++ - } - // if the count of running stages exceeds the - // throttle limit return true. - return count >= limit -} - -// matchResource is a helper function that returns -func matchResource(kinda, typea, kindb, typeb string) bool { - if kinda == "" { - kinda = "pipeline" - } - if kindb == "" { - kindb = "pipeline" - } - if typea == "" { - typea = "docker" - } - if typeb == "" { - typeb = "docker" - } - return kinda == kindb && typea == typeb -} diff --git a/scheduler/queue/queue_test.go b/scheduler/queue/queue_test.go deleted file mode 100644 index 892434607c..0000000000 --- a/scheduler/queue/queue_test.go +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package queue - -import ( - "context" - "math/rand" - "sync" - "testing" - "time" - - "github.com/drone/drone-go/drone" - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -func TestQueue(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - items := []*core.Stage{ - {ID: 3, OS: "linux", Arch: "amd64"}, - {ID: 2, OS: "linux", Arch: "amd64"}, - {ID: 1, OS: "linux", Arch: "amd64"}, - } - - ctx := context.Background() - store := mock.NewMockStageStore(controller) - store.EXPECT().ListIncomplete(ctx).Return(items, nil).Times(1) - store.EXPECT().ListIncomplete(ctx).Return(items[1:], nil).Times(1) - store.EXPECT().ListIncomplete(ctx).Return(items[2:], nil).Times(1) - - q := newQueue(ctx, store) - for _, item := range items { - next, err := q.Request(ctx, core.Filter{OS: "linux", Arch: "amd64"}) - if err != nil { - t.Error(err) - return - } - if got, want := next, item; got != want { - t.Errorf("Want build %d, got %d", want.ID, got.ID) - } - } -} - -func TestQueueCancel(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - ctx, cancel := context.WithCancel(context.Background()) - store := mock.NewMockStageStore(controller) - store.EXPECT().ListIncomplete(ctx).Return(nil, nil) - - q := newQueue(ctx, store) - - var wg sync.WaitGroup - wg.Add(1) - - go func() { - build, err := q.Request(ctx, core.Filter{OS: "linux/amd64", Arch: "amd64"}) - if err != context.Canceled { - t.Errorf("Expected context.Canceled error, got %s", err) - } - if build != nil { - t.Errorf("Expect nil build when subscribe canceled") - } - wg.Done() - }() - <-time.After(10 * time.Millisecond) - - q.Lock() - count := len(q.workers) - q.Unlock() - - if got, want := count, 1; got != want { - t.Errorf("Want %d listener, got %d", want, got) - } - - cancel() - wg.Wait() -} - -func TestQueuePush(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - item1 := &core.Stage{ - ID: 1, - OS: "linux", - Arch: "amd64", - } - item2 := &core.Stage{ - ID: 2, - OS: "linux", - Arch: "amd64", - } - - ctx := context.Background() - store := mock.NewMockStageStore(controller) - - q := &queue{ - store: store, - ready: make(chan struct{}, 1), - } - q.Schedule(ctx, item1) - q.Schedule(ctx, item2) - select { - case <-q.ready: - case <-time.After(time.Millisecond): - t.Errorf("Expect queue signaled on push") - } -} - -func TestMatchResource(t *testing.T) { - tests := []struct { - kinda, typea, kindb, typeb string - want bool - }{ - // unspecified in yaml, unspecified by agent - {"", "", "", "", true}, - - // unspecified in yaml, specified by agent - {"pipeline", "docker", "", "", true}, - {"pipeline", "", "", "", true}, - {"", "docker", "", "", true}, - - // specified in yaml, unspecified by agent - {"", "", "pipeline", "docker", true}, - {"", "", "pipeline", "", true}, - {"", "", "", "docker", true}, - - // specified in yaml, specified by agent - {"pipeline", "docker", "pipeline", "docker", true}, - {"pipeline", "exec", "pipeline", "docker", false}, - {"approval", "slack", "pipeline", "docker", false}, - - // misc - {"", "docker", "pipeline", "docker", true}, - {"pipeline", "", "pipeline", "docker", true}, - {"pipeline", "docker", "", "docker", true}, - {"pipeline", "docker", "pipeline", "", true}, - } - - for i, test := range tests { - got, want := matchResource(test.kinda, test.typea, test.kindb, test.typeb), test.want - if got != want { - t.Errorf("Unexpected results at index %d", i) - } - } -} - -func TestShouldThrottle(t *testing.T) { - tests := []struct { - ID int64 - RepoID int64 - Status string - Limit int - Want bool - }{ - // repo 1: 2 running, 1 pending - {Want: false, ID: 1, RepoID: 1, Status: drone.StatusRunning, Limit: 2}, - {Want: false, ID: 2, RepoID: 1, Status: drone.StatusRunning, Limit: 2}, - {Want: true, ID: 3, RepoID: 1, Status: drone.StatusPending, Limit: 2}, - - // repo 2: 1 running, 1 pending - {Want: false, ID: 4, RepoID: 2, Status: drone.StatusRunning, Limit: 2}, - {Want: false, ID: 5, RepoID: 2, Status: drone.StatusPending, Limit: 2}, - - // repo 3: 3 running, 1 pending - {Want: false, ID: 6, RepoID: 3, Status: drone.StatusRunning, Limit: 2}, - {Want: false, ID: 7, RepoID: 3, Status: drone.StatusRunning, Limit: 2}, - {Want: false, ID: 8, RepoID: 3, Status: drone.StatusRunning, Limit: 2}, - {Want: true, ID: 9, RepoID: 3, Status: drone.StatusPending, Limit: 2}, - - // repo 4: 2 running, 1 pending, no limit - {Want: false, ID: 10, RepoID: 4, Status: drone.StatusRunning, Limit: 0}, - {Want: false, ID: 11, RepoID: 4, Status: drone.StatusRunning, Limit: 0}, - {Want: false, ID: 12, RepoID: 4, Status: drone.StatusPending, Limit: 0}, - } - var stages []*core.Stage - for _, test := range tests { - stages = append(stages, &core.Stage{ - ID: test.ID, - RepoID: test.RepoID, - Status: test.Status, - LimitRepo: test.Limit, - }) - } - for i, test := range tests { - stage := stages[i] - if got, want := shouldThrottle(stage, stages, stage.LimitRepo), test.Want; got != want { - t.Errorf("Unexpected results at index %d", i) - } - } -} - -func TestWithinLimits(t *testing.T) { - tests := []struct { - result bool - stage *core.Stage - stages []*core.Stage - }{ - // multiple stages executing for same repository and with same - // name, but no concurrency limits exist. expect true. - { - result: true, - stage: &core.Stage{ - ID: 3, RepoID: 1, Name: "build", Limit: 0, - }, - stages: []*core.Stage{ - {ID: 1, RepoID: 1, Name: "build", Status: "running"}, - {ID: 2, RepoID: 1, Name: "build", Status: "running"}, - {ID: 3, RepoID: 1, Name: "build", Status: "pending"}, - }, - }, - - // stage with concurrency 1, no existing stages - // exist for same repository id. expect true. - { - result: true, - stage: &core.Stage{ - ID: 3, RepoID: 2, Name: "build", Limit: 0, - }, - stages: []*core.Stage{ - {ID: 1, RepoID: 1, Name: "build", Status: "running"}, - {ID: 2, RepoID: 1, Name: "build", Status: "running"}, - {ID: 3, RepoID: 2, Name: "build", Status: "pending"}, - }, - }, - - // stage with concurrency 1, no existing stages - // exist for same stage name. expect true. - { - result: true, - stage: &core.Stage{ - ID: 3, RepoID: 1, Name: "build", Limit: 0, - }, - stages: []*core.Stage{ - {ID: 1, RepoID: 1, Name: "test", Status: "running"}, - {ID: 2, RepoID: 1, Name: "test", Status: "running"}, - {ID: 3, RepoID: 1, Name: "build", Status: "pending"}, - }, - }, - - // single stage with concurrency 1, no existing stages - // exist. expect true. - { - result: true, - stage: &core.Stage{ - ID: 1, RepoID: 1, Name: "build", Limit: 1, - }, - stages: []*core.Stage{ - {ID: 1, RepoID: 1, Name: "build", Status: "pending"}, - }, - }, - - // stage with concurrency 1, other named stages - // exist in the queue, but they come after this stage. - // expect true. - { - result: true, - stage: &core.Stage{ - ID: 1, RepoID: 1, Name: "build", Limit: 1, - }, - stages: []*core.Stage{ - {ID: 1, RepoID: 1, Name: "build", Status: "pending"}, - {ID: 2, RepoID: 1, Name: "build", Status: "pending"}, - }, - }, - - // stage with concurrency 1, however, stage with same - // repository and name is already executing. expect false. - { - result: false, - stage: &core.Stage{ - ID: 2, RepoID: 1, Name: "build", Limit: 1, - }, - stages: []*core.Stage{ - {ID: 1, RepoID: 1, Name: "build", Status: "running"}, - {ID: 2, RepoID: 1, Name: "build", Status: "pending"}, - }, - }, - - // stage with concurrency 2. one existing stage in the - // queue before this stage. expect true. - { - result: true, - stage: &core.Stage{ - ID: 2, RepoID: 1, Name: "build", Limit: 2, - }, - stages: []*core.Stage{ - {ID: 1, RepoID: 1, Name: "build", Status: "running"}, - {ID: 2, RepoID: 1, Name: "build", Status: "pending"}, - {ID: 3, RepoID: 1, Name: "build", Status: "pending"}, - }, - }, - - // stage with concurrency 1. stages start out of order, and the - // second named stage starts before its predecessor. Its predecessor - // should not execute. expect false. - { - result: false, - stage: &core.Stage{ - ID: 1, RepoID: 1, Name: "build", Limit: 1, - }, - stages: []*core.Stage{ - {ID: 1, RepoID: 1, Name: "build", Status: "pending"}, - {ID: 2, RepoID: 1, Name: "build", Status: "running"}, - }, - }, - } - - for i, test := range tests { - if got, want := withinLimits(test.stage, test.stages), test.result; got != want { - t.Errorf("Unexpected results at index %d", i) - } - } -} - -func TestWithinLimits_Old(t *testing.T) { - tests := []struct { - ID int64 - RepoID int64 - Name string - Limit int - Want bool - }{ - {Want: true, ID: 1, RepoID: 1, Name: "foo"}, - {Want: true, ID: 2, RepoID: 2, Name: "bar", Limit: 1}, - {Want: true, ID: 3, RepoID: 1, Name: "bar", Limit: 1}, - {Want: false, ID: 4, RepoID: 1, Name: "bar", Limit: 1}, - {Want: false, ID: 5, RepoID: 1, Name: "bar", Limit: 1}, - {Want: true, ID: 6, RepoID: 1, Name: "baz", Limit: 2}, - {Want: true, ID: 7, RepoID: 1, Name: "baz", Limit: 2}, - {Want: false, ID: 8, RepoID: 1, Name: "baz", Limit: 2}, - {Want: false, ID: 9, RepoID: 1, Name: "baz", Limit: 2}, - {Want: true, ID: 10, RepoID: 1, Name: "baz", Limit: 0}, - } - var stages []*core.Stage - for _, test := range tests { - stages = append(stages, &core.Stage{ - ID: test.ID, - RepoID: test.RepoID, - Name: test.Name, - Limit: test.Limit, - }) - } - for i, test := range tests { - stage := stages[i] - if got, want := withinLimits(stage, stages), test.Want; got != want { - t.Errorf("Unexpected results at index %d", i) - } - } -} - -func incomplete(n int) ([]*core.Stage, error) { - ret := make([]*core.Stage, n) - for i := range ret { - ret[i] = &core.Stage{ - OS: "linux/amd64", - Arch: "amd64", - } - } - return ret, nil -} - -func TestQueueDeadlock(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - n := 10 - donechan := make(chan struct{}, n) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - store := mock.NewMockStageStore(controller) - store.EXPECT().ListIncomplete(ctx).Return(incomplete(n)).AnyTimes() - - q := newQueue(ctx, store) - doWork := func(i int) bool { - select { - case <-ctx.Done(): - return false - default: - } - ctx, cancel := context.WithTimeout(ctx, - time.Duration(i+rand.Intn(1000/n))*time.Millisecond) - defer cancel() - if i%3 == 0 { - // Randomly cancel some contexts to simulate timeouts - cancel() - } - _, err := q.Request(ctx, core.Filter{OS: "linux/amd64", Arch: "amd64"}) - if err != nil && err != context.Canceled && err != - context.DeadlineExceeded { - t.Errorf("Expected context.Canceled or context.DeadlineExceeded error, got %s", err) - } - select { - case donechan <- struct{}{}: - case <-ctx.Done(): - } - return true - } - for i := 0; i < n; i++ { - go func(i int) { - // Spawn n workers, doing work until the parent context is canceled - for doWork(i) { - } - }(i) - } - // Wait for n * 10 tasks to complete, then exit and cancel all the workers. - for seen := 0; seen < n*10; seen++ { - <-donechan - } -} diff --git a/scheduler/queue/scheduler.go b/scheduler/queue/scheduler.go deleted file mode 100644 index 44620870f9..0000000000 --- a/scheduler/queue/scheduler.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package queue - -import ( - "context" - "errors" -) - -type scheduler struct { - *queue - *canceller -} - -func (d scheduler) Stats(context.Context) (interface{}, error) { - return nil, errors.New("not implemented") -} diff --git a/scheduler/queue/scheduler_non_oss.go b/scheduler/queue/scheduler_non_oss.go deleted file mode 100644 index 54830729e4..0000000000 --- a/scheduler/queue/scheduler_non_oss.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !oss -// +build !oss - -package queue - -import ( - "context" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/service/redisdb" -) - -// New creates a new scheduler. -func New(store core.StageStore, r redisdb.RedisDB) core.Scheduler { - if r == nil { - return scheduler{ - queue: newQueue(context.Background(), store), - canceller: newCanceller(), - } - } - - sched := schedulerRedis{ - queue: newQueue(context.Background(), store), - cancellerRedis: newCancellerRedis(r), - } - - const globalMutexExpiryTime = 10 * time.Second - sched.globMx = r.NewMutex("drone-scheduler-mx", globalMutexExpiryTime) - - return sched -} diff --git a/scheduler/queue/scheduler_oss.go b/scheduler/queue/scheduler_oss.go deleted file mode 100644 index 88d7d01af4..0000000000 --- a/scheduler/queue/scheduler_oss.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build oss -// +build oss - -package queue - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/service/redisdb" -) - -// New creates a new scheduler. -func New(store core.StageStore, r redisdb.RedisDB) core.Scheduler { - return scheduler{ - queue: newQueue(context.Background(), store), - canceller: newCanceller(), - } -} diff --git a/scheduler/queue/scheduler_redis.go b/scheduler/queue/scheduler_redis.go deleted file mode 100644 index a14cf489f7..0000000000 --- a/scheduler/queue/scheduler_redis.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !oss - -package queue - -import ( - "context" - "errors" -) - -type schedulerRedis struct { - *queue - *cancellerRedis -} - -func (d schedulerRedis) Stats(context.Context) (interface{}, error) { - return nil, errors.New("not implemented") -} diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go deleted file mode 100644 index 4aba165f90..0000000000 --- a/scheduler/scheduler.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package scheduler diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index 6012d0182a..0000000000 --- a/scripts/build.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -echo "building docker images for ${GOOS}/${GOARCH} ..." - -REPO="github.com/drone/drone" - -# compile the server using the cgo -go build -ldflags "-extldflags \"-static\"" -o release/linux/${GOARCH}/drone-server ${REPO}/cmd/drone-server diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 93db19d852..0000000000 --- a/server/server.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "context" - "crypto/tls" - "net/http" - "os" - "path/filepath" - "time" - - "golang.org/x/crypto/acme/autocert" - "golang.org/x/sync/errgroup" -) - -// A Server defines parameters for running an HTTP server. -type Server struct { - Acme bool - Email string - Addr string - Cert string - Key string - Host string - Handler http.Handler -} - -const timeoutGracefulShutdown = 5 * time.Second - -// ListenAndServe initializes a server to respond to HTTP network requests. -func (s Server) ListenAndServe(ctx context.Context) error { - if s.Acme { - return s.listenAndServeAcme(ctx) - } else if s.Key != "" { - return s.listenAndServeTLS(ctx) - } - err := s.listenAndServe(ctx) - if err == http.ErrServerClosed { - err = nil - } - return err -} - -func (s Server) listenAndServe(ctx context.Context) error { - var g errgroup.Group - s1 := &http.Server{ - Addr: s.Addr, - Handler: s.Handler, - } - g.Go(func() error { - <-ctx.Done() - - ctxShutdown, cancelFunc := context.WithTimeout(context.Background(), timeoutGracefulShutdown) - defer cancelFunc() - - return s1.Shutdown(ctxShutdown) - }) - g.Go(s1.ListenAndServe) - return g.Wait() -} - -func (s Server) listenAndServeTLS(ctx context.Context) error { - var g errgroup.Group - s1 := &http.Server{ - Addr: ":http", - Handler: http.HandlerFunc(redirect), - } - s2 := &http.Server{ - Addr: ":https", - Handler: s.Handler, - } - g.Go(s1.ListenAndServe) - g.Go(func() error { - return s2.ListenAndServeTLS( - s.Cert, - s.Key, - ) - }) - g.Go(func() error { - <-ctx.Done() - - var gShutdown errgroup.Group - ctxShutdown, cancelFunc := context.WithTimeout(context.Background(), timeoutGracefulShutdown) - defer cancelFunc() - - gShutdown.Go(func() error { - return s1.Shutdown(ctxShutdown) - }) - gShutdown.Go(func() error { - return s2.Shutdown(ctxShutdown) - }) - - return gShutdown.Wait() - }) - return g.Wait() -} - -func (s Server) listenAndServeAcme(ctx context.Context) error { - var g errgroup.Group - - c := cacheDir() - m := &autocert.Manager{ - Email: s.Email, - Cache: autocert.DirCache(c), - Prompt: autocert.AcceptTOS, - HostPolicy: autocert.HostWhitelist(s.Host), - } - s1 := &http.Server{ - Addr: ":http", - Handler: m.HTTPHandler(s.Handler), - } - s2 := &http.Server{ - Addr: ":https", - Handler: s.Handler, - TLSConfig: &tls.Config{ - GetCertificate: m.GetCertificate, - NextProtos: []string{"h2", "http/1.1"}, - MinVersion: tls.VersionTLS12, - }, - } - g.Go(s1.ListenAndServe) - g.Go(func() error { - return s2.ListenAndServeTLS("", "") - }) - g.Go(func() error { - <-ctx.Done() - - var gShutdown errgroup.Group - ctxShutdown, cancelFunc := context.WithTimeout(context.Background(), timeoutGracefulShutdown) - defer cancelFunc() - - gShutdown.Go(func() error { - return s1.Shutdown(ctxShutdown) - }) - gShutdown.Go(func() error { - return s2.Shutdown(ctxShutdown) - }) - - return gShutdown.Wait() - }) - return g.Wait() -} - -func redirect(w http.ResponseWriter, req *http.Request) { - target := "https://" + req.Host + req.URL.Path - http.Redirect(w, req, target, http.StatusTemporaryRedirect) -} - -func cacheDir() string { - const base = "golang-autocert" - if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" { - return filepath.Join(xdg, base) - } - return filepath.Join(os.Getenv("HOME"), ".cache", base) -} diff --git a/service/canceler/canceler.go b/service/canceler/canceler.go deleted file mode 100644 index bfef4d77e6..0000000000 --- a/service/canceler/canceler.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package canceler - -import ( - "context" - "encoding/json" - "runtime/debug" - "time" - - "github.com/drone/drone/core" - - "github.com/hashicorp/go-multierror" - "github.com/sirupsen/logrus" -) - -var noContext = context.Background() - -type service struct { - builds core.BuildStore - events core.Pubsub - repos core.RepositoryStore - scheduler core.Scheduler - stages core.StageStore - status core.StatusService - steps core.StepStore - users core.UserStore - webhooks core.WebhookSender -} - -// New returns a new cancellation service that encapsulates -// all cancellation operations. -func New( - builds core.BuildStore, - events core.Pubsub, - repos core.RepositoryStore, - scheduler core.Scheduler, - stages core.StageStore, - status core.StatusService, - steps core.StepStore, - users core.UserStore, - webhooks core.WebhookSender, -) core.Canceler { - return &service{ - builds: builds, - events: events, - repos: repos, - scheduler: scheduler, - stages: stages, - status: status, - steps: steps, - users: users, - webhooks: webhooks, - } -} - -// Cancel cancels a build. -func (s *service) Cancel(ctx context.Context, repo *core.Repository, build *core.Build) error { - return s.cancel(ctx, repo, build, core.StatusKilled) -} - -// CancelPending cancels all pending builds of the same event -// and reference with lower build numbers. -func (s *service) CancelPending(ctx context.Context, repo *core.Repository, build *core.Build) error { - defer func() { - if err := recover(); err != nil { - debug.PrintStack() - } - }() - - // switch { - // case repo.CancelPulls && build.Event == core.EventPullRequest: - // case repo.CancelPush && build.Event == core.EventPush: - // default: - // return nil - // } - - switch build.Event { - // on the push and pull request builds can be automatically - // cancelled by the system. - case core.EventPush, core.EventPullRequest: - default: - return nil - } - - // get a list of all incomplete builds from the database - // for all repositories. this will need to be filtered. - incomplete, err := s.repos.ListIncomplete(ctx) - if err != nil { - return err - } - - var result error - for _, item := range incomplete { - // ignore incomplete items in the list that do - // not match the repository or build - if !match(build, item) { - continue - } - - err := s.cancel(ctx, repo, item.Build, core.StatusSkipped) - if err != nil { - result = multierror.Append(result, err) - } - } - - return result -} - -func (s *service) cancel(ctx context.Context, repo *core.Repository, build *core.Build, status string) error { - logger := logrus.WithFields( - logrus.Fields{ - "repo": repo.Slug, - "ref": build.Ref, - "build": build.Number, - "event": build.Event, - "status": build.Status, - }, - ) - - // do not cancel the build if the build status is - // complete. only cancel the build if the status is - // running or pending. - switch build.Status { - case core.StatusPending, core.StatusRunning: - default: - return nil - } - - // update the build status to killed. if the update fails - // due to an optimistic lock error it means the build has - // already started, and should now be ignored. - build.Status = status - build.Finished = time.Now().Unix() - if build.Started == 0 { - build.Started = time.Now().Unix() - } - - err := s.builds.Update(ctx, build) - if err != nil { - logger.WithError(err). - Warnln("canceler: cannot update build status to cancelled") - return err - } - - // notify the scheduler to cancel the build. this will - // instruct runners subscribing to the scheduler to - // cancel execution. - err = s.scheduler.Cancel(ctx, build.ID) - if err != nil { - logger.WithError(err). - Warnln("canceler: cannot signal cancelled build is complete") - } - - // update the commit status in the remote source - // control management system. - user, err := s.users.Find(ctx, repo.UserID) - if err == nil { - err := s.status.Send(ctx, user, &core.StatusInput{ - Repo: repo, - Build: build, - }) - if err != nil { - logger.WithError(err). - Debugln("canceler: cannot set status") - } - } - - stages, err := s.stages.ListSteps(ctx, build.ID) - if err != nil { - logger.WithError(err). - Debugln("canceler: cannot list build stages") - } - - // update the status of all steps to indicate they - // were killed or skipped. - for _, stage := range stages { - if stage.IsDone() { - continue - } - if stage.Started != 0 { - stage.Status = core.StatusKilled - } else { - stage.Status = core.StatusSkipped - stage.Started = time.Now().Unix() - } - stage.Stopped = time.Now().Unix() - err := s.stages.Update(ctx, stage) - if err != nil { - logger.WithError(err). - WithField("stage", stage.Number). - Debugln("canceler: cannot update stage status") - } - - // update the status of all steps to indicate they - // were killed or skipped. - for _, step := range stage.Steps { - if step.IsDone() { - continue - } - if step.Started != 0 { - step.Status = core.StatusKilled - } else { - step.Status = core.StatusSkipped - step.Started = time.Now().Unix() - } - step.Stopped = time.Now().Unix() - step.ExitCode = 130 - err := s.steps.Update(ctx, step) - if err != nil { - logger.WithError(err). - WithField("stage", stage.Number). - WithField("step", step.Number). - Debugln("canceler: cannot update step status") - } - } - } - - logger.WithError(err). - Debugln("canceler: successfully cancelled build") - - build.Stages = stages - - // trigger a pubsub event to notify subscribers that - // the build was cancelled. Specifically, this should - // live update the user interface. - repoCopy := new(core.Repository) - *repoCopy = *repo - repoCopy.Build = build - repoCopy.Build.Stages = stages - data, _ := json.Marshal(repoCopy) - err = s.events.Publish(noContext, &core.Message{ - Repository: repo.Slug, - Visibility: repo.Visibility, - Data: data, - }) - if err != nil { - logger.WithError(err). - Warnln("canceler: cannot publish cancel event") - } - - // trigger a webhook to notify subscribing systems that - // the build was cancelled. - payload := &core.WebhookData{ - Event: core.WebhookEventBuild, - Action: core.WebhookActionUpdated, - Repo: repo, - Build: build, - } - err = s.webhooks.Send(ctx, payload) - if err != nil { - logger.WithError(err). - Warnln("manager: cannot send global webhook") - } - - return nil -} diff --git a/service/canceler/canceler_test.go b/service/canceler/canceler_test.go deleted file mode 100644 index b04c78ad42..0000000000 --- a/service/canceler/canceler_test.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package canceler - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/go-chi/chi" - - "github.com/golang/mock/gomock" -) - -func TestCancelPending_IgnoreEvent(t *testing.T) { - ignore := []string{ - core.EventCron, - core.EventCustom, - core.EventPromote, - core.EventRollback, - core.EventTag, - } - for _, event := range ignore { - s := new(service) - err := s.CancelPending(noContext, nil, &core.Build{Event: event}) - if err != nil { - t.Errorf("Expect cancel skipped for event type %s", event) - } - } -} - -func TestCancelRunning_IgnoreEvent(t *testing.T) { - ignore := []string{ - core.EventCron, - core.EventCustom, - core.EventPromote, - core.EventRollback, - core.EventTag, - } - for _, event := range ignore { - s := new(service) - err := s.CancelPending(noContext, nil, &core.Build{Event: event}) - if err != nil { - t.Errorf("Expect cancel skipped for event type %s", event) - } - } -} - -func TestCancel(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockStages := []*core.Stage{ - {Status: core.StatusPassing}, - { - Status: core.StatusPending, - Steps: []*core.Step{ - {Status: core.StatusPassing}, - {Status: core.StatusPending}, - }, - }, - } - - mockBuildCopy := new(core.Build) - *mockBuildCopy = *mockBuild - - repos := mock.NewMockRepositoryStore(controller) - - events := mock.NewMockPubsub(controller) - events.EXPECT().Publish(gomock.Any(), gomock.Any()).Return(nil) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().Update(gomock.Any(), mockBuildCopy).Return(nil) - - users := mock.NewMockUserStore(controller) - users.EXPECT().Find(gomock.Any(), mockRepo.UserID).Return(mockUser, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().ListSteps(gomock.Any(), mockBuild.ID).Return(mockStages, nil) - stages.EXPECT().Update(gomock.Any(), mockStages[1]).Return(nil) - - steps := mock.NewMockStepStore(controller) - steps.EXPECT().Update(gomock.Any(), mockStages[1].Steps[1]).Return(nil) - - status := mock.NewMockStatusService(controller) - status.EXPECT().Send(gomock.Any(), mockUser, gomock.Any()).Return(nil) - - webhook := mock.NewMockWebhookSender(controller) - webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil) - - scheduler := mock.NewMockScheduler(controller) - scheduler.EXPECT().Cancel(gomock.Any(), mockBuild.ID).Return(nil) - - c := new(chi.Context) - c.URLParams.Add("owner", "octocat") - c.URLParams.Add("name", "hello-world") - c.URLParams.Add("number", "1") - - s := New(builds, events, repos, scheduler, stages, status, steps, users, webhook) - err := s.Cancel(noContext, mockRepo, mockBuildCopy) - if err != nil { - t.Error(err) - } -} - -var ( - mockRepo = &core.Repository{ - ID: 1, - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Counter: 42, - Branch: "master", - } - - mockBuild = &core.Build{ - ID: 1, - Number: 1, - RepoID: 1, - Status: core.StatusPending, - Event: core.EventPush, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Timestamp: 1299283200, - Message: "first commit", - Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e", - After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Ref: "refs/heads/master", - Source: "master", - Target: "master", - Author: "octocat", - AuthorName: "The Octocat", - AuthorEmail: "octocat@hello-world.com", - AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231", - Sender: "octocat", - } - - mockUser = &core.User{ - ID: 1, - Login: "octocat", - } -) diff --git a/service/canceler/match.go b/service/canceler/match.go deleted file mode 100644 index d868fa620b..0000000000 --- a/service/canceler/match.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package canceler - -import "github.com/drone/drone/core" - -func match(build *core.Build, with *core.Repository) bool { - // filter out existing builds for others - // repositories. - if with.ID != build.RepoID { - return false - } - // filter out builds that are newer than - // the current build. - if with.Build.Number >= build.Number { - return false - } - - if with.CancelRunning == true { - if with.Build.Status != core.StatusRunning && with.Build.Status != core.StatusPending { - return false - } - } else { - if with.Build.Status != core.StatusPending { - return false - } - } - - // filter out builds that do not match - // the same event type. - if with.Build.Event != build.Event { - return false - } - // filter out builds that do not match - // the same reference. - if with.Build.Ref != build.Ref { - return false - } - return true -} diff --git a/service/canceler/match_test.go b/service/canceler/match_test.go deleted file mode 100644 index a35628dfc9..0000000000 --- a/service/canceler/match_test.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package canceler - -import ( - "testing" - - "github.com/drone/drone/core" -) - -func TestMatchPendingBuild(t *testing.T) { - tests := []struct { - build *core.Build - repo *core.Repository - want bool - }{ - // does not match repository id - { - build: &core.Build{RepoID: 2}, - repo: &core.Repository{ID: 1}, - want: false, - }, - // does not match build number requirement that - // must be older than current build - { - build: &core.Build{RepoID: 1, Number: 2}, - repo: &core.Repository{ID: 1, Build: &core.Build{Number: 3}}, - want: false, - }, - { - build: &core.Build{RepoID: 1, Number: 2}, - repo: &core.Repository{ID: 1, Build: &core.Build{Number: 2}}, - want: false, - }, - // does not match required status - { - build: &core.Build{RepoID: 1, Number: 2}, - repo: &core.Repository{ID: 1, Build: &core.Build{Number: 1, Status: core.StatusPassing}}, - want: false, - }, - // does not match (one of) required event types - { - build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPullRequest}, - repo: &core.Repository{ID: 1, Build: &core.Build{ - Number: 1, - Status: core.StatusPending, - Event: core.EventPush, - }}, - want: false, - }, - // does not match ref - { - build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPush, Ref: "refs/heads/master"}, - repo: &core.Repository{ID: 1, Build: &core.Build{ - Number: 1, - Status: core.StatusPending, - Event: core.EventPush, - Ref: "refs/heads/develop", - }}, - want: false, - }, - - // - // successful matches - // - { - build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPush, Ref: "refs/heads/master"}, - repo: &core.Repository{ID: 1, Build: &core.Build{ - Number: 1, - Status: core.StatusPending, - Event: core.EventPush, - Ref: "refs/heads/master", - }, CancelRunning: false}, - want: true, - }, - { - build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPullRequest, Ref: "refs/heads/master"}, - repo: &core.Repository{ID: 1, Build: &core.Build{ - Number: 1, - Status: core.StatusPending, - Event: core.EventPullRequest, - Ref: "refs/heads/master", - }, CancelRunning: false}, - want: true, - }, - } - - for i, test := range tests { - if got, want := match(test.build, test.repo), test.want; got != want { - t.Errorf("Want match %v at index %d, got %v", want, i, got) - } - } -} - -func TestMatchRunningBuilds(t *testing.T) { - tests := []struct { - build *core.Build - repo *core.Repository - want bool - }{ - // does not match repository id - { - build: &core.Build{RepoID: 2}, - repo: &core.Repository{ID: 1}, - want: false, - }, - // does not match build number requirement that - // must be older than current build - { - build: &core.Build{RepoID: 1, Number: 2}, - repo: &core.Repository{ID: 1, Build: &core.Build{Number: 3}}, - want: false, - }, - { - build: &core.Build{RepoID: 1, Number: 2}, - repo: &core.Repository{ID: 1, Build: &core.Build{Number: 2}}, - want: false, - }, - // does not match required status - { - build: &core.Build{RepoID: 1, Number: 2}, - repo: &core.Repository{ID: 1, Build: &core.Build{Number: 1, Status: core.StatusError}}, - want: false, - }, - // does not match (one of) required event types - { - build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPullRequest}, - repo: &core.Repository{ID: 1, Build: &core.Build{ - Number: 1, - Status: core.StatusRunning, - Event: core.EventPush, - }}, - want: false, - }, - // does not match ref - { - build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPush, Ref: "refs/heads/master"}, - repo: &core.Repository{ID: 1, Build: &core.Build{ - Number: 1, - Status: core.StatusRunning, - Event: core.EventPush, - Ref: "refs/heads/develop", - }}, - want: false, - }, - - // - // successful matches - // - { - build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPullRequest, Ref: "refs/heads/master"}, - repo: &core.Repository{ID: 1, Build: &core.Build{ - Number: 1, - Status: core.StatusRunning, - Event: core.EventPullRequest, - Ref: "refs/heads/master", - }, CancelRunning: true}, - want: true, - }, - { - build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPush, Ref: "refs/heads/master"}, - repo: &core.Repository{ID: 1, Build: &core.Build{ - Number: 1, - Status: core.StatusRunning, - Event: core.EventPush, - Ref: "refs/heads/master", - }, CancelRunning: true}, - want: true, - }, - } - - for i, test := range tests { - if got, want := match(test.build, test.repo), test.want; got != want { - t.Errorf("Want match %v at index %d, got %v", want, i, got) - } - } -} diff --git a/service/canceler/reaper/reaper.go b/service/canceler/reaper/reaper.go deleted file mode 100644 index 9de39b11d5..0000000000 --- a/service/canceler/reaper/reaper.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package reaper - -import ( - "context" - "runtime/debug" - "time" - - "github.com/drone/drone/core" - - "github.com/hashicorp/go-multierror" - "github.com/sirupsen/logrus" -) - -// Reaper finds and kills zombie jobs that are permanently -// stuck in a pending or running state. -type Reaper struct { - Repos core.RepositoryStore - Builds core.BuildStore - Stages core.StageStore - Canceler core.Canceler - Pending time.Duration // Pending is the pending pipeline deadline - Running time.Duration // Running is the running pipeline deadline - - // Buffer is applied when calculating whether or not the timeout - // period is exceeded. The added buffer helps prevent false positives. - Buffer time.Duration -} - -// New returns a new Reaper. -func New( - repos core.RepositoryStore, - builds core.BuildStore, - stages core.StageStore, - canceler core.Canceler, - running time.Duration, - pending time.Duration, - buffer time.Duration, -) *Reaper { - if running == 0 { - running = time.Hour * 24 - } - if pending == 0 { - pending = time.Hour * 24 - } - - if buffer == 0 { - buffer = time.Minute * 30 - } - return &Reaper{ - Repos: repos, - Builds: builds, - Stages: stages, - Canceler: canceler, - Pending: pending, - Running: running, - Buffer: buffer, - } -} - -// Start starts the reaper. -func (r *Reaper) Start(ctx context.Context, dur time.Duration) error { - ticker := time.NewTicker(dur) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return nil - case <-ticker.C: - r.reap(ctx) - } - } -} - -func (r *Reaper) reap(ctx context.Context) error { - defer func() { - // taking the paranoid approach to recover from - // a panic that should absolutely never happen. - if r := recover(); r != nil { - logrus.Errorf("reaper: unexpected panic: %s", r) - debug.PrintStack() - } - }() - - logrus.Traceln("reaper: finding zombie builds") - - var result error - pending, err := r.Builds.Pending(ctx) - if err != nil { - logrus.WithError(err). - Errorf("reaper: cannot get pending builds") - result = multierror.Append(result, err) - } - for _, build := range pending { - logger := logrus. - WithField("build.id", build.ID). - WithField("build.number", build.Number). - WithField("build.repo_id", build.RepoID). - WithField("build.status", build.Status). - WithField("build.created", build.Created) - - // if a build is pending for longer than the maximum - // pending time limit, the build is maybe cancelled. - if isExceeded(build.Created, r.Pending, r.Buffer) { - logger.Traceln("reaper: cancel build: time limit exceeded") - err = r.reapMaybe(ctx, build) - if err != nil { - logger.WithError(err). - Errorln("reaper: cannot cancel build") - result = multierror.Append(result, err) - } - } else { - logger.Traceln("reaper: ignore build: time limit not exceeded") - } - } - - running, err := r.Builds.Running(ctx) - if err != nil { - logrus.WithError(err). - Errorf("reaper: cannot get running builds") - result = multierror.Append(result, err) - } - for _, build := range running { - logger := logrus. - WithField("build.id", build.ID). - WithField("build.number", build.Number). - WithField("build.repo_id", build.RepoID). - WithField("build.status", build.Status). - WithField("build.created", build.Created) - - // if a build is running for longer than the maximum - // running time limit, the build is maybe cancelled. - if isExceeded(build.Started, r.Running, r.Buffer) { - logger.Traceln("reaper: cancel build: time limit exceeded") - - err = r.reapMaybe(ctx, build) - if err != nil { - logger.WithError(err). - Errorln("reaper: cannot cancel build") - result = multierror.Append(result, err) - } - } else { - logger.Traceln("reaper: ignore build: time limit not exceeded") - } - } - - return result -} - -func (r *Reaper) reapMaybe(ctx context.Context, build *core.Build) error { - repo, err := r.Repos.Find(ctx, build.RepoID) - if err != nil { - return err - } - - // if the build status is pending we can immediately - // cancel the build and all build stages. - if build.Status == core.StatusPending { - // TODO trace log entry - return r.Canceler.Cancel(ctx, repo, build) - } - - stages, err := r.Stages.List(ctx, build.ID) - if err != nil { - return err - } - - var started int64 - for _, stage := range stages { - if stage.IsDone() { - continue - } - if stage.Started > started { - started = stage.Started - } - } - - // if the build stages are all pending we can immediately - // cancel the build. - if started == 0 { - // TODO trace log entry - return r.Canceler.Cancel(ctx, repo, build) - } - - // if the build stage has exceeded the timeout by a reasonable - // margin cancel the build and all build stages, else ignore. - if isExceeded(started, time.Duration(repo.Timeout)*time.Minute, r.Buffer) { - // TODO trace log entry - return r.Canceler.Cancel(ctx, repo, build) - } - - // TODO trace log entry - return nil -} diff --git a/service/canceler/reaper/reaper_test.go b/service/canceler/reaper/reaper_test.go deleted file mode 100644 index b98ea934eb..0000000000 --- a/service/canceler/reaper/reaper_test.go +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package reaper - -import ( - "context" - "testing" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -var nocontext = context.Background() - -// -// reap tests -// - -// this test confirms that pending builds that -// exceed the deadline are canceled, and pending -// builds that do not exceed the deadline are -// ignored. -func TestReapPending(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - defer func() { - now = time.Now - }() - now = func() time.Time { - return mustParse("2006-01-02T15:00:00") - } - - mockRepo := &core.Repository{ - ID: 2, - } - mockBuild := &core.Build{ - ID: 1, - RepoID: mockRepo.ID, - Status: core.StatusPending, - Created: mustParse("2006-01-01T00:00:00").Unix(), // expire > 24 hours, must cancel - } - mockPending := []*core.Build{ - mockBuild, - { - ID: 2, - RepoID: mockRepo.ID, - Status: core.StatusPending, - Created: mustParse("2006-01-02T14:30:00").Unix(), // expire < 1 hours, must ignore - }, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil).Times(1) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().Pending(gomock.Any()).Return(mockPending, nil) - builds.EXPECT().Running(gomock.Any()).Return(nil, nil) - - canceler := mock.NewMockCanceler(controller) - canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild) - - r := New( - repos, - builds, - nil, - canceler, - time.Hour*24, - time.Hour*24, - time.Minute*30, - ) - - r.reap(nocontext) -} - -// this test confirms that running builds that -// exceed the deadline are canceled, and running -// builds that do not exceed the deadline are -// ignored. -func TestReapRunning(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - defer func() { - now = time.Now - }() - now = func() time.Time { - return mustParse("2006-01-02T15:00:00") - } - - mockRepo := &core.Repository{ - ID: 2, - Timeout: 60, - } - mockBuild := &core.Build{ - ID: 1, - RepoID: mockRepo.ID, - Status: core.StatusRunning, - Started: mustParse("2006-01-01T00:00:00").Unix(), // expire > 24 hours, must cancel - } - mockRunning := []*core.Build{ - mockBuild, - { - ID: 2, - RepoID: mockRepo.ID, - Status: core.StatusRunning, - Started: mustParse("2006-01-02T14:30:00").Unix(), // expire < 1 hours, must ignore - }, - } - mockStages := []*core.Stage{ - { - BuildID: mockBuild.ID, - Status: core.StatusPending, - Started: 0, - }, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil).Times(1) - - builds := mock.NewMockBuildStore(controller) - builds.EXPECT().Pending(gomock.Any()).Return(nil, nil) - builds.EXPECT().Running(gomock.Any()).Return(mockRunning, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil) - - canceler := mock.NewMockCanceler(controller) - canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild) - - r := New( - repos, - builds, - stages, - canceler, - time.Hour*24, - time.Hour*24, - time.Minute*30, - ) - - r.reap(nocontext) -} - -// -// reap maybe tests -// - -// this test confirms that the build is cancelled -// if the build status is pending. -func TestReapPendingMaybe(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockBuild := &core.Build{ - ID: 1, - RepoID: 2, - Status: core.StatusPending, - } - mockRepo := &core.Repository{ - ID: 2, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil) - - canceler := mock.NewMockCanceler(controller) - canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild) - - r := &Reaper{ - Repos: repos, - Stages: nil, - Canceler: canceler, - } - - r.reapMaybe(nocontext, mockBuild) -} - -// this test confirms that the build is cancelled -// if the build status is running, and the stage -// started date is greater than the expiry date. -func TestReapRunningMaybe(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - defer func() { - now = time.Now - }() - now = func() time.Time { - return mustParse("2006-01-02T15:00:00") - } - - mockBuild := &core.Build{ - ID: 1, - RepoID: 2, - Status: core.StatusRunning, - } - mockRepo := &core.Repository{ - ID: 2, - Timeout: 60, - } - mockStages := []*core.Stage{ - { - Status: core.StatusRunning, - Started: mustParse("2006-01-02T13:00:00").Unix(), // running 2 hours, 1 hour longer than timeout - }, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil) - - canceler := mock.NewMockCanceler(controller) - canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild) - - r := &Reaper{ - Repos: repos, - Stages: stages, - Canceler: canceler, - } - - r.reapMaybe(nocontext, mockBuild) -} - -// this test confirms that if the build status is -// running, but all stages have a pending status, -// the build is cancelled (this likely points to some -// sort of race condition, and should not happen). -func TestReapRunningMaybe_AllStagesPending(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - defer func() { - now = time.Now - }() - now = func() time.Time { - return mustParse("2006-01-02T15:00:00") - } - - mockBuild := &core.Build{ - ID: 1, - RepoID: 2, - Status: core.StatusRunning, - } - mockRepo := &core.Repository{ - ID: 2, - Timeout: 60, - } - mockStages := []*core.Stage{ - { - Status: core.StatusPending, - Started: 0, - }, - { - Status: core.StatusPending, - Started: 0, - }, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil) - - canceler := mock.NewMockCanceler(controller) - canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild) - - r := &Reaper{ - Repos: repos, - Stages: stages, - Canceler: canceler, - } - - r.reapMaybe(nocontext, mockBuild) -} - -// this test confirms that if the build status is -// running, but all stages have a finished status, -// the build is cancelled (this likely points to some -// sort of race condition, and should not happen). -func TestReapRunningMaybe_AllStagesFinished(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - defer func() { - now = time.Now - }() - now = func() time.Time { - return mustParse("2006-01-02T15:00:00") - } - - mockBuild := &core.Build{ - ID: 1, - RepoID: 2, - Status: core.StatusRunning, - } - mockRepo := &core.Repository{ - ID: 2, - Timeout: 60, - } - mockStages := []*core.Stage{ - { - Status: core.StatusPassing, - Started: mustParse("2006-01-02T14:40:00").Unix(), - }, - { - Status: core.StatusPassing, - Started: mustParse("2006-01-02T14:50:00").Unix(), - }, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil) - - canceler := mock.NewMockCanceler(controller) - canceler.EXPECT().Cancel(gomock.Any(), mockRepo, mockBuild) - - r := &Reaper{ - Repos: repos, - Stages: stages, - Canceler: canceler, - } - - r.reapMaybe(nocontext, mockBuild) -} - -// this test confirms that if the build status is -// running, but the stage start time has not exceeded -// the timeout period, the build is NOT cancelled. -func TestReapRunningMaybe_NotExpired(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - defer func() { - now = time.Now - }() - now = func() time.Time { - return mustParse("2006-01-02T15:00:00") - } - - mockBuild := &core.Build{ - ID: 1, - RepoID: 2, - Status: core.StatusRunning, - } - mockRepo := &core.Repository{ - ID: 2, - Timeout: 60, - } - mockStages := []*core.Stage{ - { - Status: core.StatusPassing, - Started: mustParse("2006-01-02T14:50:00").Unix(), - }, - { - Status: core.StatusRunning, - Started: mustParse("2006-01-02T14:55:00").Unix(), - }, - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().Find(gomock.Any(), mockBuild.RepoID).Return(mockRepo, nil) - - stages := mock.NewMockStageStore(controller) - stages.EXPECT().List(gomock.Any(), mockBuild.ID).Return(mockStages, nil) - - r := &Reaper{ - Repos: repos, - Stages: stages, - Canceler: nil, - } - - r.reapMaybe(nocontext, mockBuild) -} - -// -// Failure Scenarios -// - -func TestReapRunningMaybe_ErrorGetRepo(t *testing.T) { - -} - -func TestReapRunningMaybe_ErrorListStages(t *testing.T) { - -} diff --git a/service/canceler/reaper/util.go b/service/canceler/reaper/util.go deleted file mode 100644 index 5bf710f292..0000000000 --- a/service/canceler/reaper/util.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package reaper - -import "time" - -// helper function returns the current time. -var now = time.Now - -// helper function returns true if the time exceeded the -// timeout duration. -func isExceeded(unix int64, timeout, buffer time.Duration) bool { - return now().After( - time.Unix(unix, 0).Add(timeout).Add(buffer), - ) -} diff --git a/service/canceler/reaper/util_test.go b/service/canceler/reaper/util_test.go deleted file mode 100644 index 342e1585ce..0000000000 --- a/service/canceler/reaper/util_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package reaper - -import ( - "testing" - "time" -) - -func TestIsExceeded(t *testing.T) { - defer func() { - now = time.Now - }() - now = func() time.Time { - return mustParse("2006-01-02T15:00:00") - } - var tests = []struct { - unix int64 - timeout time.Duration - buffer time.Duration - exceeded bool - }{ - // timestamp equal to current time, not expired - { - unix: mustParse("2006-01-02T15:00:00").Unix(), - timeout: time.Minute * 60, - buffer: time.Minute * 5, - exceeded: false, - }, - // timestamp is not gt current time - timeout, not expired - { - unix: mustParse("2006-01-02T14:00:00").Unix(), - timeout: time.Minute * 60, - buffer: 0, - exceeded: false, - }, - // timestamp is gt current time - timeout, expired - { - unix: mustParse("2006-01-02T13:59:00").Unix(), - timeout: time.Minute * 60, - buffer: 0, - exceeded: true, - }, - // timestamp is not gt current time - timeout - buffer, not expired - { - unix: mustParse("2006-01-02T13:59:00").Unix(), - timeout: time.Minute * 60, - buffer: time.Minute * 5, - exceeded: false, - }, - // timestamp is gt current time - timeout - buffer, expired - { - unix: mustParse("2006-01-02T13:04:05").Unix(), - timeout: time.Minute * 60, - buffer: time.Minute * 5, - exceeded: true, - }, - } - for i, test := range tests { - got, want := isExceeded(test.unix, test.timeout, test.buffer), test.exceeded - if got != want { - t.Errorf("Want exceeded %v, got %v at index %v", want, got, i) - } - } -} - -func mustParse(s string) time.Time { - t, err := time.Parse("2006-01-02T15:04:05", s) - if err != nil { - panic(err) - } - return t -} diff --git a/service/commit/commit.go b/service/commit/commit.go deleted file mode 100644 index c313b3a56a..0000000000 --- a/service/commit/commit.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package commit - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -// New returns a new CommitServiceFactory. -func New(client *scm.Client, renew core.Renewer) core.CommitService { - return &service{ - client: client, - renew: renew, - } -} - -type service struct { - renew core.Renewer - client *scm.Client -} - -func (s *service) Find(ctx context.Context, user *core.User, repo, sha string) (*core.Commit, error) { - err := s.renew.Renew(ctx, user, false) - if err != nil { - return nil, err - } - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - }) - commit, _, err := s.client.Git.FindCommit(ctx, repo, sha) - if err != nil { - return nil, err - } - return &core.Commit{ - Sha: commit.Sha, - Message: commit.Message, - Link: commit.Link, - Author: &core.Committer{ - Name: commit.Author.Name, - Email: commit.Author.Email, - Date: commit.Author.Date.Unix(), - Login: commit.Author.Login, - Avatar: commit.Author.Avatar, - }, - Committer: &core.Committer{ - Name: commit.Committer.Name, - Email: commit.Committer.Email, - Date: commit.Committer.Date.Unix(), - Login: commit.Committer.Login, - Avatar: commit.Committer.Avatar, - }, - }, nil -} - -func (s *service) FindRef(ctx context.Context, user *core.User, repo, ref string) (*core.Commit, error) { - err := s.renew.Renew(ctx, user, false) - if err != nil { - return nil, err - } - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - }) - - switch s.client.Driver { - case scm.DriverBitbucket, - scm.DriverStash: - ref = scm.TrimRef(ref) - branch, _, err := s.client.Git.FindBranch(ctx, repo, ref) // wont work for a Tag - if err != nil { - return nil, err - } - ref = branch.Sha - } - - commit, _, err := s.client.Git.FindCommit(ctx, repo, ref) - if err != nil { - return nil, err - } - return &core.Commit{ - Sha: commit.Sha, - Ref: ref, - Message: commit.Message, - Link: commit.Link, - Author: &core.Committer{ - Name: commit.Author.Name, - Email: commit.Author.Email, - Date: commit.Author.Date.Unix(), - Login: commit.Author.Login, - Avatar: commit.Author.Avatar, - }, - Committer: &core.Committer{ - Name: commit.Committer.Name, - Email: commit.Committer.Email, - Date: commit.Committer.Date.Unix(), - Login: commit.Committer.Login, - Avatar: commit.Committer.Avatar, - }, - }, nil -} - -func (s *service) ListChanges(ctx context.Context, user *core.User, repo, sha, ref string) ([]*core.Change, error) { - err := s.renew.Renew(ctx, user, false) - if err != nil { - return nil, err - } - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - }) - out, _, err := s.client.Git.ListChanges(ctx, repo, sha, scm.ListOptions{Size: 100}) - if err != nil { - return nil, err - } - var changes []*core.Change - for _, change := range out { - changes = append(changes, &core.Change{ - Path: change.Path, - Added: change.Added, - Renamed: change.Renamed, - Deleted: change.Deleted, - }) - } - return changes, nil -} diff --git a/service/commit/commit_test.go b/service/commit/commit_test.go deleted file mode 100644 index 279f524020..0000000000 --- a/service/commit/commit_test.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package commit - -import ( - "context" - "testing" - "time" - - "github.com/drone/drone/mock" - "github.com/drone/drone/mock/mockscm" - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var noContext = context.Background() - -func TestFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockCommit := &scm.Commit{ - Sha: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Message: "Merge pull request #6 from Spaceghost/patch-1\n\nNew line at end of file.", - Author: scm.Signature{ - Name: "The Octocat", - Email: "octocat@nowhere.com", - Date: time.Unix(1532303087, 0), - Login: "octocat", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - }, - Committer: scm.Signature{ - Name: "The Octocat", - Email: "octocat@nowhere.com", - Date: time.Unix(1532303087, 0), - Login: "octocat", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - }, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - } - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - mockGit := mockscm.NewMockGitService(controller) - mockGit.EXPECT().FindCommit(gomock.Any(), "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa").Return(mockCommit, nil, nil) - - client := new(scm.Client) - client.Git = mockGit - - want := &core.Commit{ - Sha: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Ref: "", - Message: "Merge pull request #6 from Spaceghost/patch-1\n\nNew line at end of file.", - Author: &core.Committer{ - Name: "The Octocat", - Email: "octocat@nowhere.com", - Date: 1532303087, - Login: "octocat", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - }, - Committer: &core.Committer{ - Name: "The Octocat", - Email: "octocat@nowhere.com", - Date: 1532303087, - Login: "octocat", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - }, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - } - - service := New(client, mockRenewer) - got, err := service.Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa") - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestFind_Err(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - mockGit := mockscm.NewMockGitService(controller) - mockGit.EXPECT().FindCommit(gomock.Any(), "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa").Return(nil, nil, scm.ErrNotFound) - - client := new(scm.Client) - client.Git = mockGit - - service := New(client, mockRenewer) - _, err := service.Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa") - if err != scm.ErrNotFound { - t.Errorf("Want not found error, got %v", err) - } -} - -func TestFind_ErrRenew(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, mockRenewer) - _, err := service.Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa") - if err != scm.ErrNotAuthorized { - t.Errorf("Want not authorized error, got %v", err) - } -} - -func TestFindRef(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockCommit := &scm.Commit{ - Sha: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Message: "Merge pull request #6 from Spaceghost/patch-1\n\nNew line at end of file.", - Author: scm.Signature{ - Name: "The Octocat", - Email: "octocat@nowhere.com", - Date: time.Unix(1532303087, 0), - Login: "octocat", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - }, - Committer: scm.Signature{ - Name: "The Octocat", - Email: "octocat@nowhere.com", - Date: time.Unix(1532303087, 0), - Login: "octocat", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - }, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - } - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - mockGit := mockscm.NewMockGitService(controller) - mockGit.EXPECT().FindCommit(gomock.Any(), "octocat/hello-world", "master").Return(mockCommit, nil, nil) - - client := new(scm.Client) - client.Git = mockGit - - want := &core.Commit{ - Sha: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Ref: "master", - Message: "Merge pull request #6 from Spaceghost/patch-1\n\nNew line at end of file.", - Author: &core.Committer{ - Name: "The Octocat", - Email: "octocat@nowhere.com", - Date: 1532303087, - Login: "octocat", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - }, - Committer: &core.Committer{ - Name: "The Octocat", - Email: "octocat@nowhere.com", - Date: 1532303087, - Login: "octocat", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - }, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - } - - service := New(client, mockRenewer) - got, err := service.FindRef(noContext, mockUser, "octocat/hello-world", "master") - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestFindRef_Err(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - mockGit := mockscm.NewMockGitService(controller) - mockGit.EXPECT().FindCommit(gomock.Any(), "octocat/hello-world", "master").Return(nil, nil, scm.ErrNotFound) - - client := new(scm.Client) - client.Git = mockGit - - service := New(client, mockRenewer) - _, err := service.FindRef(noContext, mockUser, "octocat/hello-world", "master") - if err != scm.ErrNotFound { - t.Errorf("Want not found error, got %v", err) - } -} - -func TestFindRef_ErrRenew(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, mockRenewer) - _, err := service.FindRef(noContext, mockUser, "octocat/hello-world", "master") - if err != scm.ErrNotAuthorized { - t.Errorf("Want not authorized error, got %v", err) - } -} - -func TestListChanges(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockChanges := []*scm.Change{ - { - Path: "file1", - Added: false, - Deleted: false, - Renamed: false, - }, - { - Path: "file2", - Added: true, - Deleted: false, - Renamed: false, - }, - { - Path: "file2", - Added: false, - Deleted: true, - Renamed: false, - }, - { - Path: "file3", - Added: false, - Deleted: false, - Renamed: true, - }, - } - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - mockGit := mockscm.NewMockGitService(controller) - mockGit.EXPECT().ListChanges(gomock.Any(), "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", gomock.Any()).Return(mockChanges, nil, nil) - - client := new(scm.Client) - client.Git = mockGit - - want := []*core.Change{ - { - Path: "file1", - Added: false, - Deleted: false, - Renamed: false, - }, - { - Path: "file2", - Added: true, - Deleted: false, - Renamed: false, - }, - { - Path: "file2", - Added: false, - Deleted: true, - Renamed: false, - }, - { - Path: "file3", - Added: false, - Deleted: false, - Renamed: true, - }, - } - - service := New(client, mockRenewer) - got, err := service.ListChanges(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master") - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestListChanges_Err(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - mockGit := mockscm.NewMockGitService(controller) - mockGit.EXPECT().ListChanges(gomock.Any(), "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", gomock.Any()).Return(nil, nil, scm.ErrNotFound) - - client := new(scm.Client) - client.Git = mockGit - - service := New(client, mockRenewer) - _, err := service.ListChanges(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master") - if err != scm.ErrNotFound { - t.Errorf("Want not found error, got %v", err) - } -} - -func TestListChanges_ErrRenew(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, mockRenewer) - _, err := service.ListChanges(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master") - if err != scm.ErrNotAuthorized { - t.Errorf("Want not authorized error, got %v", err) - } -} diff --git a/service/content/cache/contents.go b/service/content/cache/contents.go deleted file mode 100644 index e10ce49a52..0000000000 --- a/service/content/cache/contents.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package cache - -import ( - "context" - "fmt" - - "github.com/drone/drone/core" - - "github.com/hashicorp/golang-lru" -) - -// content key pattern used in the cache, comprised of the -// repository slug, commit and path. -const contentKey = "%s/%s/%s" - -// Contents returns a new FileService that is wrapped -// with an in-memory cache. -func Contents(base core.FileService) core.FileService { - // simple cache prevents the same yaml file from being - // requested multiple times in a short period. - cache, _ := lru.New(25) - return &service{ - service: base, - cache: cache, - } -} - -type service struct { - cache *lru.Cache - service core.FileService - user *core.User -} - -func (s *service) Find(ctx context.Context, user *core.User, repo, commit, ref, path string) (*core.File, error) { - key := fmt.Sprintf(contentKey, repo, commit, path) - cached, ok := s.cache.Get(key) - if ok { - return cached.(*core.File), nil - } - file, err := s.service.Find(ctx, user, repo, commit, ref, path) - if err != nil { - return nil, err - } - s.cache.Add(key, file) - return file, nil -} diff --git a/service/content/cache/contents_oss.go b/service/content/cache/contents_oss.go deleted file mode 100644 index 4fa8734990..0000000000 --- a/service/content/cache/contents_oss.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package cache - -import "github.com/drone/drone/core" - -// Contents returns the default FileService with no caching -// enabled. -func Contents(base core.FileService) core.FileService { - return base -} diff --git a/service/content/cache/contents_test.go b/service/content/cache/contents_test.go deleted file mode 100644 index b623c1be60..0000000000 --- a/service/content/cache/contents_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package cache - -import ( - "context" - "fmt" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/drone/go-scm/scm" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var noContext = context.Background() - -func TestFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockFile := &core.File{ - Data: []byte("hello world"), - Hash: []byte(""), - } - - mockContents := mock.NewMockFileService(controller) - mockContents.EXPECT().Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master", ".drone.yml").Return(mockFile, nil) - - service := Contents(mockContents).(*service) - - want := &core.File{ - Data: []byte("hello world"), - Hash: []byte(""), - } - - got, err := service.Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master", ".drone.yml") - if err != nil { - t.Error(err) - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } - - if len(service.cache.Keys()) == 0 { - t.Errorf("Expect item added to cache") - } -} - -func TestFindError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockContents := mock.NewMockFileService(controller) - mockContents.EXPECT().Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master", ".drone.yml").Return(nil, scm.ErrNotFound) - - service := Contents(mockContents).(*service) - - _, err := service.Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master", ".drone.yml") - if err != scm.ErrNotFound { - t.Errorf("Expect not found error") - } -} - -func TestFindCache(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockFile := &core.File{ - Data: []byte("hello world"), - Hash: []byte(""), - } - - key := fmt.Sprintf(contentKey, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", ".drone.yml") - service := Contents(nil).(*service) - service.cache.Add(key, mockFile) - - want := &core.File{ - Data: []byte("hello world"), - Hash: []byte(""), - } - - got, err := service.Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master", ".drone.yml") - if err != nil { - t.Error(err) - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} diff --git a/service/content/content.go b/service/content/content.go deleted file mode 100644 index 9eeda4a79e..0000000000 --- a/service/content/content.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package contents - -import ( - "context" - "strings" - "time" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -// default number of backoff attempts. -var attempts = 3 - -// default time to wait after failed attempt. -var wait = time.Second * 15 - -// New returns a new FileService. -func New(client *scm.Client, renewer core.Renewer) core.FileService { - return &service{ - client: client, - renewer: renewer, - attempts: attempts, - wait: wait, - } -} - -type service struct { - renewer core.Renewer - client *scm.Client - attempts int - wait time.Duration -} - -func (s *service) Find(ctx context.Context, user *core.User, repo, commit, ref, path string) (*core.File, error) { - // TODO(gogs) ability to fetch a yaml by pull request ref. - // it is not currently possible to fetch the yaml - // configuration file from a pull request sha. This - // workaround defaults to master. - if s.client.Driver == scm.DriverGogs && - strings.HasPrefix(ref, "refs/pull") { - commit = "master" - } - // TODO(gogs) ability to fetch a file in tag from commit sha. - // this is a workaround for gogs which does not allow - // fetching a file by commit sha for a tag. This forces - // fetching a file by reference instead. - if s.client.Driver == scm.DriverGogs && - strings.HasPrefix(ref, "refs/tag") { - commit = ref - } - err := s.renewer.Renew(ctx, user, false) - if err != nil { - return nil, err - } - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - }) - content, err := s.findRetry(ctx, repo, path, commit) - if err != nil { - return nil, err - } - return &core.File{ - Data: content.Data, - Hash: []byte{}, - }, nil -} - -// helper function attempts to get the yaml configuration file -// with backoff on failure. This may be required due to eventual -// consistency issues with the github datastore. -func (s *service) findRetry(ctx context.Context, repo, path, commit string) (content *scm.Content, err error) { - for i := 0; i < s.attempts; i++ { - content, _, err = s.client.Contents.Find(ctx, repo, path, commit) - // if no error is returned we can exit immediately. - if err == nil { - return - } - // wait a few seconds before retry. according to github - // support 30 seconds total should be enough time. we - // try 3 x 15 seconds, giving a total of 45 seconds. - time.Sleep(s.wait) - } - return -} diff --git a/service/content/content_test.go b/service/content/content_test.go deleted file mode 100644 index 157d319f75..0000000000 --- a/service/content/content_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package contents - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/drone/drone/mock/mockscm" - "github.com/drone/go-scm/scm" - "github.com/google/go-cmp/cmp" - - "github.com/golang/mock/gomock" -) - -var noContext = context.Background() - -func TestFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockFile := &scm.Content{ - Path: ".drone.yml", - Data: []byte("hello world"), - } - - mockContents := mockscm.NewMockContentService(controller) - mockContents.EXPECT().Find(gomock.Any(), "octocat/hello-world", ".drone.yml", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa").Return(mockFile, nil, nil) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Contents = mockContents - - want := &core.File{ - Data: []byte("hello world"), - Hash: []byte(""), - } - - service := New(client, mockRenewer) - got, err := service.Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master", ".drone.yml") - if err != nil { - t.Error(err) - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestFind_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockContents := mockscm.NewMockContentService(controller) - mockContents.EXPECT().Find(gomock.Any(), "octocat/hello-world", ".drone.yml", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa").Return(nil, nil, scm.ErrNotFound) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Contents = mockContents - - s := New(client, mockRenewer) - s.(*service).attempts = 1 - s.(*service).wait = 0 - _, err := s.Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master", ".drone.yml") - if err != scm.ErrNotFound { - t.Errorf("Expect not found error, got %s", err) - } -} - -func TestFind_RenewalError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - client := new(scm.Client) - - service := New(client, mockRenewer) - _, err := service.Find(noContext, mockUser, "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", "master", ".drone.yml") - if err == nil { - t.Errorf("Expect error refreshing token") - } -} diff --git a/service/hook/hook.go b/service/hook/hook.go deleted file mode 100644 index eb46419164..0000000000 --- a/service/hook/hook.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hook - -import ( - "context" - "time" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -// New returns a new HookService. -func New(client *scm.Client, addr string, renew core.Renewer) core.HookService { - return &service{client: client, addr: addr, renew: renew} -} - -type service struct { - renew core.Renewer - client *scm.Client - addr string -} - -func (s *service) Create(ctx context.Context, user *core.User, repo *core.Repository) error { - err := s.renew.Renew(ctx, user, false) - if err != nil { - return err - } - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - Expires: time.Unix(user.Expiry, 0), - }) - hook := &scm.HookInput{ - Name: "drone", - Target: s.addr + "/hook", - Secret: repo.Signer, - Events: scm.HookEvents{ - Branch: true, - Deployment: true, - PullRequest: true, - Push: true, - Tag: true, - }, - } - return replaceHook(ctx, s.client, repo.Slug, hook) -} - -func (s *service) Delete(ctx context.Context, user *core.User, repo *core.Repository) error { - err := s.renew.Renew(ctx, user, false) - if err != nil { - return err - } - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - Expires: time.Unix(user.Expiry, 0), - }) - return deleteHook(ctx, s.client, repo.Slug, s.addr) -} diff --git a/service/hook/hook_test.go b/service/hook/hook_test.go deleted file mode 100644 index 10bf069bef..0000000000 --- a/service/hook/hook_test.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package hook - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/drone/drone/mock/mockscm" - "github.com/drone/go-scm/scm" - - "github.com/golang/mock/gomock" -) - -var noContext = context.Background() - -func TestCreate(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockHooks := []*scm.Hook{} - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Signer: "abc123", - } - - hook := &scm.HookInput{ - Name: "drone", - Target: "https://drone.company.com/hook", - Secret: "abc123", - Events: scm.HookEvents{ - Branch: true, - Deployment: true, - PullRequest: true, - Push: true, - Tag: true, - }, - } - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - mockRepos := mockscm.NewMockRepositoryService(controller) - mockRepos.EXPECT().ListHooks(gomock.Any(), "octocat/hello-world", gomock.Any()).Return(mockHooks, nil, nil) - mockRepos.EXPECT().CreateHook(gomock.Any(), "octocat/hello-world", hook).Return(nil, nil, nil) - - client := new(scm.Client) - client.Repositories = mockRepos - - service := New(client, "https://drone.company.com", mockRenewer) - err := service.Create(noContext, mockUser, mockRepo) - if err != nil { - t.Error(err) - } -} - -func TestCreate_RenewErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, "https://drone.company.com", mockRenewer) - err := service.Create(noContext, mockUser, nil) - if err != scm.ErrNotAuthorized { - t.Errorf("Want not authorized error, got %v", err) - } -} - -func TestDelete(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockHooks := []*scm.Hook{ - { - ID: "1", - Name: "drone", - Target: "https://drone.company.com/hook", - }, - } - mockRepo := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Signer: "abc123", - } - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - mockRepos := mockscm.NewMockRepositoryService(controller) - mockRepos.EXPECT().ListHooks(gomock.Any(), "octocat/hello-world", gomock.Any()).Return(mockHooks, nil, nil) - mockRepos.EXPECT().DeleteHook(gomock.Any(), "octocat/hello-world", "1").Return(nil, nil) - - client := new(scm.Client) - client.Repositories = mockRepos - - service := New(client, "https://drone.company.com", mockRenewer) - err := service.Delete(noContext, mockUser, mockRepo) - if err != nil { - t.Error(err) - } -} - -func TestDelete_RenewErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, "https://drone.company.com", mockRenewer) - err := service.Delete(noContext, mockUser, nil) - if err != scm.ErrNotAuthorized { - t.Errorf("Want not authorized error, got %v", err) - } -} diff --git a/service/hook/parser/parse.go b/service/hook/parser/parse.go deleted file mode 100644 index 91b409b06c..0000000000 --- a/service/hook/parser/parse.go +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package parser - -import ( - "errors" - "fmt" - "net/http" - "net/http/httputil" - "os" - "strconv" - "strings" - "time" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -// TODO(bradrydzewski): stash, push hook missing link -// TODO(bradrydzewski): stash, tag hook missing timestamp -// TODO(bradrydzewski): stash, tag hook missing commit message -// TODO(bradrydzewski): stash, tag hook missing link -// TODO(bradrydzewski): stash, pull request hook missing link -// TODO(bradrydzewski): stash, hooks missing repository clone http url -// TODO(bradrydzewski): stash, hooks missing repository clone ssh url -// TODO(bradrydzewski): stash, hooks missing repository html link - -// TODO(bradrydzewski): gogs, push hook missing author avatar, using sender instead. -// TODO(bradrydzewski): gogs, pull request hook missing commit sha. -// TODO(bradrydzewski): gogs, tag hook missing commit sha. -// TODO(bradrydzewski): gogs, sender missing Name field. -// TODO(bradrydzewski): gogs, push hook missing repository html url - -// TODO(bradrydzewski): gitea, push hook missing author avatar, using sender instead. -// TODO(bradrydzewski): gitea, tag hook missing commit sha. -// TODO(bradrydzewski): gitea, sender missing Name field. -// TODO(bradrydzewski): gitea, push hook missing repository html url - -// TODO(bradrydzewski): bitbucket, pull request hook missing author email. -// TODO(bradrydzewski): bitbucket, hooks missing default repository branch. - -// TODO(bradrydzewski): github, push hook timestamp is negative value. -// TODO(bradrydzewski): github, pull request message is empty - -// represents a deleted ref in the github webhook. -const emptyCommit = "0000000000000000000000000000000000000000" - -// this is intended for local testing and instructs the handler -// to print the contents of the hook to stdout. -var debugPrintHook = false - -func init() { - debugPrintHook, _ = strconv.ParseBool( - os.Getenv("DRONE_DEBUG_DUMP_HOOK"), - ) -} - -// New returns a new HookParser. -func New(client *scm.Client) core.HookParser { - return &parser{client} -} - -type parser struct { - client *scm.Client -} - -func (p *parser) Parse(req *http.Request, secretFunc func(string) string) (*core.Hook, *core.Repository, error) { - if debugPrintHook { - // if DRONE_DEBUG_DUMP_HOOK=true print the http.Request - // headers and body to stdout. - out, _ := httputil.DumpRequest(req, true) - os.Stderr.Write(out) - } - - // callback function provides the webhook parser with - // a per-repository secret key used to verify the webhook - // payload signature for authenticity. - fn := func(webhook scm.Webhook) (string, error) { - if webhook == nil { - // HACK(bradrydzewski) if the incoming webhook is nil - // we assume it is an unknown event or action. A more - // permanent fix is to update go-scm to return an - // scm.ErrUnknownAction error. - return "", scm.ErrUnknownEvent - } - repo := webhook.Repository() - slug := scm.Join(repo.Namespace, repo.Name) - secret := secretFunc(slug) - if secret == "" { - return secret, errors.New("Cannot find repository") - } - return secret, nil - } - - payload, err := p.client.Webhooks.Parse(req, fn) - if err == scm.ErrUnknownEvent { - return nil, nil, nil - } - if err != nil { - return nil, nil, err - } - - var repo *core.Repository - var hook *core.Hook - - switch v := payload.(type) { - case *scm.PushHook: - // github sends push hooks when tags and branches are - // deleted. These hooks should be ignored. - if v.Commit.Sha == emptyCommit { - return nil, nil, nil - } - // github sends push hooks when tags are created. The - // push hook contains more information than the tag hook, - // so we choose to use the push hook for tags. - if strings.HasPrefix(v.Ref, "refs/tags/") { - hook = &core.Hook{ - Trigger: core.TriggerHook, // core.TriggerHook - Event: core.EventTag, - Action: core.ActionCreate, - Link: v.Commit.Link, - Timestamp: v.Commit.Author.Date.Unix(), - Message: v.Commit.Message, - Before: v.Before, - After: v.Commit.Sha, - Source: scm.TrimRef(v.BaseRef), - Target: scm.TrimRef(v.BaseRef), - Ref: v.Ref, - Author: v.Commit.Author.Login, - AuthorName: v.Commit.Author.Name, - AuthorEmail: v.Commit.Author.Email, - AuthorAvatar: v.Commit.Author.Avatar, - Sender: v.Sender.Login, - } - } else { - hook = &core.Hook{ - Trigger: core.TriggerHook, //core.TriggerHook, - Event: core.EventPush, - Link: v.Commit.Link, - Timestamp: v.Commit.Author.Date.Unix(), - Message: v.Commit.Message, - Before: v.Before, - After: v.Commit.Sha, - Ref: v.Ref, - Source: strings.TrimPrefix(v.Ref, "refs/heads/"), - Target: strings.TrimPrefix(v.Ref, "refs/heads/"), - Author: v.Commit.Author.Login, - AuthorName: v.Commit.Author.Name, - AuthorEmail: v.Commit.Author.Email, - AuthorAvatar: v.Commit.Author.Avatar, - Sender: v.Sender.Login, - } - } - repo = &core.Repository{ - UID: v.Repo.ID, - Namespace: v.Repo.Namespace, - Name: v.Repo.Name, - Slug: scm.Join(v.Repo.Namespace, v.Repo.Name), - Link: v.Repo.Link, - Branch: v.Repo.Branch, - Private: v.Repo.Private, - HTTPURL: v.Repo.Clone, - SSHURL: v.Repo.CloneSSH, - } - // gogs and gitea do not include the author avatar in - // the webhook, but they do include the sender avatar. - // use the sender avatar when necessary. - if hook.AuthorAvatar == "" { - hook.AuthorAvatar = v.Sender.Avatar - } - return hook, repo, nil - case *scm.TagHook: - if v.Action != scm.ActionCreate { - return nil, nil, nil - } - // when a tag is created github sends both a push hook - // and a tag create hook. The push hook contains more - // information, so we choose to use the push hook and - // ignore the native tag hook. - if p.client.Driver == scm.DriverGithub || - p.client.Driver == scm.DriverGitea || - p.client.Driver == scm.DriverGitlab { - return nil, nil, nil - } - - // the tag hook does not include the commit link, message - // or timestamp. In some cases it does not event include - // the sha (gogs). Note that we may need to fetch additional - // details to augment the webhook. - hook = &core.Hook{ - Trigger: core.TriggerHook, // core.TriggerHook, - Event: core.EventTag, - Action: core.ActionCreate, - Link: "", - Timestamp: 0, - Message: "", - After: v.Ref.Sha, - Ref: v.Ref.Name, - Source: v.Ref.Name, - Target: v.Ref.Name, - Author: v.Sender.Login, - AuthorName: v.Sender.Name, - AuthorEmail: v.Sender.Email, - AuthorAvatar: v.Sender.Avatar, - Sender: v.Sender.Login, - } - repo = &core.Repository{ - UID: v.Repo.ID, - Namespace: v.Repo.Namespace, - Name: v.Repo.Name, - Slug: scm.Join(v.Repo.Namespace, v.Repo.Name), - Link: v.Repo.Link, - Branch: v.Repo.Branch, - Private: v.Repo.Private, - HTTPURL: v.Repo.Clone, - SSHURL: v.Repo.CloneSSH, - } - // TODO(bradrydzewski) can we use scm.ExpandRef here? - if !strings.HasPrefix(hook.Ref, "refs/tags/") { - hook.Ref = fmt.Sprintf("refs/tags/%s", hook.Ref) - } - if hook.AuthorAvatar == "" { - hook.AuthorAvatar = v.Sender.Avatar - } - return hook, repo, nil - case *scm.PullRequestHook: - - // TODO(bradrydzewski) cleanup the pr close hook code. - if v.Action == scm.ActionClose { - return &core.Hook{ - Trigger: core.TriggerHook, - Event: core.EventPullRequest, - Action: core.ActionClose, - After: v.PullRequest.Sha, - Ref: v.PullRequest.Ref, - }, &core.Repository{ - UID: v.Repo.ID, - Namespace: v.Repo.Namespace, - Name: v.Repo.Name, - Slug: scm.Join(v.Repo.Namespace, v.Repo.Name), - }, nil - } - - if v.Action != scm.ActionOpen && v.Action != scm.ActionSync { - return nil, nil, nil - } - // Pull Requests are not supported for Bitbucket due - // to lack of refs (e.g. refs/pull-requests/42/from). - // Please contact Bitbucket Support if you would like to - // see this feature enabled: - // https://bitbucket.org/site/master/issues/5814/repository-refs-for-pull-requests - if p.client.Driver == scm.DriverBitbucket { - return nil, nil, nil - } - hook = &core.Hook{ - Trigger: core.TriggerHook, // core.TriggerHook, - Event: core.EventPullRequest, - Action: v.Action.String(), - Link: v.PullRequest.Link, - Timestamp: v.PullRequest.Created.Unix(), - Title: v.PullRequest.Title, - Message: v.PullRequest.Body, - Before: v.PullRequest.Base.Sha, - After: v.PullRequest.Sha, - Ref: v.PullRequest.Ref, - Fork: v.PullRequest.Fork, - Source: v.PullRequest.Source, - Target: v.PullRequest.Target, - Author: v.PullRequest.Author.Login, - AuthorName: v.PullRequest.Author.Name, - AuthorEmail: v.PullRequest.Author.Email, - AuthorAvatar: v.PullRequest.Author.Avatar, - Sender: v.Sender.Login, - } - // HACK this is a workaround for github. The pull - // request title is populated, but not the message. - if hook.Message == "" { - hook.Message = hook.Title - } - repo = &core.Repository{ - UID: v.Repo.ID, - Namespace: v.Repo.Namespace, - Name: v.Repo.Name, - Slug: scm.Join(v.Repo.Namespace, v.Repo.Name), - Link: v.Repo.Link, - Branch: v.Repo.Branch, - Private: v.Repo.Private, - HTTPURL: v.Repo.Clone, - SSHURL: v.Repo.CloneSSH, - } - if hook.AuthorAvatar == "" { - hook.AuthorAvatar = v.Sender.Avatar - } - return hook, repo, nil - case *scm.BranchHook: - - // TODO(bradrydzewski) cleanup the branch hook code. - if v.Action == scm.ActionDelete { - return &core.Hook{ - Trigger: core.TriggerHook, - Event: core.EventPush, - After: v.Ref.Sha, - Action: core.ActionDelete, - Target: scm.TrimRef(v.Ref.Name), - }, &core.Repository{ - UID: v.Repo.ID, - Namespace: v.Repo.Namespace, - Name: v.Repo.Name, - Slug: scm.Join(v.Repo.Namespace, v.Repo.Name), - }, nil - } - - if v.Action != scm.ActionCreate { - return nil, nil, nil - } - if p.client.Driver != scm.DriverStash { - return nil, nil, nil - } - hook = &core.Hook{ - Trigger: core.TriggerHook, // core.TriggerHook, - Event: core.EventPush, - Link: "", - Timestamp: 0, - Message: "", - After: v.Ref.Sha, - Ref: v.Ref.Name, - Source: v.Ref.Name, - Target: v.Ref.Name, - Author: v.Sender.Login, - AuthorName: v.Sender.Name, - AuthorEmail: v.Sender.Email, - AuthorAvatar: v.Sender.Avatar, - Sender: v.Sender.Login, - } - repo = &core.Repository{ - UID: v.Repo.ID, - Namespace: v.Repo.Namespace, - Name: v.Repo.Name, - Slug: scm.Join(v.Repo.Namespace, v.Repo.Name), - Link: v.Repo.Link, - Branch: v.Repo.Branch, - Private: v.Repo.Private, - HTTPURL: v.Repo.Clone, - SSHURL: v.Repo.CloneSSH, - } - return hook, repo, nil - case *scm.DeployHook: - hook = &core.Hook{ - Trigger: core.TriggerHook, - Event: core.EventPromote, - Link: v.TargetURL, - Timestamp: time.Now().Unix(), - Message: v.Desc, - After: v.Ref.Sha, - Ref: v.Ref.Path, - Source: v.Ref.Name, - Target: v.Ref.Name, - Author: v.Sender.Login, - AuthorName: v.Sender.Name, - AuthorEmail: v.Sender.Email, - AuthorAvatar: v.Sender.Avatar, - Sender: v.Sender.Login, - Deployment: v.Target, - DeploymentID: v.Number, - Params: toMap(v.Data), - } - repo = &core.Repository{ - UID: v.Repo.ID, - Namespace: v.Repo.Namespace, - Name: v.Repo.Name, - Slug: scm.Join(v.Repo.Namespace, v.Repo.Name), - Link: v.Repo.Link, - Branch: v.Repo.Branch, - Private: v.Repo.Private, - HTTPURL: v.Repo.Clone, - SSHURL: v.Repo.CloneSSH, - } - return hook, repo, nil - default: - return nil, nil, nil - } -} - -func toMap(src interface{}) map[string]string { - set, ok := src.(map[string]interface{}) - if !ok { - return nil - } - dst := map[string]string{} - for k, v := range set { - dst[k] = fmt.Sprint(v) - } - return dst -} diff --git a/service/hook/parser/parse_test.go b/service/hook/parser/parse_test.go deleted file mode 100644 index effeab0ef0..0000000000 --- a/service/hook/parser/parse_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package parser diff --git a/service/hook/parser/testdata/bitbucket_pull_open.json b/service/hook/parser/testdata/bitbucket_pull_open.json deleted file mode 100644 index 00825c69a4..0000000000 --- a/service/hook/parser/testdata/bitbucket_pull_open.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "Action": "opened", - "Repo": { - "ID": "{bc771cbf-829e-4c4b-b71f-a0eb3ac2b860}", - "Namespace": "brydzewski", - "Name": "foo", - "Perm": null, - "Branch": "", - "Private": true, - "Clone": "https://bitbucket.org/brydzewski/foo.git", - "CloneSSH": "git@bitbucket.org:brydzewski/foo.git", - "Link": "https://bitbucket.org/brydzewski/foo", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "PullRequest": { - "Number": 1, - "Title": "Awesome new feature", - "Body": "made some changes", - "Sha": "507a576e59b3", - "Ref": "refs/pull-requests/1/from", - "Source": "develop", - "Target": "master", - "Fork": "brydzewski/foo", - "Link": "https://bitbucket.org/brydzewski/foo/pull-requests/1", - "Closed": false, - "Merged": false, - "Author": { - "Login": "brydzewski", - "Name": "Brad Rydzewski", - "Email": "", - "Avatar": "https://bitbucket.org/account/brydzewski/avatar/32/" - }, - "Created": "2018-07-02T21:51:39.492248Z", - "Updated": "2018-07-02T21:51:39.532546Z" - }, - "Sender": { - "Login": "brydzewski", - "Name": "Brad Rydzewski", - "Email": "", - "Avatar": "https://bitbucket.org/account/brydzewski/avatar/32/" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/bitbucket_pull_open.json.golden b/service/hook/parser/testdata/bitbucket_pull_open.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/bitbucket_push.json b/service/hook/parser/testdata/bitbucket_push.json deleted file mode 100644 index 655904fa17..0000000000 --- a/service/hook/parser/testdata/bitbucket_push.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "Ref": "refs/heads/master", - "Repo": { - "ID": "{bc771cbf-829e-4c4b-b71f-a0eb3ac2b860}", - "Namespace": "brydzewski", - "Name": "foo", - "Perm": null, - "Branch": "", - "Private": true, - "Clone": "https://bitbucket.org/brydzewski/foo.git", - "CloneSSH": "git@bitbucket.org:brydzewski/foo.git", - "Link": "https://bitbucket.org/brydzewski/foo", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Commit": { - "Sha": "141977fedf5cf35aa290ac87d4b5177ac4cd9de1", - "Message": "Update README\n", - "Author": { - "Name": "Brad Rydzewski", - "Email": "brad.rydzewski@gmail.com", - "Date": "2018-07-02T20:26:56Z", - "Login": "brydzewski", - "Avatar": "https://bitbucket.org/account/brydzewski/avatar/32/" - }, - "Committer": { - "Name": "Brad Rydzewski", - "Email": "brad.rydzewski@gmail.com", - "Date": "2018-07-02T20:26:56Z", - "Login": "brydzewski", - "Avatar": "https://bitbucket.org/account/brydzewski/avatar/32/" - }, - "Link": "https://bitbucket.org/brydzewski/foo/commits/141977fedf5cf35aa290ac87d4b5177ac4cd9de1" - }, - "Sender": { - "Login": "brydzewski", - "Name": "Brad Rydzewski", - "Email": "", - "Avatar": "https://bitbucket.org/account/brydzewski/avatar/32/" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/bitbucket_push.json.golden b/service/hook/parser/testdata/bitbucket_push.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/bitbucket_tag.json b/service/hook/parser/testdata/bitbucket_tag.json deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/bitbucket_tag.json.golden b/service/hook/parser/testdata/bitbucket_tag.json.golden deleted file mode 100644 index 0f965056db..0000000000 --- a/service/hook/parser/testdata/bitbucket_tag.json.golden +++ /dev/null @@ -1,26 +0,0 @@ -{ - "Ref": { - "Name": "feature/x", - "Sha": "141977fedf5cf35aa290ac87d4b5177ac4cd9de1" - }, - "Repo": { - "ID": "{bc771cbf-829e-4c4b-b71f-a0eb3ac2b860}", - "Namespace": "brydzewski", - "Name": "foo", - "Perm": null, - "Branch": "", - "Private": true, - "Clone": "https://bitbucket.org/brydzewski/foo.git", - "CloneSSH": "git@bitbucket.org:brydzewski/foo.git", - "Link": "https://bitbucket.org/brydzewski/foo", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Action": "created", - "Sender": { - "Login": "brydzewski", - "Name": "Brad Rydzewski", - "Email": "", - "Avatar": "https://bitbucket.org/account/brydzewski/avatar/32/" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/gitea_pull_open.json b/service/hook/parser/testdata/gitea_pull_open.json deleted file mode 100644 index 3226f79720..0000000000 --- a/service/hook/parser/testdata/gitea_pull_open.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "Action": "opened", - "Repo": { - "ID": "6589", - "Namespace": "jcitizen", - "Name": "my-repo", - "Perm": { - "Pull": false, - "Push": false, - "Admin": false - }, - "Branch": "master", - "Private": false, - "Clone": "https://try.gitea.io/jcitizen/my-repo.git", - "CloneSSH": "git@try.gitea.io:jcitizen/my-repo.git", - "Link": "", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "PullRequest": { - "Number": 1, - "Title": "Add License File", - "Body": "Using a BSD License", - "Sha": "2eba238e33607c1fa49253182e9fff42baafa1eb", - "Ref": "refs/pull/1/head", - "Source": "feature", - "Target": "master", - "Fork": "jcitizen/my-repo", - "Link": "https://try.gitea.io/jcitizen/my-repo/pulls/1", - "Closed": false, - "Merged": false, - "Author": { - "Login": "jcitizen", - "Name": "", - "Email": "jane@example.com", - "Avatar": "https://secure.gravatar.com/avatar/66f07ff48e6a9cb393de7a34e03bb52a?d=identicon" - }, - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Sender": { - "Login": "jcitizen", - "Name": "", - "Email": "jane@example.com", - "Avatar": "https://secure.gravatar.com/avatar/66f07ff48e6a9cb393de7a34e03bb52a?d=identicon" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/gitea_pull_open.json.golden b/service/hook/parser/testdata/gitea_pull_open.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/gitea_push.json b/service/hook/parser/testdata/gitea_push.json deleted file mode 100644 index 2f9b3ca4b4..0000000000 --- a/service/hook/parser/testdata/gitea_push.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "Ref": "refs/heads/master", - "Repo": { - "ID": "61", - "Namespace": "gogits", - "Name": "hello-world", - "Perm": {}, - "Branch": "master", - "Private": true, - "Clone": "http://try.gitea.io/gogits/hello-world.git", - "CloneSSH": "git@localhost:gogits/hello-world.git", - "Link": "", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Commit": { - "Sha": "4522cbcefc20728a5b72b3a86af35e608622c514", - "Message": "Updated readme\n", - "Author": { - "Name": "Unknwon", - "Email": "noreply@gogs.io", - "Date": "2017-12-09T01:35:07Z", - "Login": "unknwon", - "Avatar": "" - }, - "Committer": { - "Name": "Unknwon", - "Email": "noreply@gogs.io", - "Date": "2017-12-09T01:35:07Z", - "Login": "unknwon", - "Avatar": "" - }, - "Link": "http://try.gitea.io/gogits/hello-world/compare/9836a96a253cce25d17988fcf41b8c4205cf779f...4522cbcefc20728a5b72b3a86af35e608622c514" - }, - "Sender": { - "Login": "unknwon", - "Name": "", - "Email": "noreply@gogs.io", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87" - } -} diff --git a/service/hook/parser/testdata/gitea_push.json.golden b/service/hook/parser/testdata/gitea_push.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/gitea_tag.json b/service/hook/parser/testdata/gitea_tag.json deleted file mode 100644 index dacc2ac34e..0000000000 --- a/service/hook/parser/testdata/gitea_tag.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "Ref": { - "Name": "v1.0.0", - "Sha": "" - }, - "Repo": { - "ID": "61", - "Namespace": "gogits", - "Name": "hello-world", - "Perm": {}, - "Branch": "master", - "Private": true, - "Clone": "http://try.gitea.io/gogits/hello-world.git", - "CloneSSH": "git@localhost:gogits/hello-world.git", - "Link": "", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Action": "created", - "Sender": { - "Login": "unknwon", - "Name": "", - "Email": "noreply@gogs.io", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87" - } -} diff --git a/service/hook/parser/testdata/gitea_tag.json.golden b/service/hook/parser/testdata/gitea_tag.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/github_pull_create.json b/service/hook/parser/testdata/github_pull_create.json deleted file mode 100644 index 100ff8162b..0000000000 --- a/service/hook/parser/testdata/github_pull_create.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "Action": "opened", - "Repo": { - "ID": "13933572", - "Namespace": "bradrydzewski", - "Name": "drone-test-go", - "Perm": null, - "Branch": "master", - "Private": true, - "Clone": "https://github.com/bradrydzewski/drone-test-go.git", - "CloneSSH": "git@github.com:bradrydzewski/drone-test-go.git", - "Link": "https://github.com/bradrydzewski/drone-test-go", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "PullRequest": { - "Number": 1, - "Title": "Update .drone.yml", - "Body": "", - "Sha": "d2b75aa7797ec26b088fa2dd527e9d2c052fcedd", - "Ref": "refs/pull/1/head", - "Source": "master", - "Target": "bradrydzewski-patch-1", - "Fork": "bradrydzewski/drone-test-go", - "Link": "https://github.com/bradrydzewski/drone-test-go/pull/1.diff", - "Closed": false, - "Merged": false, - "Author": { - "Login": "bradrydzewski", - "Name": "", - "Email": "", - "Avatar": "https://avatars1.githubusercontent.com/u/817538?v=4" - }, - "Created": "2018-06-22T23:54:09Z", - "Updated": "2018-06-22T23:54:09Z" - }, - "Sender": { - "Login": "bradrydzewski", - "Name": "", - "Email": "", - "Avatar": "https://avatars1.githubusercontent.com/u/817538?v=4" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/github_pull_create.json.golden b/service/hook/parser/testdata/github_pull_create.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/github_pull_sync.json b/service/hook/parser/testdata/github_pull_sync.json deleted file mode 100644 index 44f1398cc8..0000000000 --- a/service/hook/parser/testdata/github_pull_sync.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "Action": "synchronized", - "Repo": { - "ID": "13933572", - "Namespace": "bradrydzewski", - "Name": "drone-test-go", - "Perm": null, - "Branch": "master", - "Private": true, - "Clone": "https://github.com/bradrydzewski/drone-test-go.git", - "CloneSSH": "git@github.com:bradrydzewski/drone-test-go.git", - "Link": "https://github.com/bradrydzewski/drone-test-go", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "PullRequest": { - "Number": 1, - "Title": "Update .drone.yml", - "Body": "this is an edit", - "Sha": "8102e371cd01cf668893cb2d04a04d52331b1dc9", - "Ref": "refs/pull/1/head", - "Source": "master", - "Target": "bradrydzewski-patch-1", - "Fork": "bradrydzewski/drone-test-go", - "Link": "https://github.com/bradrydzewski/drone-test-go/pull/1.diff", - "Closed": false, - "Merged": false, - "Author": { - "Login": "bradrydzewski", - "Name": "", - "Email": "", - "Avatar": "https://avatars1.githubusercontent.com/u/817538?v=4" - }, - "Created": "2018-06-22T23:54:09Z", - "Updated": "2018-06-25T19:21:45Z" - }, - "Sender": { - "Login": "bradrydzewski", - "Name": "", - "Email": "", - "Avatar": "https://avatars1.githubusercontent.com/u/817538?v=4" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/github_pull_sync.json.golden b/service/hook/parser/testdata/github_pull_sync.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/github_push.json b/service/hook/parser/testdata/github_push.json deleted file mode 100644 index 067a0c4ebb..0000000000 --- a/service/hook/parser/testdata/github_push.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "Ref": "refs/heads/master", - "Repo": { - "ID": "135493233", - "Namespace": "Codertocat", - "Name": "Hello-World", - "Perm": null, - "Branch": "master", - "Private": false, - "Clone": "https://github.com/Codertocat/Hello-World.git", - "CloneSSH": "git@github.com:Codertocat/Hello-World.git", - "Link": "https://github.com/Codertocat/Hello-World", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Commit": { - "Sha": "199eddf46df50de8d02e99bf1c5fdb4101338224", - "Message": "Update README", - "Author": { - "Name": "Codertocat", - "Email": "21031067+Codertocat@users.noreply.github.com", - "Date": "0001-01-01T00:00:00Z", - "Login": "Codertocat", - "Avatar": "" - }, - "Committer": { - "Name": "GitHub", - "Email": "noreply@github.com", - "Date": "0001-01-01T00:00:00Z", - "Login": "web-flow", - "Avatar": "" - }, - "Link": "https://github.com/Codertocat/Hello-World/compare/a10867b14bb7...000000000000" - }, - "Sender": { - "Login": "Codertocat", - "Name": "", - "Email": "", - "Avatar": "https://avatars1.githubusercontent.com/u/21031067?v=4" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/github_push.json.golden b/service/hook/parser/testdata/github_push.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/github_push_tag.json b/service/hook/parser/testdata/github_push_tag.json deleted file mode 100644 index 247c48c0fc..0000000000 --- a/service/hook/parser/testdata/github_push_tag.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "Ref": "refs/tags/v0.0.1", - "Repo": { - "ID": "13933572", - "Namespace": "bradrydzewski", - "Name": "drone-test-go", - "Perm": null, - "Branch": "master", - "Private": true, - "Clone": "https://github.com/bradrydzewski/drone-test-go.git", - "CloneSSH": "git@github.com:bradrydzewski/drone-test-go.git", - "Link": "https://github.com/bradrydzewski/drone-test-go", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Commit": { - "Sha": "d2b75aa7797ec26b088fa2dd527e9d2c052fcedd", - "Message": "Update .drone.yml", - "Author": { - "Name": "Brad Rydzewski", - "Email": "brad.rydzewski@gmail.com", - "Date": "0001-01-01T00:00:00Z", - "Login": "bradrydzewski", - "Avatar": "" - }, - "Committer": { - "Name": "GitHub", - "Email": "noreply@github.com", - "Date": "0001-01-01T00:00:00Z", - "Login": "web-flow", - "Avatar": "" - }, - "Link": "https://github.com/bradrydzewski/drone-test-go/compare/v0.0.1" - }, - "Sender": { - "Login": "bradrydzewski", - "Name": "", - "Email": "", - "Avatar": "https://avatars1.githubusercontent.com/u/817538?v=4" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/github_push_tag.json.golden b/service/hook/parser/testdata/github_push_tag.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/gitlab_pull_open.json b/service/hook/parser/testdata/gitlab_pull_open.json deleted file mode 100644 index 6558c1cd22..0000000000 --- a/service/hook/parser/testdata/gitlab_pull_open.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "Action": "opened", - "Repo": { - "ID": "4861503", - "Namespace": "sytses", - "Name": "hello-world", - "Perm": null, - "Branch": "master", - "Private": false, - "Clone": "https://gitlab.com/gitlab-org/hello-world.git", - "CloneSSH": "git@gitlab.com:gitlab-org/hello-world.git", - "Link": "https://gitlab.com/gitlab-org/hello-world", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "PullRequest": { - "Number": 1, - "Title": "update readme", - "Body": "adding build instructions to readme", - "Sha": "c4c79227ed610f1151f05bbc5be33b4f340d39c8", - "Ref": "refs/merge-requests/1/head", - "Source": "feature", - "Target": "master", - "Fork": "sytses/hello-world", - "Link": "https://gitlab.com/gitlab-org/hello-world/merge_requests/1", - "Closed": false, - "Merged": false, - "Author": { - "Login": "sytses", - "Name": "Sid Sijbrandij", - "Email": "", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon" - }, - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Sender": { - "Login": "sytses", - "Name": "Sid Sijbrandij", - "Email": "", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/gitlab_pull_open.json.golden b/service/hook/parser/testdata/gitlab_pull_open.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/gitlab_push.json b/service/hook/parser/testdata/gitlab_push.json deleted file mode 100644 index 93716b63da..0000000000 --- a/service/hook/parser/testdata/gitlab_push.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "Ref": "refs/heads/master", - "Repo": { - "ID": "4861503", - "Namespace": "sytses", - "Name": "hello-world", - "Perm": null, - "Branch": "master", - "Private": false, - "Clone": "https://gitlab.com/gitlab-org/hello-world.git", - "CloneSSH": "git@gitlab.com:gitlab-org/hello-world.git", - "Link": "https://gitlab.com/gitlab-org/hello-world", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Commit": { - "Sha": "2adc9465c4edfc33834e173fe89436a7cb899a1d", - "Message": "added readme\n", - "Author": { - "Name": "Sid Sijbrandij", - "Email": "noreply@gitlab.com", - "Date": "0001-01-01T00:00:00Z", - "Login": "sytses", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon" - }, - "Committer": { - "Name": "Sid Sijbrandij", - "Email": "noreply@gitlab.com", - "Date": "0001-01-01T00:00:00Z", - "Login": "sytses", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon" - }, - "Link": "https://gitlab.com/gitlab-org/hello-world/commit/2adc9465c4edfc33834e173fe89436a7cb899a1d" - }, - "Sender": { - "Login": "sytses", - "Name": "Sid Sijbrandij", - "Email": "noreply@gitlab.com", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/gitlab_push.json.golden b/service/hook/parser/testdata/gitlab_push.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/gitlab_tag.json b/service/hook/parser/testdata/gitlab_tag.json deleted file mode 100644 index c3c09576fb..0000000000 --- a/service/hook/parser/testdata/gitlab_tag.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "Ref": { - "Name": "v1.0.0", - "Sha": "2adc9465c4edfc33834e173fe89436a7cb899a1d" - }, - "Repo": { - "ID": "4861503", - "Namespace": "sytses", - "Name": "hello-world", - "Perm": null, - "Branch": "master", - "Private": false, - "Clone": "https://gitlab.com/gitlab-org/hello-world.git", - "CloneSSH": "git@gitlab.com:gitlab-org/hello-world.git", - "Link": "https://gitlab.com/gitlab-org/hello-world", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Action": "created", - "Sender": { - "Login": "sytses", - "Name": "Sid Sijbrandij", - "Email": "noreply@gitlab.com", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/gitlab_tag.json.golden b/service/hook/parser/testdata/gitlab_tag.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/gogs_pull_create.json b/service/hook/parser/testdata/gogs_pull_create.json deleted file mode 100644 index eb79219f58..0000000000 --- a/service/hook/parser/testdata/gogs_pull_create.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "Action": "opened", - "Repo": { - "ID": "61", - "Namespace": "gogits", - "Name": "hello-world", - "Perm": {}, - "Branch": "master", - "Private": true, - "Clone": "http://try.gogs.io/gogits/hello-world.git", - "CloneSSH": "git@localhost:gogits/hello-world.git", - "Link": "", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "PullRequest": { - "Number": 2, - "Title": "huge improvements", - "Body": "", - "Sha": "", - "Ref": "refs/pull/2/head", - "Source": "feature", - "Target": "master", - "Fork": "gogits/hello-world", - "Link": "http://try.gogs.io/gogits/hello-world/pulls/2", - "Closed": false, - "Merged": false, - "Author": { - "Login": "unknwon", - "Name": "", - "Email": "noreply@gogs.io", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87" - }, - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Sender": { - "Login": "unknwon", - "Name": "", - "Email": "noreply@gogs.io", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87" - } -} diff --git a/service/hook/parser/testdata/gogs_push.json b/service/hook/parser/testdata/gogs_push.json deleted file mode 100644 index 15bb470f9d..0000000000 --- a/service/hook/parser/testdata/gogs_push.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "Ref": "refs/heads/master", - "Repo": { - "ID": "61", - "Namespace": "gogits", - "Name": "hello-world", - "Perm": {}, - "Branch": "master", - "Private": true, - "Clone": "http://try.gogs.io/gogits/hello-world.git", - "CloneSSH": "git@localhost:gogits/hello-world.git", - "Link": "", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Commit": { - "Sha": "4522cbcefc20728a5b72b3a86af35e608622c514", - "Message": "Updated readme\n", - "Author": { - "Name": "Unknwon", - "Email": "noreply@gogs.io", - "Date": "2017-12-09T01:35:07Z", - "Login": "unknwon", - "Avatar": "" - }, - "Committer": { - "Name": "Unknwon", - "Email": "noreply@gogs.io", - "Date": "2017-12-09T01:35:07Z", - "Login": "unknwon", - "Avatar": "" - }, - "Link": "http://try.gogs.io/gogits/hello-world/compare/9836a96a253cce25d17988fcf41b8c4205cf779f...4522cbcefc20728a5b72b3a86af35e608622c514" - }, - "Sender": { - "Login": "unknwon", - "Name": "", - "Email": "noreply@gogs.io", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87" - } -} diff --git a/service/hook/parser/testdata/gogs_push.json.golden b/service/hook/parser/testdata/gogs_push.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/gogs_tag.json b/service/hook/parser/testdata/gogs_tag.json deleted file mode 100644 index 4f9ee41e13..0000000000 --- a/service/hook/parser/testdata/gogs_tag.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "Ref": { - "Name": "v1.0.0", - "Sha": "" - }, - "Repo": { - "ID": "61", - "Namespace": "gogits", - "Name": "hello-world", - "Perm": {}, - "Branch": "master", - "Private": true, - "Clone": "http://try.gogs.io/gogits/hello-world.git", - "CloneSSH": "git@localhost:gogits/hello-world.git", - "Link": "", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Action": "created", - "Sender": { - "Login": "unknwon", - "Name": "", - "Email": "noreply@gogs.io", - "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87" - } -} diff --git a/service/hook/parser/testdata/gogs_tag.json.golden b/service/hook/parser/testdata/gogs_tag.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/stash_pull_create.json b/service/hook/parser/testdata/stash_pull_create.json deleted file mode 100644 index 5d29c8f493..0000000000 --- a/service/hook/parser/testdata/stash_pull_create.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "Action": "opened", - "Repo": { - "ID": "1", - "Namespace": "PRJ", - "Name": "my-repo", - "Perm": null, - "Branch": "master", - "Private": true, - "Clone": "", - "CloneSSH": "", - "Link": "", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "PullRequest": { - "Number": 2, - "Title": "added LICENSE", - "Body": "added BSD license text", - "Sha": "208b0a5c05eddadad01f2aed8802fe0c3b3eaf5e", - "Ref": "refs/pull-requests/2/from", - "Source": "develop", - "Target": "master", - "Fork": "PRJ/my-repo", - "Link": "", - "Closed": false, - "Merged": false, - "Author": { - "Login": "jcitizen", - "Name": "Jane Citizen", - "Email": "jane@example.com", - "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" - }, - "Created": "2018-07-05T12:21:30-07:00", - "Updated": "2018-07-05T12:21:30-07:00" - }, - "Sender": { - "Login": "jcitizen", - "Name": "Jane Citizen", - "Email": "jane@example.com", - "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/stash_pull_create.json.golden b/service/hook/parser/testdata/stash_pull_create.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/stash_push.json b/service/hook/parser/testdata/stash_push.json deleted file mode 100644 index 989746d4a0..0000000000 --- a/service/hook/parser/testdata/stash_push.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "Ref": "refs/heads/master", - "Repo": { - "ID": "1", - "Namespace": "PRJ", - "Name": "my-repo", - "Perm": null, - "Branch": "master", - "Private": true, - "Clone": "", - "CloneSSH": "", - "Link": "", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Commit": { - "Sha": "823b2230a56056231c9425d63758fa87078a66b4", - "Message": "", - "Author": { - "Name": "Jane Citizen", - "Email": "jane@example.com", - "Date": "2018-07-05T18:22:00Z", - "Login": "jcitizen", - "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" - }, - "Committer": { - "Name": "Jane Citizen", - "Email": "jane@example.com", - "Date": "2018-07-05T18:22:00Z", - "Login": "jcitizen", - "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" - }, - "Link": "" - }, - "Sender": { - "Login": "jcitizen", - "Name": "Jane Citizen", - "Email": "jane@example.com", - "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/stash_push.json.golden b/service/hook/parser/testdata/stash_push.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/parser/testdata/stash_push_tag.json b/service/hook/parser/testdata/stash_push_tag.json deleted file mode 100644 index 63af99d1ff..0000000000 --- a/service/hook/parser/testdata/stash_push_tag.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "Ref": { - "Name": "v1.1.0", - "Sha": "823b2230a56056231c9425d63758fa87078a66b4" - }, - "Repo": { - "ID": "1", - "Namespace": "PRJ", - "Name": "my-repo", - "Perm": null, - "Branch": "master", - "Private": true, - "Clone": "", - "CloneSSH": "", - "Link": "", - "Created": "0001-01-01T00:00:00Z", - "Updated": "0001-01-01T00:00:00Z" - }, - "Action": "created", - "Sender": { - "Login": "jcitizen", - "Name": "Jane Citizen", - "Email": "jane@example.com", - "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" - } -} \ No newline at end of file diff --git a/service/hook/parser/testdata/stash_push_tag.json.golden b/service/hook/parser/testdata/stash_push_tag.json.golden deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/service/hook/util.go b/service/hook/util.go deleted file mode 100644 index da689c8cc1..0000000000 --- a/service/hook/util.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hook - -import ( - "context" - "net/url" - - "github.com/drone/go-scm/scm" -) - -func replaceHook(ctx context.Context, client *scm.Client, repo string, hook *scm.HookInput) error { - if err := deleteHook(ctx, client, repo, hook.Target); err != nil { - return err - } - _, _, err := client.Repositories.CreateHook(ctx, repo, hook) - return err -} - -func deleteHook(ctx context.Context, client *scm.Client, repo, target string) error { - u, _ := url.Parse(target) - h, err := findHook(ctx, client, repo, u.Host) - if err != nil { - return err - } - if h == nil { - return nil - } - _, err = client.Repositories.DeleteHook(ctx, repo, h.ID) - return err -} - -func findHook(ctx context.Context, client *scm.Client, repo, host string) (*scm.Hook, error) { - hooks, _, err := client.Repositories.ListHooks(ctx, repo, scm.ListOptions{Size: 100}) - if err != nil { - return nil, err - } - for _, hook := range hooks { - u, err := url.Parse(hook.Target) - if err != nil { - continue - } - if u.Host == host { - return hook, nil - } - } - return nil, nil -} diff --git a/service/hook/util_test.go b/service/hook/util_test.go deleted file mode 100644 index 0515452a5b..0000000000 --- a/service/hook/util_test.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package hook - -import ( - "context" - "io" - "testing" - - "github.com/drone/drone/mock/mockscm" - "github.com/drone/go-scm/scm" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -func TestFindHook(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - hooks := []*scm.Hook{ - {Target: "http://192.168.0.%31/hook"}, - {Target: "https://drone.company.com/hook"}, - } - remote := mockscm.NewMockRepositoryService(controller) - remote.EXPECT().ListHooks(gomock.Any(), "octocat/hello-world", gomock.Any()).Return(hooks, nil, nil) - - client := new(scm.Client) - client.Repositories = remote - - hook, err := findHook(context.Background(), client, "octocat/hello-world", "drone.company.com") - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff(hook, hooks[1]); len(diff) > 0 { - t.Errorf(diff) - } -} - -func TestFindHook_ListError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - remote := mockscm.NewMockRepositoryService(controller) - remote.EXPECT().ListHooks(gomock.Any(), "octocat/hello-world", gomock.Any()).Return(nil, nil, io.EOF) - - client := new(scm.Client) - client.Repositories = remote - - _, err := findHook(context.Background(), client, "octocat/hello-world", "core.company.com") - if err == nil { - t.Errorf("Want hook request failure to return error") - } -} - -func TestReplaceHook_CreateHook(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - hooks := []*scm.Hook{} - hookInput := &scm.HookInput{ - Target: "https://drone.company.com/hook", - } - - remote := mockscm.NewMockRepositoryService(controller) - remote.EXPECT().ListHooks(gomock.Any(), "octocat/hello-world", gomock.Any()).Return(hooks, nil, nil) - remote.EXPECT().CreateHook(gomock.Any(), "octocat/hello-world", hookInput).Return(nil, nil, nil) - - client := new(scm.Client) - client.Repositories = remote - - err := replaceHook(context.Background(), client, "octocat/hello-world", hookInput) - if err != nil { - t.Error(err) - } -} - -func TestReplaceHook_UpdateHook(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - hooks := []*scm.Hook{ - { - ID: "1", - Target: "https://drone.company.com/hook", - }, - } - hookInput := &scm.HookInput{ - Target: "https://drone.company.com/hook", - } - - remote := mockscm.NewMockRepositoryService(controller) - remote.EXPECT().ListHooks(gomock.Any(), "octocat/hello-world", gomock.Any()).Return(hooks, nil, nil) - remote.EXPECT().DeleteHook(gomock.Any(), "octocat/hello-world", "1").Return(nil, nil) - remote.EXPECT().CreateHook(gomock.Any(), "octocat/hello-world", hookInput).Return(nil, nil, nil) - - client := new(scm.Client) - client.Repositories = remote - - err := replaceHook(context.Background(), client, "octocat/hello-world", hookInput) - if err != nil { - t.Error(err) - } -} - -func TestReplaceHook_DeleteError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - hooks := []*scm.Hook{ - { - ID: "1", - Target: "https://drone.company.com/hook", - }, - } - hookInput := &scm.HookInput{ - Target: "https://drone.company.com/hook", - } - - remote := mockscm.NewMockRepositoryService(controller) - remote.EXPECT().ListHooks(gomock.Any(), "octocat/hello-world", gomock.Any()).Return(hooks, nil, nil) - remote.EXPECT().DeleteHook(gomock.Any(), "octocat/hello-world", "1").Return(nil, io.EOF) - - client := new(scm.Client) - client.Repositories = remote - - err := replaceHook(context.Background(), client, "octocat/hello-world", hookInput) - if err == nil { - t.Errorf("Expect error if hook deletion fails") - } -} - -func TestReplaceHook_DeleteFindError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - hookInput := &scm.HookInput{ - Target: "https://drone.company.com/hook", - } - - remote := mockscm.NewMockRepositoryService(controller) - remote.EXPECT().ListHooks(gomock.Any(), "octocat/hello-world", gomock.Any()).Return(nil, nil, io.EOF) - - client := new(scm.Client) - client.Repositories = remote - - err := replaceHook(context.Background(), client, "octocat/hello-world", hookInput) - if err == nil { - t.Errorf("Expect error if hook deletion fails") - } -} - -func TestReplaceHook_CreateError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - hooks := []*scm.Hook{} - hookInput := &scm.HookInput{ - Target: "https://drone.company.com/hook", - } - - remote := mockscm.NewMockRepositoryService(controller) - remote.EXPECT().ListHooks(gomock.Any(), "octocat/hello-world", gomock.Any()).Return(hooks, nil, nil) - remote.EXPECT().CreateHook(gomock.Any(), "octocat/hello-world", hookInput).Return(nil, nil, io.EOF) - - client := new(scm.Client) - client.Repositories = remote - - err := replaceHook(context.Background(), client, "octocat/hello-world", hookInput) - if err == nil { - t.Errorf("Expect error if hook creation fails") - } -} diff --git a/service/license/load.go b/service/license/load.go deleted file mode 100644 index 8163dd31f0..0000000000 --- a/service/license/load.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !nolimit -// +build !oss - -package license - -import ( - "encoding/json" - "strings" - - "github.com/drone/drone/core" - "github.com/drone/go-license/license" - "github.com/drone/go-license/license/licenseutil" -) - -// embedded public key used to verify license signatures. -var publicKey = []byte("GB/hFnXEg63vDZ2W6mKFhLxZTuxMrlN/C/0iVZ2LfPQ=") - -// License renewal endpoint. -const licenseEndpoint = "https://license.drone.io/api/v1/license/renew" - -// Trial returns a default license with trial terms based -// on the source code management system. -func Trial(provider string) *core.License { - switch provider { - case "gitea", "gogs": - return &core.License{ - Kind: core.LicenseTrial, - Repos: 0, - Users: 0, - Builds: 0, - Nodes: 0, - } - default: - return &core.License{ - Kind: core.LicenseTrial, - Repos: 0, - Users: 0, - Builds: 5000, - Nodes: 0, - } - } -} - -// Load loads the license from file. -func Load(path string) (*core.License, error) { - pub, err := licenseutil.DecodePublicKey(publicKey) - if err != nil { - return nil, err - } - - var decoded *license.License - if strings.HasPrefix(path, "-----BEGIN LICENSE KEY-----") { - decoded, err = license.Decode([]byte(path), pub) - } else { - decoded, err = license.DecodeFile(path, pub) - } - - if err != nil { - return nil, err - } - - license := new(core.License) - license.Expires = decoded.Exp - license.Licensor = decoded.Cus - license.Subscription = decoded.Sub - license.Users = int64(decoded.Lim) - - if decoded.Dat != nil { - dat := new(core.License) - json.Unmarshal(decoded.Dat, dat) - license.Repos = dat.Repos - } - - return license, err -} diff --git a/service/license/load_test.go b/service/license/load_test.go deleted file mode 100644 index 27b50732fb..0000000000 --- a/service/license/load_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !nolimit -// +build !oss - -package license diff --git a/service/license/nolimit.go b/service/license/nolimit.go deleted file mode 100644 index 631ce1dcf4..0000000000 --- a/service/license/nolimit.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build nolimit -// +build !oss - -package license - -import ( - "github.com/drone/drone/core" -) - -// DefaultLicense is an empty license with no restrictions. -var DefaultLicense = &core.License{Kind: core.LicenseFree} - -func Trial(string) *core.License { return DefaultLicense } -func Load(string) (*core.License, error) { return DefaultLicense, nil } diff --git a/service/license/nolimit_oss.go b/service/license/nolimit_oss.go deleted file mode 100644 index a118caf0e9..0000000000 --- a/service/license/nolimit_oss.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build nolimit -// +build oss - -package license - -import ( - "github.com/drone/drone/core" -) - -// DefaultLicense is an empty license with no restrictions. -var DefaultLicense = &core.License{Kind: core.LicenseFoss} - -func Trial(string) *core.License { return DefaultLicense } -func Load(string) (*core.License, error) { return DefaultLicense, nil } diff --git a/service/license/service.go b/service/license/service.go deleted file mode 100644 index ca4fa001bc..0000000000 --- a/service/license/service.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package license - -import ( - "context" - "time" - - "github.com/drone/drone/core" -) - -// NewService returns a new License service. -func NewService( - users core.UserStore, - repos core.RepositoryStore, - builds core.BuildStore, - license *core.License, -) core.LicenseService { - return &service{ - users: users, - repos: repos, - builds: builds, - license: license, - } -} - -type service struct { - users core.UserStore - repos core.RepositoryStore - builds core.BuildStore - license *core.License -} - -func (s *service) Exceeded(ctx context.Context) (bool, error) { - if limit := s.license.Builds; limit > 0 { - count, _ := s.builds.Count(ctx) - if count > limit { - return true, core.ErrBuildLimit - } - } - if limit := s.license.Users; limit > 0 { - count, _ := s.users.Count(ctx) - if count > limit { - return true, core.ErrUserLimit - } - } - if limit := s.license.Repos; limit > 0 { - count, _ := s.repos.Count(ctx) - if count > limit { - return true, core.ErrRepoLimit - } - } - return false, nil -} - -func (s *service) Expired(ctx context.Context) bool { - return s.license.Expired() -} - -func (s *service) Expires(ctx context.Context) time.Time { - return s.license.Expires -} diff --git a/service/linker/linker.go b/service/linker/linker.go deleted file mode 100644 index 1adcd7e082..0000000000 --- a/service/linker/linker.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package linker - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -// New returns a new Linker server. -func New(client *scm.Client) core.Linker { - return &service{ - client: client, - } -} - -type service struct { - client *scm.Client -} - -func (s *service) Link(ctx context.Context, repo, ref, sha string) (string, error) { - return s.client.Linker.Resource(ctx, repo, scm.Reference{ - Path: ref, - Sha: sha, - }) -} diff --git a/service/linker/linker_test.go b/service/linker/linker_test.go deleted file mode 100644 index 5dd85e24e8..0000000000 --- a/service/linker/linker_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package linker diff --git a/service/netrc/netrc.go b/service/netrc/netrc.go deleted file mode 100644 index 9a0603fb1c..0000000000 --- a/service/netrc/netrc.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package netrc - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -var _ core.NetrcService = (*Service)(nil) - -// Service implements a netrc file generation service. -type Service struct { - client *scm.Client - renewer core.Renewer - private bool - username string - password string -} - -// New returns a new Netrc service. -func New( - client *scm.Client, - renewer core.Renewer, - private bool, - username string, - password string, -) core.NetrcService { - return &Service{ - client: client, - renewer: renewer, - private: private, - username: username, - password: password, - } -} - -// Create creates a netrc file for the user and repository. -func (s *Service) Create(ctx context.Context, user *core.User, repo *core.Repository) (*core.Netrc, error) { - // if the repository is public and private mode is disabled, - // authentication is not required. - if repo.Private == false && s.private == false { - return nil, nil - } - - netrc := new(core.Netrc) - err := netrc.SetMachine(repo.HTTPURL) - if err != nil { - return nil, err - } - - if s.username != "" && s.password != "" { - netrc.Password = s.password - netrc.Login = s.username - return netrc, nil - } - - // force refresh the authorization token to prevent - // it from expiring during pipeline execution. - err = s.renewer.Renew(ctx, user, true) - if err != nil { - return nil, err - } - - switch s.client.Driver { - case scm.DriverGitlab, scm.DriverGitee: - netrc.Login = "oauth2" - netrc.Password = user.Token - case scm.DriverBitbucket: - netrc.Login = "x-token-auth" - netrc.Password = user.Token - case scm.DriverGithub, scm.DriverGogs, scm.DriverGitea: - netrc.Password = "x-oauth-basic" - netrc.Login = user.Token - } - return netrc, nil -} diff --git a/service/netrc/netrc_test.go b/service/netrc/netrc_test.go deleted file mode 100644 index 176379f98e..0000000000 --- a/service/netrc/netrc_test.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package netrc - -import ( - "context" - "net/url" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/drone/go-scm/scm" - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" -) - -var noContext = context.Background() - -func TestNetrc(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{Private: true, HTTPURL: "https://github.com/octocat/hello-world"} - mockUser := &core.User{ - Token: "755bb80e5b", - Refresh: "e08f3fa43e", - } - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, true) - - mockClient := &scm.Client{Driver: scm.DriverGithub} - - s := New(mockClient, mockRenewer, false, "", "") - got, err := s.Create(noContext, mockUser, mockRepo) - if err != nil { - t.Error(err) - } - - want := &core.Netrc{ - Machine: "github.com", - Login: "755bb80e5b", - Password: "x-oauth-basic", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestNetrc_Gitlab(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{Private: true, HTTPURL: "https://gitlab.com/octocat/hello-world"} - mockUser := &core.User{ - Token: "755bb80e5b", - Refresh: "e08f3fa43e", - } - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, true) - - s := Service{ - renewer: mockRenewer, - client: &scm.Client{Driver: scm.DriverGitlab}, - } - got, err := s.Create(noContext, mockUser, mockRepo) - if err != nil { - t.Error(err) - } - - want := &core.Netrc{ - Machine: "gitlab.com", - Login: "oauth2", - Password: "755bb80e5b", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestNetrc_Gogs(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{Private: true, HTTPURL: "https://try.gogs.io/octocat/hello-world"} - mockUser := &core.User{ - Token: "755bb80e5b", - Refresh: "e08f3fa43e", - } - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, true) - - s := Service{ - renewer: mockRenewer, - client: &scm.Client{Driver: scm.DriverGogs}, - } - got, err := s.Create(noContext, mockUser, mockRepo) - if err != nil { - t.Error(err) - } - - want := &core.Netrc{ - Machine: "try.gogs.io", - Login: "755bb80e5b", - Password: "x-oauth-basic", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestNetrc_Bitbucket(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{Private: true, HTTPURL: "https://bitbucket.org/octocat/hello-world"} - mockUser := &core.User{ - Token: "755bb80e5b", - Refresh: "e08f3fa43e", - } - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, true) - - s := Service{ - renewer: mockRenewer, - client: &scm.Client{Driver: scm.DriverBitbucket}, - } - got, err := s.Create(noContext, mockUser, mockRepo) - if err != nil { - t.Error(err) - } - - want := &core.Netrc{ - Machine: "bitbucket.org", - Login: "x-token-auth", - Password: "755bb80e5b", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestNetrc_Gitee(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{Private: true, HTTPURL: "https://gitee.com/kit101/drone-yml-test"} - mockUser := &core.User{ - Token: "755bb80e5b", - Refresh: "e08f3fa43e", - } - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, true) - - s := Service{ - renewer: mockRenewer, - client: &scm.Client{Driver: scm.DriverGitee}, - } - got, err := s.Create(noContext, mockUser, mockRepo) - if err != nil { - t.Error(err) - } - - want := &core.Netrc{ - Machine: "gitee.com", - Login: "oauth2", - Password: "755bb80e5b", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestNetrc_Nil(t *testing.T) { - s := Service{ - private: false, - } - netrc, _ := s.Create(noContext, &core.User{}, &core.Repository{Private: false}) - if netrc != nil { - t.Errorf("Expect nil netrc file when public repository") - } -} - -func TestNetrc_MalformedURL(t *testing.T) { - s := Service{ - private: true, - } - _, err := s.Create(noContext, &core.User{}, &core.Repository{HTTPURL: ":::"}) - if _, ok := err.(*url.Error); !ok { - t.Errorf("Expect error when URL malformed") - } -} - -func TestNetrc_StaticLogin(t *testing.T) { - s := Service{ - private: true, - username: "octocat", - password: "password", - } - got, err := s.Create(noContext, &core.User{}, &core.Repository{HTTPURL: "https://github.com/octocat/hello-world"}) - if err != nil { - t.Error(err) - } - - want := &core.Netrc{ - Machine: "github.com", - Login: "octocat", - Password: "password", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestNetrc_RenewErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{Private: true, HTTPURL: "https://github.com/octocat/hello-world"} - mockUser := &core.User{ - Token: "755bb80e5b", - Refresh: "e08f3fa43e", - } - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, true).Return(scm.ErrNotAuthorized) - - s := Service{ - renewer: mockRenewer, - client: &scm.Client{Driver: scm.DriverGithub}, - } - _, err := s.Create(noContext, mockUser, mockRepo) - if err != scm.ErrNotAuthorized { - t.Errorf("Want not authorized error, got %v", err) - } -} diff --git a/service/org/cache.go b/service/org/cache.go deleted file mode 100644 index fac539ed44..0000000000 --- a/service/org/cache.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package orgs - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/drone/drone/core" - - lru "github.com/hashicorp/golang-lru" -) - -// content key pattern used in the cache, comprised of the -// organization name and username. -const contentKey = "%s/%s" - -// NewCache wraps the service with a simple cache to store -// organization membership. -func NewCache(base core.OrganizationService, size int, ttl time.Duration) core.OrganizationService { - // simple cache prevents the same yaml file from being - // requested multiple times in a short period. - cache, _ := lru.New(25) - - return &cacher{ - cache: cache, - base: base, - size: size, - ttl: ttl, - } -} - -type cacher struct { - mu sync.Mutex - - base core.OrganizationService - size int - ttl time.Duration - - cache *lru.Cache -} - -type item struct { - expiry time.Time - member bool - admin bool -} - -func (c *cacher) List(ctx context.Context, user *core.User) ([]*core.Organization, error) { - return c.base.List(ctx, user) -} - -func (c *cacher) Membership(ctx context.Context, user *core.User, name string) (bool, bool, error) { - key := fmt.Sprintf(contentKey, user.Login, name) - now := time.Now() - - // get the membership details from the cache. - cached, ok := c.cache.Get(key) - if ok { - item := cached.(*item) - // if the item is expired it can be ejected - // from the cache, else if not expired we return - // the cached results. - if now.After(item.expiry) { - c.cache.Remove(cached) - } else { - return item.member, item.admin, nil - } - } - - // get up-to-date membership details due to a cache - // miss or expired cache item. - member, admin, err := c.base.Membership(ctx, user, name) - if err != nil { - return false, false, err - } - - c.cache.Add(key, &item{ - expiry: now.Add(c.ttl), - member: member, - admin: admin, - }) - - return member, admin, nil -} diff --git a/service/org/cache_test.go b/service/org/cache_test.go deleted file mode 100644 index 7923aa7b44..0000000000 --- a/service/org/cache_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package orgs - -import ( - "testing" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -func TestCache(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - Login: "octocat", - } - - mockOrgService := mock.NewMockOrganizationService(controller) - mockOrgService.EXPECT().Membership(gomock.Any(), gomock.Any(), "github").Return(true, true, nil).Times(1) - - service := NewCache(mockOrgService, 10, time.Minute).(*cacher) - admin, member, err := service.Membership(noContext, mockUser, "github") - if err != nil { - t.Error(err) - } - - if got, want := service.cache.Len(), 1; got != want { - t.Errorf("Expect cache size %d, got %d", want, got) - } - if admin == false { - t.Errorf("Expect admin true, got false") - } - if member == false { - t.Errorf("Expect member true, got false") - } - - admin, member, err = service.Membership(noContext, mockUser, "github") - if err != nil { - t.Error(err) - } - if got, want := service.cache.Len(), 1; got != want { - t.Errorf("Expect cache size still %d, got %d", want, got) - } - if admin == false { - t.Errorf("Expect cached admin true, got false") - } - if member == false { - t.Errorf("Expect cached member true, got false") - } -} - -func TestCache_Expired(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - Login: "octocat", - } - - mockOrgService := mock.NewMockOrganizationService(controller) - mockOrgService.EXPECT().Membership(gomock.Any(), gomock.Any(), "github").Return(true, true, nil).Times(1) - - service := NewCache(mockOrgService, 10, time.Minute).(*cacher) - service.cache.Add("octocat/github", &item{ - expiry: time.Now().Add(time.Hour * -1), - member: true, - admin: true, - }) - admin, member, err := service.Membership(noContext, mockUser, "github") - if err != nil { - t.Error(err) - } - - if got, want := service.cache.Len(), 1; got != want { - t.Errorf("Expect cache size still %d, got %d", want, got) - } - if admin == false { - t.Errorf("Expect cached admin true, got false") - } - if member == false { - t.Errorf("Expect cached member true, got false") - } -} diff --git a/service/org/org.go b/service/org/org.go deleted file mode 100644 index 37a5b7c95b..0000000000 --- a/service/org/org.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package orgs - -import ( - "context" - "time" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -// New returns a new OrganizationService. -func New(client *scm.Client, renewer core.Renewer) core.OrganizationService { - return &service{ - client: client, - renewer: renewer, - } -} - -type service struct { - renewer core.Renewer - client *scm.Client -} - -func (s *service) List(ctx context.Context, user *core.User) ([]*core.Organization, error) { - err := s.renewer.Renew(ctx, user, false) - if err != nil { - return nil, err - } - token := &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - } - if user.Expiry != 0 { - token.Expires = time.Unix(user.Expiry, 0) - } - ctx = context.WithValue(ctx, scm.TokenKey{}, token) - out, _, err := s.client.Organizations.List(ctx, scm.ListOptions{Size: 100}) - if err != nil { - return nil, err - } - var orgs []*core.Organization - for _, org := range out { - orgs = append(orgs, &core.Organization{ - Name: org.Name, - Avatar: org.Avatar, - }) - } - return orgs, nil -} - -func (s *service) Membership(ctx context.Context, user *core.User, name string) (bool, bool, error) { - err := s.renewer.Renew(ctx, user, false) - if err != nil { - return false, false, err - } - token := &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - } - if user.Expiry != 0 { - token.Expires = time.Unix(user.Expiry, 0) - } - ctx = context.WithValue(ctx, scm.TokenKey{}, token) - out, _, err := s.client.Organizations.FindMembership(ctx, name, user.Login) - if err != nil { - return false, false, err - } - switch { - case out.Active == false: - return false, false, nil - case out.Role == scm.RoleUndefined: - return false, false, nil - case out.Role == scm.RoleAdmin: - return true, true, nil - default: - return true, false, nil - } -} diff --git a/service/org/org_test.go b/service/org/org_test.go deleted file mode 100644 index 34861e6dda..0000000000 --- a/service/org/org_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package orgs - -import ( - "context" - "testing" - "time" - - "github.com/drone/drone/mock" - "github.com/drone/drone/mock/mockscm" - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" - "github.com/google/go-cmp/cmp" - - "github.com/golang/mock/gomock" -) - -var noContext = context.Background() - -func TestList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - checkToken := func(ctx context.Context, opts scm.ListOptions) { - got, ok := ctx.Value(scm.TokenKey{}).(*scm.Token) - if !ok { - t.Errorf("Expect token stored in context") - return - } - want := &scm.Token{ - Token: "755bb80e5b", - Refresh: "e08f3fa43e", - Expires: time.Unix(1532292869, 0), - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } - if got, want := opts.Size, 100; got != want { - t.Errorf("Want page size %d, got %d", want, got) - } - if got, want := opts.Page, 0; got != want { - t.Errorf("Want page number %d, got %d", want, got) - } - } - - mockUser := &core.User{ - Login: "octocat", - Token: "755bb80e5b", - Refresh: "e08f3fa43e", - Expiry: 1532292869, - } - mockOrgs := []*scm.Organization{ - { - Name: "github", - Avatar: "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87", - }, - } - mockOrgService := mockscm.NewMockOrganizationService(controller) - mockOrgService.EXPECT().List(gomock.Any(), gomock.Any()).Do(checkToken).Return(mockOrgs, nil, nil) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Organizations = mockOrgService - - want := []*core.Organization{ - { - Name: "github", - Avatar: "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87", - }, - } - service := New(client, mockRenewer) - got, err := service.List(noContext, mockUser) - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestList_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockOrgs := mockscm.NewMockOrganizationService(controller) - mockOrgs.EXPECT().List(gomock.Any(), gomock.Any()).Return(nil, nil, scm.ErrNotAuthorized) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Organizations = mockOrgs - - service := New(client, mockRenewer) - got, err := service.List(noContext, mockUser) - if err == nil { - t.Errorf("Expect error finding user") - } - if got != nil { - t.Errorf("Expect nil user on error") - } -} - -func TestList_RenewalError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, mockRenewer) - _, err := service.List(noContext, mockUser) - if err == nil { - t.Errorf("Expect error refreshing token") - } -} diff --git a/service/redisdb/lockerr.go b/service/redisdb/lockerr.go deleted file mode 100644 index a94d1cdb54..0000000000 --- a/service/redisdb/lockerr.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package redisdb - -import ( - "context" -) - -// LockErr is an interface with lock and unlock functions that return an error. -// Method names are chosen so that redsync.Mutex implements the interface. -type LockErr interface { - LockContext(context.Context) error - UnlockContext(context.Context) (bool, error) -} - -// LockErrNoOp is a dummy no-op locker -type LockErrNoOp struct{} - -func (l LockErrNoOp) LockContext(context.Context) error { return nil } -func (l LockErrNoOp) UnlockContext(context.Context) (bool, error) { return false, nil } diff --git a/service/redisdb/redisdb.go b/service/redisdb/redisdb.go deleted file mode 100644 index bca1011a95..0000000000 --- a/service/redisdb/redisdb.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2021 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package redisdb - -import ( - "context" - "fmt" - "time" - - "github.com/go-redsync/redsync/v4" - "github.com/go-redsync/redsync/v4/redis/goredis/v8" - - "github.com/drone/drone/cmd/drone-server/config" - - "github.com/go-redis/redis/v8" - "github.com/sirupsen/logrus" -) - -func New(config config.Config) (srv RedisDB, err error) { - var options *redis.Options - - if config.Redis.ConnectionString != "" { - options, err = redis.ParseURL(config.Redis.ConnectionString) - if err != nil { - return - } - } else if config.Redis.Addr != "" { - options = &redis.Options{ - Addr: config.Redis.Addr, - Password: config.Redis.Password, - DB: config.Redis.DB, - } - } else { - return - } - - rdb := redis.NewClient(options) - - _, err = rdb.Ping(context.Background()).Result() - if err != nil { - err = fmt.Errorf("redis not accessibe: %w", err) - return - } - - rs := redsync.New(goredis.NewPool(rdb)) - - srv = redisService{ - rdb: rdb, - mutexGen: rs, - } - - return -} - -type RedisDB interface { - Client() redis.Cmdable - Subscribe(ctx context.Context, channelName string, channelSize int, proc PubSubProcessor) - NewMutex(name string, expiry time.Duration) LockErr -} - -type redisService struct { - rdb *redis.Client - mutexGen *redsync.Redsync -} - -// Client exposes redis.Cmdable interface -func (r redisService) Client() redis.Cmdable { - return r.rdb -} - -type PubSubProcessor interface { - ProcessMessage(s string) - ProcessError(err error) -} - -var backoffDurations = []time.Duration{ - 0, time.Second, 3 * time.Second, 5 * time.Second, 10 * time.Second, 20 * time.Second, -} - -// Subscribe subscribes to a redis pub-sub channel. The messages are processed with the supplied PubSubProcessor. -// In case of en error the function will automatically reconnect with an increasing back of delay. -// The only way to exit this function is to terminate or expire the supplied context. -func (r redisService) Subscribe(ctx context.Context, channelName string, channelSize int, proc PubSubProcessor) { - var connectTry int - for { - err := func() (err error) { - defer func() { - // panic recovery because external PubSubProcessor methods might cause panics. - if p := recover(); p != nil { - err = fmt.Errorf("redis pubsub: panic: %v", p) - } - }() - - var options []redis.ChannelOption - - if channelSize > 1 { - options = append(options, redis.WithChannelSize(channelSize)) - } - - pubsub := r.rdb.Subscribe(ctx, channelName) - ch := pubsub.Channel(options...) - - defer func() { - _ = pubsub.Close() - }() - - // make sure the connection is successful - err = pubsub.Ping(ctx) - if err != nil { - return - } - - connectTry = 0 // successfully connected, reset the counter - - logrus. - WithField("try", connectTry+1). - WithField("channel", channelName). - Trace("redis pubsub: subscribed") - - for { - select { - case m, ok := <-ch: - if !ok { - err = fmt.Errorf("redis pubsub: channel=%s closed", channelName) - return - } - - proc.ProcessMessage(m.Payload) - - case <-ctx.Done(): - err = ctx.Err() - return - } - } - }() - if err == nil { - // should not happen, the function should always exit with an error - continue - } - - proc.ProcessError(err) - - if err == context.Canceled || err == context.DeadlineExceeded { - logrus. - WithField("channel", channelName). - Trace("redis pubsub: finished") - return - } - - dur := backoffDurations[connectTry] - - logrus. - WithError(err). - WithField("try", connectTry+1). - WithField("pause", dur.String()). - WithField("channel", channelName). - Error("redis pubsub: connection failed, reconnecting") - - time.Sleep(dur) - - if connectTry < len(backoffDurations)-1 { - connectTry++ - } - } -} - -func (r redisService) NewMutex(name string, expiry time.Duration) LockErr { - var options []redsync.Option - if expiry > 0 { - options = append(options, redsync.WithExpiry(expiry)) - } - - return r.mutexGen.NewMutex(name, options...) -} diff --git a/service/repo/repo.go b/service/repo/repo.go deleted file mode 100644 index 9a578c5127..0000000000 --- a/service/repo/repo.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repo - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -type service struct { - renew core.Renewer - client *scm.Client - visibility string - trusted bool -} - -// New returns a new Repository service, providing access to the -// repository information from the source code management system. -func New(client *scm.Client, renewer core.Renewer, visibility string, trusted bool) core.RepositoryService { - return &service{ - renew: renewer, - client: client, - visibility: visibility, - trusted: trusted, - } -} - -func (s *service) List(ctx context.Context, user *core.User) ([]*core.Repository, error) { - err := s.renew.Renew(ctx, user, false) - if err != nil { - return nil, err - } - - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - }) - repos := []*core.Repository{} - opts := scm.ListOptions{Size: 100} - for { - result, meta, err := s.client.Repositories.List(ctx, opts) - if err != nil { - return nil, err - } - for _, src := range result { - if src != nil { - repos = append(repos, convertRepository(src, s.visibility, s.trusted)) - } - } - opts.Page = meta.Page.Next - opts.URL = meta.Page.NextURL - - if opts.Page == 0 && opts.URL == "" { - break - } - } - return repos, nil -} - -func (s *service) Find(ctx context.Context, user *core.User, repo string) (*core.Repository, error) { - err := s.renew.Renew(ctx, user, false) - if err != nil { - return nil, err - } - - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - }) - result, _, err := s.client.Repositories.Find(ctx, repo) - if err != nil { - return nil, err - } - return convertRepository(result, s.visibility, s.trusted), nil -} - -func (s *service) FindPerm(ctx context.Context, user *core.User, repo string) (*core.Perm, error) { - err := s.renew.Renew(ctx, user, false) - if err != nil { - return nil, err - } - - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - }) - result, _, err := s.client.Repositories.FindPerms(ctx, repo) - if err != nil { - return nil, err - } - return &core.Perm{ - Read: result.Pull, - Write: result.Push, - Admin: result.Admin, - }, nil -} diff --git a/service/repo/repo_test.go b/service/repo/repo_test.go deleted file mode 100644 index 299c79a175..0000000000 --- a/service/repo/repo_test.go +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package repo - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/drone/drone/mock/mockscm" - "github.com/drone/go-scm/scm" - "github.com/google/go-cmp/cmp" - - "github.com/golang/mock/gomock" -) - -var noContext = context.Background() - -func TestFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockRepo := &scm.Repository{ - Namespace: "octocat", - Name: "hello-world", - } - - mockRepoService := mockscm.NewMockRepositoryService(controller) - mockRepoService.EXPECT().Find(gomock.Any(), "octocat/hello-world").Return(mockRepo, nil, nil) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Repositories = mockRepoService - - service := New(client, mockRenewer, "", false) - - want := &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Visibility: "public", - } - - got, err := service.Find(noContext, mockUser, "octocat/hello-world") - if err != nil { - t.Error(err) - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestFind_Err(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRepoService := mockscm.NewMockRepositoryService(controller) - mockRepoService.EXPECT().Find(gomock.Any(), "octocat/hello-world").Return(nil, nil, scm.ErrNotFound) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Repositories = mockRepoService - - service := New(client, mockRenewer, "", false) - _, err := service.Find(noContext, mockUser, "octocat/hello-world") - if err != scm.ErrNotFound { - t.Errorf("Expect not found error, got %v", err) - } -} - -func TestFind_RefreshErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, mockRenewer, "", false) - _, err := service.Find(noContext, mockUser, "octocat/hello-world") - if err == nil { - t.Errorf("Expect error refreshing token") - } -} - -func TestFindPerm(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockPerm := &scm.Perm{ - Pull: true, - Push: true, - Admin: true, - } - - mockRepoService := mockscm.NewMockRepositoryService(controller) - mockRepoService.EXPECT().FindPerms(gomock.Any(), "octocat/hello-world").Return(mockPerm, nil, nil) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Repositories = mockRepoService - - service := New(client, mockRenewer, "", false) - - want := &core.Perm{ - Read: true, - Write: true, - Admin: true, - } - - got, err := service.FindPerm(noContext, mockUser, "octocat/hello-world") - if err != nil { - t.Error(err) - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestFindPerm_Err(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRepoService := mockscm.NewMockRepositoryService(controller) - mockRepoService.EXPECT().FindPerms(gomock.Any(), "octocat/hello-world").Return(nil, nil, scm.ErrNotFound) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Repositories = mockRepoService - - service := New(client, mockRenewer, "", false) - _, err := service.FindPerm(noContext, mockUser, "octocat/hello-world") - if err != scm.ErrNotFound { - t.Errorf("Expect not found error, got %v", err) - } -} - -func TestFindPerm_RefreshErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, mockRenewer, "", false) - _, err := service.FindPerm(noContext, mockUser, "octocat/hello-world") - if err == nil { - t.Errorf("Expect error refreshing token") - } -} - -func TestList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockRepos := []*scm.Repository{ - { - Namespace: "octocat", - Name: "hello-world", - }, - } - - mockRepoService := mockscm.NewMockRepositoryService(controller) - mockRepoService.EXPECT().List(gomock.Any(), gomock.Any()).Return(mockRepos, &scm.Response{}, nil) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Repositories = mockRepoService - - want := []*core.Repository{ - { - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Visibility: "public", - }, - } - - service := New(client, mockRenewer, "", false) - got, err := service.List(noContext, mockUser) - if err != nil { - t.Error(err) - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestList_Err(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRepoService := mockscm.NewMockRepositoryService(controller) - mockRepoService.EXPECT().List(gomock.Any(), gomock.Any()).Return(nil, &scm.Response{}, scm.ErrNotAuthorized) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Repositories = mockRepoService - - service := New(client, mockRenewer, "", false) - _, err := service.List(noContext, mockUser) - if err != scm.ErrNotAuthorized { - t.Errorf("Want not authorized error, got %v", err) - } -} - -func TestList_RefreshErr(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, mockRenewer, "", false) - _, err := service.List(noContext, mockUser) - if err == nil { - t.Errorf("Expect error refreshing token") - } -} - -func TestListWithNilRepo(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - mockRepos := []*scm.Repository{ - { - Namespace: "octocat", - Name: "hello-world", - }, - nil, - } - - mockRepoService := mockscm.NewMockRepositoryService(controller) - mockRepoService.EXPECT().List(gomock.Any(), gomock.Any()).Return(mockRepos, &scm.Response{}, nil) - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false) - - client := new(scm.Client) - client.Repositories = mockRepoService - - want := []*core.Repository{ - { - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Visibility: "public", - }, - } - - service := New(client, mockRenewer, "", false) - got, err := service.List(noContext, mockUser) - if err != nil { - t.Error(err) - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} diff --git a/service/repo/util.go b/service/repo/util.go deleted file mode 100644 index ecb62ebcea..0000000000 --- a/service/repo/util.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repo - -import ( - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -// convertRepository is a helper function that converts a -// repository from the source code management system to the -// local datastructure. -func convertRepository(src *scm.Repository, visibility string, trusted bool) *core.Repository { - return &core.Repository{ - UID: src.ID, - Namespace: src.Namespace, - Name: src.Name, - Slug: scm.Join(src.Namespace, src.Name), - HTTPURL: src.Clone, - SSHURL: src.CloneSSH, - Link: src.Link, - Private: src.Private, - Visibility: convertVisibility(src, visibility), - Branch: src.Branch, - Trusted: trusted, - Archived: src.Archived, - } -} - -// convertVisibility is a helper function that returns the -// repository visibility based on the privacy flag. -func convertVisibility(src *scm.Repository, visibility string) string { - // if the visibility is set to internal (github enterprise and gitlab) - // and the global visibility is empty, automatically set to internal. - if visibility == "" && src.Visibility == scm.VisibilityInternal { - return core.VisibilityInternal - } - - switch { - case src.Private == true: - return core.VisibilityPrivate - case visibility == core.VisibilityInternal: - return core.VisibilityInternal - default: - return core.VisibilityPublic - } -} diff --git a/service/repo/util_test.go b/service/repo/util_test.go deleted file mode 100644 index d4cfd66ce2..0000000000 --- a/service/repo/util_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package repo - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" - - "github.com/google/go-cmp/cmp" -) - -func TestConvertRepository(t *testing.T) { - from := &scm.Repository{ - ID: "42", - Namespace: "octocat", - Name: "hello-world", - Branch: "master", - Private: true, - Clone: "https://github.com/octocat/hello-world.git", - CloneSSH: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - want := &core.Repository{ - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - Private: true, - Branch: "master", - Visibility: core.VisibilityPrivate, - } - got := convertRepository(from, "", false) - if diff := cmp.Diff(want, got); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestConvertVisibility(t *testing.T) { - tests := []struct { - r *scm.Repository - v string - }{ - { - r: &scm.Repository{Private: false}, - v: core.VisibilityPublic, - }, - { - r: &scm.Repository{Private: true}, - v: core.VisibilityPrivate, - }, - { - r: &scm.Repository{Private: true, Visibility: scm.VisibilityInternal}, - v: core.VisibilityInternal, - }, - } - - for i, test := range tests { - if got, want := convertVisibility(test.r, ""), test.v; got != want { - t.Errorf("Want visibility %s, got %s for index %d", got, want, i) - } - } -} - -func TestDefinedVisibility(t *testing.T) { - from := &scm.Repository{ - ID: "42", - Namespace: "octocat", - Name: "hello-world", - Branch: "master", - Private: false, - Clone: "https://github.com/octocat/hello-world.git", - CloneSSH: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - } - want := &core.Repository{ - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - Private: false, - Branch: "master", - Visibility: core.VisibilityInternal, - } - got := convertRepository(from, "internal", false) - if diff := cmp.Diff(want, got); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/service/status/status.go b/service/status/status.go deleted file mode 100644 index 3adbdb1f52..0000000000 --- a/service/status/status.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package status - -import ( - "context" - "fmt" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" - "github.com/drone/go-scm/scm/driver/github" -) - -// Config configures the Status service. -type Config struct { - Base string - Name string - Disabled bool -} - -// New returns a new StatusService -func New(client *scm.Client, renew core.Renewer, config Config) core.StatusService { - return &service{ - client: client, - renew: renew, - base: config.Base, - name: config.Name, - disabled: config.Disabled, - } -} - -type service struct { - renew core.Renewer - client *scm.Client - base string - name string - disabled bool -} - -func (s *service) Send(ctx context.Context, user *core.User, req *core.StatusInput) error { - if s.disabled || req.Build.Event == core.EventCron { - return nil - } - - err := s.renew.Renew(ctx, user, false) - if err != nil { - return err - } - - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - }) - - // HACK(bradrydzewski) provides support for the github deployment API - if req.Build.DeployID != 0 && s.client.Driver == scm.DriverGithub { - // TODO(bradrydzewski) only update the deployment status when the - // build completes. - if req.Build.Finished == 0 { - return nil - } - _, _, err = s.client.Repositories.(*github.RepositoryService).CreateDeployStatus(ctx, req.Repo.Slug, &scm.DeployStatus{ - Number: req.Build.DeployID, - Desc: createDesc(req.Build.Status), - State: convertStatus(req.Build.Status), - Target: fmt.Sprintf("%s/%s/%d", s.base, req.Repo.Slug, req.Build.Number), - Environment: req.Build.Deploy, - }) - return err - } - - _, _, err = s.client.Repositories.CreateStatus(ctx, req.Repo.Slug, req.Build.After, &scm.StatusInput{ - Title: fmt.Sprintf("Build #%d", req.Build.Number), - Desc: createDesc(req.Build.Status), - Label: createLabel(s.name, req.Build.Event, req.Build.Deploy), - State: convertStatus(req.Build.Status), - Target: fmt.Sprintf("%s/%s/%d", s.base, req.Repo.Slug, req.Build.Number), - }) - if err == scm.ErrNotSupported { - return nil - } - return err -} diff --git a/service/status/status_test.go b/service/status/status_test.go deleted file mode 100644 index 70d7426ac8..0000000000 --- a/service/status/status_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package status - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/drone/drone/mock/mockscm" - "github.com/drone/go-scm/scm" - - "github.com/golang/mock/gomock" -) - -var noContext = context.Background() - -func TestStatus(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - statusInput := &scm.StatusInput{ - Title: "Build #1", - State: scm.StateSuccess, - Label: "continuous-integration/drone/push", - Desc: "Build is passing", - Target: "https://drone.company.com/octocat/hello-world/1", - } - - mockRepos := mockscm.NewMockRepositoryService(controller) - mockRepos.EXPECT().CreateStatus(gomock.Any(), "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", statusInput).Return(nil, nil, nil) - - client := new(scm.Client) - client.Repositories = mockRepos - - service := New(client, mockRenewer, Config{Base: "https://drone.company.com"}) - err := service.Send(noContext, mockUser, &core.StatusInput{ - Repo: &core.Repository{Slug: "octocat/hello-world"}, - Build: &core.Build{ - Number: 1, - Event: core.EventPush, - Status: core.StatusPassing, - After: "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", - }, - }) - if err != nil { - t.Error(err) - } -} - -func TestStatus_ErrNotSupported(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(nil) - - statusInput := &scm.StatusInput{ - Title: "Build #1", - State: scm.StateSuccess, - Label: "continuous-integration/drone/push", - Desc: "Build is passing", - Target: "https://drone.company.com/octocat/hello-world/1", - } - - mockRepos := mockscm.NewMockRepositoryService(controller) - mockRepos.EXPECT().CreateStatus(gomock.Any(), "octocat/hello-world", "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", statusInput).Return(nil, nil, scm.ErrNotSupported) - - client := new(scm.Client) - client.Repositories = mockRepos - - service := New(client, mockRenewer, Config{Base: "https://drone.company.com"}) - err := service.Send(noContext, mockUser, &core.StatusInput{ - Repo: &core.Repository{Slug: "octocat/hello-world"}, - Build: &core.Build{ - Number: 1, - Event: core.EventPush, - Status: core.StatusPassing, - After: "a6586b3db244fb6b1198f2b25c213ded5b44f9fa", - }, - }) - if err != nil { - t.Error(err) - } -} - -func TestStatus_RenewalError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{} - - mockRenewer := mock.NewMockRenewer(controller) - mockRenewer.EXPECT().Renew(gomock.Any(), mockUser, false).Return(scm.ErrNotAuthorized) - - service := New(nil, mockRenewer, Config{Base: "https://drone.company.com"}) - err := service.Send(noContext, mockUser, &core.StatusInput{Build: &core.Build{}}) - if err == nil { - t.Errorf("Expect error refreshing token") - } -} - -func TestStatus_Disabled(t *testing.T) { - service := New(nil, nil, Config{Disabled: true}) - err := service.Send(noContext, nil, nil) - if err != nil { - t.Error(err) - } -} diff --git a/service/status/util.go b/service/status/util.go deleted file mode 100644 index eb6d93026b..0000000000 --- a/service/status/util.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package status - -import ( - "fmt" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" - "github.com/gosimple/slug" -) - -func createLabel(name, event, deployTo string) string { - if name == "" { - name = "continuous-integration/drone" - } - switch event { - case core.EventPush: - return fmt.Sprintf("%s/push", name) - case core.EventPullRequest: - return fmt.Sprintf("%s/pr", name) - case core.EventTag: - return fmt.Sprintf("%s/tag", name) - case core.EventPromote: - return fmt.Sprintf("%s/promote/%s", name, slug.Make(deployTo)) - default: - return name - } -} - -func createDesc(state string) string { - switch state { - case core.StatusBlocked: - return "Build is pending approval" - case core.StatusDeclined: - return "Build was declined" - case core.StatusError: - return "Build encountered an error" - case core.StatusFailing: - return "Build is failing" - case core.StatusKilled: - return "Build was killed" - case core.StatusPassing: - return "Build is passing" - case core.StatusWaiting: - return "Build is pending" - case core.StatusPending: - return "Build is pending" - case core.StatusRunning: - return "Build is running" - case core.StatusSkipped: - return "Build was skipped" - default: - return "Build is in an unknown state" - } -} - -func convertStatus(state string) scm.State { - switch state { - case core.StatusBlocked: - return scm.StatePending - case core.StatusDeclined: - return scm.StateCanceled - case core.StatusError: - return scm.StateError - case core.StatusFailing: - return scm.StateFailure - case core.StatusKilled: - return scm.StateCanceled - case core.StatusPassing: - return scm.StateSuccess - case core.StatusPending: - return scm.StatePending - case core.StatusRunning: - return scm.StatePending - case core.StatusSkipped: - return scm.StateUnknown - default: - return scm.StateUnknown - } -} diff --git a/service/status/util_test.go b/service/status/util_test.go deleted file mode 100644 index 40aa2ff0e3..0000000000 --- a/service/status/util_test.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package status - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -func TestCreateLabel(t *testing.T) { - tests := []struct { - name string - event string - label string - deployTo string - }{ - { - event: core.EventPullRequest, - label: "continuous-integration/drone/pr", - }, - { - event: core.EventPush, - label: "continuous-integration/drone/push", - }, - { - event: core.EventTag, - label: "continuous-integration/drone/tag", - }, - { - event: core.EventPromote, - deployTo: "production", - label: "continuous-integration/drone/promote/production", - }, - { - event: core.EventPromote, - deployTo: "$production%", - label: "continuous-integration/drone/promote/production", - }, - { - event: "unknown", - label: "continuous-integration/drone", - }, - { - name: "drone", - event: core.EventPush, - label: "drone/push", - }, - } - for _, test := range tests { - if got, want := createLabel(test.name, test.event, test.deployTo), test.label; got != want { - t.Errorf("Want label %q, got %q", want, got) - } - } -} - -func TestCreateDesc(t *testing.T) { - tests := []struct { - status string - desc string - }{ - - { - status: core.StatusBlocked, - desc: "Build is pending approval", - }, - { - status: core.StatusDeclined, - desc: "Build was declined", - }, - { - status: core.StatusError, - desc: "Build encountered an error", - }, - { - status: core.StatusFailing, - desc: "Build is failing", - }, - { - status: core.StatusKilled, - desc: "Build was killed", - }, - { - status: core.StatusPassing, - desc: "Build is passing", - }, - { - status: core.StatusWaiting, - desc: "Build is pending", - }, - { - status: core.StatusPending, - desc: "Build is pending", - }, - { - status: core.StatusRunning, - desc: "Build is running", - }, - { - status: core.StatusSkipped, - desc: "Build was skipped", - }, - { - status: "unknown", - desc: "Build is in an unknown state", - }, - } - for _, test := range tests { - if got, want := createDesc(test.status), test.desc; got != want { - t.Errorf("Want dest %q, got %q", want, got) - } - } -} - -func TestConvertStatus(t *testing.T) { - tests := []struct { - from string - to scm.State - }{ - { - from: core.StatusBlocked, - to: scm.StatePending, - }, - { - from: core.StatusDeclined, - to: scm.StateCanceled, - }, - { - from: core.StatusError, - to: scm.StateError, - }, - { - from: core.StatusFailing, - to: scm.StateFailure, - }, - { - from: core.StatusKilled, - to: scm.StateCanceled, - }, - { - from: core.StatusPassing, - to: scm.StateSuccess, - }, - { - from: core.StatusPending, - to: scm.StatePending, - }, - { - from: core.StatusRunning, - to: scm.StatePending, - }, - { - from: core.StatusSkipped, - to: scm.StateUnknown, - }, - { - from: "unknown", - to: scm.StateUnknown, - }, - } - for _, test := range tests { - if got, want := convertStatus(test.from), test.to; got != want { - t.Errorf("Want status %v, got %v", want, got) - } - } -} diff --git a/service/syncer/filter.go b/service/syncer/filter.go deleted file mode 100644 index 2a403e85cd..0000000000 --- a/service/syncer/filter.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package syncer - -import ( - "strings" - - "github.com/drone/drone/core" -) - -// FilterFunc can be used to filter which repositories are -// synchronized with the local datastore. -type FilterFunc func(*core.Repository) bool - -// NamespaceFilter is a filter function that returns true -// if the repository namespace matches a provided namespace -// in the list. -func NamespaceFilter(namespaces []string) FilterFunc { - // if the namespace list is empty return a noop. - if len(namespaces) == 0 { - return noopFilter - } - return func(r *core.Repository) bool { - for _, namespace := range namespaces { - if strings.EqualFold(namespace, r.Namespace) { - return true - } - } - return false - } -} - -// noopFilter is a filter function that always returns true. -func noopFilter(*core.Repository) bool { - return true -} diff --git a/service/syncer/filter_oss.go b/service/syncer/filter_oss.go deleted file mode 100644 index db3f08ff02..0000000000 --- a/service/syncer/filter_oss.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package syncer - -import "github.com/drone/drone/core" - -// FilterFunc can be used to filter which repositories are -// synchronized with the local datastore. -type FilterFunc func(*core.Repository) bool - -// NamespaceFilter is a no-op filter. -func NamespaceFilter(namespaces []string) FilterFunc { - return noopFilter -} - -// noopFilter is a filter function that always returns true. -func noopFilter(*core.Repository) bool { - return true -} diff --git a/service/syncer/filter_test.go b/service/syncer/filter_test.go deleted file mode 100644 index 67345bd7e0..0000000000 --- a/service/syncer/filter_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package syncer - -import ( - "testing" - - "github.com/drone/drone/core" -) - -func TestNamespaceFilter(t *testing.T) { - tests := []struct { - namespace string - namespaces []string - match bool - }{ - { - namespace: "octocat", - namespaces: []string{"octocat"}, - match: true, - }, - { - namespace: "OCTocat", - namespaces: []string{"octOCAT"}, - match: true, - }, - { - namespace: "spaceghost", - namespaces: []string{"octocat"}, - match: false, - }, - { - namespace: "spaceghost", - namespaces: []string{}, - match: true, // no-op filter - }, - } - for _, test := range tests { - r := &core.Repository{Namespace: test.namespace} - f := NamespaceFilter(test.namespaces) - if got, want := f(r), test.match; got != want { - t.Errorf("Want match %v for namespace %q and namespaces %v", want, test.namespace, test.namespaces) - } - } -} diff --git a/service/syncer/syncer.go b/service/syncer/syncer.go deleted file mode 100644 index 78194d3d39..0000000000 --- a/service/syncer/syncer.go +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package syncer - -import ( - "context" - "runtime/debug" - "strings" - "time" - - "github.com/drone/drone/core" - - "github.com/sirupsen/logrus" -) - -// New returns a new Synchronizer. -func New( - repoz core.RepositoryService, - repos core.RepositoryStore, - users core.UserStore, - batch core.Batcher, -) *Synchronizer { - return &Synchronizer{ - repoz: repoz, - repos: repos, - users: users, - batch: batch, - match: noopFilter, - } -} - -// Synchronizer synchronizes user repositories and permissions -// between a remote source code management system and the local -// data store. -type Synchronizer struct { - repoz core.RepositoryService - repos core.RepositoryStore - users core.UserStore - batch core.Batcher - match FilterFunc -} - -// SetFilter sets the filter function. -func (s *Synchronizer) SetFilter(fn FilterFunc) { - s.match = fn -} - -// Sync synchronizes the user repository list in 6 easy steps. -func (s *Synchronizer) Sync(ctx context.Context, user *core.User) (*core.Batch, error) { - logger := logrus.WithField("login", user.Login) - logger.Debugln("syncer: begin repository sync") - - defer func() { - // taking the paranoid approach to recover from - // a panic that should absolutely never happen. - if err := recover(); err != nil { - logger = logger.WithField("error", err) - logger.Errorf("syncer: unexpected panic\n%s\n", debug.Stack()) - } - - // when the synchronization process is complete - // be sure to update the user sync date. - user.Syncing = false - user.Synced = time.Now().Unix() - s.users.Update(context.Background(), user) - }() - - if user.Syncing == false { - user.Syncing = true - err := s.users.Update(ctx, user) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("syncer: cannot update user") - return nil, err - } - } - - batch := &core.Batch{} - remote := map[string]*core.Repository{} - local := map[string]*core.Repository{} - - // - // STEP1: get the list of repositories from the remote - // source code management system (e.g. GitHub). - // - - { - repos, err := s.repoz.List(ctx, user) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("syncer: cannot get remote repository list") - return nil, err - } - for _, repo := range repos { - if strings.Count(repo.Slug, "/") > 1 { - if logrus.GetLevel() == logrus.TraceLevel { - logger.WithField("namespace", repo.Namespace). - WithField("name", repo.Name). - WithField("uid", repo.UID). - Traceln("syncer: skipping subrepositories") - } - } else if repo.Archived { - if logrus.GetLevel() == logrus.TraceLevel { - logger.WithField("namespace", repo.Namespace). - WithField("name", repo.Name). - WithField("uid", repo.UID). - Traceln("syncer: skipping archived repositories") - } - } else if s.match(repo) { - remote[repo.UID] = repo - if logrus.GetLevel() == logrus.TraceLevel { - logger.WithField("namespace", repo.Namespace). - WithField("name", repo.Name). - WithField("uid", repo.UID). - Traceln("syncer: remote repository matches filter") - } - } else { - if logrus.GetLevel() == logrus.TraceLevel { - logger.WithField("namespace", repo.Namespace). - WithField("name", repo.Name). - WithField("uid", repo.UID). - Traceln("syncer: remote repository does not match filter") - } - } - } - } - - // - // STEP2: get the list of repositories stored in the - // local database. - // - - { - repos, err := s.repos.List(ctx, user.ID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("syncer: cannot get cached repository list") - return nil, err - } - - for _, repo := range repos { - local[repo.UID] = repo - } - } - - // - // STEP3 find repos that exist in the remote system, - // but do not exist locally. Insert. - // - - for k, v := range remote { - _, ok := local[k] - if ok { - continue - } - v.Synced = time.Now().Unix() - v.Created = time.Now().Unix() - v.Updated = time.Now().Unix() - v.Version = 1 - batch.Insert = append(batch.Insert, v) - - if logrus.GetLevel() == logrus.TraceLevel { - logger.WithField("namespace", v.Namespace). - WithField("name", v.Name). - WithField("uid", v.UID). - Traceln("syncer: remote repository not in database") - } - } - - // - // STEP4 find repos that exist in the remote system and - // in the local system, but with incorrect data. Update. - // - - for k, v := range local { - vv, ok := remote[k] - if !ok { - continue - } - if diff(v, vv) { - merge(v, vv) - v.Synced = time.Now().Unix() - v.Updated = time.Now().Unix() - batch.Update = append(batch.Update, v) - - if logrus.GetLevel() == logrus.TraceLevel { - logger.WithField("namespace", v.Namespace). - WithField("name", v.Name). - WithField("uid", v.UID). - Traceln("syncer: repository requires update") - } - } - } - - // - // STEP5 find repos that exist in the local system, - // but not in the remote system. Revoke permissions. - // - - for k, v := range local { - _, ok := remote[k] - if ok { - continue - } - batch.Revoke = append(batch.Revoke, v) - - if logrus.GetLevel() == logrus.TraceLevel { - logger.WithField("namespace", v.Namespace). - WithField("name", v.Name). - WithField("uid", v.UID). - Traceln("syncer: repository in database not in remote repository list") - } - } - - // - // STEP6 update the database. - // - - if err := s.batch.Batch(ctx, user, batch); err != nil { - logger = logger.WithError(err) - logger.Warnln("syncer: cannot batch update") - return nil, err - } - - logger.Debugln("syncer: finished repository sync") - return batch, nil -} diff --git a/service/syncer/syncer_test.go b/service/syncer/syncer_test.go deleted file mode 100644 index 0d7ae7aaf0..0000000000 --- a/service/syncer/syncer_test.go +++ /dev/null @@ -1,485 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package syncer - -import ( - "context" - "database/sql" - "io/ioutil" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/drone/go-scm/scm" - "github.com/sirupsen/logrus" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -// TODO(bradrydzewski) test failure to update user -// TODO(bradrydzewski) test recover from unexpected panic - -var noContext = context.Background() - -func init() { - logrus.SetOutput(ioutil.Discard) - logrus.SetLevel(logrus.TraceLevel) -} - -func TestSync(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - batcher := mock.NewMockBatcher(controller) - batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - - repoStore := mock.NewMockRepositoryStore(controller) - repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{}, nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{ - { - UID: "1", - Slug: "octocat/hello-world", - Namespace: "octocat", - Name: "hello-world", - Private: false, - Visibility: core.VisibilityPublic, - }, - }, nil) - - s := New( - repoService, - repoStore, - userStore, - batcher, - ) - got, err := s.Sync(context.Background(), user) - if err != nil { - t.Error(err) - } - - want := &core.Batch{ - Insert: []*core.Repository{ - { - UID: "1", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Visibility: core.VisibilityPublic, - Version: 1, - }, - }, - } - - ignore := cmpopts.IgnoreFields(core.Repository{}, - "Synced", "Created", "Updated") - if diff := cmp.Diff(got, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that we are able to recognize when -// a repository has been updated. -func TestSync_Update(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - batcher := mock.NewMockBatcher(controller) - batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - - repoStore := mock.NewMockRepositoryStore(controller) - repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{ - {UID: "1", Namespace: "octocat", Name: "hello-world"}, - {UID: "2", Namespace: "octocat", Name: "Spoon-Knife", Private: false}, - }, nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{ - { - UID: "1", - Slug: "octocat/hello-world", - Namespace: "octocat", - Name: "hello-world", - }, - { - UID: "2", - Slug: "octocat/Spoon-Knife", - Namespace: "octocat", - Name: "Spoon-Knife", - Private: true, - }, - }, nil) - - s := New( - repoService, - repoStore, - userStore, - batcher, - ) - got, err := s.Sync(context.Background(), user) - if err != nil { - t.Error(err) - } - - want := &core.Batch{ - Update: []*core.Repository{ - { - UID: "2", - Namespace: "octocat", - Name: "Spoon-Knife", - Slug: "octocat/Spoon-Knife", - Private: true, - }, - }, - } - - ignore := cmpopts.IgnoreFields(core.Repository{}, - "Synced", "Created", "Updated") - if diff := cmp.Diff(got, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that we are able to recognize when -// a repository has been renamed. -func TestSync_Rename(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - batcher := mock.NewMockBatcher(controller) - batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - - repoStore := mock.NewMockRepositoryStore(controller) - repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{ - {ID: 101, UID: "1", Namespace: "octocat", Name: "hello-world"}, - {ID: 102, UID: "2", Namespace: "octocat", Name: "Spoon-Knife"}, - }, nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{ - { - UID: "1", - Slug: "octocat/hello-world", - Namespace: "octocat", - Name: "hello-world", - }, - { - UID: "2", - Slug: "octocat/Spoon-Knife", - Namespace: "octocat", - Name: "Spork-Knife", - }, - }, nil) - - s := New( - repoService, - repoStore, - userStore, - batcher, - ) - got, err := s.Sync(context.Background(), user) - if err != nil { - t.Error(err) - } - want := &core.Batch{ - Update: []*core.Repository{ - {ID: 102, UID: "2", Namespace: "octocat", Name: "Spork-Knife", Slug: "octocat/Spork-Knife"}, - }, - } - ignore := cmpopts.IgnoreFields(core.Repository{}, - "Synced", "Created", "Updated") - if diff := cmp.Diff(got, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that we are able to recognize when -// the user permission to the repository have been revoked. -func TestSync_Revoke(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - batcher := mock.NewMockBatcher(controller) - batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - - repoStore := mock.NewMockRepositoryStore(controller) - repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{ - {UID: "1", Namespace: "octocat", Name: "hello-world"}, - }, nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{}, nil) - - s := New( - repoService, - repoStore, - userStore, - batcher, - ) - got, err := s.Sync(context.Background(), user) - if err != nil { - t.Error(err) - } - want := &core.Batch{ - Revoke: []*core.Repository{ - {UID: "1", Namespace: "octocat", Name: "hello-world"}, - }, - } - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -// this test verifies that we invoke the batch update even -// if there are no batch updates to make. This is important -// because the batcher resets permissions and forces Drone -// to re-synchronize. -func TestSync_EmptyBatch(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - batcher := mock.NewMockBatcher(controller) - batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return(nil, nil) - - repoStore := mock.NewMockRepositoryStore(controller) - repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return(nil, nil) - - s := New( - repoService, - repoStore, - userStore, - batcher, - ) - batch, err := s.Sync(context.Background(), user) - if err != nil { - t.Error(err) - } - if want, got := len(batch.Insert), 0; got != want { - t.Errorf("Want %d batch inserts, got %d", want, got) - } - if want, got := len(batch.Update), 0; got != want { - t.Errorf("Want %d batch updates, got %d", want, got) - } - if want, got := len(batch.Revoke), 0; got != want { - t.Errorf("Want %d batch revokes, got %d", want, got) - } -} - -// this test verifies that an error returned by the source -// code management system causes the synchronization process to -// exit and is returned to the caller. -func TestSync_RemoteError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return(nil, scm.ErrNotFound) - - s := New( - repoService, - nil, - userStore, - nil, - ) - _, err := s.Sync(context.Background(), user) - if got, want := err, scm.ErrNotFound; got != want { - t.Errorf("Want error %s, got %s", want, got) - } -} - -// this test verifies that an error returned by the internal -// repository datastore causes the synchronization process to -// exit and is returned to the caller. -func TestSync_StoreError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{}, nil) - - repoStore := mock.NewMockRepositoryStore(controller) - repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) - - s := Synchronizer{ - repoz: repoService, - users: userStore, - repos: repoStore, - } - _, err := s.Sync(context.Background(), user) - if got, want := err, sql.ErrNoRows; got != want { - t.Errorf("Want error %s, got %s", want, got) - } -} - -// this test verifies that an error returned by the batcher -// causes the synchronization process to exit and is returned -// to the caller. -func TestSync_BatchError(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{}, nil) - - repoStore := mock.NewMockRepositoryStore(controller) - repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return(nil, nil) - - batcher := mock.NewMockBatcher(controller) - batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(sql.ErrNoRows) - - s := New( - repoService, - repoStore, - userStore, - batcher, - ) - _, err := s.Sync(context.Background(), user) - if got, want := err, sql.ErrNoRows; got != want { - t.Errorf("Want error %s, got %s", want, got) - } -} - -// this test verifies that sub-repositories are skipped. They -// are unsupported by Drone and should not be ignored. -func TestSync_SkipSubrepo(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - batcher := mock.NewMockBatcher(controller) - batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - - repoStore := mock.NewMockRepositoryStore(controller) - repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{}, nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{ - { - UID: "1", - Slug: "octocat/hello/world", - Namespace: "octocat", - Name: "hello-world", - Private: false, - Visibility: core.VisibilityPublic, - }, - }, nil) - - s := New( - repoService, - repoStore, - userStore, - batcher, - ) - got, err := s.Sync(context.Background(), user) - if err != nil { - t.Error(err) - } - - want := &core.Batch{} - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Errorf(diff) - } -} - -func TestSyncArchive(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - user := &core.User{ID: 1} - - userStore := mock.NewMockUserStore(controller) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - userStore.EXPECT().Update(gomock.Any(), user).Return(nil) - - batcher := mock.NewMockBatcher(controller) - batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - - repoStore := mock.NewMockRepositoryStore(controller) - repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{}, nil) - - repoService := mock.NewMockRepositoryService(controller) - repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{ - { - UID: "1", - Slug: "octocat/hello-world", - Namespace: "octocat", - Name: "hello-world", - Private: false, - Visibility: core.VisibilityPublic, - Archived: true, - }, - }, nil) - - s := New( - repoService, - repoStore, - userStore, - batcher, - ) - got, err := s.Sync(context.Background(), user) - if err != nil { - t.Error(err) - } - - want := &core.Batch{} - - ignore := cmpopts.IgnoreFields(core.Repository{}, - "Synced", "Created", "Updated") - if diff := cmp.Diff(got, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/service/syncer/util.go b/service/syncer/util.go deleted file mode 100644 index 82386e03a6..0000000000 --- a/service/syncer/util.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package syncer - -import ( - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -// merge is a helper function that merges a subset of -// values from the source to the destination repository. -func merge(dst, src *core.Repository) { - dst.Namespace = src.Namespace - dst.Name = src.Name - dst.HTTPURL = src.HTTPURL - dst.SSHURL = src.SSHURL - dst.Private = src.Private - dst.Branch = src.Branch - dst.Slug = scm.Join(src.Namespace, src.Name) - - // the gitea and gogs repository endpoints do not - // return the html url, so we need to ensure we do - // not replace the existing value with a zero value. - if src.Link != "" { - dst.Link = src.Link - } -} - -// diff is a helper function that compares two repositories -// and returns true if a subset of values are different. -func diff(a, b *core.Repository) bool { - switch { - case a.Namespace != b.Namespace: - return true - case a.Name != b.Name: - return true - case a.HTTPURL != b.HTTPURL: - return true - case a.SSHURL != b.SSHURL: - return true - case a.Private != b.Private: - return true - case a.Branch != b.Branch: - return true - case a.Link != b.Link: - return true - default: - return false - } -} diff --git a/service/syncer/util_test.go b/service/syncer/util_test.go deleted file mode 100644 index a59d4b1c10..0000000000 --- a/service/syncer/util_test.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package syncer - -import ( - "testing" - - "github.com/drone/drone/core" - "github.com/google/go-cmp/cmp" -) - -// import ( -// "testing" - -// "github.com/drone/drone/core" -// "github.com/drone/go-scm/scm" - -// "github.com/google/go-cmp/cmp" -// ) - -// func TestConvertRepository(t *testing.T) { -// from := &scm.Repository{ -// ID: "42", -// Namespace: "octocat", -// Name: "hello-world", -// Branch: "master", -// Private: true, -// Clone: "https://github.com/octocat/hello-world.git", -// CloneSSH: "git@github.com:octocat/hello-world.git", -// Link: "https://github.com/octocat/hello-world", -// } -// want := &core.Repository{ -// UID: "42", -// Namespace: "octocat", -// Name: "hello-world", -// Slug: "octocat/hello-world", -// HTTPURL: "https://github.com/octocat/hello-world.git", -// SSHURL: "git@github.com:octocat/hello-world.git", -// Link: "https://github.com/octocat/hello-world", -// Private: true, -// Branch: "master", -// Visibility: core.VisibilityPrivate, -// } -// got := convertRepository(from) -// if diff := cmp.Diff(want, got); len(diff) != 0 { -// t.Errorf(diff) -// } -// } - -// func TestConvertVisibility(t *testing.T) { -// tests := []struct { -// r *scm.Repository -// v string -// }{ -// { -// r: &scm.Repository{Private: false}, -// v: core.VisibilityPublic, -// }, -// { -// r: &scm.Repository{Private: true}, -// v: core.VisibilityPrivate, -// }, -// } - -// for i, test := range tests { -// if got, want := convertVisibility(test.r), test.v; got != want { -// t.Errorf("Want visibility %s, got %s for index %d", got, want, i) -// } -// } -// } - -func TestDiff(t *testing.T) { - tests := []struct { - a *core.Repository - b *core.Repository - r bool - }{ - { - a: &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - Private: true, - Branch: "master", - }, - b: &core.Repository{ - Namespace: "octocat", - Name: "hello-world", - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - Private: true, - Branch: "master", - }, - r: false, - }, - { - a: &core.Repository{Namespace: "octocat"}, - b: &core.Repository{Namespace: "spaceghost"}, - r: true, - }, - { - a: &core.Repository{Name: "hello-world"}, - b: &core.Repository{Name: "hola-mundo"}, - r: true, - }, - { - a: &core.Repository{HTTPURL: "https://github.com/octocat/hello-world.git"}, - b: &core.Repository{HTTPURL: "https://github.com/octocat/hola-mundo.git"}, - r: true, - }, - { - a: &core.Repository{SSHURL: "git@github.com:octocat/hello-world.git"}, - b: &core.Repository{SSHURL: "git@github.com:octocat/hola-mundo.git"}, - r: true, - }, - { - a: &core.Repository{Link: "https://github.com/octocat/hello-world"}, - b: &core.Repository{Link: "https://github.com/octocat/hola-mundo"}, - r: true, - }, - { - a: &core.Repository{Private: false}, - b: &core.Repository{Private: true}, - r: true, - }, - { - a: &core.Repository{Branch: "master"}, - b: &core.Repository{Branch: "develop"}, - r: true, - }, - } - - for i, test := range tests { - if got, want := diff(test.a, test.b), test.r; got != want { - t.Errorf("Want diff %v, got %v for index %d", got, want, i) - } - } -} - -func TestMerge(t *testing.T) { - dst := &core.Repository{ - ID: 1, - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - HTTPURL: "https://github.com/octocat/hello-world.git", - SSHURL: "git@github.com:octocat/hello-world.git", - Link: "https://github.com/octocat/hello-world", - Private: true, - Branch: "master", - Visibility: core.VisibilityPublic, - Active: true, - Counter: 99, - Version: 2, - Signer: "DRONESIGNER", - Secret: "DRONESECRET", - } - src := &core.Repository{ - Namespace: "spaceghost", - Name: "hola-mundo", - HTTPURL: "https://github.com/spaceghost/hola-mundo.git", - SSHURL: "git@github.com:spaceghost/hola-mundo.git", - Link: "https://github.com/spaceghost/hola-mundo", - Private: false, - Branch: "develop", - } - merged := &core.Repository{ - ID: 1, - UID: "42", - Namespace: "spaceghost", - Name: "hola-mundo", - Slug: "spaceghost/hola-mundo", - HTTPURL: "https://github.com/spaceghost/hola-mundo.git", - SSHURL: "git@github.com:spaceghost/hola-mundo.git", - Link: "https://github.com/spaceghost/hola-mundo", - Private: false, - Branch: "develop", - Visibility: core.VisibilityPublic, - Active: true, - Counter: 99, - Version: 2, - Signer: "DRONESIGNER", - Secret: "DRONESECRET", - } - merge(dst, src) - if diff := cmp.Diff(merged, dst); len(diff) != 0 { - t.Errorf(diff) - } -} diff --git a/service/token/renew.go b/service/token/renew.go deleted file mode 100644 index 76d8ab03ac..0000000000 --- a/service/token/renew.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package token - -import ( - "context" - "time" - - "github.com/drone/drone/core" - - "github.com/drone/go-scm/scm" - "github.com/drone/go-scm/scm/transport/oauth2" -) - -// expiryDelta determines how earlier a token should be considered -// expired than its actual expiration time. It is used to avoid late -// expirations due to client-server time mismatches. -const expiryDelta = time.Minute - -type renewer struct { - refresh *oauth2.Refresher - users core.UserStore -} - -// Renewer returns a new Renewer. -func Renewer(refresh *oauth2.Refresher, store core.UserStore) core.Renewer { - return &renewer{ - refresh: refresh, - users: store, - } -} - -func (r *renewer) Renew(ctx context.Context, user *core.User, force bool) error { - if r.refresh == nil { - return nil - } - t := &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - Expires: time.Unix(user.Expiry, 0), - } - if expired(t) == false && force == false { - return nil - } - err := r.refresh.Refresh(t) - if err != nil { - return err - } - user.Token = t.Token - user.Refresh = t.Refresh - user.Expiry = t.Expires.Unix() - return r.users.Update(ctx, user) -} - -// expired reports whether the token is expired. -func expired(token *scm.Token) bool { - if len(token.Refresh) == 0 { - return false - } - if token.Expires.IsZero() && len(token.Token) != 0 { - return false - } - return token.Expires.Add(-expiryDelta). - Before(time.Now()) -} diff --git a/service/token/renew_test.go b/service/token/renew_test.go deleted file mode 100644 index 5ea7064789..0000000000 --- a/service/token/renew_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package token diff --git a/service/transfer/transfer.go b/service/transfer/transfer.go deleted file mode 100644 index 31eb5a87aa..0000000000 --- a/service/transfer/transfer.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2020 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package transfer - -import ( - "context" - "runtime/debug" - - "github.com/drone/drone/core" - - "github.com/hashicorp/go-multierror" - "github.com/sirupsen/logrus" -) - -// Transferer handles transfering repository ownership from one -// user to another user account. -type Transferer struct { - Repos core.RepositoryStore - Perms core.PermStore -} - -// New returns a new repository transfer service. -func New(repos core.RepositoryStore, perms core.PermStore) core.Transferer { - return &Transferer{ - Repos: repos, - Perms: perms, - } -} - -// Transfer transfers all repositories owned by the specified user -// to an alternate account with sufficient admin permissions. -func (t *Transferer) Transfer(ctx context.Context, user *core.User) error { - defer func() { - // taking the paranoid approach to recover from - // a panic that should absolutely never happen. - if r := recover(); r != nil { - logrus.Errorf("transferer: unexpected panic: %s", r) - debug.PrintStack() - } - }() - - repos, err := t.Repos.List(ctx, user.ID) - if err != nil { - return err - } - - var result error - for _, repo := range repos { - // only transfer repository ownership if the deactivated - // user owns the repository. - if repo.UserID != user.ID { - continue - } - - members, err := t.Perms.List(ctx, repo.UID) - if err != nil { - result = multierror.Append(result, err) - continue - } - - var admin int64 - for _, member := range members { - // only transfer the repository to an admin user - // that is not equal to the deactivated user. - if repo.UserID == member.UserID { - continue - } - if member.Admin { - admin = member.UserID - break - } - } - - if admin == 0 { - logrus. - WithField("repo.id", repo.ID). - WithField("repo.namespace", repo.Namespace). - WithField("repo.name", repo.Name). - Traceln("repository disabled") - } else { - logrus. - WithField("repo.id", repo.ID). - WithField("repo.namespace", repo.Namespace). - WithField("repo.name", repo.Name). - WithField("old.user.id", repo.UserID). - WithField("new.user.id", admin). - Traceln("repository owner re-assigned") - } - - // if no alternate user was found the repository id - // is reset to the zero value, indicating the repository - // has no owner. - repo.UserID = admin - err = t.Repos.Update(ctx, repo) - if err != nil { - result = multierror.Append(result, err) - } - } - - return result -} diff --git a/service/transfer/transfer_test.go b/service/transfer/transfer_test.go deleted file mode 100644 index e6d8095bae..0000000000 --- a/service/transfer/transfer_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2020 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package transfer - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" -) - -var nocontext = context.Background() - -func TestTransfer(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - ID: 1, - UserID: 2, - UID: "123", - } - mockRepos := []*core.Repository{ - mockRepo, - } - mockCollabs := []*core.Collaborator{ - { - UserID: 1, // do not match non-admin - Admin: false, - }, - { - UserID: 2, // do not match existing owner - Admin: true, - }, - { - UserID: 3, - Admin: true, - }, - } - mockUser := &core.User{ - ID: 2, - } - - checkRepo := func(ctx context.Context, updated *core.Repository) error { - if updated.UserID != 3 { - t.Errorf("Expect repository owner id assigned to user id 3") - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().List(gomock.Any(), mockUser.ID).Return(mockRepos, nil).Times(1) - repos.EXPECT().Update(gomock.Any(), mockRepo).Do(checkRepo).Times(1) - - perms := mock.NewMockPermStore(controller) - perms.EXPECT().List(gomock.Any(), mockRepo.UID).Return(mockCollabs, nil).Times(1) - - r := New( - repos, - perms, - ) - - err := r.Transfer(nocontext, mockUser) - if err != nil { - t.Error(err) - } -} - -func TestTransfer_NoOwner(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockRepo := &core.Repository{ - ID: 1, - UserID: 2, - UID: "123", - } - mockRepos := []*core.Repository{ - mockRepo, - } - mockCollabs := []*core.Collaborator{ - { - UserID: 2, // same user id - Admin: true, - }, - } - mockUser := &core.User{ - ID: 2, - } - - checkRepo := func(ctx context.Context, updated *core.Repository) error { - if updated.UserID != 0 { - t.Errorf("Expect repository owner id reset to zero value") - } - return nil - } - - repos := mock.NewMockRepositoryStore(controller) - repos.EXPECT().List(gomock.Any(), mockUser.ID).Return(mockRepos, nil).Times(1) - repos.EXPECT().Update(gomock.Any(), mockRepo).Do(checkRepo).Times(1) - - perms := mock.NewMockPermStore(controller) - perms.EXPECT().List(gomock.Any(), mockRepo.UID).Return(mockCollabs, nil).Times(1) - - r := New( - repos, - perms, - ) - - err := r.Transfer(nocontext, mockUser) - if err != nil { - t.Error(err) - } -} diff --git a/service/user/user.go b/service/user/user.go deleted file mode 100644 index 9b0efcbb80..0000000000 --- a/service/user/user.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package user - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/go-scm/scm" -) - -type service struct { - client *scm.Client - renew core.Renewer -} - -// New returns a new User service that provides access to -// user data from the source code management system. -func New(client *scm.Client, renew core.Renewer) core.UserService { - return &service{client: client, renew: renew} -} - -func (s *service) Find(ctx context.Context, access, refresh string) (*core.User, error) { - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: access, - Refresh: refresh, - }) - src, _, err := s.client.Users.Find(ctx) - if err != nil { - return nil, err - } - return convert(src), nil -} - -func (s *service) FindLogin(ctx context.Context, user *core.User, login string) (*core.User, error) { - err := s.renew.Renew(ctx, user, false) - if err != nil { - return nil, err - } - - ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ - Token: user.Token, - Refresh: user.Refresh, - }) - src, _, err := s.client.Users.FindLogin(ctx, login) - if err != nil { - return nil, err - } - return convert(src), nil -} - -func convert(src *scm.User) *core.User { - dst := &core.User{ - Login: src.Login, - Email: src.Email, - Avatar: src.Avatar, - } - if !src.Created.IsZero() { - dst.Created = src.Created.Unix() - } - if !src.Updated.IsZero() { - dst.Updated = src.Updated.Unix() - } - return dst -} diff --git a/service/user/user_test.go b/service/user/user_test.go deleted file mode 100644 index 79e8570e10..0000000000 --- a/service/user/user_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package user - -import ( - "context" - "testing" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock/mockscm" - "github.com/drone/go-scm/scm" - "github.com/google/go-cmp/cmp" - - "github.com/golang/mock/gomock" -) - -var noContext = context.Background() - -func TestFind(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - checkToken := func(ctx context.Context) { - got, ok := ctx.Value(scm.TokenKey{}).(*scm.Token) - if !ok { - t.Errorf("Expect token stored in context") - return - } - want := &scm.Token{ - Token: "755bb80e5b", - Refresh: "e08f3fa43e", - } - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } - } - - now := time.Now() - mockUser := &scm.User{ - Login: "octocat", - Email: "octocat@github.com", - Avatar: "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87", - Created: now, - Updated: now, - } - mockUsers := mockscm.NewMockUserService(controller) - mockUsers.EXPECT().Find(gomock.Any()).Do(checkToken).Return(mockUser, nil, nil) - - client := new(scm.Client) - client.Users = mockUsers - - want := &core.User{ - Login: "octocat", - Email: "octocat@github.com", - Avatar: "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87", - Created: now.Unix(), - Updated: now.Unix(), - } - got, err := New(client, nil).Find(noContext, "755bb80e5b", "e08f3fa43e") - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff(got, want); diff != "" { - t.Errorf(diff) - } -} - -func TestFind_Error(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUsers := mockscm.NewMockUserService(controller) - mockUsers.EXPECT().Find(gomock.Any()).Return(nil, nil, scm.ErrNotFound) - - client := new(scm.Client) - client.Users = mockUsers - - got, err := New(client, nil).Find(noContext, "755bb80e5b", "e08f3fa43e") - if err == nil { - t.Errorf("Expect error finding user") - } - if got != nil { - t.Errorf("Expect nil user on error") - } -} diff --git a/session/config.go b/session/config.go deleted file mode 100644 index bb7191e031..0000000000 --- a/session/config.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package session - -import "time" - -// Config provides the session configuration. -type Config struct { - Secure bool - Secret string - Timeout time.Duration -} - -// NewConfig returns a new session configuration. -func NewConfig(secret string, timeout time.Duration, secure bool) Config { - return Config{ - Secure: secure, - Secret: secret, - Timeout: timeout, - } -} diff --git a/session/session.go b/session/session.go deleted file mode 100644 index 86e2d96d57..0000000000 --- a/session/session.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package session - -import ( - "net/http" - "strings" - "time" - - "github.com/drone/drone/core" - - "github.com/dchest/authcookie" -) - -// New returns a new cookie-based session management. -func New(users core.UserStore, config Config) core.Session { - return &session{ - secret: []byte(config.Secret), - secure: config.Secure, - timeout: config.Timeout, - users: users, - } -} - -type session struct { - users core.UserStore - secret []byte - secure bool - timeout time.Duration - - administrator string // administrator account - prometheus string // prometheus account - autoscaler string // autoscaler account -} - -func (s *session) Create(w http.ResponseWriter, user *core.User) error { - cookie := &http.Cookie{ - Name: "_session_", - Path: "/", - MaxAge: 2147483647, - HttpOnly: true, - Secure: s.secure, - Value: authcookie.NewSinceNow( - user.Login, - s.timeout, - s.secret, - ), - } - w.Header().Add("Set-Cookie", cookie.String()+"; SameSite=lax") - return nil -} - -func (s *session) Delete(w http.ResponseWriter) error { - w.Header().Add("Set-Cookie", "_session_=deleted; Path=/; Max-Age=0") - return nil -} - -func (s *session) Get(r *http.Request) (*core.User, error) { - switch { - case isAuthorizationToken(r): - return s.fromToken(r) - case isAuthorizationParameter(r): - return s.fromToken(r) - default: - return s.fromSession(r) - } -} - -func (s *session) fromSession(r *http.Request) (*core.User, error) { - cookie, err := r.Cookie("_session_") - if err != nil { - return nil, nil - } - login := authcookie.Login(cookie.Value, s.secret) - if login == "" { - return nil, nil - } - return s.users.FindLogin(r.Context(), login) -} - -func (s *session) fromToken(r *http.Request) (*core.User, error) { - return s.users.FindToken(r.Context(), - extractToken(r), - ) -} - -func isAuthorizationToken(r *http.Request) bool { - return r.Header.Get("Authorization") != "" -} - -func isAuthorizationParameter(r *http.Request) bool { - return r.FormValue("access_token") != "" -} - -func extractToken(r *http.Request) string { - bearer := r.Header.Get("Authorization") - if bearer == "" { - bearer = r.FormValue("access_token") - } - return strings.TrimPrefix(bearer, "Bearer ") -} diff --git a/session/session_test.go b/session/session_test.go deleted file mode 100644 index 9cada0bda8..0000000000 --- a/session/session_test.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package session - -import ( - "database/sql" - "net/http" - "net/http/httptest" - "regexp" - "testing" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/dchest/authcookie" - "github.com/golang/mock/gomock" -) - -// This test verifies that a user is returned when a valid -// authorization token included in the http.Request access_token -// query parameter. -func TestGet_Token_QueryParam(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - Login: "octocat", - Hash: "ulSxuA0FKjNiOFIchk18NNvC6ygSxdtKjiOAS", - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindToken(gomock.Any(), mockUser.Hash).Return(mockUser, nil) - - session := New(users, NewConfig("correct-horse-battery-staple", time.Hour, false)) - r := httptest.NewRequest("GET", "/?access_token=ulSxuA0FKjNiOFIchk18NNvC6ygSxdtKjiOAS", nil) - user, _ := session.Get(r) - if user != mockUser { - t.Errorf("Want authenticated user") - } -} - -// This test verifies that a user is returned when a valid -// authorization token included in the Authorization header. -func TestGet_Token_Header(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - Login: "octocat", - Hash: "ulSxuA0FKjNiOFIchk18NNvC6ygSxdtKjiOAS", - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindToken(gomock.Any(), mockUser.Hash).Return(mockUser, nil) - - session := New(users, NewConfig("correct-horse-battery-staple", time.Hour, false)) - r := httptest.NewRequest("GET", "/", nil) - r.Header.Set("Authorization", "Bearer ulSxuA0FKjNiOFIchk18NNvC6ygSxdtKjiOAS") - user, _ := session.Get(r) - if user != mockUser { - t.Errorf("Want authenticated user") - } -} - -func TestGet_Token_NoSession(t *testing.T) { - r := httptest.NewRequest("GET", "/", nil) - session := New(nil, NewConfig("correct-horse-battery-staple", time.Hour, false)) - user, _ := session.Get(r) - if user != nil { - t.Errorf("Expect empty session") - } -} - -func TestGet_Token_UserNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindToken(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) - - r := httptest.NewRequest("GET", "/?access_token=ulSxuA0FKjNiOFIchk18NNvC6ygSxdtKjiOAS", nil) - session := New(users, NewConfig("correct-horse-battery-staple", time.Hour, false)) - user, _ := session.Get(r) - if user != nil { - t.Errorf("Expect empty session") - } -} - -func TestGet_Cookie(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUser := &core.User{ - Login: "octocat", - Admin: true, - Hash: "$2a$04$wD3oI7rqUlVy7xNh0B0FqOnNlw0bkVhxCi.XZNi2BTMnqIODIT4Xa", - } - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), gomock.Any()).Return(mockUser, nil) - - secret := "correct-horse-battery-staple" - s := authcookie.New("octocat", time.Now().Add(time.Hour), []byte(secret)) - r := httptest.NewRequest("GET", "/", nil) - r.AddCookie(&http.Cookie{ - Name: "_session_", - Value: s, - }) - session := New(users, Config{Secure: false, Secret: secret, Timeout: time.Hour}) - user, err := session.Get(r) - if err != nil { - t.Error(err) - return - } - if user != mockUser { - t.Errorf("Want authenticated user") - } -} - -func TestGet_Cookie_NoCookie(t *testing.T) { - r := httptest.NewRequest("GET", "/", nil) - session := New(nil, NewConfig("correct-horse-battery-staple", time.Hour, false)) - user, _ := session.Get(r) - if user != nil { - t.Errorf("Expect nil user when no cookie") - } -} - -func TestGet_Cookie_Expired(t *testing.T) { - secret := "correct-horse-battery-staple" - s := authcookie.New("octocat", time.Now().Add(-1*time.Hour), []byte(secret)) - r := httptest.NewRequest("GET", "/", nil) - r.AddCookie(&http.Cookie{ - Name: "_session_", - Value: s, - }) - - session := New(nil, NewConfig("correct-horse-battery-staple", time.Hour, false)) - user, _ := session.Get(r) - if user != nil { - t.Errorf("Expect nil user when no cookie") - } -} - -func TestGet_Cookie_UserNotFound(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - users := mock.NewMockUserStore(controller) - users.EXPECT().FindLogin(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows) - - secret := "correct-horse-battery-staple" - s := authcookie.New("octocat", time.Now().Add(time.Hour), []byte(secret)) - r := httptest.NewRequest("GET", "/", nil) - r.AddCookie(&http.Cookie{ - Name: "_session_", - Value: s, - }) - - session := New(users, Config{Secure: false, Secret: secret, Timeout: time.Hour}) - user, _ := session.Get(r) - if user != nil { - t.Errorf("Expect empty session") - } -} - -func TestDelete(t *testing.T) { - w := httptest.NewRecorder() - - s := new(session) - err := s.Delete(w) - if err != nil { - t.Error(err) - } - - want := "_session_=deleted; Path=/; Max-Age=0" - got := w.Header().Get("Set-Cookie") - if got != want { - t.Errorf("Want header %q, got %q", want, got) - } -} - -func TestCreate(t *testing.T) { - w := httptest.NewRecorder() - - user := &core.User{ - ID: 1, - Login: "octocat", - } - s := &session{ - timeout: time.Minute, - secret: []byte("correct-horse-battery-staple"), - } - err := s.Create(w, user) - if err != nil { - t.Error(err) - } - - // TODO(bradrydzewski) improve this test to check the individual - // header parts, including the session string, to ensure the - // authcookie is set correctly and can be parsed. - - got := w.Header().Get("Set-Cookie") - want := "_session_=(.+); Path=/; Max-Age=2147483647; HttpOnly; SameSite=lax" - matched, err := regexp.MatchString(want, got) - if err != nil { - t.Error(err) - } - if !matched { - t.Error("Unexpected Set-Cookie header value") - } -} diff --git a/session/testdata/mapping.json b/session/testdata/mapping.json deleted file mode 100644 index d755fe63ed..0000000000 --- a/session/testdata/mapping.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "octocat": "this-is-a-test-secret", - "spaceghost": "this-is-an-invalid-secret" -} \ No newline at end of file diff --git a/store/batch/batch.go b/store/batch/batch.go deleted file mode 100644 index 3bfd04e945..0000000000 --- a/store/batch/batch.go +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package batch - -import ( - "context" - "fmt" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new Batcher. -func New(db *db.DB) core.Batcher { - return &batchUpdater{db} -} - -type batchUpdater struct { - db *db.DB -} - -func (b *batchUpdater) Batch(ctx context.Context, user *core.User, batch *core.Batch) error { - return b.db.Update(func(execer db.Execer, binder db.Binder) error { - now := time.Now().Unix() - - // - // the repository list API does not return permissions, which means we have - // no way of knowing if permissions are current or not. We therefore mark all - // permissions stale in the database, so that each one must be individually - // verified at runtime. - // - - stmt := permResetStmt - switch b.db.Driver() { - case db.Postgres: - stmt = permResetStmtPostgres - } - - _, err := execer.Exec(stmt, now, user.ID) - if err != nil { - return fmt.Errorf("Error resetting permissions: %s", err) - } - - for _, repo := range batch.Insert { - - // - // insert repository - // TODO: group inserts in batches of N - // - - stmt := repoInsertIgnoreStmt - switch b.db.Driver() { - case db.Mysql: - stmt = repoInsertIgnoreStmtMysql - case db.Postgres: - stmt = repoInsertIgnoreStmtPostgres - } - - params := repos.ToParams(repo) - stmt, args, err := binder.BindNamed(stmt, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - if err != nil { - return fmt.Errorf("Error inserting repository: %s: %s: %s", repo.Slug, repo.UID, err) - } - - // - // insert permissions - // TODO: group inserts in batches of N - // - - stmt = permInsertIgnoreStmt - switch b.db.Driver() { - case db.Mysql: - stmt = permInsertIgnoreStmtMysql - case db.Postgres: - stmt = permInsertIgnoreStmtPostgres - } - - _, err = execer.Exec(stmt, - user.ID, - repo.UID, - now, - now, - ) - if err != nil { - return fmt.Errorf("Error inserting permissions: %s: %s: %s", repo.Slug, repo.UID, err) - } - } - - // - // update existing repositories - // TODO: group updates in batches of N - // - - for _, repo := range batch.Update { - params := repos.ToParams(repo) - stmt, args, err := binder.BindNamed(repoUpdateRemoteStmt, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - if err != nil { - return fmt.Errorf("Error updating repository: %s: %s: %s", repo.Slug, repo.UID, err) - } - - stmt = permInsertIgnoreStmt - switch b.db.Driver() { - case db.Mysql: - stmt = permInsertIgnoreStmtMysql - case db.Postgres: - stmt = permInsertIgnoreStmtPostgres - } - - _, err = execer.Exec(stmt, - user.ID, - repo.UID, - now, - now, - ) - if err != nil { - return fmt.Errorf("Error inserting permissions: %s: %s: %s", repo.Slug, repo.UID, err) - } - } - - // - // revoke permissions - // TODO: group deletes in batches of N - // - - for _, repo := range batch.Revoke { - stmt := permRevokeStmt - switch b.db.Driver() { - case db.Postgres: - stmt = permRevokeStmtPostgres - } - - _, err = execer.Exec(stmt, user.ID, repo.UID) - if err != nil { - return fmt.Errorf("Error revoking permissions: %s: %s: %s", repo.Slug, repo.UID, err) - } - } - - return nil - }) -} - -const stmtInsertBase = ` -( - repo_uid -,repo_user_id -,repo_namespace -,repo_name -,repo_slug -,repo_scm -,repo_clone_url -,repo_ssh_url -,repo_html_url -,repo_active -,repo_private -,repo_visibility -,repo_branch -,repo_counter -,repo_config -,repo_timeout -,repo_throttle -,repo_trusted -,repo_protected -,repo_no_forks -,repo_no_pulls -,repo_cancel_pulls -,repo_cancel_push -,repo_cancel_running -,repo_synced -,repo_created -,repo_updated -,repo_version -,repo_signer -,repo_secret -) VALUES ( - :repo_uid -,:repo_user_id -,:repo_namespace -,:repo_name -,:repo_slug -,:repo_scm -,:repo_clone_url -,:repo_ssh_url -,:repo_html_url -,:repo_active -,:repo_private -,:repo_visibility -,:repo_branch -,:repo_counter -,:repo_config -,:repo_timeout -,:repo_throttle -,:repo_trusted -,:repo_protected -,:repo_no_forks -,:repo_no_pulls -,:repo_cancel_pulls -,:repo_cancel_push -,:repo_cancel_running -,:repo_synced -,:repo_created -,:repo_updated -,:repo_version -,:repo_signer -,:repo_secret -) -` - -const repoInsertIgnoreStmt = ` -INSERT OR IGNORE INTO repos ` + stmtInsertBase - -const repoInsertIgnoreStmtMysql = ` -INSERT IGNORE INTO repos ` + stmtInsertBase - -const repoInsertIgnoreStmtPostgres = ` -INSERT INTO repos ` + stmtInsertBase + ` ON CONFLICT DO NOTHING` - -const repoUpdateRemoteStmt = ` -UPDATE repos SET - repo_namespace=:repo_namespace -,repo_name=:repo_name -,repo_slug=:repo_slug -,repo_clone_url=:repo_clone_url -,repo_ssh_url=:repo_ssh_url -,repo_html_url=:repo_html_url -,repo_private=:repo_private -,repo_branch=:repo_branch -,repo_updated=:repo_updated -WHERE repo_id=:repo_id -` - -const repoUpdateRemoteStmtPostgres = ` -UPDATE repos SET - repo_namespace=$1 -,repo_name=$2 -,repo_slug=$3 -,repo_clone_url=$4 -,repo_ssh_url=$5 -,repo_html_url=$6 -,repo_private=$7 -,repo_branch=$8 -,repo_updated=$9 -WHERE repo_id=$10 -` - -const permInsertIgnoreStmt = ` -INSERT OR IGNORE INTO perms ( - perm_user_id -,perm_repo_uid -,perm_read -,perm_write -,perm_admin -,perm_synced -,perm_created -,perm_updated -) values ( - ? -,? -,1 -,0 -,0 -,0 -,? -,? -) -` - -const permInsertIgnoreStmtMysql = ` -INSERT IGNORE INTO perms ( - perm_user_id -,perm_repo_uid -,perm_read -,perm_write -,perm_admin -,perm_synced -,perm_created -,perm_updated -) values ( - ? -,? -,1 -,0 -,0 -,0 -,? -,? -) -` - -const permInsertIgnoreStmtPostgres = ` -INSERT INTO perms ( - perm_user_id -,perm_repo_uid -,perm_read -,perm_write -,perm_admin -,perm_synced -,perm_created -,perm_updated -) values ( - $1 -,$2 -,true -,false -,false -,0 -,$3 -,$4 -) ON CONFLICT DO NOTHING -` - -// this resets the synced date indicating that -// the system should refresh the permissions next -// time the user attempts to access the resource -const permResetStmt = ` -UPDATE perms SET - perm_updated = ? -,perm_synced = 0 -WHERE perm_user_id = ? -` - -const permResetStmtPostgres = ` -UPDATE perms SET - perm_updated = $1 -,perm_synced = 0 -WHERE perm_user_id = $2 -` - -const permRevokeStmt = ` -DELETE FROM perms -WHERE perm_user_id = ? -AND perm_repo_uid = ? -` - -const permRevokeStmtPostgres = ` -DELETE FROM perms -WHERE perm_user_id = $1 -AND perm_repo_uid = $2 -` diff --git a/store/batch/batch_test.go b/store/batch/batch_test.go deleted file mode 100644 index 45d731f186..0000000000 --- a/store/batch/batch_test.go +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package batch - -import ( - "context" - "database/sql" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/perm" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/db/dbtest" - "github.com/drone/drone/store/shared/encrypt" - "github.com/drone/drone/store/user" -) - -var noContext = context.TODO() - -func TestBatch(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - batcher := New(conn).(*batchUpdater) - repos := repos.New(conn) - perms := perm.New(conn) - - user, err := seedUser(batcher.db) - if err != nil { - t.Error(err) - } - - t.Run("Insert", testBatchInsert(batcher, repos, perms, user)) - t.Run("Update", testBatchUpdate(batcher, repos, perms, user)) - t.Run("Delete", testBatchDelete(batcher, repos, perms, user)) - t.Run("DuplicateID", testBatchDuplicateID(batcher, repos, perms, user)) - t.Run("DuplicateSlug", testBatchDuplicateSlug(batcher, repos, perms, user)) - t.Run("DuplicateRename", testBatchDuplicateRename(batcher, repos, perms, user)) -} - -func testBatchInsert( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - batch := &core.Batch{ - Insert: []*core.Repository{ - { - UserID: 1, - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Private: false, - Visibility: "public", - }, - }, - } - err := batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - - repo, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - _, err = perms.Find(noContext, repo.UID, user.ID) - if err != nil { - t.Errorf("Want permissions, got error %q", err) - } - } -} - -func testBatchUpdate( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - before, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - batch := &core.Batch{ - Update: []*core.Repository{ - { - ID: before.ID, - UserID: 1, - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Private: true, - }, - }, - } - - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - - after, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - if got, want := after.Private, true; got != want { - t.Errorf("Want repository Private %v, got %v", want, got) - } - } -} - -func testBatchDelete( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - repo, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - _, err = perms.Find(noContext, repo.UID, user.ID) - if err != nil { - t.Errorf("Want permissions, got error %q", err) - } - - batch := &core.Batch{ - Revoke: []*core.Repository{ - { - ID: repo.ID, - UserID: 1, - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Private: true, - }, - }, - } - - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - - _, err = perms.Find(noContext, repo.UID, user.ID) - if err != sql.ErrNoRows { - t.Errorf("Want sql.ErrNoRows got %v", err) - } - } -} - -func testBatchDuplicateID( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - before, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - batch := &core.Batch{ - Insert: []*core.Repository{ - { - ID: 0, - UserID: 1, - UID: "43", // Updated ID - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - }, - { - ID: 0, - UserID: 1, - UID: "43", // Updated ID - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - }, - { - ID: 0, - UserID: 1, - UID: "64778136", - Namespace: "octocat", - Name: "linguist", - Slug: "octocat/linguist", - }, - }, - Update: []*core.Repository{ - { - ID: before.ID, - UserID: 1, - UID: "44", // Updated ID - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Private: true, - }, - }, - } - - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - - added, err := repos.FindName(noContext, "octocat", "linguist") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - if got, want := added.UID, "64778136"; got != want { - t.Errorf("Want added repository UID %v, got %v", want, got) - } - } -} - -// the purpose of this unit test is to understand what happens -// when a repository is deleted, re-created with the same name, -// but has a different unique identifier. -func testBatchDuplicateSlug( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - _, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - batch := &core.Batch{ - Insert: []*core.Repository{ - { - ID: 0, - UserID: 1, - UID: "99", // Updated ID - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - }, - }, - } - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - } -} - -// the purpose of this unit test is to understand what happens -// when a repository is deleted, re-created with a new name, and -// then updated back to the old name. -// -// TODO(bradrydzewski) for sqlite consider UPDATE OR REPLACE. -// TODO(bradrydzewski) for mysql consider UPDATE IGNORE. -// TODO(bradrydzewski) consider breaking rename into a separate set of logic that checks for existing records. -func testBatchDuplicateRename( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - batch := &core.Batch{ - Insert: []*core.Repository{ - { - ID: 0, - UserID: 1, - UID: "200", - Namespace: "octocat", - Name: "test-1", - Slug: "octocat/test-1", - }, - { - ID: 0, - UserID: 1, - UID: "201", - Namespace: "octocat", - Name: "test-2", - Slug: "octocat/test-2", - }, - }, - } - err := batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - return - } - - before, err := repos.FindName(noContext, "octocat", "test-2") - if err != nil { - t.Errorf("Want repository, got error %q", err) - return - } - before.Name = "test-1" - before.Slug = "octocat/test-1" - - batch = &core.Batch{ - Update: []*core.Repository{before}, - } - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Skip(err) - } - } -} - -func seedUser(db *db.DB) (*core.User, error) { - enc, _ := encrypt.New("") - out := &core.User{Login: "octocat"} - err := user.New(db, enc).Create(noContext, out) - return out, err -} diff --git a/store/batch2/batch.go b/store/batch2/batch.go deleted file mode 100644 index e51a61d1b6..0000000000 --- a/store/batch2/batch.go +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package batch2 - -import ( - "context" - "fmt" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new Batcher. -func New(db *db.DB) core.Batcher { - return &batchUpdater{db} -} - -type batchUpdater struct { - db *db.DB -} - -func (b *batchUpdater) Batch(ctx context.Context, user *core.User, batch *core.Batch) error { - return b.db.Update(func(execer db.Execer, binder db.Binder) error { - now := time.Now().Unix() - - // - // the repository list API does not return permissions, which means we have - // no way of knowing if permissions are current or not. We therefore mark all - // permissions stale in the database, so that each one must be individually - // verified at runtime. - // - - stmt := permResetStmt - switch b.db.Driver() { - case db.Postgres: - stmt = permResetStmtPostgres - } - - _, err := execer.Exec(stmt, now, user.ID) - if err != nil { - return fmt.Errorf("batch: cannot reset permissions: %s", err) - } - - // if the repository exists with the same name, - // but a different unique identifier, attempt to - // delete the previous entry. - var insert []*core.Repository - var update []*core.Repository - for _, repo := range append(batch.Insert, batch.Update...) { - params := repos.ToParams(repo) - stmt, args, err := binder.BindNamed(repoDeleteDeleted, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return fmt.Errorf("batch: cannot remove duplicate repository: %s: %s: %s", repo.Slug, repo.UID, err) - } - rows, _ := res.RowsAffected() - if rows > 0 { - insert = append(insert, repo) - } else if repo.ID > 0 { - update = append(update, repo) - } else { - insert = append(insert, repo) - } - } - - for _, repo := range insert { - - // - // insert repository - // TODO: group inserts in batches of N - // - - stmt := repoInsertIgnoreStmt - switch b.db.Driver() { - case db.Mysql: - stmt = repoInsertIgnoreStmtMysql - case db.Postgres: - stmt = repoInsertIgnoreStmtPostgres - } - - params := repos.ToParams(repo) - stmt, args, err := binder.BindNamed(stmt, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - if err != nil { - return fmt.Errorf("batch: cannot insert repository: %s: %s: %s", repo.Slug, repo.UID, err) - } - - // - // insert permissions - // TODO: group inserts in batches of N - // - - stmt = permInsertIgnoreStmt - switch b.db.Driver() { - case db.Mysql: - stmt = permInsertIgnoreStmtMysql - case db.Postgres: - stmt = permInsertIgnoreStmtPostgres - } - - _, err = execer.Exec(stmt, - user.ID, - repo.UID, - now, - now, - ) - if err != nil { - return fmt.Errorf("batch: cannot insert permissions: %s: %s: %s", repo.Slug, repo.UID, err) - } - } - - // - // update existing repositories - // TODO: group updates in batches of N - // - - for _, repo := range update { - params := repos.ToParams(repo) - - // // if the repository exists with the same name, - // // but a different unique identifier, attempt to - // // delete the previous entry. - // stmt, args, err := binder.BindNamed(repoDeleteDeleted, params) - // if err != nil { - // return err - // } - // res, err := execer.Exec(stmt, args...) - // if err != nil { - // return fmt.Errorf("batch: cannot remove duplicate repository: %s: %s: %s", repo.Slug, repo.UID, err) - // } - // rows, _ := res.RowsAffected() - // if rows > 0 { - // stmt := repoInsertIgnoreStmt - // switch b.db.Driver() { - // case db.Mysql: - // stmt = repoInsertIgnoreStmtMysql - // case db.Postgres: - // stmt = repoInsertIgnoreStmtPostgres - // } - - // params := repos.ToParams(repo) - // stmt, args, err := binder.BindNamed(stmt, params) - // if err != nil { - // return err - // } - // _, err = execer.Exec(stmt, args...) - // if err != nil { - // return fmt.Errorf("batch: cannot insert repository: %s: %s: %s", repo.Slug, repo.UID, err) - // } - // } else { - stmt, args, err := binder.BindNamed(repoUpdateRemoteStmt, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - if err != nil { - return fmt.Errorf("batch: cannot update repository: %s: %s: %s", repo.Slug, repo.UID, err) - } - // } - - stmt = permInsertIgnoreStmt - switch b.db.Driver() { - case db.Mysql: - stmt = permInsertIgnoreStmtMysql - case db.Postgres: - stmt = permInsertIgnoreStmtPostgres - } - - _, err = execer.Exec(stmt, - user.ID, - repo.UID, - now, - now, - ) - if err != nil { - return fmt.Errorf("batch: cannot insert permissions: %s: %s: %s", repo.Slug, repo.UID, err) - } - } - - // - // revoke permissions - // TODO: group deletes in batches of N - // - - for _, repo := range batch.Revoke { - stmt := permRevokeStmt - switch b.db.Driver() { - case db.Postgres: - stmt = permRevokeStmtPostgres - } - - _, err = execer.Exec(stmt, user.ID, repo.UID) - if err != nil { - return fmt.Errorf("batch: cannot revoking permissions: %s: %s: %s", repo.Slug, repo.UID, err) - } - } - - return nil - }) -} - -const stmtInsertBase = ` -( - repo_uid -,repo_user_id -,repo_namespace -,repo_name -,repo_slug -,repo_scm -,repo_clone_url -,repo_ssh_url -,repo_html_url -,repo_active -,repo_private -,repo_visibility -,repo_branch -,repo_counter -,repo_config -,repo_timeout -,repo_throttle -,repo_trusted -,repo_protected -,repo_no_forks -,repo_no_pulls -,repo_cancel_pulls -,repo_cancel_push -,repo_cancel_running -,repo_synced -,repo_created -,repo_updated -,repo_version -,repo_signer -,repo_secret -) VALUES ( - :repo_uid -,:repo_user_id -,:repo_namespace -,:repo_name -,:repo_slug -,:repo_scm -,:repo_clone_url -,:repo_ssh_url -,:repo_html_url -,:repo_active -,:repo_private -,:repo_visibility -,:repo_branch -,:repo_counter -,:repo_config -,:repo_timeout -,:repo_throttle -,:repo_trusted -,:repo_protected -,:repo_no_forks -,:repo_no_pulls -,:repo_cancel_pulls -,:repo_cancel_push -,:repo_cancel_running -,:repo_synced -,:repo_created -,:repo_updated -,:repo_version -,:repo_signer -,:repo_secret -) -` - -const repoInsertIgnoreStmt = ` -INSERT OR IGNORE INTO repos ` + stmtInsertBase - -const repoInsertIgnoreStmtMysql = ` -INSERT IGNORE INTO repos ` + stmtInsertBase - -const repoInsertIgnoreStmtPostgres = ` -INSERT INTO repos ` + stmtInsertBase + ` ON CONFLICT DO NOTHING` - -const repoUpdateRemoteStmt = ` -UPDATE repos SET - repo_namespace=:repo_namespace -,repo_name=:repo_name -,repo_slug=:repo_slug -,repo_clone_url=:repo_clone_url -,repo_ssh_url=:repo_ssh_url -,repo_html_url=:repo_html_url -,repo_private=:repo_private -,repo_branch=:repo_branch -,repo_updated=:repo_updated -WHERE repo_id=:repo_id -` - -const repoUpdateRemoteStmtPostgres = ` -UPDATE repos SET - repo_namespace=$1 -,repo_name=$2 -,repo_slug=$3 -,repo_clone_url=$4 -,repo_ssh_url=$5 -,repo_html_url=$6 -,repo_private=$7 -,repo_branch=$8 -,repo_updated=$9 -WHERE repo_id=$10 -` - -const permInsertIgnoreStmt = ` -INSERT OR IGNORE INTO perms ( - perm_user_id -,perm_repo_uid -,perm_read -,perm_write -,perm_admin -,perm_synced -,perm_created -,perm_updated -) values ( - ? -,? -,1 -,0 -,0 -,0 -,? -,? -) -` - -const permInsertIgnoreStmtMysql = ` -INSERT IGNORE INTO perms ( - perm_user_id -,perm_repo_uid -,perm_read -,perm_write -,perm_admin -,perm_synced -,perm_created -,perm_updated -) values ( - ? -,? -,1 -,0 -,0 -,0 -,? -,? -) -` - -const permInsertIgnoreStmtPostgres = ` -INSERT INTO perms ( - perm_user_id -,perm_repo_uid -,perm_read -,perm_write -,perm_admin -,perm_synced -,perm_created -,perm_updated -) values ( - $1 -,$2 -,true -,false -,false -,0 -,$3 -,$4 -) ON CONFLICT DO NOTHING -` - -// this statement deletes a repository that was -// deleted in version control and then re-created -// with the same name (and thus has a different -// unique identifier) -const repoDeleteDeleted = ` -DELETE FROM repos -WHERE repo_slug = :repo_slug - AND repo_uid != :repo_uid -` - -// this resets the synced date indicating that -// the system should refresh the permissions next -// time the user attempts to access the resource -const permResetStmt = ` -UPDATE perms SET - perm_updated = ? -,perm_synced = 0 -WHERE perm_user_id = ? -` - -const permResetStmtPostgres = ` -UPDATE perms SET - perm_updated = $1 -,perm_synced = 0 -WHERE perm_user_id = $2 -` - -const permRevokeStmt = ` -DELETE FROM perms -WHERE perm_user_id = ? -AND perm_repo_uid = ? -` - -const permRevokeStmtPostgres = ` -DELETE FROM perms -WHERE perm_user_id = $1 -AND perm_repo_uid = $2 -` diff --git a/store/batch2/batch_test.go b/store/batch2/batch_test.go deleted file mode 100644 index 72a8bc3bb7..0000000000 --- a/store/batch2/batch_test.go +++ /dev/null @@ -1,397 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package batch2 - -import ( - "context" - "database/sql" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/perm" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/db/dbtest" - "github.com/drone/drone/store/shared/encrypt" - "github.com/drone/drone/store/user" -) - -var noContext = context.TODO() - -func TestBatch(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - batcher := New(conn).(*batchUpdater) - repos := repos.New(conn) - perms := perm.New(conn) - - user, err := seedUser(batcher.db) - if err != nil { - t.Error(err) - } - - t.Run("Insert", testBatchInsert(batcher, repos, perms, user)) - t.Run("Update", testBatchUpdate(batcher, repos, perms, user)) - t.Run("Delete", testBatchDelete(batcher, repos, perms, user)) - t.Run("DuplicateID", testBatchDuplicateID(batcher, repos, perms, user)) - t.Run("DuplicateSlug", testBatchDuplicateSlug(batcher, repos, perms, user)) - t.Run("DuplicateRename", testBatchDuplicateRename(batcher, repos, perms, user)) - t.Run("DuplicateRecreateRename", testBatchDuplicateRecreateRename(batcher, repos, perms, user)) - -} - -func testBatchInsert( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - batch := &core.Batch{ - Insert: []*core.Repository{ - { - UserID: 1, - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Private: false, - Visibility: "public", - }, - }, - } - err := batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - - repo, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - _, err = perms.Find(noContext, repo.UID, user.ID) - if err != nil { - t.Errorf("Want permissions, got error %q", err) - } - } -} - -func testBatchUpdate( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - before, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - batch := &core.Batch{ - Update: []*core.Repository{ - { - ID: before.ID, - UserID: 1, - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Private: true, - }, - }, - } - - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - - after, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - if got, want := after.Private, true; got != want { - t.Errorf("Want repository Private %v, got %v", want, got) - } - } -} - -func testBatchDelete( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - repo, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - _, err = perms.Find(noContext, repo.UID, user.ID) - if err != nil { - t.Errorf("Want permissions, got error %q", err) - } - - batch := &core.Batch{ - Revoke: []*core.Repository{ - { - ID: repo.ID, - UserID: 1, - UID: "42", - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Private: true, - }, - }, - } - - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - - _, err = perms.Find(noContext, repo.UID, user.ID) - if err != sql.ErrNoRows { - t.Errorf("Want sql.ErrNoRows got %v", err) - } - } -} - -func testBatchDuplicateID( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - before, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - } - - batchDuplicate := &core.Batch{ - Insert: []*core.Repository{ - { - ID: 0, - UserID: 1, - UID: "43", // Updated ID - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - }, - { - ID: 0, - UserID: 1, - UID: "43", // Updated ID - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - }, - }, - } - - err = batcher.Batch(noContext, user, batchDuplicate) - if err != nil { - t.Error(err) - return - } - - batch := &core.Batch{ - Insert: []*core.Repository{ - { - ID: 0, - UserID: 1, - UID: "64778136", - Namespace: "octocat", - Name: "linguist", - Slug: "octocat/linguist", - }, - }, - Update: []*core.Repository{ - { - ID: before.ID, - UserID: 1, - UID: "44", // Updated ID - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - Private: true, - }, - }, - } - - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - return - } - - added, err := repos.FindName(noContext, "octocat", "linguist") - if err != nil { - t.Errorf("Want inserted repository, got error %q", err) - } - - if got, want := added.UID, "64778136"; got != want { - t.Errorf("Want inserted repository UID %v, got %v", want, got) - } - - renamed, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want renamed repository, got error %q", err) - } - - if got, want := renamed.UID, "44"; got != want { - t.Errorf("Want renamed repository UID %v, got %v", want, got) - } - } -} - -// the purpose of this unit test is to understand what happens -// when a repository is deleted, re-created with the same name, -// but has a different unique identifier. -func testBatchDuplicateSlug( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - _, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - return - } - - batch := &core.Batch{ - Insert: []*core.Repository{ - { - ID: 0, - UserID: 1, - UID: "99", // Updated ID - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - }, - }, - } - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - } -} - -// the purpose of this unit test is to understand what happens -// when a repository is deleted, re-created with a different name, -// renamed to the original name, but has a different unique identifier. -func testBatchDuplicateRecreateRename( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - _, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Errorf("Want repository, got error %q", err) - return - } - - batch := &core.Batch{ - Update: []*core.Repository{ - { - ID: 0, - UserID: 1, - UID: "8888", // Updated ID - Namespace: "octocat", - Name: "hello-world", - Slug: "octocat/hello-world", - }, - }, - } - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - } - } -} - -// the purpose of this unit test is to understand what happens -// when a repository is deleted, re-created with a new name, and -// then updated back to the old name. -// -// TODO(bradrydzewski) for sqlite consider UPDATE OR REPLACE. -// TODO(bradrydzewski) for mysql consider UPDATE IGNORE. -// TODO(bradrydzewski) consider breaking rename into a separate set of logic that checks for existing records. -func testBatchDuplicateRename( - batcher core.Batcher, - repos core.RepositoryStore, - perms core.PermStore, - user *core.User, -) func(t *testing.T) { - return func(t *testing.T) { - batch := &core.Batch{ - Insert: []*core.Repository{ - { - ID: 0, - UserID: 1, - UID: "200", - Namespace: "octocat", - Name: "test-1", - Slug: "octocat/test-1", - }, - { - ID: 0, - UserID: 1, - UID: "201", - Namespace: "octocat", - Name: "test-2", - Slug: "octocat/test-2", - }, - }, - } - err := batcher.Batch(noContext, user, batch) - if err != nil { - t.Error(err) - return - } - - before, err := repos.FindName(noContext, "octocat", "test-2") - if err != nil { - t.Errorf("Want repository, got error %q", err) - return - } - before.Name = "test-1" - before.Slug = "octocat/test-1" - - batch = &core.Batch{ - Update: []*core.Repository{before}, - } - err = batcher.Batch(noContext, user, batch) - if err != nil { - t.Skip(err) - } - } -} - -func seedUser(db *db.DB) (*core.User, error) { - enc, _ := encrypt.New("") - out := &core.User{Login: "octocat"} - err := user.New(db, enc).Create(noContext, out) - return out, err -} diff --git a/store/build/build.go b/store/build/build.go deleted file mode 100644 index 0f4869ba5b..0000000000 --- a/store/build/build.go +++ /dev/null @@ -1,843 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package build - -import ( - "context" - "fmt" - "regexp" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// regular expression to extract the pull request number -// from the git ref (e.g. refs/pulls/{d}/head) -var pr = regexp.MustCompile("\\d+") - -// New returns a new Buildcore. -func New(db *db.DB) core.BuildStore { - return &buildStore{db} -} - -type buildStore struct { - db *db.DB -} - -// Find returns a build from the datacore. -func (s *buildStore) Find(ctx context.Context, id int64) (*core.Build, error) { - out := &core.Build{ID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -// FindNumber returns a build from the datastore by build number. -func (s *buildStore) FindNumber(ctx context.Context, repo, number int64) (*core.Build, error) { - out := &core.Build{Number: number, RepoID: repo} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryNumber, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -// FindLast returns the last build from the datastore by ref. -func (s *buildStore) FindRef(ctx context.Context, repo int64, ref string) (*core.Build, error) { - out := &core.Build{RepoID: repo, Ref: ref} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryRowRef, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -// List returns a list of builds from the datastore by repository id. -func (s *buildStore) List(ctx context.Context, repo int64, limit, offset int) ([]*core.Build, error) { - var out []*core.Build - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{ - "build_repo_id": repo, - "limit": limit, - "offset": offset, - } - stmt, args, err := binder.BindNamed(queryRepo, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -// ListRef returns a list of builds from the datastore by ref. -func (s *buildStore) ListRef(ctx context.Context, repo int64, ref string, limit, offset int) ([]*core.Build, error) { - var out []*core.Build - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{ - "build_repo_id": repo, - "build_ref": ref, - "limit": limit, - "offset": offset, - } - stmt, args, err := binder.BindNamed(queryRef, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -// LatestBranches returns a list of the latest build by branch. -func (s *buildStore) LatestBranches(ctx context.Context, repo int64) ([]*core.Build, error) { - return s.latest(ctx, repo, "branch") -} - -// LatestPulls returns a list of the latest builds by pull requests. -func (s *buildStore) LatestPulls(ctx context.Context, repo int64) ([]*core.Build, error) { - return s.latest(ctx, repo, "pull_request") -} - -// LatestDeploys returns a list of the latest builds by target deploy. -func (s *buildStore) LatestDeploys(ctx context.Context, repo int64) ([]*core.Build, error) { - return s.latest(ctx, repo, "deployment") -} - -func (s *buildStore) latest(ctx context.Context, repo int64, event string) ([]*core.Build, error) { - var out []*core.Build - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{ - "latest_repo_id": repo, - "latest_type": event, - } - stmt, args, err := binder.BindNamed(queryLatestList, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -// Pending returns a list of pending builds from the datastore by repository id. -func (s *buildStore) Pending(ctx context.Context) ([]*core.Build, error) { - var out []*core.Build - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - rows, err := queryer.Query(queryPending) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -// Running returns a list of running builds from the datastore by repository id. -func (s *buildStore) Running(ctx context.Context) ([]*core.Build, error) { - var out []*core.Build - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - rows, err := queryer.Query(queryRunning) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -// Create persists a build to the datacore. -func (s *buildStore) Create(ctx context.Context, build *core.Build, stages []*core.Stage) error { - var err error - switch s.db.Driver() { - case db.Postgres: - err = s.createPostgres(ctx, build, stages) - default: - err = s.create(ctx, build, stages) - } - if err != nil { - return err - } - var event, name string - switch build.Event { - case core.EventPullRequest: - event = "pull_request" - name = pr.FindString(build.Ref) - case core.EventPush: - event = "branch" - name = build.Target - case core.EventPromote, core.EventRollback: - event = "deployment" - name = build.Deploy - default: - return nil - } - return s.index(ctx, build.ID, build.RepoID, event, name) -} - -func (s *buildStore) create(ctx context.Context, build *core.Build, stages []*core.Stage) error { - build.Version = 1 - return s.db.Update(func(execer db.Execer, binder db.Binder) error { - params := toParams(build) - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - build.ID, err = res.LastInsertId() - if err != nil { - return err - } - - for _, stage := range stages { - stage.Version = 1 - stage.BuildID = build.ID - params := toStageParams(stage) - stmt, args, err := binder.BindNamed(stmtStageInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - stage.ID, err = res.LastInsertId() - } - return err - }) -} - -func (s *buildStore) createPostgres(ctx context.Context, build *core.Build, stages []*core.Stage) error { - build.Version = 1 - return s.db.Update(func(execer db.Execer, binder db.Binder) error { - params := toParams(build) - stmt, args, err := binder.BindNamed(stmtInsertPg, params) - if err != nil { - return err - } - err = execer.QueryRow(stmt, args...).Scan(&build.ID) - if err != nil { - return err - } - - for _, stage := range stages { - stage.Version = 1 - stage.BuildID = build.ID - params := toStageParams(stage) - stmt, args, err := binder.BindNamed(stmtStageInsertPg, params) - if err != nil { - return err - } - err = execer.QueryRow(stmt, args...).Scan(&stage.ID) - if err != nil { - return err - } - } - return err - }) -} - -// Update updates a build in the datacore. -func (s *buildStore) Update(ctx context.Context, build *core.Build) error { - versionNew := build.Version + 1 - versionOld := build.Version - - err := s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(build) - params["build_version_old"] = versionOld - params["build_version_new"] = versionNew - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - effected, err := res.RowsAffected() - if err != nil { - return err - } - if effected == 0 { - return db.ErrOptimisticLock - } - return nil - }) - if err == nil { - build.Version = versionNew - } - return err -} - -func (s *buildStore) index(ctx context.Context, build, repo int64, event, name string) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := map[string]interface{}{ - "latest_repo_id": repo, - "latest_build_id": build, - "latest_type": event, - "latest_name": name, - "latest_created": time.Now().Unix(), - "latest_updated": time.Now().Unix(), - "latest_deleted": time.Now().Unix(), - } - stmtInsert := stmtInsertLatest - switch s.db.Driver() { - case db.Postgres: - stmtInsert = stmtInsertLatestPg - case db.Mysql: - stmtInsert = stmtInsertLatestMysql - } - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -// Delete deletes a build from the datacore. -func (s *buildStore) Delete(ctx context.Context, build *core.Build) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(build) - stmt, args, err := binder.BindNamed(stmtDelete, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -// DeletePull deletes a pull request index from the datastore. -func (s *buildStore) DeletePull(ctx context.Context, repo int64, number int) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := map[string]interface{}{ - "latest_repo_id": repo, - "latest_name": fmt.Sprint(number), - "latest_type": "pull_request", - } - stmt, args, err := binder.BindNamed(stmtDeleteLatest, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -// DeleteBranch deletes a branch index from the datastore. -func (s *buildStore) DeleteBranch(ctx context.Context, repo int64, branch string) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := map[string]interface{}{ - "latest_repo_id": repo, - "latest_name": branch, - "latest_type": "branch", - } - stmt, args, err := binder.BindNamed(stmtDeleteLatest, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -// DeleteDeploy deletes a deploy index from the datastore. -func (s *buildStore) DeleteDeploy(ctx context.Context, repo int64, environment string) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := map[string]interface{}{ - "latest_repo_id": repo, - "latest_name": environment, - "latest_type": "deployment", - } - stmt, args, err := binder.BindNamed(stmtDeleteLatest, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -// Purge deletes builds from the database where the build number is less than n. -func (s *buildStore) Purge(ctx context.Context, repo, number int64) error { - build := &core.Build{ - RepoID: repo, - Number: number, - } - stageErr := s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(build) - stmt, args, err := binder.BindNamed(stmtPurge, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) - if stageErr != nil { - return stageErr - } - if s.db.Driver() == db.Postgres || s.db.Driver() == db.Mysql { - // purge orphaned stages - err := s.db.Update(func(execer db.Execer, binder db.Binder) error { - _, err := execer.Exec(stmtStagePurge) - return err - }) - if err != nil { - return err - } - // purge orphaned steps - err = s.db.Update(func(execer db.Execer, binder db.Binder) error { - _, err := execer.Exec(stmtStepPurge) - return err - }) - return err - } - return nil -} - -// Count returns a count of builds. -func (s *buildStore) Count(ctx context.Context) (i int64, err error) { - err = s.db.View(func(queryer db.Queryer, binder db.Binder) error { - return queryer.QueryRow(queryCount).Scan(&i) - }) - return -} - -const queryCount = ` -SELECT COUNT(*) -FROM builds -` - -const queryBase = ` -SELECT - build_id -,build_repo_id -,build_trigger -,build_number -,build_parent -,build_status -,build_error -,build_event -,build_action -,build_link -,build_timestamp -,build_title -,build_message -,build_before -,build_after -,build_ref -,build_source_repo -,build_source -,build_target -,build_author -,build_author_name -,build_author_email -,build_author_avatar -,build_sender -,build_params -,build_cron -,build_deploy -,build_deploy_id -,build_debug -,build_started -,build_finished -,build_created -,build_updated -,build_version -` - -const queryKey = queryBase + ` -FROM builds -WHERE build_id = :build_id -` - -const queryNumber = queryBase + ` -FROM builds -WHERE build_repo_id = :build_repo_id - AND build_number = :build_number -` - -const queryRef = queryBase + ` -FROM builds -WHERE build_repo_id = :build_repo_id - AND build_ref = :build_ref -ORDER BY build_id DESC -LIMIT :limit OFFSET :offset -` - -const queryRowRef = queryBase + ` -FROM builds -WHERE build_repo_id = :build_repo_id - AND build_ref = :build_ref -ORDER BY build_id DESC -LIMIT 1 -` - -const queryRepo = queryBase + ` -FROM builds -WHERE build_repo_id = :build_repo_id -ORDER BY build_id DESC -LIMIT :limit OFFSET :offset -` - -const queryPending = queryBase + ` -FROM builds -WHERE EXISTS ( - SELECT stage_id - FROM stages - WHERE stages.stage_build_id = builds.build_id - AND stages.stage_status = 'pending' -) -ORDER BY build_id ASC -` - -const queryRunning = queryBase + ` -FROM builds -WHERE EXISTS ( - SELECT stage_id - FROM stages - WHERE stages.stage_build_id = builds.build_id - AND stages.stage_status = 'running' -) -ORDER BY build_id ASC -` - -// const queryRunningOLD = queryBase + ` -// FROM builds -// WHERE build_status = 'running' -// ORDER BY build_id ASC -// ` - -const queryAll = queryBase + ` -FROM builds -WHERE build_id > :build_id -LIMIT :limit OFFSET :offset -` - -const stmtUpdate = ` -UPDATE builds SET - build_parent = :build_parent -,build_status = :build_status -,build_error = :build_error -,build_event = :build_event -,build_action = :build_action -,build_link = :build_link -,build_timestamp = :build_timestamp -,build_title = :build_title -,build_message = :build_message -,build_before = :build_before -,build_after = :build_after -,build_ref = :build_ref -,build_source_repo = :build_source_repo -,build_source = :build_source -,build_target = :build_target -,build_author = :build_author -,build_author_name = :build_author_name -,build_author_email = :build_author_email -,build_author_avatar = :build_author_avatar -,build_sender = :build_sender -,build_params = :build_params -,build_cron = :build_cron -,build_deploy = :build_deploy -,build_started = :build_started -,build_finished = :build_finished -,build_updated = :build_updated -,build_version = :build_version_new -WHERE build_id = :build_id - AND build_version = :build_version_old -` - -const stmtInsert = ` -INSERT INTO builds ( - build_repo_id -,build_trigger -,build_number -,build_parent -,build_status -,build_error -,build_event -,build_action -,build_link -,build_timestamp -,build_title -,build_message -,build_before -,build_after -,build_ref -,build_source_repo -,build_source -,build_target -,build_author -,build_author_name -,build_author_email -,build_author_avatar -,build_sender -,build_params -,build_cron -,build_deploy -,build_deploy_id -,build_debug -,build_started -,build_finished -,build_created -,build_updated -,build_version -) VALUES ( - :build_repo_id -,:build_trigger -,:build_number -,:build_parent -,:build_status -,:build_error -,:build_event -,:build_action -,:build_link -,:build_timestamp -,:build_title -,:build_message -,:build_before -,:build_after -,:build_ref -,:build_source_repo -,:build_source -,:build_target -,:build_author -,:build_author_name -,:build_author_email -,:build_author_avatar -,:build_sender -,:build_params -,:build_cron -,:build_deploy -,:build_deploy_id -,:build_debug -,:build_started -,:build_finished -,:build_created -,:build_updated -,:build_version -) -` - -const stmtInsertPg = stmtInsert + ` -RETURNING build_id -` - -const stmtStageInsert = ` -INSERT INTO stages ( - stage_repo_id -,stage_build_id -,stage_number -,stage_name -,stage_kind -,stage_type -,stage_status -,stage_error -,stage_errignore -,stage_exit_code -,stage_limit -,stage_limit_repo -,stage_os -,stage_arch -,stage_variant -,stage_kernel -,stage_machine -,stage_started -,stage_stopped -,stage_created -,stage_updated -,stage_version -,stage_on_success -,stage_on_failure -,stage_depends_on -,stage_labels -) VALUES ( - :stage_repo_id -,:stage_build_id -,:stage_number -,:stage_name -,:stage_kind -,:stage_type -,:stage_status -,:stage_error -,:stage_errignore -,:stage_exit_code -,:stage_limit -,:stage_limit_repo -,:stage_os -,:stage_arch -,:stage_variant -,:stage_kernel -,:stage_machine -,:stage_started -,:stage_stopped -,:stage_created -,:stage_updated -,:stage_version -,:stage_on_success -,:stage_on_failure -,:stage_depends_on -,:stage_labels -) -` - -const stmtStageInsertPg = stmtStageInsert + ` -RETURNING stage_id -` - -const stmtDelete = ` -DELETE FROM builds -WHERE build_id = :build_id -` - -const stmtPurge = ` -DELETE FROM builds -WHERE build_repo_id = :build_repo_id -AND build_number < :build_number -` -const stmtStagePurge = ` -DELETE FROM stages -WHERE stage_build_id NOT IN ( - SELECT build_id FROM builds -)` - -const stmtStepPurge = ` -DELETE FROM steps -WHERE step_stage_id NOT IN ( - SELECT stage_id FROM stages -)` - -// -// latest builds index -// - -const stmtInsertLatest = ` -INSERT INTO latest ( - latest_repo_id -,latest_build_id -,latest_type -,latest_name -,latest_created -,latest_updated -,latest_deleted -) VALUES ( - :latest_repo_id -,:latest_build_id -,:latest_type -,:latest_name -,:latest_created -,:latest_updated -,:latest_deleted -) ON CONFLICT (latest_repo_id, latest_type, latest_name) -DO UPDATE SET latest_build_id = EXCLUDED.latest_build_id -` - -const stmtInsertLatestPg = ` -INSERT INTO latest ( - latest_repo_id -,latest_build_id -,latest_type -,latest_name -,latest_created -,latest_updated -,latest_deleted -) VALUES ( - :latest_repo_id -,:latest_build_id -,:latest_type -,:latest_name -,:latest_created -,:latest_updated -,:latest_deleted -) ON CONFLICT (latest_repo_id, latest_type, latest_name) -DO UPDATE SET latest_build_id = EXCLUDED.latest_build_id -` - -const stmtInsertLatestMysql = ` -INSERT INTO latest ( - latest_repo_id -,latest_build_id -,latest_type -,latest_name -,latest_created -,latest_updated -,latest_deleted -) VALUES ( - :latest_repo_id -,:latest_build_id -,:latest_type -,:latest_name -,:latest_created -,:latest_updated -,:latest_deleted -) ON DUPLICATE KEY UPDATE latest_build_id = :latest_build_id -` - -const stmtDeleteLatest = ` -DELETE FROM latest -WHERE latest_repo_id = :latest_repo_id - AND latest_type = :latest_type - AND latest_name = :latest_name -` - -const queryLatestList = queryBase + ` -FROM builds -WHERE build_id IN ( - SELECT latest_build_id - FROM latest - WHERE latest_repo_id = :latest_repo_id - AND latest_type = :latest_type -) -` diff --git a/store/build/build_test.go b/store/build/build_test.go deleted file mode 100644 index d7d8d0f542..0000000000 --- a/store/build/build_test.go +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package build - -import ( - "context" - "database/sql" - "fmt" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - - "github.com/drone/drone/store/shared/db/dbtest" -) - -var noContext = context.TODO() - -func TestBuild(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - _ = dbtest.Disconnect(conn) - }() - - store := New(conn).(*buildStore) - t.Run("Create", testBuildCreate(store)) - t.Run("Purge", testBuildPurge(store)) - t.Run("Count", testBuildCount(store)) - t.Run("Pending", testBuildPending(store)) - t.Run("Running", testBuildRunning(store)) - t.Run("Latest", testBuildLatest(store)) -} - -func testBuildCreate(store *buildStore) func(t *testing.T) { - return func(t *testing.T) { - build := &core.Build{ - RepoID: 1, - Number: 99, - Event: core.EventPush, - Ref: "refs/heads/master", - Target: "master", - } - stage := &core.Stage{ - RepoID: 42, - Number: 1, - } - err := store.Create(noContext, build, []*core.Stage{stage}) - if err != nil { - t.Error(err) - } - if build.ID == 0 { - t.Errorf("Want build ID assigned, got %d", build.ID) - } - if got, want := build.Version, int64(1); got != want { - t.Errorf("Want build Version %d, got %d", want, got) - } - t.Run("Find", testBuildFind(store, build)) - t.Run("FindNumber", testBuildFindNumber(store, build)) - t.Run("FindRef", testBuildFindRef(store, build)) - t.Run("List", testBuildList(store, build)) - t.Run("ListRef", testBuildListRef(store, build)) - t.Run("Update", testBuildUpdate(store, build)) - t.Run("Locking", testBuildLocking(store, build)) - t.Run("Delete", testBuildDelete(store, build)) - } -} - -func testBuildFind(store *buildStore, build *core.Build) func(t *testing.T) { - return func(t *testing.T) { - result, err := store.Find(noContext, build.ID) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testBuild(result)) - } - } -} - -func testBuildFindNumber(store *buildStore, build *core.Build) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.FindNumber(noContext, build.RepoID, build.Number) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testBuild(item)) - } - } -} - -func testBuildFindRef(store *buildStore, build *core.Build) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.FindRef(noContext, build.RepoID, build.Ref) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testBuild(item)) - } - } -} - -func testBuildList(store *buildStore, build *core.Build) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.List(noContext, build.RepoID, 10, 0) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want list count %d, got %d", want, got) - } else { - t.Run("Fields", testBuild(list[0])) - } - } -} - -func testBuildListRef(store *buildStore, build *core.Build) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.ListRef(noContext, build.RepoID, build.Ref, 10, 0) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want list count %d, got %d", want, got) - } else { - t.Run("Fields", testBuild(list[0])) - } - } -} - -func testBuildUpdate(store *buildStore, build *core.Build) func(t *testing.T) { - return func(t *testing.T) { - before := &core.Build{ - ID: build.ID, - RepoID: build.RepoID, - Number: build.Number, - Status: core.StatusFailing, - Version: build.Version, - } - err := store.Update(noContext, before) - if err != nil { - t.Error(err) - return - } - if got, want := before.Version, build.Version+1; got != want { - t.Errorf("Want incremented version %d, got %d", want, got) - } - after, err := store.Find(noContext, before.ID) - if err != nil { - t.Error(err) - return - } - if got, want := after.Version, build.Version+1; got != want { - t.Errorf("Want incremented version %d, got %d", want, got) - } - if got, want := after.Status, before.Status; got != want { - t.Errorf("Want updated build status %v, got %v", want, got) - } - } -} - -func testBuildLocking(store *buildStore, build *core.Build) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.Find(noContext, build.ID) - if err != nil { - t.Error(err) - return - } - item.Version = 1 - err = store.Update(noContext, item) - if err == nil { - t.Errorf("Want Optimistic Lock Error, got nil") - } else if err != db.ErrOptimisticLock { - t.Errorf("Want Optimistic Lock Error") - } - } -} - -func testBuildDelete(store *buildStore, build *core.Build) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.Find(noContext, build.ID) - if err != nil { - t.Error(err) - } - err = store.Delete(noContext, item) - if err != nil { - t.Error(err) - } - _, err = store.Find(noContext, item.ID) - if want, got := sql.ErrNoRows, err; got != want { - t.Errorf("Want %q, got %q", want, got) - } - } -} - -func testBuildPurge(store *buildStore) func(t *testing.T) { - return func(t *testing.T) { - _ = store.db.Update(func(execer db.Execer, binder db.Binder) error { - _, _ = execer.Exec("DELETE FROM builds") - _, _ = execer.Exec("DELETE FROM stages") - _, _ = execer.Exec("DELETE FROM steps") - return nil - }) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 98}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusPending}}) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 99}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusPending}}) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 100}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusPending}}) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 101}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusPending}}) - // get the first stageid - var startingStageID int64 - _ = store.db.View(func(queryer db.Queryer, binder db.Binder) error { - return queryer.QueryRow("SELECT stage_id FROM stages limit 1").Scan(&startingStageID) - }) - // lets add steps to the builds - _ = store.db.Update(func(execer db.Execer, binder db.Binder) error { - _, _ = execer.Exec(fmt.Sprintf("INSERT INTO steps (step_stage_id, step_number, step_status) VALUES (%d, 1, 'pending')", startingStageID)) - _, _ = execer.Exec(fmt.Sprintf("INSERT INTO steps (step_stage_id, step_number, step_status) VALUES (%d, 1, 'pending')", startingStageID+1)) - _, _ = execer.Exec(fmt.Sprintf("INSERT INTO steps (step_stage_id, step_number, step_status) VALUES (%d, 1, 'pending')", startingStageID+2)) - _, _ = execer.Exec(fmt.Sprintf("INSERT INTO steps (step_stage_id, step_number, step_status) VALUES (%d, 1, 'pending')", startingStageID+3)) - return nil - }) - - before, err := store.List(noContext, 1, 100, 0) - if err != nil { - t.Error(err) - } - if got, want := len(before), 4; got != want { - t.Errorf("Want build count %d, got %d", want, got) - } - // count the number of stages - countOfStages := 4 - _ = store.db.View(func(queryer db.Queryer, binder db.Binder) error { - return queryer.QueryRow("SELECT count(*) FROM stages").Scan(&countOfStages) - }) - want := 4 - if want != countOfStages { - t.Errorf("Want stage count %d, got %d", want, countOfStages) - } - // count the number of steps - countOfSteps := 4 - _ = store.db.View(func(queryer db.Queryer, binder db.Binder) error { - return queryer.QueryRow("SELECT count(*) FROM steps").Scan(&countOfSteps) - }) - want = 4 - if want != countOfSteps { - t.Errorf("Want step count %d, got %d", want, countOfSteps) - } - // purge the builds - err = store.Purge(noContext, 1, 100) - if err != nil { - t.Error(err) - } - after, err := store.List(noContext, 1, 100, 0) - if err != nil { - t.Error(err) - } - // we want 2 builds - want = 2 - got := len(after) - if got != want { - t.Errorf("Want build count %d, got %d", want, got) - } - for _, build := range after { - if build.Number < 100 { - t.Errorf("Expect purge if build number is less than 100") - } - } - // check that orphaned stages are deleted - _ = store.db.View(func(queryer db.Queryer, binder db.Binder) error { - return queryer.QueryRow("SELECT count(*) FROM stages").Scan(&countOfStages) - }) - want = 2 - if want != countOfStages { - t.Errorf("Want stage count %d, got %d", want, countOfStages) - } - // check that orphaned steps are deleted - // count the number of steps - countOfSteps = 2 - _ = store.db.View(func(queryer db.Queryer, binder db.Binder) error { - return queryer.QueryRow("SELECT count(*) FROM steps").Scan(&countOfSteps) - }) - want = 2 - if want != countOfSteps { - t.Errorf("Want step count %d, got %d", want, countOfSteps) - } - } -} - -func testBuildCount(store *buildStore) func(t *testing.T) { - return func(t *testing.T) { - _ = store.db.Update(func(execer db.Execer, binder db.Binder) error { - _, err := execer.Exec("DELETE FROM builds") - return err - }) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 98}, nil) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 99}, nil) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 100}, nil) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 101}, nil) - - count, err := store.Count(noContext) - if err != nil { - t.Error(err) - } else if got, want := count, int64(4); got != want { - t.Errorf("Want build count %d, got %d", want, got) - } - } -} - -func testBuildPending(store *buildStore) func(t *testing.T) { - return func(t *testing.T) { - _ = store.db.Update(func(execer db.Execer, binder db.Binder) error { - _, _ = execer.Exec("DELETE FROM builds") - _, _ = execer.Exec("DELETE FROM stages") - return nil - }) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 98, Status: core.StatusPending}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusPending}}) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 99, Status: core.StatusPending}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusPending}}) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 100, Status: core.StatusRunning}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusRunning}}) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 101, Status: core.StatusPassing}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusPassing}}) - - count, err := store.Count(noContext) - if err != nil { - t.Error(err) - } else if got, want := count, int64(4); got != want { - t.Errorf("Want build count %d, got %d", want, got) - } - list, err := store.Pending(noContext) - if err != nil { - t.Error(err) - } else if got, want := len(list), 2; got != want { - t.Errorf("Want list count %d, got %d", want, got) - } - } -} - -func testBuildRunning(store *buildStore) func(t *testing.T) { - return func(t *testing.T) { - _ = store.db.Update(func(execer db.Execer, binder db.Binder) error { - _, _ = execer.Exec("DELETE FROM builds") - _, _ = execer.Exec("DELETE FROM stages") - return nil - }) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 98, Status: core.StatusRunning}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusRunning}}) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 99, Status: core.StatusRunning}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusRunning}}) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 100, Status: core.StatusBlocked}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusBlocked}}) - _ = store.Create(noContext, &core.Build{RepoID: 1, Number: 101, Status: core.StatusPassing}, []*core.Stage{{RepoID: 1, Number: 1, Status: core.StatusPassing}}) - - count, err := store.Count(noContext) - if err != nil { - t.Error(err) - } else if got, want := count, int64(4); got != want { - t.Errorf("Want build count %d, got %d", want, got) - } - list, err := store.Running(noContext) - if err != nil { - t.Error(err) - } else if got, want := len(list), 2; got != want { - t.Errorf("Want list count %d, got %d", want, got) - } - } -} - -func testBuildLatest(store *buildStore) func(t *testing.T) { - return func(t *testing.T) { - _ = store.db.Update(func(execer db.Execer, binder db.Binder) error { - _, _ = execer.Exec("DELETE FROM stages") - _, _ = execer.Exec("DELETE FROM latest") - _, _ = execer.Exec("DELETE FROM builds") - return nil - }) - - // step 1: insert the initial builds - build := &core.Build{ - RepoID: 1, - Number: 99, - Event: core.EventPush, - Ref: "refs/heads/master", - Target: "master", - } - - err := store.Create(noContext, build, []*core.Stage{}) - if err != nil { - t.Error(err) - return - } - - develop := &core.Build{ - RepoID: 1, - Number: 100, - Event: core.EventPush, - Ref: "refs/heads/develop", - Target: "develop", - } - err = store.Create(noContext, develop, []*core.Stage{}) - if err != nil { - t.Error(err) - return - } - - err = store.Create(noContext, &core.Build{ - RepoID: 1, - Number: 999, - Event: core.EventPullRequest, - Ref: "refs/pulls/10/head", - Source: "develop", - Target: "master", - }, []*core.Stage{}) - if err != nil { - t.Error(err) - return - } - - // step 2: verify the latest build number was captured - latest, _ := store.LatestBranches(noContext, build.RepoID) - if len(latest) != 2 { - t.Errorf("Expect latest branch list == 1, got %d", len(latest)) - return - } - if got, want := latest[0].Number, build.Number; got != want { - t.Errorf("Expected latest master build number %d, got %d", want, got) - } - if got, want := latest[1].Number, develop.Number; got != want { - t.Errorf("Expected latest develop build number %d, got %d", want, got) - return - } - - build = &core.Build{ - RepoID: 1, - Number: 101, - Event: core.EventPush, - Ref: "refs/heads/master", - Target: "master", - } - err = store.Create(noContext, build, []*core.Stage{}) - if err != nil { - t.Error(err) - return - } - - latest, _ = store.LatestBranches(noContext, build.RepoID) - if len(latest) != 2 { - t.Errorf("Expect latest branch list == 1") - return - } - if got, want := latest[1].Number, build.Number; got != want { - t.Errorf("Expected latest build number %d, got %d", want, got) - return - } - - err = store.DeleteBranch(noContext, build.RepoID, build.Target) - if err != nil { - t.Error(err) - return - } - - latest, _ = store.LatestBranches(noContext, build.RepoID) - if len(latest) != 1 { - t.Errorf("Expect latest branch list == 1 after delete") - return - } - } -} - -func testBuild(item *core.Build) func(t *testing.T) { - return func(t *testing.T) { - if got, want := item.RepoID, int64(1); got != want { - t.Errorf("Want build repo ID %d, got %d", want, got) - } - if got, want := item.Number, int64(99); got != want { - t.Errorf("Want build number %d, got %d", want, got) - } - if got, want := item.Ref, "refs/heads/master"; got != want { - t.Errorf("Want build ref %q, got %q", want, got) - } - } -} diff --git a/store/build/scan.go b/store/build/scan.go deleted file mode 100644 index 4377ed28e8..0000000000 --- a/store/build/scan.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package build - -import ( - "database/sql" - "encoding/json" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - - "github.com/jmoiron/sqlx/types" -) - -// helper function converts the Build structure to a set -// of named query parameters. -func toParams(build *core.Build) map[string]interface{} { - return map[string]interface{}{ - "build_id": build.ID, - "build_repo_id": build.RepoID, - "build_trigger": build.Trigger, - "build_number": build.Number, - "build_parent": build.Parent, - "build_status": build.Status, - "build_error": build.Error, - "build_event": build.Event, - "build_action": build.Action, - "build_link": build.Link, - "build_timestamp": build.Timestamp, - "build_title": build.Title, - "build_message": build.Message, - "build_before": build.Before, - "build_after": build.After, - "build_ref": build.Ref, - "build_source_repo": build.Fork, - "build_source": build.Source, - "build_target": build.Target, - "build_author": build.Author, - "build_author_name": build.AuthorName, - "build_author_email": build.AuthorEmail, - "build_author_avatar": build.AuthorAvatar, - "build_sender": build.Sender, - "build_params": encodeParams(build.Params), - "build_cron": build.Cron, - "build_deploy": build.Deploy, - "build_deploy_id": build.DeployID, - "build_debug": build.Debug, - "build_started": build.Started, - "build_finished": build.Finished, - "build_created": build.Created, - "build_updated": build.Updated, - "build_version": build.Version, - } -} - -// helper function converts the Stage structure to a set -// of named query parameters. -func toStageParams(stage *core.Stage) map[string]interface{} { - return map[string]interface{}{ - "stage_id": stage.ID, - "stage_repo_id": stage.RepoID, - "stage_build_id": stage.BuildID, - "stage_number": stage.Number, - "stage_name": stage.Name, - "stage_kind": stage.Kind, - "stage_type": stage.Type, - "stage_status": stage.Status, - "stage_error": stage.Error, - "stage_errignore": stage.ErrIgnore, - "stage_exit_code": stage.ExitCode, - "stage_limit": stage.Limit, - "stage_limit_repo": stage.LimitRepo, - "stage_os": stage.OS, - "stage_arch": stage.Arch, - "stage_variant": stage.Variant, - "stage_kernel": stage.Kernel, - "stage_machine": stage.Machine, - "stage_started": stage.Started, - "stage_stopped": stage.Stopped, - "stage_created": stage.Created, - "stage_updated": stage.Updated, - "stage_version": stage.Version, - "stage_on_success": stage.OnSuccess, - "stage_on_failure": stage.OnFailure, - "stage_depends_on": encodeSlice(stage.DependsOn), - "stage_labels": encodeParams(stage.Labels), - } -} - -func encodeParams(v map[string]string) types.JSONText { - raw, _ := json.Marshal(v) - return types.JSONText(raw) -} - -func encodeSlice(v []string) types.JSONText { - raw, _ := json.Marshal(v) - return types.JSONText(raw) -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(scanner db.Scanner, dest *core.Build) error { - paramsJSON := types.JSONText{} - err := scanner.Scan( - &dest.ID, - &dest.RepoID, - &dest.Trigger, - &dest.Number, - &dest.Parent, - &dest.Status, - &dest.Error, - &dest.Event, - &dest.Action, - &dest.Link, - &dest.Timestamp, - &dest.Title, - &dest.Message, - &dest.Before, - &dest.After, - &dest.Ref, - &dest.Fork, - &dest.Source, - &dest.Target, - &dest.Author, - &dest.AuthorName, - &dest.AuthorEmail, - &dest.AuthorAvatar, - &dest.Sender, - ¶msJSON, - &dest.Cron, - &dest.Deploy, - &dest.DeployID, - &dest.Debug, - &dest.Started, - &dest.Finished, - &dest.Created, - &dest.Updated, - &dest.Version, - ) - dest.Params = map[string]string{} - json.Unmarshal(paramsJSON, &dest.Params) - return err -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRows(rows *sql.Rows) ([]*core.Build, error) { - defer rows.Close() - - builds := []*core.Build{} - for rows.Next() { - build := new(core.Build) - err := scanRow(rows, build) - if err != nil { - return nil, err - } - builds = append(builds, build) - } - return builds, nil -} diff --git a/store/card/card.go b/store/card/card.go deleted file mode 100644 index 4918920fe3..0000000000 --- a/store/card/card.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. -// +build !oss - -package card - -import ( - "bytes" - "context" - "io" - "io/ioutil" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new card database store. -func New(db *db.DB) core.CardStore { - return &cardStore{ - db: db, - } -} - -type cardStore struct { - db *db.DB -} - -type card struct { - Id int64 `json:"id,omitempty"` - Data []byte `json:"card_data"` -} - -func (c cardStore) Find(ctx context.Context, step int64) (io.ReadCloser, error) { - out := &card{Id: step} - err := c.db.View(func(queryer db.Queryer, binder db.Binder) error { - params, err := toParams(out) - if err != nil { - return err - } - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - - return ioutil.NopCloser( - bytes.NewBuffer(out.Data), - ), err -} - -func (c cardStore) Create(ctx context.Context, step int64, r io.Reader) error { - data, err := ioutil.ReadAll(r) - if err != nil { - return err - } - return c.db.Lock(func(execer db.Execer, binder db.Binder) error { - in := &card{ - Id: step, - Data: data, - } - params, err := toParams(in) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -func (c *cardStore) Update(ctx context.Context, step int64, r io.Reader) error { - data, err := ioutil.ReadAll(r) - if err != nil { - return err - } - return c.db.Lock(func(execer db.Execer, binder db.Binder) error { - card := &card{ - Id: step, - Data: data, - } - params, err := toParams(card) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -func (c cardStore) Delete(ctx context.Context, step int64) error { - return c.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := &card{ - Id: step, - } - stmt, args, err := binder.BindNamed(stmtDelete, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -const queryBase = ` -SELECT - card_id -,card_data -` - -const queryKey = queryBase + ` -FROM cards -WHERE card_id = :card_id -LIMIT 1 -` - -const stmtInsert = ` -INSERT INTO cards ( - card_id -,card_data -) VALUES ( - :card_id -,:card_data -) -` - -const stmtUpdate = ` -UPDATE cards -SET card_data = :card_data -WHERE card_id = :card_id -` - -const stmtDelete = ` -DELETE FROM cards -WHERE card_id = :card_id -` - -const stmtInsertPostgres = stmtInsert + ` -RETURNING card_id -` diff --git a/store/card/card_oss.go b/store/card/card_oss.go deleted file mode 100644 index 0c3d3c2fd6..0000000000 --- a/store/card/card_oss.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package card - -import ( - "context" - "io" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -func New(db *db.DB) core.CardStore { - return new(noop) -} - -type noop struct{} - -func (noop) Find(ctx context.Context, step int64) (io.ReadCloser, error) { - return nil, nil -} - -func (noop) Create(ctx context.Context, step int64, r io.Reader) error { - return nil -} - -func (noop) Update(ctx context.Context, step int64, r io.Reader) error { - return nil -} - -func (noop) Delete(ctx context.Context, step int64) error { - return nil -} diff --git a/store/card/card_test.go b/store/card/card_test.go deleted file mode 100644 index 89bd424e86..0000000000 --- a/store/card/card_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package card - -import ( - "bytes" - "context" - "database/sql" - "io/ioutil" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/build" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db/dbtest" - "github.com/drone/drone/store/step" -) - -var noContext = context.TODO() - -func TestCard(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - // seed with a dummy repository - dummyRepo := &core.Repository{UID: "1", Slug: "octocat/hello-world"} - repos := repos.New(conn) - repos.Create(noContext, dummyRepo) - - // seed with a dummy stage - stage := &core.Stage{Number: 1} - stages := []*core.Stage{stage} - - // seed with a dummy build - dummyBuild := &core.Build{Number: 1, RepoID: dummyRepo.ID} - builds := build.New(conn) - builds.Create(noContext, dummyBuild, stages) - - // seed with a dummy step - dummyStep := &core.Step{Number: 1, StageID: stage.ID} - steps := step.New(conn) - steps.Create(noContext, dummyStep) - - store := New(conn).(*cardStore) - t.Run("Create", testCardCreate(store, dummyStep)) - t.Run("Find", testFindCard(store, dummyStep)) - t.Run("Update", testLogsUpdate(store, dummyStep)) -} - -func testCardCreate(store *cardStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - buf := ioutil.NopCloser( - bytes.NewBuffer([]byte("{\"type\": \"AdaptiveCard\"}")), - ) - err := store.Create(noContext, step.ID, buf) - if err != nil { - t.Error(err) - } - } -} - -func testFindCard(card *cardStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - r, err := card.Find(noContext, step.ID) - if err != nil { - t.Error(err) - } else { - data, err := ioutil.ReadAll(r) - if err != nil { - t.Error(err) - return - } - if got, want := string(data), "{\"type\": \"AdaptiveCard\"}"; got != want { - t.Errorf("Want card data output stream %q, got %q", want, got) - } - } - } -} - -func testLogsUpdate(store *cardStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - buf := bytes.NewBufferString("hola mundo") - err := store.Update(noContext, step.ID, buf) - if err != nil { - t.Error(err) - return - } - r, err := store.Find(noContext, step.ID) - if err != nil { - t.Error(err) - return - } - data, err := ioutil.ReadAll(r) - if err != nil { - t.Error(err) - return - } - if got, want := string(data), "hola mundo"; got != want { - t.Errorf("Want updated log output stream %q, got %q", want, got) - } - } -} - -func testLogsDelete(store *cardStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - err := store.Delete(noContext, step.ID) - if err != nil { - t.Error(err) - return - } - _, err = store.Find(noContext, step.ID) - if got, want := sql.ErrNoRows, err; got != want { - t.Errorf("Want sql.ErrNoRows, got %v", got) - return - } - } -} diff --git a/store/card/scan.go b/store/card/scan.go deleted file mode 100644 index 495ee1ca4d..0000000000 --- a/store/card/scan.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package card - -import ( - "github.com/drone/drone/store/shared/db" -) - -// helper function converts the card structure to a set -// of named query parameters. -func toParams(card *card) (map[string]interface{}, error) { - return map[string]interface{}{ - "card_id": card.Id, - "card_data": card.Data, - }, nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(scanner db.Scanner, dst *card) error { - err := scanner.Scan( - &dst.Id, - &dst.Data, - ) - if err != nil { - return err - } - return nil -} diff --git a/store/cron/cron.go b/store/cron/cron.go deleted file mode 100644 index b4abe4f821..0000000000 --- a/store/cron/cron.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package cron - -// NewCronStore returns a new CronStore. -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new Cron database store. -func New(db *db.DB) core.CronStore { - return &cronStore{db} -} - -type cronStore struct { - db *db.DB -} - -func (s *cronStore) List(ctx context.Context, id int64) ([]*core.Cron, error) { - var out []*core.Cron - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"cron_repo_id": id} - stmt, args, err := binder.BindNamed(queryRepo, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *cronStore) Ready(ctx context.Context, before int64) ([]*core.Cron, error) { - var out []*core.Cron - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"cron_next": before} - stmt, args, err := binder.BindNamed(queryReady, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *cronStore) Find(ctx context.Context, id int64) (*core.Cron, error) { - out := &core.Cron{ID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *cronStore) FindName(ctx context.Context, id int64, name string) (*core.Cron, error) { - out := &core.Cron{Name: name, RepoID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryName, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *cronStore) Create(ctx context.Context, cron *core.Cron) error { - if s.db.Driver() == db.Postgres { - return s.createPostgres(ctx, cron) - } - return s.create(ctx, cron) -} - -func (s *cronStore) create(ctx context.Context, cron *core.Cron) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(cron) - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - cron.ID, err = res.LastInsertId() - return err - }) -} - -func (s *cronStore) createPostgres(ctx context.Context, cron *core.Cron) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(cron) - stmt, args, err := binder.BindNamed(stmtInsertPg, params) - if err != nil { - return err - } - return execer.QueryRow(stmt, args...).Scan(&cron.ID) - }) -} - -func (s *cronStore) Update(ctx context.Context, cron *core.Cron) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(cron) - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -func (s *cronStore) Delete(ctx context.Context, cron *core.Cron) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(cron) - stmt, args, err := binder.BindNamed(stmtDelete, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -const queryBase = ` -SELECT - cron_id -,cron_repo_id -,cron_name -,cron_expr -,cron_next -,cron_prev -,cron_event -,cron_branch -,cron_target -,cron_disabled -,cron_created -,cron_updated -,cron_version -` - -const queryKey = queryBase + ` -FROM cron -WHERE cron_id = :cron_id -LIMIT 1 -` - -const queryName = queryBase + ` -FROM cron -WHERE cron_name = :cron_name - AND cron_repo_id = :cron_repo_id -LIMIT 1 -` - -const queryRepo = queryBase + ` -FROM cron -WHERE cron_repo_id = :cron_repo_id -ORDER BY cron_name -` - -const queryReady = queryBase + ` -FROM cron -WHERE cron_next < :cron_next -ORDER BY cron_name -` - -const stmtUpdate = ` -UPDATE cron SET - cron_repo_id = :cron_repo_id -,cron_name = :cron_name -,cron_expr = :cron_expr -,cron_next = :cron_next -,cron_prev = :cron_prev -,cron_event = :cron_event -,cron_branch = :cron_branch -,cron_target = :cron_target -,cron_disabled = :cron_disabled -,cron_created = :cron_created -,cron_updated = :cron_updated -,cron_version = :cron_version -WHERE cron_id = :cron_id -` - -const stmtDelete = ` -DELETE FROM cron -WHERE cron_id = :cron_id -` - -const stmtInsert = ` -INSERT INTO cron ( - cron_repo_id -,cron_name -,cron_expr -,cron_next -,cron_prev -,cron_event -,cron_branch -,cron_target -,cron_disabled -,cron_created -,cron_updated -,cron_version -) VALUES ( - :cron_repo_id -,:cron_name -,:cron_expr -,:cron_next -,:cron_prev -,:cron_event -,:cron_branch -,:cron_target -,:cron_disabled -,:cron_created -,:cron_updated -,:cron_version -) -` - -const stmtInsertPg = stmtInsert + ` -RETURNING cron_id -` diff --git a/store/cron/cron_oss.go b/store/cron/cron_oss.go deleted file mode 100644 index aff1fb76c5..0000000000 --- a/store/cron/cron_oss.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package cron - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new Secret database store. -func New(db *db.DB) core.CronStore { - return new(noop) -} - -type noop struct{} - -func (noop) List(ctx context.Context, id int64) ([]*core.Cron, error) { - return nil, nil -} - -func (noop) Ready(ctx context.Context, id int64) ([]*core.Cron, error) { - return nil, nil -} - -func (noop) Find(ctx context.Context, id int64) (*core.Cron, error) { - return nil, nil -} - -func (noop) FindName(ctx context.Context, id int64, name string) (*core.Cron, error) { - return nil, nil -} - -func (noop) Create(ctx context.Context, secret *core.Cron) error { - return nil -} - -func (noop) Update(context.Context, *core.Cron) error { - return nil -} - -func (noop) Delete(context.Context, *core.Cron) error { - return nil -} diff --git a/store/cron/cron_test.go b/store/cron/cron_test.go deleted file mode 100644 index bb185c040f..0000000000 --- a/store/cron/cron_test.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package cron - -import ( - "context" - "database/sql" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db/dbtest" -) - -var noContext = context.TODO() - -func TestCron(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - // seeds the database with a dummy repository. - repo := &core.Repository{UID: "1", Slug: "octocat/hello-world"} - repos := repos.New(conn) - if err := repos.Create(noContext, repo); err != nil { - t.Error(err) - } - - store := New(conn).(*cronStore) - t.Run("Create", testCronCreate(store, repos, repo)) -} - -func testCronCreate(store *cronStore, repos core.RepositoryStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Cron{ - RepoID: repo.ID, - Name: "nightly", - Expr: "00 00 * * *", - Next: 1000000000, - } - err := store.Create(noContext, item) - if err != nil { - t.Error(err) - } - if item.ID == 0 { - t.Errorf("Want cron ID assigned, got %d", item.ID) - } - - t.Run("Find", testCronFind(store, item)) - t.Run("FindName", testCronFindName(store, repo)) - t.Run("List", testCronList(store, repo)) - t.Run("Read", testCronReady(store, repo)) - t.Run("Update", testCronUpdate(store, repo)) - t.Run("Delete", testCronDelete(store, repo)) - t.Run("Fkey", testCronForeignKey(store, repos, repo)) - } -} - -func testCronFind(store *cronStore, cron *core.Cron) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.Find(noContext, cron.ID) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testCron(item)) - } - } -} - -func testCronFindName(store *cronStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.FindName(noContext, repo.ID, "nightly") - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testCron(item)) - } - } -} - -func testCronList(store *cronStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.List(noContext, repo.ID) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testCron(list[0])) - } - } -} - -func testCronReady(store *cronStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Cron{ - RepoID: repo.ID, - Name: "daily", - Expr: "00 00 * * *", - Next: 1000000002, // ignored (1 second too late) - } - err := store.Create(noContext, item) - if err != nil { - t.Error(err) - return - } - list, err := store.Ready(noContext, 1000000001) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testCron(list[0])) - } - } -} - -func testCronUpdate(store *cronStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - before, err := store.FindName(noContext, repo.ID, "nightly") - if err != nil { - t.Error(err) - return - } - err = store.Update(noContext, before) - if err != nil { - t.Error(err) - return - } - after, err := store.Find(noContext, before.ID) - if err != nil { - t.Error(err) - return - } - if after == nil { - t.Fail() - } - } -} - -func testCronDelete(store *cronStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - cron, err := store.FindName(noContext, repo.ID, "nightly") - if err != nil { - t.Error(err) - return - } - err = store.Delete(noContext, cron) - if err != nil { - t.Error(err) - return - } - _, err = store.Find(noContext, cron.ID) - if got, want := sql.ErrNoRows, err; got != want { - t.Errorf("Want sql.ErrNoRows, got %v", got) - return - } - } -} - -func testCronForeignKey(store *cronStore, repos core.RepositoryStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Cron{ - RepoID: repo.ID, - Name: "nightly", - Expr: "00 00 * * *", - } - store.Create(noContext, item) - before, _ := store.List(noContext, repo.ID) - if len(before) == 0 { - t.Errorf("Want non-empty cron list") - return - } - - err := repos.Delete(noContext, repo) - if err != nil { - t.Error(err) - return - } - after, _ := store.List(noContext, repo.ID) - if len(after) != 0 { - t.Errorf("Want empty cron list") - } - } -} - -func testCron(item *core.Cron) func(t *testing.T) { - return func(t *testing.T) { - if got, want := item.Name, "nightly"; got != want { - t.Errorf("Want cron name %q, got %q", want, got) - } - if got, want := item.Expr, "00 00 * * *"; got != want { - t.Errorf("Want cron name %q, got %q", want, got) - } - } -} diff --git a/store/cron/scan.go b/store/cron/scan.go deleted file mode 100644 index 68a73b20ba..0000000000 --- a/store/cron/scan.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package cron - -import ( - "database/sql" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// helper function converts the User structure to a set -// of named query parameters. -func toParams(cron *core.Cron) map[string]interface{} { - return map[string]interface{}{ - "cron_id": cron.ID, - "cron_repo_id": cron.RepoID, - "cron_name": cron.Name, - "cron_expr": cron.Expr, - "cron_next": cron.Next, - "cron_prev": cron.Prev, - "cron_event": cron.Event, - "cron_branch": cron.Branch, - "cron_target": cron.Target, - "cron_disabled": cron.Disabled, - "cron_created": cron.Created, - "cron_updated": cron.Updated, - "cron_version": cron.Version, - } -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(scanner db.Scanner, dst *core.Cron) error { - return scanner.Scan( - &dst.ID, - &dst.RepoID, - &dst.Name, - &dst.Expr, - &dst.Next, - &dst.Prev, - &dst.Event, - &dst.Branch, - &dst.Target, - &dst.Disabled, - &dst.Created, - &dst.Updated, - &dst.Version, - ) -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRows(rows *sql.Rows) ([]*core.Cron, error) { - defer rows.Close() - - crons := []*core.Cron{} - for rows.Next() { - cron := new(core.Cron) - err := scanRow(rows, cron) - if err != nil { - return nil, err - } - crons = append(crons, cron) - } - return crons, nil -} diff --git a/store/logs/azureblob.go b/store/logs/azureblob.go deleted file mode 100644 index a0f10b15c1..0000000000 --- a/store/logs/azureblob.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package logs - -import ( - "context" - "fmt" - "io" - "net/url" - - "github.com/Azure/azure-storage-blob-go/azblob" - "github.com/drone/drone/core" -) - -// NewAzureBlobEnv returns a new Azure blob log store. -func NewAzureBlobEnv(containerName, storageAccountName, storageAccessKey string) core.LogStore { - return &azureBlobStore{ - containerName: containerName, - storageAccountName: storageAccountName, - storageAccessKey: storageAccessKey, - containerURL: nil, - } -} - -type azureBlobStore struct { - containerName string - storageAccountName string - storageAccessKey string - containerURL *azblob.ContainerURL -} - -func (az *azureBlobStore) Find(ctx context.Context, step int64) (io.ReadCloser, error) { - err := az.getContainerURL() - if err != nil { - return nil, err - } - blobURL := az.containerURL.NewBlockBlobURL(fmt.Sprintf("%d", step)) - out, err := blobURL.Download(ctx, 0, azblob.CountToEnd, azblob.BlobAccessConditions{}, false) - if err != nil { - return nil, err - } - return out.Body(azblob.RetryReaderOptions{}), nil -} - -func (az *azureBlobStore) Create(ctx context.Context, step int64, r io.Reader) error { - err := az.getContainerURL() - if err != nil { - return err - } - opts := &azblob.UploadStreamToBlockBlobOptions{ - BufferSize: 4 * 1024 * 1024, - MaxBuffers: 5, - } - blobURL := az.containerURL.NewBlockBlobURL(fmt.Sprintf("%d", step)) - _, err = azblob.UploadStreamToBlockBlob(ctx, r, blobURL, *opts) - return err -} - -func (az *azureBlobStore) Update(ctx context.Context, step int64, r io.Reader) error { - return az.Create(ctx, step, r) -} - -func (az *azureBlobStore) Delete(ctx context.Context, step int64) error { - err := az.getContainerURL() - if err != nil { - return err - } - blobURL := az.containerURL.NewBlockBlobURL(fmt.Sprintf("%d", step)) - _, err = blobURL.Delete(ctx, azblob.DeleteSnapshotsOptionInclude, azblob.BlobAccessConditions{}) - return err -} - -func (az *azureBlobStore) getContainerURL() error { - if az.containerURL != nil { - return nil - } - if len(az.storageAccountName) == 0 || len(az.storageAccessKey) == 0 { - return fmt.Errorf("Either the storage account or storage access key environment variable is not set") - } - credential, err := azblob.NewSharedKeyCredential(az.storageAccountName, az.storageAccessKey) - - if err != nil { - return err - } - - p := azblob.NewPipeline(credential, azblob.PipelineOptions{}) - URL, err := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s", az.storageAccountName, az.containerName)) - - if err != nil { - return err - } - - containerURL := azblob.NewContainerURL(*URL, p) - az.containerURL = &containerURL - return nil -} diff --git a/store/logs/azureblob_oss.go b/store/logs/azureblob_oss.go deleted file mode 100644 index 3438078f77..0000000000 --- a/store/logs/azureblob_oss.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package logs - -import "github.com/drone/drone/core" - -// New returns a zero value LogStore. -func NewAzureBlobEnv(containerName, storageAccountName, storageAccessKey string) core.LogStore { - return nil -} diff --git a/store/logs/combine.go b/store/logs/combine.go deleted file mode 100644 index fe7a15cec9..0000000000 --- a/store/logs/combine.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package logs - -import ( - "context" - "io" - - "github.com/drone/drone/core" -) - -// NewCombined returns a new combined log store that will fallback -// to a secondary log store when necessary. This can be useful when -// migrating from database logs to s3, where logs for older builds -// are still being stored in the database, and newer logs in s3. -func NewCombined(primary, secondary core.LogStore) core.LogStore { - return &combined{ - primary: primary, - secondary: secondary, - } -} - -type combined struct { - primary, secondary core.LogStore -} - -func (s *combined) Find(ctx context.Context, step int64) (io.ReadCloser, error) { - rc, err := s.primary.Find(ctx, step) - if err == nil { - return rc, err - } - return s.secondary.Find(ctx, step) -} - -func (s *combined) Create(ctx context.Context, step int64, r io.Reader) error { - return s.primary.Create(ctx, step, r) -} - -func (s *combined) Update(ctx context.Context, step int64, r io.Reader) error { - return s.primary.Update(ctx, step, r) -} - -func (s *combined) Delete(ctx context.Context, step int64) error { - err := s.primary.Delete(ctx, step) - if err != nil { - err = s.secondary.Delete(ctx, step) - } - return err -} diff --git a/store/logs/logs.go b/store/logs/logs.go deleted file mode 100644 index 9b9c78535f..0000000000 --- a/store/logs/logs.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package logs - -import ( - "bytes" - "context" - "io" - "io/ioutil" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new LogStore. -func New(db *db.DB) core.LogStore { - return &logStore{db} -} - -type logStore struct { - db *db.DB -} - -func (s *logStore) Find(ctx context.Context, step int64) (io.ReadCloser, error) { - out := &logs{ID: step} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - query, args, err := binder.BindNamed(queryKey, out) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return ioutil.NopCloser( - bytes.NewBuffer(out.Data), - ), err -} - -func (s *logStore) Create(ctx context.Context, step int64, r io.Reader) error { - data, err := ioutil.ReadAll(r) - if err != nil { - return err - } - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := &logs{ - ID: step, - Data: data, - } - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -func (s *logStore) Update(ctx context.Context, step int64, r io.Reader) error { - data, err := ioutil.ReadAll(r) - if err != nil { - return err - } - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := &logs{ - ID: step, - Data: data, - } - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -func (s *logStore) Delete(ctx context.Context, step int64) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := &logs{ - ID: step, - } - stmt, args, err := binder.BindNamed(stmtDelete, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -type logs struct { - ID int64 `db:"log_id"` - Data []byte `db:"log_data"` -} - -const queryKey = ` -SELECT - log_id -,log_data -FROM logs -WHERE log_id = :log_id -` - -const stmtInsert = ` -INSERT INTO logs ( - log_id -,log_data -) VALUES ( - :log_id -,:log_data -) -` - -const stmtUpdate = ` -UPDATE logs -SET log_data = :log_data -WHERE log_id = :log_id -` - -const stmtDelete = ` -DELETE FROM logs -WHERE log_id = :log_id -` diff --git a/store/logs/logs_test.go b/store/logs/logs_test.go deleted file mode 100644 index 2495e245ca..0000000000 --- a/store/logs/logs_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package logs - -import ( - "bytes" - "context" - "database/sql" - "io/ioutil" - "testing" - - "github.com/drone/drone/store/shared/db/dbtest" - "github.com/drone/drone/core" - "github.com/drone/drone/store/build" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/step" -) - -var noContext = context.TODO() - -func TestLogs(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - // seed with a dummy repository - arepo := &core.Repository{UID: "1", Slug: "octocat/hello-world"} - repos := repos.New(conn) - repos.Create(noContext, arepo) - - // seed with a dummy stage - stage := &core.Stage{Number: 1} - stages := []*core.Stage{stage} - - // seed with a dummy build - abuild := &core.Build{Number: 1, RepoID: arepo.ID} - builds := build.New(conn) - builds.Create(noContext, abuild, stages) - - // seed with a dummy step - astep := &core.Step{Number: 1, StageID: stage.ID} - steps := step.New(conn) - steps.Create(noContext, astep) - - store := New(conn).(*logStore) - t.Run("Create", testLogsCreate(store, astep)) - t.Run("Find", testLogsFind(store, astep)) - t.Run("Update", testLogsUpdate(store, astep)) - t.Run("Delete", testLogsDelete(store, astep)) -} - -func testLogsCreate(store *logStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - buf := bytes.NewBufferString("hello world") - err := store.Create(noContext, step.ID, buf) - if err != nil { - t.Error(err) - } - } -} - -func testLogsFind(store *logStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - r, err := store.Find(noContext, step.ID) - if err != nil { - t.Error(err) - return - } - data, err := ioutil.ReadAll(r) - if err != nil { - t.Error(err) - return - } - if got, want := string(data), "hello world"; got != want { - t.Errorf("Want log output stream %q, got %q", want, got) - } - } -} - -func testLogsUpdate(store *logStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - buf := bytes.NewBufferString("hola mundo") - err := store.Update(noContext, step.ID, buf) - if err != nil { - t.Error(err) - return - } - r, err := store.Find(noContext, step.ID) - if err != nil { - t.Error(err) - return - } - data, err := ioutil.ReadAll(r) - if err != nil { - t.Error(err) - return - } - if got, want := string(data), "hola mundo"; got != want { - t.Errorf("Want updated log output stream %q, got %q", want, got) - } - } -} - -func testLogsDelete(store *logStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - err := store.Delete(noContext, step.ID) - if err != nil { - t.Error(err) - return - } - _, err = store.Find(noContext, step.ID) - if got, want := sql.ErrNoRows, err; got != want { - t.Errorf("Want sql.ErrNoRows, got %v", got) - return - } - } -} diff --git a/store/logs/s3.go b/store/logs/s3.go deleted file mode 100644 index 6f5e92d083..0000000000 --- a/store/logs/s3.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package logs - -import ( - "context" - "fmt" - "io" - "path" - "strings" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3manager" - - "github.com/drone/drone/core" -) - -// NewS3Env returns a new S3 log store. -func NewS3Env(bucket, prefix, endpoint string, pathStyle bool) core.LogStore { - disableSSL := false - - if endpoint != "" { - disableSSL = !strings.HasPrefix(endpoint, "https://") - } - - return &s3store{ - bucket: bucket, - prefix: prefix, - session: session.Must( - session.NewSession(&aws.Config{ - Endpoint: aws.String(endpoint), - DisableSSL: aws.Bool(disableSSL), - S3ForcePathStyle: aws.Bool(pathStyle), - }), - ), - } -} - -// NewS3 returns a new S3 log store. -func NewS3(session *session.Session, bucket, prefix string) core.LogStore { - return &s3store{ - bucket: bucket, - prefix: prefix, - session: session, - } -} - -type s3store struct { - bucket string - prefix string - session *session.Session -} - -func (s *s3store) Find(ctx context.Context, step int64) (io.ReadCloser, error) { - svc := s3.New(s.session) - out, err := svc.GetObject(&s3.GetObjectInput{ - Bucket: aws.String(s.bucket), - Key: aws.String(s.key(step)), - }) - if err != nil { - return nil, err - } - return out.Body, nil -} - -func (s *s3store) Create(ctx context.Context, step int64, r io.Reader) error { - uploader := s3manager.NewUploader(s.session) - input := &s3manager.UploadInput{ - ACL: aws.String("private"), - Bucket: aws.String(s.bucket), - Key: aws.String(s.key(step)), - Body: r, - } - _, err := uploader.Upload(input) - return err -} - -func (s *s3store) Update(ctx context.Context, step int64, r io.Reader) error { - return s.Create(ctx, step, r) -} - -func (s *s3store) Delete(ctx context.Context, step int64) error { - svc := s3.New(s.session) - _, err := svc.DeleteObject(&s3.DeleteObjectInput{ - Bucket: aws.String(s.bucket), - Key: aws.String(s.key(step)), - }) - return err -} - -func (s *s3store) key(step int64) string { - return path.Join("/", s.prefix, fmt.Sprint(step)) -} diff --git a/store/logs/s3_oss.go b/store/logs/s3_oss.go deleted file mode 100644 index 9cb958468b..0000000000 --- a/store/logs/s3_oss.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package logs - -import "github.com/drone/drone/core" - -// New returns a zero value LogStore. -func NewS3Env(bucket, prefix, endpoint string, pathStyle bool) core.LogStore { - return nil -} diff --git a/store/logs/s3_test.go b/store/logs/s3_test.go deleted file mode 100644 index a7530834a6..0000000000 --- a/store/logs/s3_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package logs - -import "testing" - -func TestKey(t *testing.T) { - tests := []struct { - bucket string - prefix string - result string - }{ - { - bucket: "test-bucket", - prefix: "drone/logs", - result: "/drone/logs/1", - }, - { - bucket: "test-bucket", - prefix: "/drone/logs", - result: "/drone/logs/1", - }, - } - for _, test := range tests { - s := &s3store{ - bucket: test.bucket, - prefix: test.prefix, - } - if got, want := s.key(1), test.result; got != want { - t.Errorf("Want key %s, got %s", want, got) - } - } -} diff --git a/store/logs/scan.go b/store/logs/scan.go deleted file mode 100644 index 651b966248..0000000000 --- a/store/logs/scan.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package logs - -import "github.com/drone/drone/store/shared/db" - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(scanner db.Scanner, dst *logs) error { - return scanner.Scan( - &dst.ID, - &dst.Data, - ) -} diff --git a/store/perm/perm.go b/store/perm/perm.go deleted file mode 100644 index b2bca074b2..0000000000 --- a/store/perm/perm.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package perm - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new PermStore. -func New(db *db.DB) core.PermStore { - return &permStore{db} -} - -type permStore struct { - db *db.DB -} - -// Find returns a project member from the datastore. -func (s *permStore) Find(ctx context.Context, repo string, user int64) (*core.Perm, error) { - out := &core.Perm{RepoUID: repo, UserID: user} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -// List returns a list of project members from the datastore. -func (s *permStore) List(ctx context.Context, repo string) ([]*core.Collaborator, error) { - var out []*core.Collaborator - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"repo_uid": repo} - stmt, args, err := binder.BindNamed(queryCollabs, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanCollabRows(rows) - return err - }) - return out, err -} - -// Create persists a project member to the datastore. -func (s *permStore) Create(ctx context.Context, perm *core.Perm) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(perm) - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -// Update persists an updated project member to the datastore. -func (s *permStore) Update(ctx context.Context, perm *core.Perm) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(perm) - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -// Delete deletes a project member from the datastore. -func (s *permStore) Delete(ctx context.Context, perm *core.Perm) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(perm) - stmt, args, err := binder.BindNamed(stmtDelete, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -const queryKey = ` -SELECT - perm_user_id -,perm_repo_uid -,perm_read -,perm_write -,perm_admin -,perm_synced -,perm_created -,perm_updated -FROM perms -WHERE perm_user_id = :perm_user_id - AND perm_repo_uid = :perm_repo_uid -` - -const queryCollabs = ` -SELECT - perm_user_id -,perm_repo_uid -,user_login -,user_avatar -,perm_read -,perm_write -,perm_admin -,perm_synced -,perm_created -,perm_updated -FROM users -INNER JOIN perms ON perms.perm_user_id = users.user_id -WHERE perms.perm_repo_uid = :repo_uid -ORDER BY user_login ASC -` - -const stmtDelete = ` -DELETE FROM perms -WHERE perm_user_id = :perm_user_id - AND perm_repo_uid = :perm_repo_uid -` - -const stmtUpdate = ` -UPDATE perms -SET - perm_read = :perm_read -,perm_write = :perm_write -,perm_admin = :perm_admin -,perm_synced = :perm_synced -,perm_updated = :perm_updated -WHERE perm_user_id = :perm_user_id - AND perm_repo_uid = :perm_repo_uid -` - -const stmtInsert = ` -INSERT INTO perms ( - perm_user_id -,perm_repo_uid -,perm_read -,perm_write -,perm_admin -,perm_synced -,perm_created -,perm_updated -) VALUES ( - :perm_user_id -,:perm_repo_uid -,:perm_read -,:perm_write -,:perm_admin -,:perm_synced -,:perm_created -,:perm_updated -) -` diff --git a/store/perm/perm_test.go b/store/perm/perm_test.go deleted file mode 100644 index b8f2d5c4cf..0000000000 --- a/store/perm/perm_test.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package perm - -import ( - "context" - "database/sql" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db/dbtest" - "github.com/drone/drone/store/shared/encrypt" - "github.com/drone/drone/store/user" -) - -var noContext = context.TODO() - -func TestPerms(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - // no-op encrypter - enc, _ := encrypt.New("") - - // seeds the database with a dummy user account. - auser := &core.User{Login: "spaceghost"} - users := user.New(conn, enc) - err = users.Create(noContext, auser) - if err != nil { - t.Error(err) - } - - // seeds the database with a dummy repository. - arepo := &core.Repository{UID: "1", Slug: "octocat/hello-world"} - repos := repos.New(conn) - err = repos.Create(noContext, arepo) - if err != nil { - t.Error(err) - } - if err != nil { - t.Error(err) - } - - store := New(conn).(*permStore) - t.Run("Create", testPermCreate(store, auser, arepo)) - t.Run("Find", testPermFind(store, auser, arepo)) - t.Run("List", testPermList(store, auser, arepo)) - t.Run("Update", testPermUpdate(store, auser, arepo)) - t.Run("Delete", testPermDelete(store, auser, arepo)) -} - -func testPermCreate(store *permStore, user *core.User, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Perm{ - UserID: user.ID, - RepoUID: repo.UID, - Read: true, - Write: true, - Admin: false, - } - err := store.Create(noContext, item) - if err != nil { - t.Error(err) - } - } -} - -func testPermFind(store *permStore, user *core.User, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.Find(noContext, repo.UID, user.ID) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testPerm(item)) - } - } -} - -func testPermList(store *permStore, user *core.User, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.List(noContext, repo.UID) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want collaborator count %d, got %d", want, got) - return - } - if got, want := list[0].Login, user.Login; got != want { - t.Errorf("Want username %q, got %q", want, got) - } - t.Run("Fields", testPerm( - &core.Perm{ - Read: list[0].Read, - Write: list[0].Write, - Admin: list[0].Admin, - }, - )) - } -} - -func testPermUpdate(store *permStore, user *core.User, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - before := &core.Perm{ - UserID: user.ID, - RepoUID: repo.UID, - Read: true, - Write: true, - Admin: true, - } - err := store.Update(noContext, before) - if err != nil { - t.Error(err) - return - } - after, err := store.Find(noContext, before.RepoUID, before.UserID) - if err != nil { - t.Error(err) - return - } - if got, want := after.Admin, before.Admin; got != want { - t.Errorf("Want updated Admin %v, got %v", want, got) - } - } -} - -func testPermDelete(store *permStore, user *core.User, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - err := store.Delete(noContext, &core.Perm{UserID: user.ID, RepoUID: repo.UID}) - if err != nil { - t.Error(err) - return - } - _, err = store.Find(noContext, "3", user.ID) - if got, want := sql.ErrNoRows, err; got != want { - t.Errorf("Want sql.ErrNoRows, got %v", got) - return - } - } -} - -func testPerm(item *core.Perm) func(t *testing.T) { - return func(t *testing.T) { - if got, want := item.Read, true; got != want { - t.Errorf("Want Read %v, got %v", want, got) - } - if got, want := item.Write, true; got != want { - t.Errorf("Want Write %v, got %v", want, got) - } - if got, want := item.Admin, false; got != want { - t.Errorf("Want Admin %v, got %v", want, got) - } - } -} diff --git a/store/perm/scan.go b/store/perm/scan.go deleted file mode 100644 index d56c09d20f..0000000000 --- a/store/perm/scan.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package perm - -import ( - "database/sql" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// helper function converts the Perm structure to a set -// of named query parameters. -func toParams(perm *core.Perm) map[string]interface{} { - return map[string]interface{}{ - "perm_user_id": perm.UserID, - "perm_repo_uid": perm.RepoUID, - "perm_read": perm.Read, - "perm_write": perm.Write, - "perm_admin": perm.Admin, - "perm_synced": perm.Synced, - "perm_created": perm.Created, - "perm_updated": perm.Updated, - } -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(scanner db.Scanner, dst *core.Perm) error { - return scanner.Scan( - &dst.UserID, - &dst.RepoUID, - &dst.Read, - &dst.Write, - &dst.Admin, - &dst.Synced, - &dst.Created, - &dst.Updated, - ) -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanCollabRow(scanner db.Scanner, dst *core.Collaborator) error { - return scanner.Scan( - &dst.UserID, - &dst.RepoUID, - &dst.Login, - &dst.Avatar, - &dst.Read, - &dst.Write, - &dst.Admin, - &dst.Synced, - &dst.Created, - &dst.Updated, - ) -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanCollabRows(rows *sql.Rows) ([]*core.Collaborator, error) { - defer rows.Close() - - collabs := []*core.Collaborator{} - for rows.Next() { - collab := new(core.Collaborator) - err := scanCollabRow(rows, collab) - if err != nil { - return nil, err - } - collabs = append(collabs, collab) - } - return collabs, nil -} diff --git a/store/repos/repos.go b/store/repos/repos.go deleted file mode 100644 index ac883bf692..0000000000 --- a/store/repos/repos.go +++ /dev/null @@ -1,585 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new RepositoryStore. -func New(db *db.DB) core.RepositoryStore { - return &repoStore{db} -} - -type repoStore struct { - db *db.DB -} - -func (s *repoStore) List(ctx context.Context, id int64) ([]*core.Repository, error) { - var out []*core.Repository - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"user_id": id} - query, args, err := binder.BindNamed(queryPerms, params) - if err != nil { - return err - } - rows, err := queryer.Query(query, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *repoStore) ListLatest(ctx context.Context, id int64) ([]*core.Repository, error) { - var out []*core.Repository - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{ - "user_id": id, - "repo_active": true, - } - stmt := queryRepoWithBuild - if s.db.Driver() == db.Postgres { - stmt = queryRepoWithBuildPostgres - } - query, args, err := binder.BindNamed(stmt, params) - if err != nil { - return err - } - rows, err := queryer.Query(query, args...) - if err != nil { - return err - } - out, err = scanRowsBuild(rows) - return err - }) - return out, err -} - -func (s *repoStore) ListRecent(ctx context.Context, id int64) ([]*core.Repository, error) { - var out []*core.Repository - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"user_id": id} - query, args, err := binder.BindNamed(queryRepoWithBuildAll, params) - if err != nil { - return err - } - rows, err := queryer.Query(query, args...) - if err != nil { - return err - } - out, err = scanRowsBuild(rows) - return err - }) - return out, err -} - -func (s *repoStore) ListIncomplete(ctx context.Context) ([]*core.Repository, error) { - var out []*core.Repository - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - rows, err := queryer.Query(queryRepoWithBuildIncomplete) - if err != nil { - return err - } - out, err = scanRowsBuild(rows) - return err - }) - return out, err -} - -func (s *repoStore) ListRunningStatus(ctx context.Context) ([]*core.RepoBuildStage, error) { - var out []*core.RepoBuildStage - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - rows, err := queryer.Query(queryReposRunningStatus) - if err != nil { - return err - } - out, err = repoBuildStageRowsBuild(rows) - return err - }) - return out, err -} - -func (s *repoStore) ListAll(ctx context.Context, limit, offset int) ([]*core.Repository, error) { - var out []*core.Repository - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{ - "limit": limit, - "offset": offset, - } - query, args, err := binder.BindNamed(queryAll, params) - if err != nil { - return err - } - rows, err := queryer.Query(query, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *repoStore) Find(ctx context.Context, id int64) (*core.Repository, error) { - out := &core.Repository{ID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := ToParams(out) - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *repoStore) FindName(ctx context.Context, namespace, name string) (*core.Repository, error) { - out := &core.Repository{Slug: namespace + "/" + name} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := ToParams(out) - query, args, err := binder.BindNamed(querySlug, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *repoStore) Create(ctx context.Context, repo *core.Repository) error { - if s.db.Driver() == db.Postgres { - return s.createPostgres(ctx, repo) - } - return s.create(ctx, repo) -} - -func (s *repoStore) create(ctx context.Context, repo *core.Repository) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - repo.Version = 1 // set the initial record version - params := ToParams(repo) - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - repo.ID, err = res.LastInsertId() - return err - }) -} - -func (s *repoStore) createPostgres(ctx context.Context, repo *core.Repository) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - repo.Version = 1 // set the initial record version - params := ToParams(repo) - stmt, args, err := binder.BindNamed(stmtInsertPg, params) - if err != nil { - return err - } - return execer.QueryRow(stmt, args...).Scan(&repo.ID) - }) -} - -func (s *repoStore) Activate(ctx context.Context, repo *core.Repository) error { - return s.Update(ctx, repo) -} - -func (s *repoStore) Update(ctx context.Context, repo *core.Repository) error { - versionNew := repo.Version + 1 - versionOld := repo.Version - err := s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := ToParams(repo) - params["repo_version_old"] = versionOld - params["repo_version_new"] = versionNew - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - effected, err := res.RowsAffected() - if err != nil { - return err - } - if effected == 0 { - return db.ErrOptimisticLock - } - return nil - }) - if err == nil { - repo.Version = versionNew - } - return err -} - -func (s *repoStore) Delete(ctx context.Context, repo *core.Repository) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := ToParams(repo) - stmt, args, _ := binder.BindNamed(stmtDelete, params) - _, err := execer.Exec(stmt, args...) - return err - }) -} - -func (s *repoStore) Count(ctx context.Context) (i int64, err error) { - err = s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"repo_active": true} - query, args, _ := binder.BindNamed(queryCount, params) - return queryer.QueryRow(query, args...).Scan(&i) - }) - return -} - -func (s *repoStore) Increment(ctx context.Context, repo *core.Repository) (*core.Repository, error) { - for { - repo.Counter++ - err := s.Update(ctx, repo) - if err == nil { - return repo, nil - } - if err != nil && err != db.ErrOptimisticLock { - return repo, err - } - repo, err = s.Find(ctx, repo.ID) - if err != nil { - return nil, err - } - } -} - -const queryCount = ` -SELECT count(*) -FROM repos -WHERE repo_active = :repo_active -` - -const queryCols = ` -SELECT - repo_id -,repo_uid -,repo_user_id -,repo_namespace -,repo_name -,repo_slug -,repo_scm -,repo_clone_url -,repo_ssh_url -,repo_html_url -,repo_active -,repo_private -,repo_visibility -,repo_branch -,repo_counter -,repo_config -,repo_timeout -,repo_throttle -,repo_trusted -,repo_protected -,repo_no_forks -,repo_no_pulls -,repo_cancel_pulls -,repo_cancel_push -,repo_cancel_running -,repo_synced -,repo_created -,repo_updated -,repo_version -,repo_signer -,repo_secret -` - -const queryColsBuilds = queryCols + ` -,build_id -,build_repo_id -,build_trigger -,build_number -,build_parent -,build_status -,build_error -,build_event -,build_action -,build_link -,build_timestamp -,build_title -,build_message -,build_before -,build_after -,build_ref -,build_source_repo -,build_source -,build_target -,build_author -,build_author_name -,build_author_email -,build_author_avatar -,build_sender -,build_params -,build_cron -,build_deploy -,build_deploy_id -,build_debug -,build_started -,build_finished -,build_created -,build_updated -,build_version -` - -const queryKey = queryCols + ` -FROM repos -WHERE repo_id = :repo_id -` - -const querySlug = queryCols + ` -FROM repos -WHERE repo_slug = :repo_slug -` - -const queryPerms = queryCols + ` -FROM repos -INNER JOIN perms ON perms.perm_repo_uid = repos.repo_uid -WHERE perms.perm_user_id = :user_id -ORDER BY repo_slug ASC -` - -const queryAll = queryCols + ` -FROM repos -LIMIT :limit OFFSET :offset -` - -const stmtDelete = ` -DELETE FROM repos WHERE repo_id = :repo_id -` - -const stmtInsert = ` -INSERT INTO repos ( - repo_uid -,repo_user_id -,repo_namespace -,repo_name -,repo_slug -,repo_scm -,repo_clone_url -,repo_ssh_url -,repo_html_url -,repo_active -,repo_private -,repo_visibility -,repo_branch -,repo_counter -,repo_config -,repo_timeout -,repo_throttle -,repo_trusted -,repo_protected -,repo_no_forks -,repo_no_pulls -,repo_cancel_pulls -,repo_cancel_push -,repo_cancel_running -,repo_synced -,repo_created -,repo_updated -,repo_version -,repo_signer -,repo_secret -) VALUES ( - :repo_uid -,:repo_user_id -,:repo_namespace -,:repo_name -,:repo_slug -,:repo_scm -,:repo_clone_url -,:repo_ssh_url -,:repo_html_url -,:repo_active -,:repo_private -,:repo_visibility -,:repo_branch -,:repo_counter -,:repo_config -,:repo_timeout -,:repo_throttle -,:repo_trusted -,:repo_protected -,:repo_no_forks -,:repo_no_pulls -,:repo_cancel_pulls -,:repo_cancel_push -,:repo_cancel_running -,:repo_synced -,:repo_created -,:repo_updated -,:repo_version -,:repo_signer -,:repo_secret -) -` - -const stmtInsertPg = stmtInsert + ` -RETURNING repo_id -` - -const stmtPermInsert = ` -INSERT INTO perms VALUES ( - :perm_user_id -,:perm_repo_uid -,:perm_read -,:perm_write -,:perm_admin -,:perm_synced -,:perm_created -,:perm_updated -) -` - -const stmtUpdate = ` -UPDATE repos SET - repo_user_id = :repo_user_id -,repo_namespace = :repo_namespace -,repo_name = :repo_name -,repo_slug = :repo_slug -,repo_scm = :repo_scm -,repo_clone_url = :repo_clone_url -,repo_ssh_url = :repo_ssh_url -,repo_html_url = :repo_html_url -,repo_branch = :repo_branch -,repo_private = :repo_private -,repo_visibility = :repo_visibility -,repo_active = :repo_active -,repo_config = :repo_config -,repo_trusted = :repo_trusted -,repo_protected = :repo_protected -,repo_no_forks = :repo_no_forks -,repo_no_pulls = :repo_no_pulls -,repo_cancel_pulls = :repo_cancel_pulls -,repo_cancel_push = :repo_cancel_push -,repo_cancel_running = :repo_cancel_running -,repo_timeout = :repo_timeout -,repo_throttle = :repo_throttle -,repo_counter = :repo_counter -,repo_synced = :repo_synced -,repo_created = :repo_created -,repo_updated = :repo_updated -,repo_version = :repo_version_new -,repo_signer = :repo_signer -,repo_secret = :repo_secret -WHERE repo_id = :repo_id - AND repo_version = :repo_version_old -` - -// TODO(bradrydzewski) this query needs performance tuning. -// one approach that is promising is the ability to use the -// repo_counter (latest build number) to join on the build -// table. -// -// FROM repos LEFT OUTER JOIN builds ON ( -// repos.repo_id = builds.build_repo_id AND -// builds.build_number = repos.repo_counter -// ) -// INNER JOIN perms ON perms.perm_repo_uid = repos.repo_uid -// - -const queryRepoWithBuild = queryColsBuilds + ` -FROM repos LEFT OUTER JOIN builds ON build_id = ( - SELECT build_id FROM builds - WHERE builds.build_repo_id = repos.repo_id - ORDER BY build_id DESC - LIMIT 1 -) -INNER JOIN perms ON perms.perm_repo_uid = repos.repo_uid -WHERE perms.perm_user_id = :user_id -ORDER BY repo_slug ASC -` - -const queryRepoWithBuildPostgres = queryColsBuilds + ` -FROM repos LEFT OUTER JOIN builds ON build_id = ( - SELECT DISTINCT ON (build_repo_id) build_id FROM builds - WHERE builds.build_repo_id = repos.repo_id - ORDER BY build_repo_id, build_id DESC -) -INNER JOIN perms ON perms.perm_repo_uid = repos.repo_uid -WHERE perms.perm_user_id = :user_id -ORDER BY repo_slug ASC -` - -const queryRepoWithBuildAll = queryColsBuilds + ` -FROM repos -INNER JOIN perms ON perms.perm_repo_uid = repos.repo_uid -INNER JOIN builds ON builds.build_repo_id = repos.repo_id -WHERE perms.perm_user_id = :user_id -ORDER BY build_id DESC -LIMIT 25; -` - -const queryRepoWithBuildIncomplete = queryColsBuilds + ` -FROM repos -INNER JOIN builds ON builds.build_repo_id = repos.repo_id -WHERE EXISTS ( - SELECT stage_id - FROM stages - WHERE stages.stage_build_id = builds.build_id - AND stages.stage_status IN ('pending', 'running') -) -ORDER BY build_id DESC -LIMIT 50; -` -const queryReposRunningStatus = ` -SELECT -repo_namespace -,repo_name -,repo_slug -,build_number -,build_author -,build_author_name -,build_author_email -,build_author_avatar -,build_sender -,build_started -,build_finished -,build_created -,build_updated -,stage_name -,stage_kind -,stage_type -,stage_status -,stage_machine -,stage_os -,stage_arch -,stage_variant -,stage_kernel -,stage_limit -,stage_limit_repo -,stage_started -,stage_stopped -FROM repos -INNER JOIN builds ON builds.build_repo_id = repos.repo_id -inner join stages on stages.stage_build_id = builds.build_id -where stages.stage_status IN ('pending', 'running') -ORDER BY build_id DESC; -` diff --git a/store/repos/repos_test.go b/store/repos/repos_test.go deleted file mode 100644 index 2506a7a6c4..0000000000 --- a/store/repos/repos_test.go +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package repos - -import ( - "context" - "encoding/json" - "io/ioutil" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/db/dbtest" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -var noContext = context.TODO() - -func TestRepo(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - store := New(conn).(*repoStore) - t.Run("Create", testRepoCreate(store)) - t.Run("Count", testRepoCount(store)) - t.Run("Find", testRepoFind(store)) - t.Run("FindName", testRepoFindName(store)) - t.Run("List", testRepoList(store)) - t.Run("ListLatest", testRepoListLatest(store)) - t.Run("Update", testRepoUpdate(store)) - t.Run("Activate", testRepoActivate(store)) - t.Run("Locking", testRepoLocking(store)) - t.Run("Increment", testRepoIncrement(store)) - t.Run("Delete", testRepoDelete(store)) -} - -func testRepoCreate(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - out, err := ioutil.ReadFile("testdata/repo.json") - if err != nil { - t.Error(err) - return - } - repo := &core.Repository{} - err = json.Unmarshal(out, repo) - if err != nil { - t.Error(err) - return - } - err = repos.Create(noContext, repo) - if err != nil { - t.Error(err) - } - if got := repo.ID; got == 0 { - t.Errorf("Want non-zero ID") - } - if got, want := repo.Version, int64(1); got != want { - t.Errorf("Want Version %d, got %d", want, got) - } - - err = repos.db.Update(func(execer db.Execer, binder db.Binder) error { - query, args, _ := binder.BindNamed(stmtPermInsert, map[string]interface{}{ - "perm_user_id": 1, - "perm_repo_uid": repo.UID, - "perm_read": true, - "perm_write": true, - "perm_admin": true, - "perm_synced": 0, - "perm_created": 0, - "perm_updated": 0, - }) - _, err = execer.Exec(query, args...) - return err - }) - if err != nil { - t.Error(err) - } - } -} - -func testRepoCount(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - count, err := repos.Count(noContext) - if err != nil { - t.Error(err) - } - if got, want := count, int64(1); got != want { - t.Errorf("Want count %d, got %d", want, got) - } - } -} - -func testRepoFind(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - named, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Error(err) - return - } - got, err := repos.Find(noContext, named.ID) - if err != nil { - t.Error(err) - return - } - - want := &core.Repository{} - raw, err := ioutil.ReadFile("testdata/repo.json.golden") - if err != nil { - t.Error(err) - return - } - err = json.Unmarshal(raw, want) - if err != nil { - t.Error(err) - return - } - - ignore := cmpopts.IgnoreFields(core.Repository{}, "ID") - if diff := cmp.Diff(got, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } - } -} - -func testRepoFindName(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - got, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Error(err) - return - } - - want := &core.Repository{} - raw, err := ioutil.ReadFile("testdata/repo.json.golden") - if err != nil { - t.Error(err) - return - } - err = json.Unmarshal(raw, want) - if err != nil { - t.Error(err) - return - } - - ignore := cmpopts.IgnoreFields(core.Repository{}, "ID") - if diff := cmp.Diff(got, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } - } -} - -func testRepoList(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - repos, err := repos.List(noContext, 1) - if err != nil { - t.Error(err) - return - } - if got, want := len(repos), 1; got != want { - t.Errorf("Want Repo count %d, got %d", want, got) - return - } - - if err != nil { - t.Error(err) - return - } - - got, want := repos[0], &core.Repository{} - raw, err := ioutil.ReadFile("testdata/repo.json.golden") - if err != nil { - t.Error(err) - return - } - err = json.Unmarshal(raw, want) - if err != nil { - t.Error(err) - return - } - - ignore := cmpopts.IgnoreFields(core.Repository{}, "ID") - if diff := cmp.Diff(got, want, ignore); len(diff) != 0 { - t.Errorf(diff) - } - } -} - -func testRepoListLatest(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - repos, err := repos.ListLatest(noContext, 1) - if err != nil { - t.Error(err) - return - } - if got, want := len(repos), 1; got != want { - t.Errorf("Want Repo count %d, got %d", want, got) - } else if repos[0].Build != nil { - t.Errorf("Expect nil build") - } else { - t.Run("Fields", testRepo(repos[0])) - } - } -} - -func testRepoUpdate(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - before, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Error(err) - return - } - - version := before.Version - before.Private = true - err = repos.Update(noContext, before) - if err != nil { - t.Error(err) - return - } - after, err := repos.Find(noContext, before.ID) - if err != nil { - t.Error(err) - return - } - if got, want := after.Version, version+1; got != want { - t.Errorf("Want version incremented on update") - } - if got, want := before.Private, after.Private; got != want { - t.Errorf("Want updated Repo private %v, got %v", want, got) - } - } -} - -func testRepoActivate(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - before, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Error(err) - return - } - before.Active = true - err = repos.Activate(noContext, before) - if err != nil { - t.Error(err) - return - } - after, err := repos.Find(noContext, before.ID) - if err != nil { - t.Error(err) - return - } - if got, want := before.Active, after.Active; got != want { - t.Errorf("Want updated Repo Active %v, got %v", want, got) - } - } -} - -func testRepoLocking(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - repo, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Error(err) - return - } - repo.Version = 1 - err = repos.Update(noContext, repo) - if err == nil { - t.Errorf("Want Optimistic Lock Error, got nil") - } else if err != db.ErrOptimisticLock { - t.Errorf("Want Optimistic Lock Error") - } - } -} - -func testRepoIncrement(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - repo, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - t.Error(err) - return - } - before := repo.Counter - repo.Version-- - repo, err = repos.Increment(noContext, repo) - if err != nil { - t.Error(err) - return - } - if got, want := repo.Counter, before+1; got != want { - t.Errorf("Want count incremented to %d, got %d", want, got) - } - } -} - -func testRepoDelete(repos *repoStore) func(t *testing.T) { - return func(t *testing.T) { - count, _ := repos.Count(noContext) - if got, want := count, int64(1); got != want { - t.Errorf("Want Repo table count %d, got %d", want, got) - return - } - - repo, err := repos.FindName(noContext, "octocat", "hello-world") - if err != nil { - return - } - - err = repos.Delete(noContext, repo) - if err != nil { - t.Error(err) - } - - count, _ = repos.Count(noContext) - if got, want := count, int64(0); got != want { - t.Errorf("Want Repo table count %d, got %d", want, got) - return - } - } -} - -func testRepo(repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - if got, want := repo.UserID, int64(1); got != want { - t.Errorf("Want UserID %d, got %d", want, got) - } - if got, want := repo.Namespace, "octocat"; got != want { - t.Errorf("Want Namespace %q, got %q", want, got) - } - if got, want := repo.Name, "hello-world"; got != want { - t.Errorf("Want Name %q, got %q", want, got) - } - if got, want := repo.Slug, "octocat/hello-world"; got != want { - t.Errorf("Want Slug %q, got %q", want, got) - } - if got, want := repo.UID, "42"; got != want { - t.Errorf("Want UID %q, got %q", want, got) - } - } -} diff --git a/store/repos/scan.go b/store/repos/scan.go deleted file mode 100644 index 9ddbd13202..0000000000 --- a/store/repos/scan.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "database/sql" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// ToParams converts the Repository structure to a set -// of named query parameters. -func ToParams(v *core.Repository) map[string]interface{} { - return map[string]interface{}{ - "repo_id": v.ID, - "repo_uid": v.UID, - "repo_user_id": v.UserID, - "repo_namespace": v.Namespace, - "repo_name": v.Name, - "repo_slug": v.Slug, - "repo_scm": v.SCM, - "repo_clone_url": v.HTTPURL, - "repo_ssh_url": v.SSHURL, - "repo_html_url": v.Link, - "repo_branch": v.Branch, - "repo_private": v.Private, - "repo_visibility": v.Visibility, - "repo_active": v.Active, - "repo_config": v.Config, - "repo_trusted": v.Trusted, - "repo_protected": v.Protected, - "repo_no_forks": v.IgnoreForks, - "repo_no_pulls": v.IgnorePulls, - "repo_cancel_pulls": v.CancelPulls, - "repo_cancel_push": v.CancelPush, - "repo_cancel_running": v.CancelRunning, - "repo_timeout": v.Timeout, - "repo_throttle": v.Throttle, - "repo_counter": v.Counter, - "repo_synced": v.Synced, - "repo_created": v.Created, - "repo_updated": v.Updated, - "repo_version": v.Version, - "repo_signer": v.Signer, - "repo_secret": v.Secret, - } -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(scanner db.Scanner, dest *core.Repository) error { - return scanner.Scan( - &dest.ID, - &dest.UID, - &dest.UserID, - &dest.Namespace, - &dest.Name, - &dest.Slug, - &dest.SCM, - &dest.HTTPURL, - &dest.SSHURL, - &dest.Link, - &dest.Active, - &dest.Private, - &dest.Visibility, - &dest.Branch, - &dest.Counter, - &dest.Config, - &dest.Timeout, - &dest.Throttle, - &dest.Trusted, - &dest.Protected, - &dest.IgnoreForks, - &dest.IgnorePulls, - &dest.CancelPulls, - &dest.CancelPush, - &dest.CancelRunning, - &dest.Synced, - &dest.Created, - &dest.Updated, - &dest.Version, - &dest.Signer, - &dest.Secret, - ) -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRows(rows *sql.Rows) ([]*core.Repository, error) { - defer rows.Close() - - repos := []*core.Repository{} - for rows.Next() { - repo := new(core.Repository) - err := scanRow(rows, repo) - if err != nil { - return nil, err - } - repos = append(repos, repo) - } - return repos, nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRowBuild(scanner db.Scanner, dest *core.Repository) error { - build := new(nullBuild) - err := scanner.Scan( - &dest.ID, - &dest.UID, - &dest.UserID, - &dest.Namespace, - &dest.Name, - &dest.Slug, - &dest.SCM, - &dest.HTTPURL, - &dest.SSHURL, - &dest.Link, - &dest.Active, - &dest.Private, - &dest.Visibility, - &dest.Branch, - &dest.Counter, - &dest.Config, - &dest.Timeout, - &dest.Throttle, - &dest.Trusted, - &dest.Protected, - &dest.IgnoreForks, - &dest.IgnorePulls, - &dest.CancelPulls, - &dest.CancelPush, - &dest.CancelRunning, - &dest.Synced, - &dest.Created, - &dest.Updated, - &dest.Version, - &dest.Signer, - &dest.Secret, - // build parameters - &build.ID, - &build.RepoID, - &build.Trigger, - &build.Number, - &build.Parent, - &build.Status, - &build.Error, - &build.Event, - &build.Action, - &build.Link, - &build.Timestamp, - &build.Title, - &build.Message, - &build.Before, - &build.After, - &build.Ref, - &build.Fork, - &build.Source, - &build.Target, - &build.Author, - &build.AuthorName, - &build.AuthorEmail, - &build.AuthorAvatar, - &build.Sender, - &build.Params, - &build.Cron, - &build.Deploy, - &build.DeployID, - &build.Debug, - &build.Started, - &build.Finished, - &build.Created, - &build.Updated, - &build.Version, - ) - if build.ID.Int64 != 0 { - dest.Build = build.value() - } - return err -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRowsBuild(rows *sql.Rows) ([]*core.Repository, error) { - defer rows.Close() - - repos := []*core.Repository{} - for rows.Next() { - repo := new(core.Repository) - err := scanRowBuild(rows, repo) - if err != nil { - return nil, err - } - repos = append(repos, repo) - } - return repos, nil -} - -// helper function scans the sql.Row and copies the column values to the destination object. -func repoBuildStageRowBuild(scanner db.Scanner, dest *core.RepoBuildStage) error { - err := scanner.Scan( - &dest.RepoNamespace, - &dest.RepoName, - &dest.RepoSlug, - &dest.BuildNumber, - &dest.BuildAuthor, - &dest.BuildAuthorName, - &dest.BuildAuthorEmail, - &dest.BuildAuthorAvatar, - &dest.BuildSender, - &dest.BuildStarted, - &dest.BuildFinished, - &dest.BuildCreated, - &dest.BuildUpdated, - &dest.StageName, - &dest.StageKind, - &dest.StageType, - &dest.StageStatus, - &dest.StageMachine, - &dest.StageOS, - &dest.StageArch, - &dest.StageVariant, - &dest.StageKernel, - &dest.StageLimit, - &dest.StageLimitRepo, - &dest.StageStarted, - &dest.StageStopped, - ) - return err -} - -// helper function scans the sql.Row and copies the column values to the destination object. -func repoBuildStageRowsBuild(rows *sql.Rows) ([]*core.RepoBuildStage, error) { - defer rows.Close() - - slices := []*core.RepoBuildStage{} - for rows.Next() { - row := new(core.RepoBuildStage) - err := repoBuildStageRowBuild(rows, row) - if err != nil { - return nil, err - } - slices = append(slices, row) - } - return slices, nil -} diff --git a/store/repos/scan_test.go b/store/repos/scan_test.go deleted file mode 100644 index 5b8603a32f..0000000000 --- a/store/repos/scan_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package repos diff --git a/store/repos/testdata/repo.json b/store/repos/testdata/repo.json deleted file mode 100644 index 524770448e..0000000000 --- a/store/repos/testdata/repo.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "id": 0, - "uid": "42", - "user_id": 1, - "namespace": "octocat", - "name": "hello-world", - "slug": "octocat/hello-world", - "scm": "git", - "git_http_url": "https://github.com/octocat/hello-world.git", - "git_ssh_url": "git@github.com:octocat/hello-world.git", - "link": "https://github.com/octocat/hello-world", - "default_branch": "master", - "private": true, - "visibility": "internal", - "active": true, - "config_path": ".drone.yml", - "trusted": true, - "timeout": 900, - "counter": 22, - "synced": 0, - "created": 0, - "updated": 0, - "version": 0, - "endpoints": { - "approvals": { - "endpoint": "https://requestb.in/912ec803", - "signer": "22cf869033dc0b1a0da65d2c478e4bf82ad9470d4c34e1d9b583af0b0840380d", - "skip_verify": true - }, - "config": { - "endpoint": "https://requestb.in/b2ce49e4", - "signer": "0860baa72c42a0e438f9361b077235858bfb0e526a14c609d28b695e212cd9e1", - "skip_verify": true - }, - "registry": { - "endpoint": "https://requestb.in/a541068d", - "secret": "236ce83561fae0a93f25777e2e60bb59", - "signer": "0860baa72c42a0e438f9361b077235858bfb0e526a14c609d28b695e212cd9e1", - "skip_verify": true - }, - "secrets": { - "endpoint": "https://requestb.in/4c64bfa5", - "secret": "2cb65bd9607673c3b6ab7b103b451e54", - "signer": "5e21d95b66146f1ea3008be4b6d89ef5a898c1489f449353102fb43bb3f11595", - "skip_verify": true - }, - "webhook": { - "endpoint": "https://requestb.in/0043cf7d", - "signer": "d1b9f36dec2de09a2a7f7cc9ed3619b50de5fee8af8ad739ecf1fa53356984e0", - "skip_verify": true - } - } -} \ No newline at end of file diff --git a/store/repos/testdata/repo.json.golden b/store/repos/testdata/repo.json.golden deleted file mode 100644 index 9ddf8735ef..0000000000 --- a/store/repos/testdata/repo.json.golden +++ /dev/null @@ -1,53 +0,0 @@ -{ - "id": 0, - "uid": "42", - "user_id": 1, - "namespace": "octocat", - "name": "hello-world", - "slug": "octocat/hello-world", - "scm": "git", - "git_http_url": "https://github.com/octocat/hello-world.git", - "git_ssh_url": "git@github.com:octocat/hello-world.git", - "link": "https://github.com/octocat/hello-world", - "default_branch": "master", - "private": true, - "visibility": "internal", - "active": true, - "config_path": ".drone.yml", - "trusted": true, - "timeout": 900, - "counter": 22, - "synced": 0, - "created": 0, - "updated": 0, - "version": 1, - "endpoints": { - "approvals": { - "endpoint": "https://requestb.in/912ec803", - "signer": "22cf869033dc0b1a0da65d2c478e4bf82ad9470d4c34e1d9b583af0b0840380d", - "skip_verify": true - }, - "config": { - "endpoint": "https://requestb.in/b2ce49e4", - "signer": "0860baa72c42a0e438f9361b077235858bfb0e526a14c609d28b695e212cd9e1", - "skip_verify": true - }, - "registry": { - "endpoint": "https://requestb.in/a541068d", - "secret": "236ce83561fae0a93f25777e2e60bb59", - "signer": "0860baa72c42a0e438f9361b077235858bfb0e526a14c609d28b695e212cd9e1", - "skip_verify": true - }, - "secrets": { - "endpoint": "https://requestb.in/4c64bfa5", - "secret": "2cb65bd9607673c3b6ab7b103b451e54", - "signer": "5e21d95b66146f1ea3008be4b6d89ef5a898c1489f449353102fb43bb3f11595", - "skip_verify": true - }, - "webhook": { - "endpoint": "https://requestb.in/0043cf7d", - "signer": "d1b9f36dec2de09a2a7f7cc9ed3619b50de5fee8af8ad739ecf1fa53356984e0", - "skip_verify": true - } - } -} \ No newline at end of file diff --git a/store/repos/type.go b/store/repos/type.go deleted file mode 100644 index f66cb64f0b..0000000000 --- a/store/repos/type.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package repos - -import ( - "database/sql" - "encoding/json" - - "github.com/drone/drone/core" - - "github.com/jmoiron/sqlx/types" -) - -type nullBuild struct { - ID sql.NullInt64 - RepoID sql.NullInt64 - ConfigID sql.NullInt64 - Trigger sql.NullString - Number sql.NullInt64 - Parent sql.NullInt64 - Status sql.NullString - Error sql.NullString - Event sql.NullString - Action sql.NullString - Link sql.NullString - Timestamp sql.NullInt64 - Title sql.NullString - Message sql.NullString - Before sql.NullString - After sql.NullString - Ref sql.NullString - Fork sql.NullString - Source sql.NullString - Target sql.NullString - Author sql.NullString - AuthorName sql.NullString - AuthorEmail sql.NullString - AuthorAvatar sql.NullString - Sender sql.NullString - Params types.JSONText - Cron sql.NullString - Deploy sql.NullString - DeployID sql.NullInt64 - Debug sql.NullBool - Started sql.NullInt64 - Finished sql.NullInt64 - Created sql.NullInt64 - Updated sql.NullInt64 - Version sql.NullInt64 -} - -func (b *nullBuild) value() *core.Build { - params := map[string]string{} - json.Unmarshal(b.Params, ¶ms) - - build := &core.Build{ - ID: b.ID.Int64, - RepoID: b.RepoID.Int64, - Trigger: b.Trigger.String, - Number: b.Number.Int64, - Parent: b.Parent.Int64, - Status: b.Status.String, - Error: b.Error.String, - Event: b.Event.String, - Action: b.Action.String, - Link: b.Link.String, - Timestamp: b.Timestamp.Int64, - Title: b.Title.String, - Message: b.Message.String, - Before: b.Before.String, - After: b.After.String, - Ref: b.Ref.String, - Fork: b.Fork.String, - Source: b.Source.String, - Target: b.Target.String, - Author: b.Author.String, - AuthorName: b.AuthorName.String, - AuthorEmail: b.AuthorEmail.String, - AuthorAvatar: b.AuthorAvatar.String, - Sender: b.Sender.String, - Params: params, - Cron: b.Cron.String, - Deploy: b.Deploy.String, - DeployID: b.DeployID.Int64, - Debug: b.Debug.Bool, - Started: b.Started.Int64, - Finished: b.Finished.Int64, - Created: b.Created.Int64, - Updated: b.Updated.Int64, - Version: b.Version.Int64, - } - return build -} diff --git a/store/secret/global/scan.go b/store/secret/global/scan.go deleted file mode 100644 index 8b975aac91..0000000000 --- a/store/secret/global/scan.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package global - -import ( - "database/sql" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/encrypt" -) - -// helper function converts the User structure to a set -// of named query parameters. -func toParams(encrypt encrypt.Encrypter, secret *core.Secret) (map[string]interface{}, error) { - ciphertext, err := encrypt.Encrypt(secret.Data) - if err != nil { - return nil, err - } - return map[string]interface{}{ - "secret_id": secret.ID, - "secret_namespace": secret.Namespace, - "secret_name": secret.Name, - "secret_type": secret.Type, - "secret_data": ciphertext, - "secret_pull_request": secret.PullRequest, - "secret_pull_request_push": secret.PullRequestPush, - }, nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(encrypt encrypt.Encrypter, scanner db.Scanner, dst *core.Secret) error { - var ciphertext []byte - err := scanner.Scan( - &dst.ID, - &dst.Namespace, - &dst.Name, - &dst.Type, - &ciphertext, - &dst.PullRequest, - &dst.PullRequestPush, - ) - if err != nil { - return err - } - plaintext, err := encrypt.Decrypt(ciphertext) - if err != nil { - return err - } - dst.Data = plaintext - return nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRows(encrypt encrypt.Encrypter, rows *sql.Rows) ([]*core.Secret, error) { - defer rows.Close() - - secrets := []*core.Secret{} - for rows.Next() { - sec := new(core.Secret) - err := scanRow(encrypt, rows, sec) - if err != nil { - return nil, err - } - secrets = append(secrets, sec) - } - return secrets, nil -} diff --git a/store/secret/global/secret.go b/store/secret/global/secret.go deleted file mode 100644 index 1f37e56f97..0000000000 --- a/store/secret/global/secret.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package global - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/encrypt" -) - -// New returns a new global Secret database store. -func New(db *db.DB, enc encrypt.Encrypter) core.GlobalSecretStore { - return &secretStore{ - db: db, - enc: enc, - } -} - -type secretStore struct { - db *db.DB - enc encrypt.Encrypter -} - -func (s *secretStore) List(ctx context.Context, namespace string) ([]*core.Secret, error) { - var out []*core.Secret - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"secret_namespace": namespace} - stmt, args, err := binder.BindNamed(queryNamespace, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(s.enc, rows) - return err - }) - return out, err -} - -func (s *secretStore) ListAll(ctx context.Context) ([]*core.Secret, error) { - var out []*core.Secret - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - rows, err := queryer.Query(queryAll) - if err != nil { - return err - } - out, err = scanRows(s.enc, rows) - return err - }) - return out, err -} - -func (s *secretStore) Find(ctx context.Context, id int64) (*core.Secret, error) { - out := &core.Secret{ID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params, err := toParams(s.enc, out) - if err != nil { - return err - } - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(s.enc, row, out) - }) - return out, err -} - -func (s *secretStore) FindName(ctx context.Context, namespace, name string) (*core.Secret, error) { - out := &core.Secret{Name: name, Namespace: namespace} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params, err := toParams(s.enc, out) - if err != nil { - return err - } - query, args, err := binder.BindNamed(queryName, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(s.enc, row, out) - }) - return out, err -} - -func (s *secretStore) Create(ctx context.Context, secret *core.Secret) error { - if s.db.Driver() == db.Postgres { - return s.createPostgres(ctx, secret) - } - return s.create(ctx, secret) -} - -func (s *secretStore) create(ctx context.Context, secret *core.Secret) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, secret) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - secret.ID, err = res.LastInsertId() - return err - }) -} - -func (s *secretStore) createPostgres(ctx context.Context, secret *core.Secret) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, secret) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtInsertPg, params) - if err != nil { - return err - } - return execer.QueryRow(stmt, args...).Scan(&secret.ID) - }) -} - -func (s *secretStore) Update(ctx context.Context, secret *core.Secret) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, secret) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -func (s *secretStore) Delete(ctx context.Context, secret *core.Secret) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, secret) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtDelete, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -const queryBase = ` -SELECT - secret_id -,secret_namespace -,secret_name -,secret_type -,secret_data -,secret_pull_request -,secret_pull_request_push -` - -const queryKey = queryBase + ` -FROM orgsecrets -WHERE secret_id = :secret_id -LIMIT 1 -` - -const queryAll = queryBase + ` -FROM orgsecrets -ORDER BY secret_name -` - -const queryName = queryBase + ` -FROM orgsecrets -WHERE secret_name = :secret_name - AND secret_namespace = :secret_namespace -LIMIT 1 -` - -const queryNamespace = queryBase + ` -FROM orgsecrets -WHERE secret_namespace = :secret_namespace -ORDER BY secret_name -` - -const stmtUpdate = ` -UPDATE orgsecrets SET - secret_data = :secret_data -,secret_pull_request = :secret_pull_request -,secret_pull_request_push = :secret_pull_request_push -WHERE secret_id = :secret_id -` - -const stmtDelete = ` -DELETE FROM orgsecrets -WHERE secret_id = :secret_id -` - -const stmtInsert = ` -INSERT INTO orgsecrets ( - secret_namespace -,secret_name -,secret_type -,secret_data -,secret_pull_request -,secret_pull_request_push -) VALUES ( - :secret_namespace -,:secret_name -,:secret_type -,:secret_data -,:secret_pull_request -,:secret_pull_request_push -) -` - -const stmtInsertPg = stmtInsert + ` -RETURNING secret_id -` diff --git a/store/secret/global/secret_oss.go b/store/secret/global/secret_oss.go deleted file mode 100644 index 1ed7270324..0000000000 --- a/store/secret/global/secret_oss.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package global - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/encrypt" -) - -// New returns a new Secret database store. -func New(db *db.DB, enc encrypt.Encrypter) core.GlobalSecretStore { - return new(noop) -} - -type noop struct{} - -func (noop) List(context.Context, string) ([]*core.Secret, error) { - return nil, nil -} - -func (noop) ListAll(context.Context) ([]*core.Secret, error) { - return nil, nil -} - -func (noop) Find(context.Context, int64) (*core.Secret, error) { - return nil, nil -} - -func (noop) FindName(context.Context, string, string) (*core.Secret, error) { - return nil, nil -} - -func (noop) Create(context.Context, *core.Secret) error { - return nil -} - -func (noop) Update(context.Context, *core.Secret) error { - return nil -} - -func (noop) Delete(context.Context, *core.Secret) error { - return nil -} diff --git a/store/secret/global/secret_test.go b/store/secret/global/secret_test.go deleted file mode 100644 index fbbb8f45fb..0000000000 --- a/store/secret/global/secret_test.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package global - -import ( - "context" - "database/sql" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db/dbtest" - "github.com/drone/drone/store/shared/encrypt" -) - -var noContext = context.TODO() - -func TestSecret(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - store := New(conn, nil).(*secretStore) - store.enc, _ = encrypt.New("fb4b4d6267c8a5ce8231f8b186dbca92") - t.Run("Create", testSecretCreate(store)) -} - -func testSecretCreate(store *secretStore) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Secret{ - Namespace: "octocat", - Name: "password", - Data: "correct-horse-battery-staple", - } - err := store.Create(noContext, item) - if err != nil { - t.Error(err) - } - if item.ID == 0 { - t.Errorf("Want secret ID assigned, got %d", item.ID) - } - - t.Run("Find", testSecretFind(store, item)) - t.Run("FindName", testSecretFindName(store)) - t.Run("List", testSecretList(store)) - t.Run("ListAll", testSecretListAll(store)) - t.Run("Update", testSecretUpdate(store)) - t.Run("Delete", testSecretDelete(store)) - } -} - -func testSecretFind(store *secretStore, secret *core.Secret) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.Find(noContext, secret.ID) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testSecret(item)) - } - } -} - -func testSecretFindName(store *secretStore) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.FindName(noContext, "octocat", "password") - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testSecret(item)) - } - } -} - -func testSecretList(store *secretStore) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.List(noContext, "octocat") - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testSecret(list[0])) - } - } -} - -func testSecretListAll(store *secretStore) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.ListAll(noContext) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testSecret(list[0])) - } - } -} - -func testSecretUpdate(store *secretStore) func(t *testing.T) { - return func(t *testing.T) { - before, err := store.FindName(noContext, "octocat", "password") - if err != nil { - t.Error(err) - return - } - err = store.Update(noContext, before) - if err != nil { - t.Error(err) - return - } - after, err := store.Find(noContext, before.ID) - if err != nil { - t.Error(err) - return - } - if after == nil { - t.Fail() - } - } -} - -func testSecretDelete(store *secretStore) func(t *testing.T) { - return func(t *testing.T) { - secret, err := store.FindName(noContext, "octocat", "password") - if err != nil { - t.Error(err) - return - } - err = store.Delete(noContext, secret) - if err != nil { - t.Error(err) - return - } - _, err = store.Find(noContext, secret.ID) - if got, want := sql.ErrNoRows, err; got != want { - t.Errorf("Want sql.ErrNoRows, got %v", got) - return - } - } -} - -func testSecret(item *core.Secret) func(t *testing.T) { - return func(t *testing.T) { - if got, want := item.Name, "password"; got != want { - t.Errorf("Want secret name %q, got %q", want, got) - } - if got, want := item.Data, "correct-horse-battery-staple"; got != want { - t.Errorf("Want secret data %q, got %q", want, got) - } - } -} diff --git a/store/secret/scan.go b/store/secret/scan.go deleted file mode 100644 index f83df2ac8e..0000000000 --- a/store/secret/scan.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secret - -import ( - "database/sql" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/encrypt" -) - -// helper function converts the User structure to a set -// of named query parameters. -func toParams(encrypt encrypt.Encrypter, secret *core.Secret) (map[string]interface{}, error) { - ciphertext, err := encrypt.Encrypt(secret.Data) - if err != nil { - return nil, err - } - return map[string]interface{}{ - "secret_id": secret.ID, - "secret_repo_id": secret.RepoID, - "secret_name": secret.Name, - "secret_data": ciphertext, - "secret_pull_request": secret.PullRequest, - "secret_pull_request_push": secret.PullRequestPush, - }, nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(encrypt encrypt.Encrypter, scanner db.Scanner, dst *core.Secret) error { - var ciphertext []byte - err := scanner.Scan( - &dst.ID, - &dst.RepoID, - &dst.Name, - &ciphertext, - &dst.PullRequest, - &dst.PullRequestPush, - ) - if err != nil { - return err - } - plaintext, err := encrypt.Decrypt(ciphertext) - if err != nil { - return err - } - dst.Data = plaintext - return nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRows(encrypt encrypt.Encrypter, rows *sql.Rows) ([]*core.Secret, error) { - defer rows.Close() - - secrets := []*core.Secret{} - for rows.Next() { - sec := new(core.Secret) - err := scanRow(encrypt, rows, sec) - if err != nil { - return nil, err - } - secrets = append(secrets, sec) - } - return secrets, nil -} diff --git a/store/secret/secret.go b/store/secret/secret.go deleted file mode 100644 index 062eb3c47d..0000000000 --- a/store/secret/secret.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secret - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/encrypt" -) - -// New returns a new Secret database store. -func New(db *db.DB, enc encrypt.Encrypter) core.SecretStore { - return &secretStore{ - db: db, - enc: enc, - } -} - -type secretStore struct { - db *db.DB - enc encrypt.Encrypter -} - -func (s *secretStore) List(ctx context.Context, id int64) ([]*core.Secret, error) { - var out []*core.Secret - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"secret_repo_id": id} - stmt, args, err := binder.BindNamed(queryRepo, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(s.enc, rows) - return err - }) - return out, err -} - -func (s *secretStore) Find(ctx context.Context, id int64) (*core.Secret, error) { - out := &core.Secret{ID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params, err := toParams(s.enc, out) - if err != nil { - return err - } - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(s.enc, row, out) - }) - return out, err -} - -func (s *secretStore) FindName(ctx context.Context, id int64, name string) (*core.Secret, error) { - out := &core.Secret{Name: name, RepoID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params, err := toParams(s.enc, out) - if err != nil { - return err - } - query, args, err := binder.BindNamed(queryName, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(s.enc, row, out) - }) - return out, err -} - -func (s *secretStore) Create(ctx context.Context, secret *core.Secret) error { - if s.db.Driver() == db.Postgres { - return s.createPostgres(ctx, secret) - } - return s.create(ctx, secret) -} - -func (s *secretStore) create(ctx context.Context, secret *core.Secret) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, secret) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - secret.ID, err = res.LastInsertId() - return err - }) -} - -func (s *secretStore) createPostgres(ctx context.Context, secret *core.Secret) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, secret) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtInsertPg, params) - if err != nil { - return err - } - return execer.QueryRow(stmt, args...).Scan(&secret.ID) - }) -} - -func (s *secretStore) Update(ctx context.Context, secret *core.Secret) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, secret) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -func (s *secretStore) Delete(ctx context.Context, secret *core.Secret) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, secret) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtDelete, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -const queryBase = ` -SELECT - secret_id -,secret_repo_id -,secret_name -,secret_data -,secret_pull_request -,secret_pull_request_push -` - -const queryKey = queryBase + ` -FROM secrets -WHERE secret_id = :secret_id -LIMIT 1 -` - -const queryName = queryBase + ` -FROM secrets -WHERE secret_name = :secret_name - AND secret_repo_id = :secret_repo_id -LIMIT 1 -` - -const queryRepo = queryBase + ` -FROM secrets -WHERE secret_repo_id = :secret_repo_id -ORDER BY secret_name -` - -const stmtUpdate = ` -UPDATE secrets SET - secret_data = :secret_data -,secret_pull_request = :secret_pull_request -,secret_pull_request_push = :secret_pull_request_push -WHERE secret_id = :secret_id -` - -const stmtDelete = ` -DELETE FROM secrets -WHERE secret_id = :secret_id -` - -const stmtInsert = ` -INSERT INTO secrets ( - secret_repo_id -,secret_name -,secret_data -,secret_pull_request -,secret_pull_request_push -) VALUES ( - :secret_repo_id -,:secret_name -,:secret_data -,:secret_pull_request -,:secret_pull_request_push -) -` - -const stmtInsertPg = stmtInsert + ` -RETURNING secret_id -` diff --git a/store/secret/secret_oss.go b/store/secret/secret_oss.go deleted file mode 100644 index dff3486433..0000000000 --- a/store/secret/secret_oss.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package secret - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/encrypt" -) - -// New returns a new Secret database store. -func New(db *db.DB, enc encrypt.Encrypter) core.SecretStore { - return new(noop) -} - -type noop struct{} - -func (noop) List(ctx context.Context, id int64) ([]*core.Secret, error) { - return nil, nil -} - -func (noop) Find(ctx context.Context, id int64) (*core.Secret, error) { - return nil, nil -} - -func (noop) FindName(ctx context.Context, id int64, name string) (*core.Secret, error) { - return nil, nil -} - -func (noop) Create(ctx context.Context, secret *core.Secret) error { - return nil -} - -func (noop) Update(context.Context, *core.Secret) error { - return nil -} - -func (noop) Delete(context.Context, *core.Secret) error { - return nil -} diff --git a/store/secret/secret_test.go b/store/secret/secret_test.go deleted file mode 100644 index 08fcaaf2ea..0000000000 --- a/store/secret/secret_test.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package secret - -import ( - "context" - "database/sql" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db/dbtest" - "github.com/drone/drone/store/shared/encrypt" -) - -var noContext = context.TODO() - -func TestSecret(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - // seeds the database with a dummy repository. - repo := &core.Repository{UID: "1", Slug: "octocat/hello-world"} - repos := repos.New(conn) - if err := repos.Create(noContext, repo); err != nil { - t.Error(err) - } - - store := New(conn, nil).(*secretStore) - store.enc, _ = encrypt.New("fb4b4d6267c8a5ce8231f8b186dbca92") - t.Run("Create", testSecretCreate(store, repos, repo)) -} - -func testSecretCreate(store *secretStore, repos core.RepositoryStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Secret{ - RepoID: repo.ID, - Name: "password", - Data: "correct-horse-battery-staple", - } - err := store.Create(noContext, item) - if err != nil { - t.Error(err) - } - if item.ID == 0 { - t.Errorf("Want secret ID assigned, got %d", item.ID) - } - - t.Run("Find", testSecretFind(store, item)) - t.Run("FindName", testSecretFindName(store, repo)) - t.Run("List", testSecretList(store, repo)) - t.Run("Update", testSecretUpdate(store, repo)) - t.Run("Delete", testSecretDelete(store, repo)) - t.Run("Fkey", testSecretForeignKey(store, repos, repo)) - } -} - -func testSecretFind(store *secretStore, secret *core.Secret) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.Find(noContext, secret.ID) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testSecret(item)) - } - } -} - -func testSecretFindName(store *secretStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.FindName(noContext, repo.ID, "password") - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testSecret(item)) - } - } -} - -func testSecretList(store *secretStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.List(noContext, repo.ID) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testSecret(list[0])) - } - } -} - -func testSecretUpdate(store *secretStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - before, err := store.FindName(noContext, repo.ID, "password") - if err != nil { - t.Error(err) - return - } - err = store.Update(noContext, before) - if err != nil { - t.Error(err) - return - } - after, err := store.Find(noContext, before.ID) - if err != nil { - t.Error(err) - return - } - if after == nil { - t.Fail() - } - } -} - -func testSecretDelete(store *secretStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - secret, err := store.FindName(noContext, repo.ID, "password") - if err != nil { - t.Error(err) - return - } - err = store.Delete(noContext, secret) - if err != nil { - t.Error(err) - return - } - _, err = store.Find(noContext, secret.ID) - if got, want := sql.ErrNoRows, err; got != want { - t.Errorf("Want sql.ErrNoRows, got %v", got) - return - } - } -} - -func testSecretForeignKey(store *secretStore, repos core.RepositoryStore, repo *core.Repository) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Secret{ - RepoID: repo.ID, - Name: "password", - Data: "correct-horse-battery-staple", - } - store.Create(noContext, item) - before, _ := store.List(noContext, repo.ID) - if len(before) == 0 { - t.Errorf("Want non-empty secret list") - return - } - - err := repos.Delete(noContext, repo) - if err != nil { - t.Error(err) - return - } - after, _ := store.List(noContext, repo.ID) - if len(after) != 0 { - t.Errorf("Want empty secret list") - } - } -} - -func testSecret(item *core.Secret) func(t *testing.T) { - return func(t *testing.T) { - if got, want := item.Name, "password"; got != want { - t.Errorf("Want secret name %q, got %q", want, got) - } - if got, want := item.Data, "correct-horse-battery-staple"; got != want { - t.Errorf("Want secret data %q, got %q", want, got) - } - } -} - -// The purpose of this unit test is to ensure that plaintext -// data can still be read from the database if encryption is -// added at a later time. -func TestSecretCryptoChange(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - // seeds the database with a dummy repository. - repo := &core.Repository{UID: "1", Slug: "octocat/hello-world"} - repos := repos.New(conn) - if err := repos.Create(noContext, repo); err != nil { - t.Error(err) - } - - store := New(conn, nil).(*secretStore) - store.enc, _ = encrypt.New("") - - item := &core.Secret{ - RepoID: repo.ID, - Name: "password", - Data: "correct-horse-battery-staple", - } - - // create the secret with the secret value stored as plaintext - err = store.Create(noContext, item) - if err != nil { - t.Error(err) - return - } - if item.ID == 0 { - t.Errorf("Want secret ID assigned, got %d", item.ID) - return - } - - // update the store to use encryption - store.enc, _ = encrypt.New("fb4b4d6267c8a5ce8231f8b186dbca92") - store.enc.(*encrypt.Aesgcm).Compat = true - - // fetch the secret from the database - got, err := store.Find(noContext, item.ID) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testSecret(got)) - } -} diff --git a/store/shared/db/conn.go b/store/shared/db/conn.go deleted file mode 100644 index c637b829f3..0000000000 --- a/store/shared/db/conn.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package db - -import ( - "database/sql" - "sync" - "time" - - "github.com/jmoiron/sqlx" - - "github.com/drone/drone/store/shared/migrate/mysql" - "github.com/drone/drone/store/shared/migrate/postgres" - "github.com/drone/drone/store/shared/migrate/sqlite" -) - -// Connect to a database and verify with a ping. -func Connect(driver, datasource string, maxOpenConnections int) (*DB, error) { - db, err := sql.Open(driver, datasource) - if err != nil { - return nil, err - } - switch driver { - case "mysql": - db.SetMaxIdleConns(0) - } - if err := pingDatabase(db); err != nil { - return nil, err - } - if err := setupDatabase(db, driver); err != nil { - return nil, err - } - // generally set to 0, user configured for larger installs - db.SetMaxOpenConns(maxOpenConnections) - - var engine Driver - var locker Locker - switch driver { - case "mysql": - engine = Mysql - locker = &nopLocker{} - case "postgres": - engine = Postgres - locker = &nopLocker{} - default: - engine = Sqlite - locker = &sync.RWMutex{} - } - - return &DB{ - conn: sqlx.NewDb(db, driver), - lock: locker, - driver: engine, - }, nil -} - -// helper function to ping the database with backoff to ensure -// a connection can be established before we proceed with the -// database setup and migration. -func pingDatabase(db *sql.DB) (err error) { - for i := 0; i < 30; i++ { - err = db.Ping() - if err == nil { - return - } - time.Sleep(time.Second) - } - return -} - -// helper function to setup the database by performing automated -// database migration steps. -func setupDatabase(db *sql.DB, driver string) error { - switch driver { - case "mysql": - return mysql.Migrate(db) - case "postgres": - return postgres.Migrate(db) - default: - return sqlite.Migrate(db) - } -} diff --git a/store/shared/db/conn_oss.go b/store/shared/db/conn_oss.go deleted file mode 100644 index 79529766b6..0000000000 --- a/store/shared/db/conn_oss.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package db - -import ( - "database/sql" - "sync" - - "github.com/jmoiron/sqlx" - - "github.com/drone/drone/store/shared/migrate/sqlite" -) - -// Connect to an embedded sqlite database. -func Connect(driver, datasource string, maxOpenConnections int) (*DB, error) { - db, err := sql.Open(driver, datasource) - if err != nil { - return nil, err - } - - db.SetMaxOpenConns(maxOpenConnections) - - if err := sqlite.Migrate(db); err != nil { - return nil, err - } - return &DB{ - conn: sqlx.NewDb(db, driver), - lock: &sync.RWMutex{}, - driver: Sqlite, - }, nil -} diff --git a/store/shared/db/conn_test.go b/store/shared/db/conn_test.go deleted file mode 100644 index 9355b27059..0000000000 --- a/store/shared/db/conn_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package db diff --git a/store/shared/db/db.go b/store/shared/db/db.go deleted file mode 100644 index eb54fadf02..0000000000 --- a/store/shared/db/db.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package db - -import ( - "database/sql" - "runtime/debug" - - "github.com/jmoiron/sqlx" -) - -// Driver defines the database driver. -type Driver int - -// Database driver enums. -const ( - Sqlite = iota + 1 - Mysql - Postgres -) - -type ( - // A Scanner represents an object that can be scanned - // for values. - Scanner interface { - Scan(dest ...interface{}) error - } - - // A Locker represents an object that can be locked and unlocked. - Locker interface { - Lock() - Unlock() - RLock() - RUnlock() - } - - // Binder interface defines database field bindings. - Binder interface { - BindNamed(query string, arg interface{}) (string, []interface{}, error) - } - - // Queryer interface defines a set of methods for - // querying the database. - Queryer interface { - Query(query string, args ...interface{}) (*sql.Rows, error) - QueryRow(query string, args ...interface{}) *sql.Row - } - - // Execer interface defines a set of methods for executing - // read and write commands against the database. - Execer interface { - Queryer - Exec(query string, args ...interface{}) (sql.Result, error) - } - - // DB is a pool of zero or more underlying connections to - // the drone database. - DB struct { - conn *sqlx.DB - lock Locker - driver Driver - } -) - -// View executes a function within the context of a managed read-only -// transaction. Any error that is returned from the function is returned -// from the View() method. -func (db *DB) View(fn func(Queryer, Binder) error) error { - db.lock.RLock() - err := fn(db.conn, db.conn) - db.lock.RUnlock() - return err -} - -// Lock obtains a write lock to the database (sqlite only) and executes -// a function. Any error that is returned from the function is returned -// from the Lock() method. -func (db *DB) Lock(fn func(Execer, Binder) error) error { - db.lock.Lock() - err := fn(db.conn, db.conn) - db.lock.Unlock() - return err -} - -// Update executes a function within the context of a read-write managed -// transaction. If no error is returned from the function then the -// transaction is committed. If an error is returned then the entire -// transaction is rolled back. Any error that is returned from the function -// or returned from the commit is returned from the Update() method. -func (db *DB) Update(fn func(Execer, Binder) error) (err error) { - db.lock.Lock() - defer db.lock.Unlock() - - tx, err := db.conn.Begin() - if err != nil { - return err - } - - defer func() { - if p := recover(); p != nil { - err = tx.Rollback() - debug.PrintStack() - } else if err != nil { - tx.Rollback() - } else { - err = tx.Commit() - } - }() - - err = fn(tx, db.conn) - return err -} - -// Driver returns the name of the SQL driver. -func (db *DB) Driver() Driver { - return db.driver -} - -// Close closes the database connection. -func (db *DB) Close() error { - return db.conn.Close() -} diff --git a/store/shared/db/db_test.go b/store/shared/db/db_test.go deleted file mode 100644 index 9355b27059..0000000000 --- a/store/shared/db/db_test.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package db diff --git a/store/shared/db/dbtest/dbtest.go b/store/shared/db/dbtest/dbtest.go deleted file mode 100644 index 21a578e2f6..0000000000 --- a/store/shared/db/dbtest/dbtest.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dbtest - -import ( - "os" - "strconv" - - "github.com/drone/drone/store/shared/db" - - // blank imports are used to load database drivers - // for unit tests. Only unit tests should be importing - // this package. - _ "github.com/go-sql-driver/mysql" - _ "github.com/lib/pq" - _ "github.com/mattn/go-sqlite3" -) - -// Connect opens a new test database connection. -func Connect() (*db.DB, error) { - var ( - driver = "sqlite3" - config = ":memory:?_foreign_keys=1" - maxConnections = 0 - ) - if os.Getenv("DRONE_DATABASE_DRIVER") != "" { - driver = os.Getenv("DRONE_DATABASE_DRIVER") - config = os.Getenv("DRONE_DATABASE_DATASOURCE") - maxConnectionsString := os.Getenv("DRONE_DATABASE_MAX_CONNECTIONS") - maxConnections, _ = strconv.Atoi(maxConnectionsString) - } - return db.Connect(driver, config, maxConnections) -} - -// Reset resets the database state. -func Reset(d *db.DB) { - d.Lock(func(tx db.Execer, _ db.Binder) error { - tx.Exec("DELETE FROM cron") - tx.Exec("DELETE FROM cards") - tx.Exec("DELETE FROM logs") - tx.Exec("DELETE FROM steps") - tx.Exec("DELETE FROM stages") - tx.Exec("DELETE FROM latest") - tx.Exec("DELETE FROM builds") - tx.Exec("DELETE FROM perms") - tx.Exec("DELETE FROM repos") - tx.Exec("DELETE FROM users") - tx.Exec("DELETE FROM templates") - tx.Exec("DELETE FROM orgsecrets") - return nil - }) -} - -// Disconnect closes the database connection. -func Disconnect(d *db.DB) error { - return d.Close() -} diff --git a/store/shared/db/error.go b/store/shared/db/error.go deleted file mode 100644 index 91308e393b..0000000000 --- a/store/shared/db/error.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package db - -import "errors" - -// ErrOptimisticLock is returned by if the struct being -// modified has a Version field and the value is not equal -// to the current value in the database -var ErrOptimisticLock = errors.New("Optimistic Lock Error") diff --git a/store/shared/db/nop.go b/store/shared/db/nop.go deleted file mode 100644 index 85f60eb7c4..0000000000 --- a/store/shared/db/nop.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package db - -type nopLocker struct{} - -func (nopLocker) Lock() {} -func (nopLocker) Unlock() {} -func (nopLocker) RLock() {} -func (nopLocker) RUnlock() {} diff --git a/store/shared/encrypt/aesgcm.go b/store/shared/encrypt/aesgcm.go deleted file mode 100644 index 831799394d..0000000000 --- a/store/shared/encrypt/aesgcm.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package encrypt - -import ( - "crypto/cipher" - "crypto/rand" - "errors" - "io" -) - -// Aesgcm provides an encrypter that uses the aesgcm encryption -// algorithm. -type Aesgcm struct { - block cipher.Block - Compat bool -} - -// Encrypt encrypts the plaintext using aesgcm. -func (e *Aesgcm) Encrypt(plaintext string) ([]byte, error) { - gcm, err := cipher.NewGCM(e.block) - if err != nil { - return nil, err - } - - nonce := make([]byte, gcm.NonceSize()) - _, err = io.ReadFull(rand.Reader, nonce) - if err != nil { - return nil, err - } - - return gcm.Seal(nonce, nonce, []byte(plaintext), nil), nil -} - -// Decrypt decrypts the ciphertext using aesgcm. -func (e *Aesgcm) Decrypt(ciphertext []byte) (string, error) { - gcm, err := cipher.NewGCM(e.block) - if err != nil { - return "", err - } - - if len(ciphertext) < gcm.NonceSize() { - // if the decryption utility is running in compatibility - // mode, it will return the ciphertext as plain text if - // decryption fails. This should be used when running the - // database in mixed-mode, where there is a mix of encrypted - // and unencrypted content. - if e.Compat { - return string(ciphertext), nil - } - return "", errors.New("malformed ciphertext") - } - - plaintext, err := gcm.Open(nil, - ciphertext[:gcm.NonceSize()], - ciphertext[gcm.NonceSize():], - nil, - ) - // if the decryption utility is running in compatibility - // mode, it will return the ciphertext as plain text if - // decryption fails. This should be used when running the - // database in mixed-mode, where there is a mix of encrypted - // and unencrypted content. - if err != nil && e.Compat { - return string(ciphertext), nil - } - return string(plaintext), err -} diff --git a/store/shared/encrypt/aesgcm_test.go b/store/shared/encrypt/aesgcm_test.go deleted file mode 100644 index 7e917c64a8..0000000000 --- a/store/shared/encrypt/aesgcm_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package encrypt - -import "testing" - -func TestAesgcm(t *testing.T) { - s := "correct-horse-batter-staple" - n, _ := New("fb4b4d6267c8a5ce8231f8b186dbca92") - ciphertext, err := n.Encrypt(s) - if err != nil { - t.Error(err) - } - plaintext, err := n.Decrypt(ciphertext) - if err != nil { - t.Error(err) - } - if want, got := plaintext, s; got != want { - t.Errorf("Want plaintext %q, got %q", want, got) - } -} - -func TestAesgcmFail(t *testing.T) { - s := "correct-horse-batter-staple" - n, _ := New("ea1c5a9145c8a5ce8231f8b186dbcabc") - ciphertext, err := n.Encrypt(s) - if err != nil { - t.Error(err) - } - n, _ = New("fb4b4d6267c8a5ce8231f8b186dbca92") - _, err = n.Decrypt(ciphertext) - if err == nil { - t.Error("Expect error when encryption and decryption keys mismatch") - } -} - -func TestAesgcmCompat(t *testing.T) { - s := "correct-horse-batter-staple" - n, _ := New("") - ciphertext, err := n.Encrypt(s) - if err != nil { - t.Error(err) - } - n, _ = New("ea1c5a9145c8a5ce8231f8b186dbcabc") - n.(*Aesgcm).Compat = true - plaintext, err := n.Decrypt(ciphertext) - if err != nil { - t.Error(err) - } - if want, got := plaintext, s; got != want { - t.Errorf("Want plaintext %q, got %q", want, got) - } -} diff --git a/store/shared/encrypt/encrypt.go b/store/shared/encrypt/encrypt.go deleted file mode 100644 index c552bc6929..0000000000 --- a/store/shared/encrypt/encrypt.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package encrypt - -import ( - "crypto/aes" - "errors" -) - -// indicates key size is too small. -var errKeySize = errors.New("encryption key must be 32 bytes") - -// Encrypter provides database field encryption and decryption. -// Encrypted values are currently limited to strings, which is -// reflected in the interface design. -type Encrypter interface { - Encrypt(plaintext string) ([]byte, error) - Decrypt(ciphertext []byte) (string, error) -} - -// New provides a new database field encrypter. -func New(key string) (Encrypter, error) { - if key == "" { - return &none{}, nil - } - if len(key) != 32 { - return nil, errKeySize - } - b := []byte(key) - block, err := aes.NewCipher(b) - if err != nil { - return nil, err - } - return &Aesgcm{block: block}, nil -} diff --git a/store/shared/encrypt/none.go b/store/shared/encrypt/none.go deleted file mode 100644 index b780d6cf4b..0000000000 --- a/store/shared/encrypt/none.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package encrypt - -// none is an encryption strategy that stores secret -// values in plain text. This is the default strategy -// when no key is specified. -type none struct { -} - -func (*none) Encrypt(plaintext string) ([]byte, error) { - return []byte(plaintext), nil -} - -func (*none) Decrypt(ciphertext []byte) (string, error) { - return string(ciphertext), nil -} diff --git a/store/shared/encrypt/none_test.go b/store/shared/encrypt/none_test.go deleted file mode 100644 index b645dcbecc..0000000000 --- a/store/shared/encrypt/none_test.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -package encrypt - -import "testing" - -func TestNone(t *testing.T) { - n, _ := New("") - ciphertext, err := n.Encrypt("correct-horse-batter-staple") - if err != nil { - t.Error(err) - } - plaintext, err := n.Decrypt(ciphertext) - if err != nil { - t.Error(err) - } - if want, got := plaintext, "correct-horse-batter-staple"; got != want { - t.Errorf("Want plaintext %q, got %q", want, got) - } -} diff --git a/store/shared/migrate/README.md b/store/shared/migrate/README.md deleted file mode 100644 index a31b79963f..0000000000 --- a/store/shared/migrate/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Building SQL DDL into Drone - -These folders contain the code for the different of databases that drone can use. They contain the SQL necessary to create the necessary tables and migrate between versions (IE the DDL). This SQL is generated into a go file and included as part of the Drone binary. - -## Making a changes to the database DDL - -Any new changes to the database structure are always put into a new SQL file. Follow the naming scheme in the `store/shared/migrate//files` of the SQL files by incrementing the number file name and give it a good description of what changes are being made. - -Changes will need to be implemented for all supported databases, making similar changes for eg Mysql/Postgres/Sqllite. - -**NB** Any changes to the database structure will need to be reflected for the relevant `struct` in the `core` directory. Changing the objects in the `store` directory for the ORM. Finally Possibly in the repositories github.com/drone/drone-go and github.com/drone/runner-go. - -## Generating Go from the SQL files - -To generate the go files you will need to install the golang command line tool `Togo` so it is on your users PATH. - -### Steps to install Togo - -``` bash -# in your workspace -git clone git@github.com:bradrydzewski/togo.git -cd togo -go get github.com/bradrydzewski/togo -``` - -### Generating go DDL - -Enter the desired database's implementation folder, and run the following. It will update the `ddl_gen.go` file. - -``` bash -go generate -``` diff --git a/store/shared/migrate/mysql/ddl.go b/store/shared/migrate/mysql/ddl.go deleted file mode 100644 index 41f71516a4..0000000000 --- a/store/shared/migrate/mysql/ddl.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package mysql - -//go:generate togo ddl -package mysql -dialect mysql diff --git a/store/shared/migrate/mysql/ddl_gen.go b/store/shared/migrate/mysql/ddl_gen.go deleted file mode 100644 index 156aa4563d..0000000000 --- a/store/shared/migrate/mysql/ddl_gen.go +++ /dev/null @@ -1,793 +0,0 @@ -package mysql - -import ( - "database/sql" -) - -var migrations = []struct { - name string - stmt string -}{ - { - name: "create-table-users", - stmt: createTableUsers, - }, - { - name: "create-table-repos", - stmt: createTableRepos, - }, - { - name: "alter-table-repos-add-column-no-fork", - stmt: alterTableReposAddColumnNoFork, - }, - { - name: "alter-table-repos-add-column-no-pulls", - stmt: alterTableReposAddColumnNoPulls, - }, - { - name: "alter-table-repos-add-column-cancel-pulls", - stmt: alterTableReposAddColumnCancelPulls, - }, - { - name: "alter-table-repos-add-column-cancel-push", - stmt: alterTableReposAddColumnCancelPush, - }, - { - name: "alter-table-repos-add-column-throttle", - stmt: alterTableReposAddColumnThrottle, - }, - { - name: "alter-table-repos-add-column-cancel-running", - stmt: alterTableReposAddColumnCancelRunning, - }, - { - name: "create-table-perms", - stmt: createTablePerms, - }, - { - name: "create-index-perms-user", - stmt: createIndexPermsUser, - }, - { - name: "create-index-perms-repo", - stmt: createIndexPermsRepo, - }, - { - name: "create-table-builds", - stmt: createTableBuilds, - }, - { - name: "create-index-builds-repo", - stmt: createIndexBuildsRepo, - }, - { - name: "create-index-builds-author", - stmt: createIndexBuildsAuthor, - }, - { - name: "create-index-builds-sender", - stmt: createIndexBuildsSender, - }, - { - name: "create-index-builds-ref", - stmt: createIndexBuildsRef, - }, - { - name: "alter-table-builds-add-column-debug", - stmt: alterTableBuildsAddColumnDebug, - }, - { - name: "create-table-stages", - stmt: createTableStages, - }, - { - name: "create-index-stages-build", - stmt: createIndexStagesBuild, - }, - { - name: "create-table-unfinished", - stmt: createTableUnfinished, - }, - { - name: "create-trigger-stage-insert", - stmt: createTriggerStageInsert, - }, - { - name: "create-trigger-stage-update", - stmt: createTriggerStageUpdate, - }, - { - name: "alter-table-stages-add-column-limit-repos", - stmt: alterTableStagesAddColumnLimitRepos, - }, - { - name: "create-table-steps", - stmt: createTableSteps, - }, - { - name: "create-index-steps-stage", - stmt: createIndexStepsStage, - }, - { - name: "create-table-logs", - stmt: createTableLogs, - }, - { - name: "create-table-cron", - stmt: createTableCron, - }, - { - name: "create-index-cron-repo", - stmt: createIndexCronRepo, - }, - { - name: "create-index-cron-next", - stmt: createIndexCronNext, - }, - { - name: "create-table-secrets", - stmt: createTableSecrets, - }, - { - name: "create-index-secrets-repo", - stmt: createIndexSecretsRepo, - }, - { - name: "create-index-secrets-repo-name", - stmt: createIndexSecretsRepoName, - }, - { - name: "create-table-nodes", - stmt: createTableNodes, - }, - { - name: "alter-table-builds-add-column-cron", - stmt: alterTableBuildsAddColumnCron, - }, - { - name: "create-table-org-secrets", - stmt: createTableOrgSecrets, - }, - { - name: "alter-table-builds-add-column-deploy-id", - stmt: alterTableBuildsAddColumnDeployId, - }, - { - name: "create-table-latest", - stmt: createTableLatest, - }, - { - name: "create-index-latest-repo", - stmt: createIndexLatestRepo, - }, - { - name: "create-table-template", - stmt: createTableTemplate, - }, - { - name: "create-index-template-namespace", - stmt: createIndexTemplateNamespace, - }, - { - name: "alter-table-steps-add-column-step-depends-on", - stmt: alterTableStepsAddColumnStepDependsOn, - }, - { - name: "alter-table-steps-add-column-step-image", - stmt: alterTableStepsAddColumnStepImage, - }, - { - name: "alter-table-steps-add-column-step-detached", - stmt: alterTableStepsAddColumnStepDetached, - }, - { - name: "create-table-cards", - stmt: createTableCards, - }, - { - name: "create-index-cards-card-build", - stmt: createIndexCardsCardBuild, - }, - { - name: "create-index-cards-card_step", - stmt: createIndexCardsCardstep, - }, - { - name: "drop-table-cards", - stmt: dropTableCards, - }, - { - name: "alter-table-steps-add-column-step_schema", - stmt: alterTableStepsAddColumnStepschema, - }, - { - name: "create-new-table-cards", - stmt: createNewTableCards, - }, -} - -// Migrate performs the database migration. If the migration fails -// and error is returned. -func Migrate(db *sql.DB) error { - if err := createTable(db); err != nil { - return err - } - completed, err := selectCompleted(db) - if err != nil && err != sql.ErrNoRows { - return err - } - for _, migration := range migrations { - if _, ok := completed[migration.name]; ok { - - continue - } - - if _, err := db.Exec(migration.stmt); err != nil { - return err - } - if err := insertMigration(db, migration.name); err != nil { - return err - } - - } - return nil -} - -func createTable(db *sql.DB) error { - _, err := db.Exec(migrationTableCreate) - return err -} - -func insertMigration(db *sql.DB, name string) error { - _, err := db.Exec(migrationInsert, name) - return err -} - -func selectCompleted(db *sql.DB) (map[string]struct{}, error) { - migrations := map[string]struct{}{} - rows, err := db.Query(migrationSelect) - if err != nil { - return nil, err - } - defer rows.Close() - for rows.Next() { - var name string - if err := rows.Scan(&name); err != nil { - return nil, err - } - migrations[name] = struct{}{} - } - return migrations, nil -} - -// -// migration table ddl and sql -// - -var migrationTableCreate = ` -CREATE TABLE IF NOT EXISTS migrations ( - name VARCHAR(255) -,UNIQUE(name) -) -` - -var migrationInsert = ` -INSERT INTO migrations (name) VALUES (?) -` - -var migrationSelect = ` -SELECT name FROM migrations -` - -// -// 001_create_table_user.sql -// - -var createTableUsers = ` -CREATE TABLE IF NOT EXISTS users ( - user_id INTEGER PRIMARY KEY AUTO_INCREMENT -,user_login VARCHAR(250) -,user_email VARCHAR(500) -,user_admin BOOLEAN -,user_machine BOOLEAN -,user_active BOOLEAN -,user_avatar VARCHAR(2000) -,user_syncing BOOLEAN -,user_synced INTEGER -,user_created INTEGER -,user_updated INTEGER -,user_last_login INTEGER -,user_oauth_token BLOB -,user_oauth_refresh BLOB -,user_oauth_expiry INTEGER -,user_hash VARCHAR(500) -,UNIQUE(user_login) -,UNIQUE(user_hash) -); -` - -// -// 002_create_table_repos.sql -// - -var createTableRepos = ` -CREATE TABLE IF NOT EXISTS repos ( - repo_id INTEGER PRIMARY KEY AUTO_INCREMENT -,repo_uid VARCHAR(250) -,repo_user_id INTEGER -,repo_namespace VARCHAR(250) -,repo_name VARCHAR(250) -,repo_slug VARCHAR(250) -,repo_scm VARCHAR(50) -,repo_clone_url VARCHAR(2000) -,repo_ssh_url VARCHAR(2000) -,repo_html_url VARCHAR(2000) -,repo_active BOOLEAN -,repo_private BOOLEAN -,repo_visibility VARCHAR(50) -,repo_branch VARCHAR(250) -,repo_counter INTEGER -,repo_config VARCHAR(500) -,repo_timeout INTEGER -,repo_trusted BOOLEAN -,repo_protected BOOLEAN -,repo_synced INTEGER -,repo_created INTEGER -,repo_updated INTEGER -,repo_version INTEGER -,repo_signer VARCHAR(50) -,repo_secret VARCHAR(50) -,UNIQUE(repo_slug) -,UNIQUE(repo_uid) -); -` - -var alterTableReposAddColumnNoFork = ` -ALTER TABLE repos ADD COLUMN repo_no_forks BOOLEAN NOT NULL DEFAULT false; -` - -var alterTableReposAddColumnNoPulls = ` -ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT false; -` - -var alterTableReposAddColumnCancelPulls = ` -ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT false; -` - -var alterTableReposAddColumnCancelPush = ` -ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT false; -` - -var alterTableReposAddColumnThrottle = ` -ALTER TABLE repos ADD COLUMN repo_throttle INTEGER NOT NULL DEFAULT 0; -` - -var alterTableReposAddColumnCancelRunning = ` -ALTER TABLE repos ADD COLUMN repo_cancel_running BOOLEAN NOT NULL DEFAULT false; -` - -// -// 003_create_table_perms.sql -// - -var createTablePerms = ` -CREATE TABLE IF NOT EXISTS perms ( - perm_user_id INTEGER -,perm_repo_uid VARCHAR(250) -,perm_read BOOLEAN -,perm_write BOOLEAN -,perm_admin BOOLEAN -,perm_synced INTEGER -,perm_created INTEGER -,perm_updated INTEGER -,PRIMARY KEY(perm_user_id, perm_repo_uid) -); -` - -var createIndexPermsUser = ` -CREATE INDEX ix_perms_user ON perms (perm_user_id); -` - -var createIndexPermsRepo = ` -CREATE INDEX ix_perms_repo ON perms (perm_repo_uid); -` - -// -// 004_create_table_builds.sql -// - -var createTableBuilds = ` -CREATE TABLE IF NOT EXISTS builds ( - build_id INTEGER PRIMARY KEY AUTO_INCREMENT -,build_repo_id INTEGER -,build_config_id INTEGER -,build_trigger VARCHAR(250) -,build_number INTEGER -,build_parent INTEGER -,build_status VARCHAR(50) -,build_error VARCHAR(500) -,build_event VARCHAR(50) -,build_action VARCHAR(50) -,build_link VARCHAR(1000) -,build_timestamp INTEGER -,build_title VARCHAR(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci -,build_message VARCHAR(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci -,build_before VARCHAR(50) -,build_after VARCHAR(50) -,build_ref VARCHAR(500) -,build_source_repo VARCHAR(250) -,build_source VARCHAR(500) -,build_target VARCHAR(500) -,build_author VARCHAR(500) -,build_author_name VARCHAR(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci -,build_author_email VARCHAR(500) -,build_author_avatar VARCHAR(1000) -,build_sender VARCHAR(500) -,build_deploy VARCHAR(500) -,build_params VARCHAR(2000) -,build_started INTEGER -,build_finished INTEGER -,build_created INTEGER -,build_updated INTEGER -,build_version INTEGER -,UNIQUE(build_repo_id, build_number) -); -` - -var createIndexBuildsRepo = ` -CREATE INDEX ix_build_repo ON builds (build_repo_id); -` - -var createIndexBuildsAuthor = ` -CREATE INDEX ix_build_author ON builds (build_author); -` - -var createIndexBuildsSender = ` -CREATE INDEX ix_build_sender ON builds (build_sender); -` - -var createIndexBuildsRef = ` -CREATE INDEX ix_build_ref ON builds (build_repo_id, build_ref); -` - -var alterTableBuildsAddColumnDebug = ` -ALTER TABLE builds ADD COLUMN build_debug BOOLEAN NOT NULL DEFAULT false; -` - -// -// 005_create_table_stages.sql -// - -var createTableStages = ` -CREATE TABLE IF NOT EXISTS stages ( - stage_id INTEGER PRIMARY KEY AUTO_INCREMENT -,stage_repo_id INTEGER -,stage_build_id INTEGER -,stage_number INTEGER -,stage_name VARCHAR(100) -,stage_kind VARCHAR(50) -,stage_type VARCHAR(50) -,stage_status VARCHAR(50) -,stage_error VARCHAR(500) -,stage_errignore BOOLEAN -,stage_exit_code INTEGER -,stage_limit INTEGER -,stage_os VARCHAR(50) -,stage_arch VARCHAR(50) -,stage_variant VARCHAR(10) -,stage_kernel VARCHAR(50) -,stage_machine VARCHAR(500) -,stage_started INTEGER -,stage_stopped INTEGER -,stage_created INTEGER -,stage_updated INTEGER -,stage_version INTEGER -,stage_on_success BOOLEAN -,stage_on_failure BOOLEAN -,stage_depends_on TEXT -,stage_labels TEXT -,UNIQUE(stage_build_id, stage_number) -); -` - -var createIndexStagesBuild = ` -CREATE INDEX ix_stages_build ON stages (stage_build_id); -` - -var createTableUnfinished = ` -CREATE TABLE IF NOT EXISTS stages_unfinished ( -stage_id INTEGER PRIMARY KEY -); -` - -var createTriggerStageInsert = ` -CREATE TRIGGER stage_insert AFTER INSERT ON stages -FOR EACH ROW -BEGIN - IF NEW.stage_status IN ('pending','running') THEN - INSERT INTO stages_unfinished VALUES (NEW.stage_id); - END IF; -END; -` - -var createTriggerStageUpdate = ` -CREATE TRIGGER stage_update AFTER UPDATE ON stages -FOR EACH ROW -BEGIN - IF NEW.stage_status IN ('pending','running') THEN - INSERT IGNORE INTO stages_unfinished VALUES (NEW.stage_id); - ELSEIF OLD.stage_status IN ('pending','running') THEN - DELETE FROM stages_unfinished WHERE stage_id = OLD.stage_id; - END IF; -END; -` - -var alterTableStagesAddColumnLimitRepos = ` -ALTER TABLE stages ADD COLUMN stage_limit_repo INTEGER NOT NULL DEFAULT 0; -` - -// -// 006_create_table_steps.sql -// - -var createTableSteps = ` -CREATE TABLE IF NOT EXISTS steps ( - step_id INTEGER PRIMARY KEY AUTO_INCREMENT -,step_stage_id INTEGER -,step_number INTEGER -,step_name VARCHAR(100) -,step_status VARCHAR(50) -,step_error VARCHAR(500) -,step_errignore BOOLEAN -,step_exit_code INTEGER -,step_started INTEGER -,step_stopped INTEGER -,step_version INTEGER -,UNIQUE(step_stage_id, step_number) -); -` - -var createIndexStepsStage = ` -CREATE INDEX ix_steps_stage ON steps (step_stage_id); -` - -// -// 007_create_table_logs.sql -// - -var createTableLogs = ` -CREATE TABLE IF NOT EXISTS logs ( - log_id INTEGER PRIMARY KEY -,log_data MEDIUMBLOB -); -` - -// -// 008_create_table_cron.sql -// - -var createTableCron = ` -CREATE TABLE IF NOT EXISTS cron ( - cron_id INTEGER PRIMARY KEY AUTO_INCREMENT -,cron_repo_id INTEGER -,cron_name VARCHAR(50) -,cron_expr VARCHAR(50) -,cron_next INTEGER -,cron_prev INTEGER -,cron_event VARCHAR(50) -,cron_branch VARCHAR(250) -,cron_target VARCHAR(250) -,cron_disabled BOOLEAN -,cron_created INTEGER -,cron_updated INTEGER -,cron_version INTEGER -,UNIQUE(cron_repo_id, cron_name) -,FOREIGN KEY(cron_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); -` - -var createIndexCronRepo = ` -CREATE INDEX ix_cron_repo ON cron (cron_repo_id); -` - -var createIndexCronNext = ` -CREATE INDEX ix_cron_next ON cron (cron_next); -` - -// -// 009_create_table_secrets.sql -// - -var createTableSecrets = ` -CREATE TABLE IF NOT EXISTS secrets ( - secret_id INTEGER PRIMARY KEY AUTO_INCREMENT -,secret_repo_id INTEGER -,secret_name VARCHAR(500) -,secret_data BLOB -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_repo_id, secret_name) -,FOREIGN KEY(secret_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); -` - -var createIndexSecretsRepo = ` -CREATE INDEX ix_secret_repo ON secrets (secret_repo_id); -` - -var createIndexSecretsRepoName = ` -CREATE INDEX ix_secret_repo_name ON secrets (secret_repo_id, secret_name); -` - -// -// 010_create_table_nodes.sql -// - -var createTableNodes = ` -CREATE TABLE IF NOT EXISTS nodes ( - node_id INTEGER PRIMARY KEY AUTO_INCREMENT -,node_uid VARCHAR(500) -,node_provider VARCHAR(50) -,node_state VARCHAR(50) -,node_name VARCHAR(50) -,node_image VARCHAR(500) -,node_region VARCHAR(100) -,node_size VARCHAR(100) -,node_os VARCHAR(50) -,node_arch VARCHAR(50) -,node_kernel VARCHAR(50) -,node_variant VARCHAR(50) -,node_address VARCHAR(500) -,node_capacity INTEGER -,node_filter VARCHAR(2000) -,node_labels VARCHAR(2000) -,node_error VARCHAR(2000) -,node_ca_key BLOB -,node_ca_cert BLOB -,node_tls_key BLOB -,node_tls_cert BLOB -,node_tls_name VARCHAR(500) -,node_paused BOOLEAN -,node_protected BOOLEAN -,node_created INTEGER -,node_updated INTEGER -,node_pulled INTEGER - -,UNIQUE(node_name) -); -` - -// -// 011_add_column_builds_cron.sql -// - -var alterTableBuildsAddColumnCron = ` -ALTER TABLE builds ADD COLUMN build_cron VARCHAR(50) NOT NULL DEFAULT ''; -` - -// -// 012_create_table_global_secrets.sql -// - -var createTableOrgSecrets = ` -CREATE TABLE IF NOT EXISTS orgsecrets ( - secret_id INTEGER PRIMARY KEY AUTO_INCREMENT -,secret_namespace VARCHAR(50) -,secret_name VARCHAR(200) -,secret_type VARCHAR(50) -,secret_data BLOB -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_namespace, secret_name) -); -` - -// -// 013_add_column_builds_deploy_id.sql -// - -var alterTableBuildsAddColumnDeployId = ` -ALTER TABLE builds ADD COLUMN build_deploy_id INTEGER NOT NULL DEFAULT 0; -` - -// -// 014_create_table_refs.sql -// - -var createTableLatest = ` -CREATE TABLE IF NOT EXISTS latest ( - latest_repo_id INTEGER -,latest_build_id INTEGER -,latest_type VARCHAR(50) -,latest_name VARCHAR(500) -,latest_created INTEGER -,latest_updated INTEGER -,latest_deleted INTEGER -,PRIMARY KEY(latest_repo_id, latest_type, latest_name) -); -` - -var createIndexLatestRepo = ` -CREATE INDEX ix_latest_repo ON latest (latest_repo_id); -` - -// -// 015_create_table_templates.sql -// - -var createTableTemplate = ` -CREATE TABLE IF NOT EXISTS templates ( - template_id INTEGER PRIMARY KEY AUTO_INCREMENT - ,template_name VARCHAR(500) - ,template_namespace VARCHAR(50) - ,template_data BLOB - ,template_created INTEGER - ,template_updated INTEGER - ,UNIQUE(template_name, template_namespace) - ); -` - -var createIndexTemplateNamespace = ` -CREATE INDEX ix_template_namespace ON templates (template_namespace); -` - -// -// 016_add_columns_steps.sql -// - -var alterTableStepsAddColumnStepDependsOn = ` -ALTER TABLE steps ADD COLUMN step_depends_on TEXT NULL; -` - -var alterTableStepsAddColumnStepImage = ` -ALTER TABLE steps ADD COLUMN step_image VARCHAR(1000) NOT NULL DEFAULT ''; -` - -var alterTableStepsAddColumnStepDetached = ` -ALTER TABLE steps ADD COLUMN step_detached BOOLEAN NOT NULL DEFAULT FALSE; -` - -// -// 017_create_table_cards.sql -// - -var createTableCards = ` -CREATE TABLE IF NOT EXISTS cards ( - card_id INTEGER PRIMARY KEY AUTO_INCREMENT - ,card_build INTEGER - ,card_stage INTEGER - ,card_step INTEGER - ,card_schema TEXT - ,card_data TEXT -); -` - -var createIndexCardsCardBuild = ` -CREATE INDEX ix_cards_build ON cards (card_build); -` - -var createIndexCardsCardstep = ` -CREATE UNIQUE INDEX ix_cards_step ON cards (card_step); -` - -// -// 018_amend_table_cards.sql -// - -var dropTableCards = ` -DROP TABLE IF EXISTS cards; -` - -var alterTableStepsAddColumnStepschema = ` -ALTER TABLE steps - ADD COLUMN step_schema VARCHAR(2000) NOT NULL DEFAULT ''; -` - -var createNewTableCards = ` -CREATE TABLE IF NOT EXISTS cards -( - card_id INTEGER PRIMARY KEY, - card_data BLOB, - FOREIGN KEY (card_id) REFERENCES steps (step_id) ON DELETE CASCADE -); -` diff --git a/store/shared/migrate/mysql/files/001_create_table_user.sql b/store/shared/migrate/mysql/files/001_create_table_user.sql deleted file mode 100644 index b90f6695fc..0000000000 --- a/store/shared/migrate/mysql/files/001_create_table_user.sql +++ /dev/null @@ -1,22 +0,0 @@ --- name: create-table-users - -CREATE TABLE IF NOT EXISTS users ( - user_id INTEGER PRIMARY KEY AUTO_INCREMENT -,user_login VARCHAR(250) -,user_email VARCHAR(500) -,user_admin BOOLEAN -,user_machine BOOLEAN -,user_active BOOLEAN -,user_avatar VARCHAR(2000) -,user_syncing BOOLEAN -,user_synced INTEGER -,user_created INTEGER -,user_updated INTEGER -,user_last_login INTEGER -,user_oauth_token BLOB -,user_oauth_refresh BLOB -,user_oauth_expiry INTEGER -,user_hash VARCHAR(500) -,UNIQUE(user_login) -,UNIQUE(user_hash) -); diff --git a/store/shared/migrate/mysql/files/002_create_table_repos.sql b/store/shared/migrate/mysql/files/002_create_table_repos.sql deleted file mode 100644 index feabda2ff5..0000000000 --- a/store/shared/migrate/mysql/files/002_create_table_repos.sql +++ /dev/null @@ -1,55 +0,0 @@ --- name: create-table-repos - -CREATE TABLE IF NOT EXISTS repos ( - repo_id INTEGER PRIMARY KEY AUTO_INCREMENT -,repo_uid VARCHAR(250) -,repo_user_id INTEGER -,repo_namespace VARCHAR(250) -,repo_name VARCHAR(250) -,repo_slug VARCHAR(250) -,repo_scm VARCHAR(50) -,repo_clone_url VARCHAR(2000) -,repo_ssh_url VARCHAR(2000) -,repo_html_url VARCHAR(2000) -,repo_active BOOLEAN -,repo_private BOOLEAN -,repo_visibility VARCHAR(50) -,repo_branch VARCHAR(250) -,repo_counter INTEGER -,repo_config VARCHAR(500) -,repo_timeout INTEGER -,repo_trusted BOOLEAN -,repo_protected BOOLEAN -,repo_synced INTEGER -,repo_created INTEGER -,repo_updated INTEGER -,repo_version INTEGER -,repo_signer VARCHAR(50) -,repo_secret VARCHAR(50) -,UNIQUE(repo_slug) -,UNIQUE(repo_uid) -); - --- name: alter-table-repos-add-column-no-fork - -ALTER TABLE repos ADD COLUMN repo_no_forks BOOLEAN NOT NULL DEFAULT false; - --- name: alter-table-repos-add-column-no-pulls - -ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT false; - --- name: alter-table-repos-add-column-cancel-pulls - -ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT false; - --- name: alter-table-repos-add-column-cancel-push - -ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT false; - --- name: alter-table-repos-add-column-throttle - -ALTER TABLE repos ADD COLUMN repo_throttle INTEGER NOT NULL DEFAULT 0; - --- name: alter-table-repos-add-column-cancel-running - -ALTER TABLE repos ADD COLUMN repo_cancel_running BOOLEAN NOT NULL DEFAULT false; diff --git a/store/shared/migrate/mysql/files/003_create_table_perms.sql b/store/shared/migrate/mysql/files/003_create_table_perms.sql deleted file mode 100644 index 4622536c8f..0000000000 --- a/store/shared/migrate/mysql/files/003_create_table_perms.sql +++ /dev/null @@ -1,23 +0,0 @@ --- name: create-table-perms - -CREATE TABLE IF NOT EXISTS perms ( - perm_user_id INTEGER -,perm_repo_uid VARCHAR(250) -,perm_read BOOLEAN -,perm_write BOOLEAN -,perm_admin BOOLEAN -,perm_synced INTEGER -,perm_created INTEGER -,perm_updated INTEGER -,PRIMARY KEY(perm_user_id, perm_repo_uid) ---,FOREIGN KEY(perm_user_id) REFERENCES users(user_id) ON DELETE CASCADE ---,FOREIGN KEY(perm_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-perms-user - -CREATE INDEX ix_perms_user ON perms (perm_user_id); - --- name: create-index-perms-repo - -CREATE INDEX ix_perms_repo ON perms (perm_repo_uid); diff --git a/store/shared/migrate/mysql/files/004_create_table_builds.sql b/store/shared/migrate/mysql/files/004_create_table_builds.sql deleted file mode 100644 index b914e07c56..0000000000 --- a/store/shared/migrate/mysql/files/004_create_table_builds.sql +++ /dev/null @@ -1,58 +0,0 @@ --- name: create-table-builds - -CREATE TABLE IF NOT EXISTS builds ( - build_id INTEGER PRIMARY KEY AUTO_INCREMENT -,build_repo_id INTEGER -,build_config_id INTEGER -,build_trigger VARCHAR(250) -,build_number INTEGER -,build_parent INTEGER -,build_status VARCHAR(50) -,build_error VARCHAR(500) -,build_event VARCHAR(50) -,build_action VARCHAR(50) -,build_link VARCHAR(1000) -,build_timestamp INTEGER -,build_title VARCHAR(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci -,build_message VARCHAR(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci -,build_before VARCHAR(50) -,build_after VARCHAR(50) -,build_ref VARCHAR(500) -,build_source_repo VARCHAR(250) -,build_source VARCHAR(500) -,build_target VARCHAR(500) -,build_author VARCHAR(500) -,build_author_name VARCHAR(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci -,build_author_email VARCHAR(500) -,build_author_avatar VARCHAR(1000) -,build_sender VARCHAR(500) -,build_deploy VARCHAR(500) -,build_params VARCHAR(2000) -,build_started INTEGER -,build_finished INTEGER -,build_created INTEGER -,build_updated INTEGER -,build_version INTEGER -,UNIQUE(build_repo_id, build_number) ---,FOREIGN KEY(build_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-builds-repo - -CREATE INDEX ix_build_repo ON builds (build_repo_id); - --- name: create-index-builds-author - -CREATE INDEX ix_build_author ON builds (build_author); - --- name: create-index-builds-sender - -CREATE INDEX ix_build_sender ON builds (build_sender); - --- name: create-index-builds-ref - -CREATE INDEX ix_build_ref ON builds (build_repo_id, build_ref); - --- name: alter-table-builds-add-column-debug - -ALTER TABLE builds ADD COLUMN build_debug BOOLEAN NOT NULL DEFAULT false; diff --git a/store/shared/migrate/mysql/files/005_create_table_stages.sql b/store/shared/migrate/mysql/files/005_create_table_stages.sql deleted file mode 100644 index f835f02ca1..0000000000 --- a/store/shared/migrate/mysql/files/005_create_table_stages.sql +++ /dev/null @@ -1,67 +0,0 @@ --- name: create-table-stages - -CREATE TABLE IF NOT EXISTS stages ( - stage_id INTEGER PRIMARY KEY AUTO_INCREMENT -,stage_repo_id INTEGER -,stage_build_id INTEGER -,stage_number INTEGER -,stage_name VARCHAR(100) -,stage_kind VARCHAR(50) -,stage_type VARCHAR(50) -,stage_status VARCHAR(50) -,stage_error VARCHAR(500) -,stage_errignore BOOLEAN -,stage_exit_code INTEGER -,stage_limit INTEGER -,stage_os VARCHAR(50) -,stage_arch VARCHAR(50) -,stage_variant VARCHAR(10) -,stage_kernel VARCHAR(50) -,stage_machine VARCHAR(500) -,stage_started INTEGER -,stage_stopped INTEGER -,stage_created INTEGER -,stage_updated INTEGER -,stage_version INTEGER -,stage_on_success BOOLEAN -,stage_on_failure BOOLEAN -,stage_depends_on TEXT -,stage_labels TEXT -,UNIQUE(stage_build_id, stage_number) -); - --- name: create-index-stages-build - -CREATE INDEX ix_stages_build ON stages (stage_build_id); - --- name: create-table-unfinished - -CREATE TABLE IF NOT EXISTS stages_unfinished ( -stage_id INTEGER PRIMARY KEY -); - --- name: create-trigger-stage-insert - -CREATE TRIGGER stage_insert AFTER INSERT ON stages -FOR EACH ROW -BEGIN - IF NEW.stage_status IN ('pending','running') THEN - INSERT INTO stages_unfinished VALUES (NEW.stage_id); - END IF; -END; - --- name: create-trigger-stage-update - -CREATE TRIGGER stage_update AFTER UPDATE ON stages -FOR EACH ROW -BEGIN - IF NEW.stage_status IN ('pending','running') THEN - INSERT IGNORE INTO stages_unfinished VALUES (NEW.stage_id); - ELSEIF OLD.stage_status IN ('pending','running') THEN - DELETE FROM stages_unfinished WHERE stage_id = OLD.stage_id; - END IF; -END; - --- name: alter-table-stages-add-column-limit-repos - -ALTER TABLE stages ADD COLUMN stage_limit_repo INTEGER NOT NULL DEFAULT 0; diff --git a/store/shared/migrate/mysql/files/006_create_table_steps.sql b/store/shared/migrate/mysql/files/006_create_table_steps.sql deleted file mode 100644 index 81b433f94f..0000000000 --- a/store/shared/migrate/mysql/files/006_create_table_steps.sql +++ /dev/null @@ -1,20 +0,0 @@ --- name: create-table-steps - -CREATE TABLE IF NOT EXISTS steps ( - step_id INTEGER PRIMARY KEY AUTO_INCREMENT -,step_stage_id INTEGER -,step_number INTEGER -,step_name VARCHAR(100) -,step_status VARCHAR(50) -,step_error VARCHAR(500) -,step_errignore BOOLEAN -,step_exit_code INTEGER -,step_started INTEGER -,step_stopped INTEGER -,step_version INTEGER -,UNIQUE(step_stage_id, step_number) -); - --- name: create-index-steps-stage - -CREATE INDEX ix_steps_stage ON steps (step_stage_id); diff --git a/store/shared/migrate/mysql/files/007_create_table_logs.sql b/store/shared/migrate/mysql/files/007_create_table_logs.sql deleted file mode 100644 index b1a60996bd..0000000000 --- a/store/shared/migrate/mysql/files/007_create_table_logs.sql +++ /dev/null @@ -1,6 +0,0 @@ --- name: create-table-logs - -CREATE TABLE IF NOT EXISTS logs ( - log_id INTEGER PRIMARY KEY -,log_data MEDIUMBLOB -); diff --git a/store/shared/migrate/mysql/files/008_create_table_cron.sql b/store/shared/migrate/mysql/files/008_create_table_cron.sql deleted file mode 100644 index 64b259292d..0000000000 --- a/store/shared/migrate/mysql/files/008_create_table_cron.sql +++ /dev/null @@ -1,27 +0,0 @@ --- name: create-table-cron - -CREATE TABLE IF NOT EXISTS cron ( - cron_id INTEGER PRIMARY KEY AUTO_INCREMENT -,cron_repo_id INTEGER -,cron_name VARCHAR(50) -,cron_expr VARCHAR(50) -,cron_next INTEGER -,cron_prev INTEGER -,cron_event VARCHAR(50) -,cron_branch VARCHAR(250) -,cron_target VARCHAR(250) -,cron_disabled BOOLEAN -,cron_created INTEGER -,cron_updated INTEGER -,cron_version INTEGER -,UNIQUE(cron_repo_id, cron_name) -,FOREIGN KEY(cron_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-cron-repo - -CREATE INDEX ix_cron_repo ON cron (cron_repo_id); - --- name: create-index-cron-next - -CREATE INDEX ix_cron_next ON cron (cron_next); diff --git a/store/shared/migrate/mysql/files/009_create_table_secrets.sql b/store/shared/migrate/mysql/files/009_create_table_secrets.sql deleted file mode 100644 index 7c0876f16c..0000000000 --- a/store/shared/migrate/mysql/files/009_create_table_secrets.sql +++ /dev/null @@ -1,20 +0,0 @@ --- name: create-table-secrets - -CREATE TABLE IF NOT EXISTS secrets ( - secret_id INTEGER PRIMARY KEY AUTO_INCREMENT -,secret_repo_id INTEGER -,secret_name VARCHAR(500) -,secret_data BLOB -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_repo_id, secret_name) -,FOREIGN KEY(secret_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-secrets-repo - -CREATE INDEX ix_secret_repo ON secrets (secret_repo_id); - --- name: create-index-secrets-repo-name - -CREATE INDEX ix_secret_repo_name ON secrets (secret_repo_id, secret_name); diff --git a/store/shared/migrate/mysql/files/010_create_table_nodes.sql b/store/shared/migrate/mysql/files/010_create_table_nodes.sql deleted file mode 100644 index f11c962741..0000000000 --- a/store/shared/migrate/mysql/files/010_create_table_nodes.sql +++ /dev/null @@ -1,33 +0,0 @@ --- name: create-table-nodes - -CREATE TABLE IF NOT EXISTS nodes ( - node_id INTEGER PRIMARY KEY AUTO_INCREMENT -,node_uid VARCHAR(500) -,node_provider VARCHAR(50) -,node_state VARCHAR(50) -,node_name VARCHAR(50) -,node_image VARCHAR(500) -,node_region VARCHAR(100) -,node_size VARCHAR(100) -,node_os VARCHAR(50) -,node_arch VARCHAR(50) -,node_kernel VARCHAR(50) -,node_variant VARCHAR(50) -,node_address VARCHAR(500) -,node_capacity INTEGER -,node_filter VARCHAR(2000) -,node_labels VARCHAR(2000) -,node_error VARCHAR(2000) -,node_ca_key BLOB -,node_ca_cert BLOB -,node_tls_key BLOB -,node_tls_cert BLOB -,node_tls_name VARCHAR(500) -,node_paused BOOLEAN -,node_protected BOOLEAN -,node_created INTEGER -,node_updated INTEGER -,node_pulled INTEGER - -,UNIQUE(node_name) -); diff --git a/store/shared/migrate/mysql/files/011_add_column_builds_cron.sql b/store/shared/migrate/mysql/files/011_add_column_builds_cron.sql deleted file mode 100644 index 11d82ac814..0000000000 --- a/store/shared/migrate/mysql/files/011_add_column_builds_cron.sql +++ /dev/null @@ -1,3 +0,0 @@ --- name: alter-table-builds-add-column-cron - -ALTER TABLE builds ADD COLUMN build_cron VARCHAR(50) NOT NULL DEFAULT ''; diff --git a/store/shared/migrate/mysql/files/012_create_table_global_secrets.sql b/store/shared/migrate/mysql/files/012_create_table_global_secrets.sql deleted file mode 100644 index 917abad493..0000000000 --- a/store/shared/migrate/mysql/files/012_create_table_global_secrets.sql +++ /dev/null @@ -1,12 +0,0 @@ --- name: create-table-org-secrets - -CREATE TABLE IF NOT EXISTS orgsecrets ( - secret_id INTEGER PRIMARY KEY AUTO_INCREMENT -,secret_namespace VARCHAR(50) -,secret_name VARCHAR(200) -,secret_type VARCHAR(50) -,secret_data BLOB -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_namespace, secret_name) -); diff --git a/store/shared/migrate/mysql/files/013_add_column_builds_deploy_id.sql b/store/shared/migrate/mysql/files/013_add_column_builds_deploy_id.sql deleted file mode 100644 index b09a583256..0000000000 --- a/store/shared/migrate/mysql/files/013_add_column_builds_deploy_id.sql +++ /dev/null @@ -1,3 +0,0 @@ --- name: alter-table-builds-add-column-deploy-id - -ALTER TABLE builds ADD COLUMN build_deploy_id INTEGER NOT NULL DEFAULT 0; diff --git a/store/shared/migrate/mysql/files/014_create_table_refs.sql b/store/shared/migrate/mysql/files/014_create_table_refs.sql deleted file mode 100644 index f342b88d59..0000000000 --- a/store/shared/migrate/mysql/files/014_create_table_refs.sql +++ /dev/null @@ -1,16 +0,0 @@ --- name: create-table-latest - -CREATE TABLE IF NOT EXISTS latest ( - latest_repo_id INTEGER -,latest_build_id INTEGER -,latest_type VARCHAR(50) -,latest_name VARCHAR(500) -,latest_created INTEGER -,latest_updated INTEGER -,latest_deleted INTEGER -,PRIMARY KEY(latest_repo_id, latest_type, latest_name) -); - --- name: create-index-latest-repo - -CREATE INDEX ix_latest_repo ON latest (latest_repo_id); diff --git a/store/shared/migrate/mysql/files/015_create_table_templates.sql b/store/shared/migrate/mysql/files/015_create_table_templates.sql deleted file mode 100644 index ab8ddc1eb5..0000000000 --- a/store/shared/migrate/mysql/files/015_create_table_templates.sql +++ /dev/null @@ -1,15 +0,0 @@ --- name: create-table-template - -CREATE TABLE IF NOT EXISTS templates ( - template_id INTEGER PRIMARY KEY AUTO_INCREMENT - ,template_name VARCHAR(500) - ,template_namespace VARCHAR(50) - ,template_data BLOB - ,template_created INTEGER - ,template_updated INTEGER - ,UNIQUE(template_name, template_namespace) - ); - --- name: create-index-template-namespace - -CREATE INDEX ix_template_namespace ON templates (template_namespace); \ No newline at end of file diff --git a/store/shared/migrate/mysql/files/016_add_columns_steps.sql b/store/shared/migrate/mysql/files/016_add_columns_steps.sql deleted file mode 100644 index 6df6c67c16..0000000000 --- a/store/shared/migrate/mysql/files/016_add_columns_steps.sql +++ /dev/null @@ -1,11 +0,0 @@ --- name: alter-table-steps-add-column-step-depends-on - -ALTER TABLE steps ADD COLUMN step_depends_on TEXT NULL; - --- name: alter-table-steps-add-column-step-image - -ALTER TABLE steps ADD COLUMN step_image VARCHAR(1000) NOT NULL DEFAULT ''; - --- name: alter-table-steps-add-column-step-detached - -ALTER TABLE steps ADD COLUMN step_detached BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/store/shared/migrate/mysql/files/017_create_table_cards.sql b/store/shared/migrate/mysql/files/017_create_table_cards.sql deleted file mode 100644 index 6418b0619a..0000000000 --- a/store/shared/migrate/mysql/files/017_create_table_cards.sql +++ /dev/null @@ -1,16 +0,0 @@ --- name: create-table-cards - -CREATE TABLE IF NOT EXISTS cards ( - card_id INTEGER PRIMARY KEY AUTO_INCREMENT - ,card_build INTEGER - ,card_stage INTEGER - ,card_step INTEGER - ,card_schema TEXT - ,card_data TEXT -); - --- name: create-index-cards-card-build -CREATE INDEX ix_cards_build ON cards (card_build); - --- name: create-index-cards-card_step -CREATE UNIQUE INDEX ix_cards_step ON cards (card_step); diff --git a/store/shared/migrate/mysql/files/018_amend_table_cards.sql b/store/shared/migrate/mysql/files/018_amend_table_cards.sql deleted file mode 100644 index e041289aa0..0000000000 --- a/store/shared/migrate/mysql/files/018_amend_table_cards.sql +++ /dev/null @@ -1,16 +0,0 @@ --- name: drop-table-cards - -DROP TABLE IF EXISTS cards; - --- name: alter-table-steps-add-column-step_schema - -ALTER TABLE steps - ADD COLUMN step_schema VARCHAR(2000) NOT NULL DEFAULT ''; - --- name: create-new-table-cards -CREATE TABLE IF NOT EXISTS cards -( - card_id INTEGER PRIMARY KEY, - card_data BLOB, - FOREIGN KEY (card_id) REFERENCES steps (step_id) ON DELETE CASCADE -); \ No newline at end of file diff --git a/store/shared/migrate/postgres/ddl.go b/store/shared/migrate/postgres/ddl.go deleted file mode 100644 index 99354fc753..0000000000 --- a/store/shared/migrate/postgres/ddl.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package postgres - -//go:generate togo ddl -package postgres -dialect postgres diff --git a/store/shared/migrate/postgres/ddl_gen.go b/store/shared/migrate/postgres/ddl_gen.go deleted file mode 100644 index d87aaf5016..0000000000 --- a/store/shared/migrate/postgres/ddl_gen.go +++ /dev/null @@ -1,771 +0,0 @@ -package postgres - -import ( - "database/sql" -) - -var migrations = []struct { - name string - stmt string -}{ - { - name: "create-table-users", - stmt: createTableUsers, - }, - { - name: "create-table-repos", - stmt: createTableRepos, - }, - { - name: "alter-table-repos-add-column-no-fork", - stmt: alterTableReposAddColumnNoFork, - }, - { - name: "alter-table-repos-add-column-no-pulls", - stmt: alterTableReposAddColumnNoPulls, - }, - { - name: "alter-table-repos-add-column-cancel-pulls", - stmt: alterTableReposAddColumnCancelPulls, - }, - { - name: "alter-table-repos-add-column-cancel-push", - stmt: alterTableReposAddColumnCancelPush, - }, - { - name: "alter-table-repos-add-column-throttle", - stmt: alterTableReposAddColumnThrottle, - }, - { - name: "alter-table-repos-add-column-cancel-running", - stmt: alterTableReposAddColumnCancelRunning, - }, - { - name: "create-table-perms", - stmt: createTablePerms, - }, - { - name: "create-index-perms-user", - stmt: createIndexPermsUser, - }, - { - name: "create-index-perms-repo", - stmt: createIndexPermsRepo, - }, - { - name: "create-table-builds", - stmt: createTableBuilds, - }, - { - name: "create-index-builds-incomplete", - stmt: createIndexBuildsIncomplete, - }, - { - name: "create-index-builds-repo", - stmt: createIndexBuildsRepo, - }, - { - name: "create-index-builds-author", - stmt: createIndexBuildsAuthor, - }, - { - name: "create-index-builds-sender", - stmt: createIndexBuildsSender, - }, - { - name: "create-index-builds-ref", - stmt: createIndexBuildsRef, - }, - { - name: "alter-table-builds-add-column-debug", - stmt: alterTableBuildsAddColumnDebug, - }, - { - name: "create-table-stages", - stmt: createTableStages, - }, - { - name: "create-index-stages-build", - stmt: createIndexStagesBuild, - }, - { - name: "create-index-stages-status", - stmt: createIndexStagesStatus, - }, - { - name: "alter-table-stages-add-column-limit-repos", - stmt: alterTableStagesAddColumnLimitRepos, - }, - { - name: "create-table-steps", - stmt: createTableSteps, - }, - { - name: "create-index-steps-stage", - stmt: createIndexStepsStage, - }, - { - name: "create-table-logs", - stmt: createTableLogs, - }, - { - name: "create-table-cron", - stmt: createTableCron, - }, - { - name: "create-index-cron-repo", - stmt: createIndexCronRepo, - }, - { - name: "create-index-cron-next", - stmt: createIndexCronNext, - }, - { - name: "create-table-secrets", - stmt: createTableSecrets, - }, - { - name: "create-index-secrets-repo", - stmt: createIndexSecretsRepo, - }, - { - name: "create-index-secrets-repo-name", - stmt: createIndexSecretsRepoName, - }, - { - name: "create-table-nodes", - stmt: createTableNodes, - }, - { - name: "alter-table-builds-add-column-cron", - stmt: alterTableBuildsAddColumnCron, - }, - { - name: "create-table-org-secrets", - stmt: createTableOrgSecrets, - }, - { - name: "alter-table-builds-add-column-deploy-id", - stmt: alterTableBuildsAddColumnDeployId, - }, - { - name: "create-table-latest", - stmt: createTableLatest, - }, - { - name: "create-index-latest-repo", - stmt: createIndexLatestRepo, - }, - { - name: "create-table-template", - stmt: createTableTemplate, - }, - { - name: "create-index-template-namespace", - stmt: createIndexTemplateNamespace, - }, - { - name: "alter-table-steps-add-column-step-depends-on", - stmt: alterTableStepsAddColumnStepDependsOn, - }, - { - name: "alter-table-steps-add-column-step-image", - stmt: alterTableStepsAddColumnStepImage, - }, - { - name: "alter-table-steps-add-column-step-detached", - stmt: alterTableStepsAddColumnStepDetached, - }, - { - name: "create-table-cards", - stmt: createTableCards, - }, - { - name: "create-index-cards-card_build", - stmt: createIndexCardsCardbuild, - }, - { - name: "create-index-cards-card_step", - stmt: createIndexCardsCardstep, - }, - { - name: "drop-table-cards", - stmt: dropTableCards, - }, - { - name: "alter-table-steps-add-column-step_schema", - stmt: alterTableStepsAddColumnStepschema, - }, - { - name: "create-new-table-cards", - stmt: createNewTableCards, - }, -} - -// Migrate performs the database migration. If the migration fails -// and error is returned. -func Migrate(db *sql.DB) error { - if err := createTable(db); err != nil { - return err - } - completed, err := selectCompleted(db) - if err != nil && err != sql.ErrNoRows { - return err - } - for _, migration := range migrations { - if _, ok := completed[migration.name]; ok { - - continue - } - - if _, err := db.Exec(migration.stmt); err != nil { - return err - } - if err := insertMigration(db, migration.name); err != nil { - return err - } - - } - return nil -} - -func createTable(db *sql.DB) error { - _, err := db.Exec(migrationTableCreate) - return err -} - -func insertMigration(db *sql.DB, name string) error { - _, err := db.Exec(migrationInsert, name) - return err -} - -func selectCompleted(db *sql.DB) (map[string]struct{}, error) { - migrations := map[string]struct{}{} - rows, err := db.Query(migrationSelect) - if err != nil { - return nil, err - } - defer rows.Close() - for rows.Next() { - var name string - if err := rows.Scan(&name); err != nil { - return nil, err - } - migrations[name] = struct{}{} - } - return migrations, nil -} - -// -// migration table ddl and sql -// - -var migrationTableCreate = ` -CREATE TABLE IF NOT EXISTS migrations ( - name VARCHAR(255) -,UNIQUE(name) -) -` - -var migrationInsert = ` -INSERT INTO migrations (name) VALUES ($1) -` - -var migrationSelect = ` -SELECT name FROM migrations -` - -// -// 001_create_table_user.sql -// - -var createTableUsers = ` -CREATE TABLE IF NOT EXISTS users ( - user_id SERIAL PRIMARY KEY -,user_login VARCHAR(250) -,user_email VARCHAR(500) -,user_admin BOOLEAN -,user_active BOOLEAN -,user_machine BOOLEAN -,user_avatar VARCHAR(2000) -,user_syncing BOOLEAN -,user_synced INTEGER -,user_created INTEGER -,user_updated INTEGER -,user_last_login INTEGER -,user_oauth_token BYTEA -,user_oauth_refresh BYTEA -,user_oauth_expiry INTEGER -,user_hash VARCHAR(500) -,UNIQUE(user_login) -,UNIQUE(user_hash) -); -` - -// -// 002_create_table_repos.sql -// - -var createTableRepos = ` -CREATE TABLE IF NOT EXISTS repos ( - repo_id SERIAL PRIMARY KEY -,repo_uid VARCHAR(250) -,repo_user_id INTEGER -,repo_namespace VARCHAR(250) -,repo_name VARCHAR(250) -,repo_slug VARCHAR(250) -,repo_scm VARCHAR(50) -,repo_clone_url VARCHAR(2000) -,repo_ssh_url VARCHAR(2000) -,repo_html_url VARCHAR(2000) -,repo_active BOOLEAN -,repo_private BOOLEAN -,repo_visibility VARCHAR(50) -,repo_branch VARCHAR(250) -,repo_counter INTEGER -,repo_config VARCHAR(500) -,repo_timeout INTEGER -,repo_trusted BOOLEAN -,repo_protected BOOLEAN -,repo_synced INTEGER -,repo_created INTEGER -,repo_updated INTEGER -,repo_version INTEGER -,repo_signer VARCHAR(50) -,repo_secret VARCHAR(50) -,UNIQUE(repo_slug) -,UNIQUE(repo_uid) -); -` - -var alterTableReposAddColumnNoFork = ` -ALTER TABLE repos ADD COLUMN repo_no_forks BOOLEAN NOT NULL DEFAULT false; -` - -var alterTableReposAddColumnNoPulls = ` -ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT false; -` - -var alterTableReposAddColumnCancelPulls = ` -ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT false; -` - -var alterTableReposAddColumnCancelPush = ` -ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT false; -` - -var alterTableReposAddColumnThrottle = ` -ALTER TABLE repos ADD COLUMN repo_throttle INTEGER NOT NULL DEFAULT 0; -` - -var alterTableReposAddColumnCancelRunning = ` -ALTER TABLE repos ADD COLUMN repo_cancel_running BOOLEAN NOT NULL DEFAULT false; -` - -// -// 003_create_table_perms.sql -// - -var createTablePerms = ` -CREATE TABLE IF NOT EXISTS perms ( - perm_user_id INTEGER -,perm_repo_uid VARCHAR(250) -,perm_read BOOLEAN -,perm_write BOOLEAN -,perm_admin BOOLEAN -,perm_synced INTEGER -,perm_created INTEGER -,perm_updated INTEGER -,PRIMARY KEY(perm_user_id, perm_repo_uid) -); -` - -var createIndexPermsUser = ` -CREATE INDEX IF NOT EXISTS ix_perms_user ON perms (perm_user_id); -` - -var createIndexPermsRepo = ` -CREATE INDEX IF NOT EXISTS ix_perms_repo ON perms (perm_repo_uid); -` - -// -// 004_create_table_builds.sql -// - -var createTableBuilds = ` -CREATE TABLE IF NOT EXISTS builds ( - build_id SERIAL PRIMARY KEY -,build_repo_id INTEGER -,build_config_id INTEGER -,build_trigger VARCHAR(250) -,build_number INTEGER -,build_parent INTEGER -,build_status VARCHAR(50) -,build_error VARCHAR(500) -,build_event VARCHAR(50) -,build_action VARCHAR(50) -,build_link VARCHAR(2000) -,build_timestamp INTEGER -,build_title VARCHAR(2000) -,build_message VARCHAR(2000) -,build_before VARCHAR(50) -,build_after VARCHAR(50) -,build_ref VARCHAR(500) -,build_source_repo VARCHAR(250) -,build_source VARCHAR(500) -,build_target VARCHAR(500) -,build_author VARCHAR(500) -,build_author_name VARCHAR(500) -,build_author_email VARCHAR(500) -,build_author_avatar VARCHAR(2000) -,build_sender VARCHAR(500) -,build_deploy VARCHAR(500) -,build_params VARCHAR(4000) -,build_started INTEGER -,build_finished INTEGER -,build_created INTEGER -,build_updated INTEGER -,build_version INTEGER -,UNIQUE(build_repo_id, build_number) -); -` - -var createIndexBuildsIncomplete = ` -CREATE INDEX IF NOT EXISTS ix_build_incomplete ON builds (build_status) -WHERE build_status IN ('pending', 'running'); -` - -var createIndexBuildsRepo = ` -CREATE INDEX IF NOT EXISTS ix_build_repo ON builds (build_repo_id); -` - -var createIndexBuildsAuthor = ` -CREATE INDEX IF NOT EXISTS ix_build_author ON builds (build_author); -` - -var createIndexBuildsSender = ` -CREATE INDEX IF NOT EXISTS ix_build_sender ON builds (build_sender); -` - -var createIndexBuildsRef = ` -CREATE INDEX IF NOT EXISTS ix_build_ref ON builds (build_repo_id, build_ref); -` - -var alterTableBuildsAddColumnDebug = ` -ALTER TABLE builds ADD COLUMN build_debug BOOLEAN NOT NULL DEFAULT false; -` - -// -// 005_create_table_stages.sql -// - -var createTableStages = ` -CREATE TABLE IF NOT EXISTS stages ( - stage_id SERIAL PRIMARY KEY -,stage_repo_id INTEGER -,stage_build_id INTEGER -,stage_number INTEGER -,stage_name VARCHAR(100) -,stage_kind VARCHAR(50) -,stage_type VARCHAR(50) -,stage_status VARCHAR(50) -,stage_error VARCHAR(500) -,stage_errignore BOOLEAN -,stage_exit_code INTEGER -,stage_limit INTEGER -,stage_os VARCHAR(50) -,stage_arch VARCHAR(50) -,stage_variant VARCHAR(10) -,stage_kernel VARCHAR(50) -,stage_machine VARCHAR(500) -,stage_started INTEGER -,stage_stopped INTEGER -,stage_created INTEGER -,stage_updated INTEGER -,stage_version INTEGER -,stage_on_success BOOLEAN -,stage_on_failure BOOLEAN -,stage_depends_on TEXT -,stage_labels TEXT -,UNIQUE(stage_build_id, stage_number) -); -` - -var createIndexStagesBuild = ` -CREATE INDEX IF NOT EXISTS ix_stages_build ON stages (stage_build_id); -` - -var createIndexStagesStatus = ` -CREATE INDEX IF NOT EXISTS ix_stage_in_progress ON stages (stage_status) -WHERE stage_status IN ('pending', 'running'); -` - -var alterTableStagesAddColumnLimitRepos = ` -ALTER TABLE stages ADD COLUMN stage_limit_repo INTEGER NOT NULL DEFAULT 0; -` - -// -// 006_create_table_steps.sql -// - -var createTableSteps = ` -CREATE TABLE IF NOT EXISTS steps ( - step_id SERIAL PRIMARY KEY -,step_stage_id INTEGER -,step_number INTEGER -,step_name VARCHAR(100) -,step_status VARCHAR(50) -,step_error VARCHAR(500) -,step_errignore BOOLEAN -,step_exit_code INTEGER -,step_started INTEGER -,step_stopped INTEGER -,step_version INTEGER -,UNIQUE(step_stage_id, step_number) -); -` - -var createIndexStepsStage = ` -CREATE INDEX IF NOT EXISTS ix_steps_stage ON steps (step_stage_id); -` - -// -// 007_create_table_logs.sql -// - -var createTableLogs = ` -CREATE TABLE IF NOT EXISTS logs ( - log_id SERIAL PRIMARY KEY -,log_data BYTEA -); -` - -// -// 008_create_table_cron.sql -// - -var createTableCron = ` -CREATE TABLE IF NOT EXISTS cron ( - cron_id SERIAL PRIMARY KEY -,cron_repo_id INTEGER -,cron_name VARCHAR(50) -,cron_expr VARCHAR(50) -,cron_next INTEGER -,cron_prev INTEGER -,cron_event VARCHAR(50) -,cron_branch VARCHAR(250) -,cron_target VARCHAR(250) -,cron_disabled BOOLEAN -,cron_created INTEGER -,cron_updated INTEGER -,cron_version INTEGER -,UNIQUE(cron_repo_id, cron_name) -,FOREIGN KEY(cron_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); -` - -var createIndexCronRepo = ` -CREATE INDEX IF NOT EXISTS ix_cron_repo ON cron (cron_repo_id); -` - -var createIndexCronNext = ` -CREATE INDEX IF NOT EXISTS ix_cron_next ON cron (cron_next); -` - -// -// 009_create_table_secrets.sql -// - -var createTableSecrets = ` -CREATE TABLE IF NOT EXISTS secrets ( - secret_id SERIAL PRIMARY KEY -,secret_repo_id INTEGER -,secret_name VARCHAR(500) -,secret_data BYTEA -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_repo_id, secret_name) -,FOREIGN KEY(secret_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); -` - -var createIndexSecretsRepo = ` -CREATE INDEX IF NOT EXISTS ix_secret_repo ON secrets (secret_repo_id); -` - -var createIndexSecretsRepoName = ` -CREATE INDEX IF NOT EXISTS ix_secret_repo_name ON secrets (secret_repo_id, secret_name); -` - -// -// 010_create_table_nodes.sql -// - -var createTableNodes = ` -CREATE TABLE IF NOT EXISTS nodes ( - node_id SERIAL PRIMARY KEY -,node_uid VARCHAR(500) -,node_provider VARCHAR(50) -,node_state VARCHAR(50) -,node_name VARCHAR(50) -,node_image VARCHAR(500) -,node_region VARCHAR(100) -,node_size VARCHAR(100) -,node_os VARCHAR(50) -,node_arch VARCHAR(50) -,node_kernel VARCHAR(50) -,node_variant VARCHAR(50) -,node_address VARCHAR(500) -,node_capacity INTEGER -,node_filter VARCHAR(2000) -,node_labels VARCHAR(2000) -,node_error VARCHAR(2000) -,node_ca_key BYTEA -,node_ca_cert BYTEA -,node_tls_key BYTEA -,node_tls_cert BYTEA -,node_tls_name VARCHAR(500) -,node_paused BOOLEAN -,node_protected BOOLEAN -,node_created INTEGER -,node_updated INTEGER -,node_pulled INTEGER - -,UNIQUE(node_name) -); -` - -// -// 011_add_column_builds_cron.sql -// - -var alterTableBuildsAddColumnCron = ` -ALTER TABLE builds ADD COLUMN build_cron VARCHAR(50) NOT NULL DEFAULT ''; -` - -// -// 012_create_table_org_secrets.sql -// - -var createTableOrgSecrets = ` -CREATE TABLE IF NOT EXISTS orgsecrets ( - secret_id SERIAL PRIMARY KEY -,secret_namespace VARCHAR(50) -,secret_name VARCHAR(200) -,secret_type VARCHAR(50) -,secret_data BYTEA -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_namespace, secret_name) -); -` - -// -// 013_add_column_builds_deploy_id.sql -// - -var alterTableBuildsAddColumnDeployId = ` -ALTER TABLE builds ADD COLUMN build_deploy_id INTEGER NOT NULL DEFAULT 0; -` - -// -// 015_create_table_refs.sql -// - -var createTableLatest = ` -CREATE TABLE IF NOT EXISTS latest ( - latest_repo_id INTEGER -,latest_build_id INTEGER -,latest_type VARCHAR(50) -,latest_name VARCHAR(500) -,latest_created INTEGER -,latest_updated INTEGER -,latest_deleted INTEGER -,PRIMARY KEY(latest_repo_id, latest_type, latest_name) -); -` - -var createIndexLatestRepo = ` -CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id); -` - -// -// 016_create_template_tables.sql -// - -var createTableTemplate = ` -CREATE TABLE IF NOT EXISTS templates ( - template_id SERIAL PRIMARY KEY - ,template_name TEXT - ,template_namespace VARCHAR(50) - ,template_data BYTEA - ,template_created INTEGER - ,template_updated INTEGER -,UNIQUE(template_name, template_namespace) -); -` - -var createIndexTemplateNamespace = ` -CREATE INDEX IF NOT EXISTS ix_template_namespace ON templates (template_namespace); -` - -// -// 017_add_columns_steps.sql -// - -var alterTableStepsAddColumnStepDependsOn = ` -ALTER TABLE steps ADD COLUMN step_depends_on TEXT NOT NULL DEFAULT ''; -` - -var alterTableStepsAddColumnStepImage = ` -ALTER TABLE steps ADD COLUMN step_image VARCHAR(1000) NOT NULL DEFAULT ''; -` - -var alterTableStepsAddColumnStepDetached = ` -ALTER TABLE steps ADD COLUMN step_detached BOOLEAN NOT NULL DEFAULT FALSE; -` - -// -// 018_create_table_cards.sql -// - -var createTableCards = ` -CREATE TABLE IF NOT EXISTS cards ( - card_id SERIAL PRIMARY KEY - ,card_build INTEGER - ,card_stage INTEGER - ,card_step INTEGER - ,card_schema TEXT - ,card_data TEXT -); -` - -var createIndexCardsCardbuild = ` -CREATE INDEX IF NOT EXISTS ix_cards_build ON cards (card_build); -` - -var createIndexCardsCardstep = ` -CREATE UNIQUE INDEX IF NOT EXISTS ix_cards_step ON cards (card_step); -` - -// -// 019_amend_table_cards.sql -// - -var dropTableCards = ` -DROP TABLE IF EXISTS cards; -` - -var alterTableStepsAddColumnStepschema = ` -ALTER TABLE steps - ADD COLUMN step_schema VARCHAR(2000) NOT NULL DEFAULT ''; -` - -var createNewTableCards = ` -CREATE TABLE IF NOT EXISTS cards -( - card_id SERIAL PRIMARY KEY, - card_data BYTEA, - FOREIGN KEY (card_id) REFERENCES steps (step_id) ON DELETE CASCADE -); -` diff --git a/store/shared/migrate/postgres/files/001_create_table_user.sql b/store/shared/migrate/postgres/files/001_create_table_user.sql deleted file mode 100644 index 47432c8dc7..0000000000 --- a/store/shared/migrate/postgres/files/001_create_table_user.sql +++ /dev/null @@ -1,22 +0,0 @@ --- name: create-table-users - -CREATE TABLE IF NOT EXISTS users ( - user_id SERIAL PRIMARY KEY -,user_login VARCHAR(250) -,user_email VARCHAR(500) -,user_admin BOOLEAN -,user_active BOOLEAN -,user_machine BOOLEAN -,user_avatar VARCHAR(2000) -,user_syncing BOOLEAN -,user_synced INTEGER -,user_created INTEGER -,user_updated INTEGER -,user_last_login INTEGER -,user_oauth_token BYTEA -,user_oauth_refresh BYTEA -,user_oauth_expiry INTEGER -,user_hash VARCHAR(500) -,UNIQUE(user_login) -,UNIQUE(user_hash) -); diff --git a/store/shared/migrate/postgres/files/002_create_table_repos.sql b/store/shared/migrate/postgres/files/002_create_table_repos.sql deleted file mode 100644 index 06e0020ba8..0000000000 --- a/store/shared/migrate/postgres/files/002_create_table_repos.sql +++ /dev/null @@ -1,55 +0,0 @@ --- name: create-table-repos - -CREATE TABLE IF NOT EXISTS repos ( - repo_id SERIAL PRIMARY KEY -,repo_uid VARCHAR(250) -,repo_user_id INTEGER -,repo_namespace VARCHAR(250) -,repo_name VARCHAR(250) -,repo_slug VARCHAR(250) -,repo_scm VARCHAR(50) -,repo_clone_url VARCHAR(2000) -,repo_ssh_url VARCHAR(2000) -,repo_html_url VARCHAR(2000) -,repo_active BOOLEAN -,repo_private BOOLEAN -,repo_visibility VARCHAR(50) -,repo_branch VARCHAR(250) -,repo_counter INTEGER -,repo_config VARCHAR(500) -,repo_timeout INTEGER -,repo_trusted BOOLEAN -,repo_protected BOOLEAN -,repo_synced INTEGER -,repo_created INTEGER -,repo_updated INTEGER -,repo_version INTEGER -,repo_signer VARCHAR(50) -,repo_secret VARCHAR(50) -,UNIQUE(repo_slug) -,UNIQUE(repo_uid) -); - --- name: alter-table-repos-add-column-no-fork - -ALTER TABLE repos ADD COLUMN repo_no_forks BOOLEAN NOT NULL DEFAULT false; - --- name: alter-table-repos-add-column-no-pulls - -ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT false; - --- name: alter-table-repos-add-column-cancel-pulls - -ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT false; - --- name: alter-table-repos-add-column-cancel-push - -ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT false; - --- name: alter-table-repos-add-column-throttle - -ALTER TABLE repos ADD COLUMN repo_throttle INTEGER NOT NULL DEFAULT 0; - --- name: alter-table-repos-add-column-cancel-running - -ALTER TABLE repos ADD COLUMN repo_cancel_running BOOLEAN NOT NULL DEFAULT false; \ No newline at end of file diff --git a/store/shared/migrate/postgres/files/003_create_table_perms.sql b/store/shared/migrate/postgres/files/003_create_table_perms.sql deleted file mode 100644 index d5c61b3906..0000000000 --- a/store/shared/migrate/postgres/files/003_create_table_perms.sql +++ /dev/null @@ -1,23 +0,0 @@ --- name: create-table-perms - -CREATE TABLE IF NOT EXISTS perms ( - perm_user_id INTEGER -,perm_repo_uid VARCHAR(250) -,perm_read BOOLEAN -,perm_write BOOLEAN -,perm_admin BOOLEAN -,perm_synced INTEGER -,perm_created INTEGER -,perm_updated INTEGER -,PRIMARY KEY(perm_user_id, perm_repo_uid) ---,FOREIGN KEY(perm_user_id) REFERENCES users(user_id) ON DELETE CASCADE ---,FOREIGN KEY(perm_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-perms-user - -CREATE INDEX IF NOT EXISTS ix_perms_user ON perms (perm_user_id); - --- name: create-index-perms-repo - -CREATE INDEX IF NOT EXISTS ix_perms_repo ON perms (perm_repo_uid); diff --git a/store/shared/migrate/postgres/files/004_create_table_builds.sql b/store/shared/migrate/postgres/files/004_create_table_builds.sql deleted file mode 100644 index 1f54881cd8..0000000000 --- a/store/shared/migrate/postgres/files/004_create_table_builds.sql +++ /dev/null @@ -1,63 +0,0 @@ --- name: create-table-builds - -CREATE TABLE IF NOT EXISTS builds ( - build_id SERIAL PRIMARY KEY -,build_repo_id INTEGER -,build_config_id INTEGER -,build_trigger VARCHAR(250) -,build_number INTEGER -,build_parent INTEGER -,build_status VARCHAR(50) -,build_error VARCHAR(500) -,build_event VARCHAR(50) -,build_action VARCHAR(50) -,build_link VARCHAR(2000) -,build_timestamp INTEGER -,build_title VARCHAR(2000) -,build_message VARCHAR(2000) -,build_before VARCHAR(50) -,build_after VARCHAR(50) -,build_ref VARCHAR(500) -,build_source_repo VARCHAR(250) -,build_source VARCHAR(500) -,build_target VARCHAR(500) -,build_author VARCHAR(500) -,build_author_name VARCHAR(500) -,build_author_email VARCHAR(500) -,build_author_avatar VARCHAR(2000) -,build_sender VARCHAR(500) -,build_deploy VARCHAR(500) -,build_params VARCHAR(4000) -,build_started INTEGER -,build_finished INTEGER -,build_created INTEGER -,build_updated INTEGER -,build_version INTEGER -,UNIQUE(build_repo_id, build_number) ---,FOREIGN KEY(build_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-builds-incomplete - -CREATE INDEX IF NOT EXISTS ix_build_incomplete ON builds (build_status) -WHERE build_status IN ('pending', 'running'); - --- name: create-index-builds-repo - -CREATE INDEX IF NOT EXISTS ix_build_repo ON builds (build_repo_id); - --- name: create-index-builds-author - -CREATE INDEX IF NOT EXISTS ix_build_author ON builds (build_author); - --- name: create-index-builds-sender - -CREATE INDEX IF NOT EXISTS ix_build_sender ON builds (build_sender); - --- name: create-index-builds-ref - -CREATE INDEX IF NOT EXISTS ix_build_ref ON builds (build_repo_id, build_ref); - --- name: alter-table-builds-add-column-debug - -ALTER TABLE builds ADD COLUMN build_debug BOOLEAN NOT NULL DEFAULT false; diff --git a/store/shared/migrate/postgres/files/005_create_table_stages.sql b/store/shared/migrate/postgres/files/005_create_table_stages.sql deleted file mode 100644 index 32505d3ce2..0000000000 --- a/store/shared/migrate/postgres/files/005_create_table_stages.sql +++ /dev/null @@ -1,44 +0,0 @@ --- name: create-table-stages - -CREATE TABLE IF NOT EXISTS stages ( - stage_id SERIAL PRIMARY KEY -,stage_repo_id INTEGER -,stage_build_id INTEGER -,stage_number INTEGER -,stage_name VARCHAR(100) -,stage_kind VARCHAR(50) -,stage_type VARCHAR(50) -,stage_status VARCHAR(50) -,stage_error VARCHAR(500) -,stage_errignore BOOLEAN -,stage_exit_code INTEGER -,stage_limit INTEGER -,stage_os VARCHAR(50) -,stage_arch VARCHAR(50) -,stage_variant VARCHAR(10) -,stage_kernel VARCHAR(50) -,stage_machine VARCHAR(500) -,stage_started INTEGER -,stage_stopped INTEGER -,stage_created INTEGER -,stage_updated INTEGER -,stage_version INTEGER -,stage_on_success BOOLEAN -,stage_on_failure BOOLEAN -,stage_depends_on TEXT -,stage_labels TEXT -,UNIQUE(stage_build_id, stage_number) -); - --- name: create-index-stages-build - -CREATE INDEX IF NOT EXISTS ix_stages_build ON stages (stage_build_id); - --- name: create-index-stages-status - -CREATE INDEX IF NOT EXISTS ix_stage_in_progress ON stages (stage_status) -WHERE stage_status IN ('pending', 'running'); - --- name: alter-table-stages-add-column-limit-repos - -ALTER TABLE stages ADD COLUMN stage_limit_repo INTEGER NOT NULL DEFAULT 0; diff --git a/store/shared/migrate/postgres/files/006_create_table_steps.sql b/store/shared/migrate/postgres/files/006_create_table_steps.sql deleted file mode 100644 index ff9ade1da1..0000000000 --- a/store/shared/migrate/postgres/files/006_create_table_steps.sql +++ /dev/null @@ -1,20 +0,0 @@ --- name: create-table-steps - -CREATE TABLE IF NOT EXISTS steps ( - step_id SERIAL PRIMARY KEY -,step_stage_id INTEGER -,step_number INTEGER -,step_name VARCHAR(100) -,step_status VARCHAR(50) -,step_error VARCHAR(500) -,step_errignore BOOLEAN -,step_exit_code INTEGER -,step_started INTEGER -,step_stopped INTEGER -,step_version INTEGER -,UNIQUE(step_stage_id, step_number) -); - --- name: create-index-steps-stage - -CREATE INDEX IF NOT EXISTS ix_steps_stage ON steps (step_stage_id); diff --git a/store/shared/migrate/postgres/files/007_create_table_logs.sql b/store/shared/migrate/postgres/files/007_create_table_logs.sql deleted file mode 100644 index 25b00c687b..0000000000 --- a/store/shared/migrate/postgres/files/007_create_table_logs.sql +++ /dev/null @@ -1,6 +0,0 @@ --- name: create-table-logs - -CREATE TABLE IF NOT EXISTS logs ( - log_id SERIAL PRIMARY KEY -,log_data BYTEA -); diff --git a/store/shared/migrate/postgres/files/008_create_table_cron.sql b/store/shared/migrate/postgres/files/008_create_table_cron.sql deleted file mode 100644 index b890519269..0000000000 --- a/store/shared/migrate/postgres/files/008_create_table_cron.sql +++ /dev/null @@ -1,27 +0,0 @@ --- name: create-table-cron - -CREATE TABLE IF NOT EXISTS cron ( - cron_id SERIAL PRIMARY KEY -,cron_repo_id INTEGER -,cron_name VARCHAR(50) -,cron_expr VARCHAR(50) -,cron_next INTEGER -,cron_prev INTEGER -,cron_event VARCHAR(50) -,cron_branch VARCHAR(250) -,cron_target VARCHAR(250) -,cron_disabled BOOLEAN -,cron_created INTEGER -,cron_updated INTEGER -,cron_version INTEGER -,UNIQUE(cron_repo_id, cron_name) -,FOREIGN KEY(cron_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-cron-repo - -CREATE INDEX IF NOT EXISTS ix_cron_repo ON cron (cron_repo_id); - --- name: create-index-cron-next - -CREATE INDEX IF NOT EXISTS ix_cron_next ON cron (cron_next); diff --git a/store/shared/migrate/postgres/files/009_create_table_secrets.sql b/store/shared/migrate/postgres/files/009_create_table_secrets.sql deleted file mode 100644 index cafb290d2e..0000000000 --- a/store/shared/migrate/postgres/files/009_create_table_secrets.sql +++ /dev/null @@ -1,20 +0,0 @@ --- name: create-table-secrets - -CREATE TABLE IF NOT EXISTS secrets ( - secret_id SERIAL PRIMARY KEY -,secret_repo_id INTEGER -,secret_name VARCHAR(500) -,secret_data BYTEA -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_repo_id, secret_name) -,FOREIGN KEY(secret_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-secrets-repo - -CREATE INDEX IF NOT EXISTS ix_secret_repo ON secrets (secret_repo_id); - --- name: create-index-secrets-repo-name - -CREATE INDEX IF NOT EXISTS ix_secret_repo_name ON secrets (secret_repo_id, secret_name); diff --git a/store/shared/migrate/postgres/files/010_create_table_nodes.sql b/store/shared/migrate/postgres/files/010_create_table_nodes.sql deleted file mode 100644 index 1c84c4a915..0000000000 --- a/store/shared/migrate/postgres/files/010_create_table_nodes.sql +++ /dev/null @@ -1,33 +0,0 @@ --- name: create-table-nodes - -CREATE TABLE IF NOT EXISTS nodes ( - node_id SERIAL PRIMARY KEY -,node_uid VARCHAR(500) -,node_provider VARCHAR(50) -,node_state VARCHAR(50) -,node_name VARCHAR(50) -,node_image VARCHAR(500) -,node_region VARCHAR(100) -,node_size VARCHAR(100) -,node_os VARCHAR(50) -,node_arch VARCHAR(50) -,node_kernel VARCHAR(50) -,node_variant VARCHAR(50) -,node_address VARCHAR(500) -,node_capacity INTEGER -,node_filter VARCHAR(2000) -,node_labels VARCHAR(2000) -,node_error VARCHAR(2000) -,node_ca_key BYTEA -,node_ca_cert BYTEA -,node_tls_key BYTEA -,node_tls_cert BYTEA -,node_tls_name VARCHAR(500) -,node_paused BOOLEAN -,node_protected BOOLEAN -,node_created INTEGER -,node_updated INTEGER -,node_pulled INTEGER - -,UNIQUE(node_name) -); diff --git a/store/shared/migrate/postgres/files/011_add_column_builds_cron.sql b/store/shared/migrate/postgres/files/011_add_column_builds_cron.sql deleted file mode 100644 index 11d82ac814..0000000000 --- a/store/shared/migrate/postgres/files/011_add_column_builds_cron.sql +++ /dev/null @@ -1,3 +0,0 @@ --- name: alter-table-builds-add-column-cron - -ALTER TABLE builds ADD COLUMN build_cron VARCHAR(50) NOT NULL DEFAULT ''; diff --git a/store/shared/migrate/postgres/files/012_create_table_org_secrets.sql b/store/shared/migrate/postgres/files/012_create_table_org_secrets.sql deleted file mode 100644 index 7541ff069e..0000000000 --- a/store/shared/migrate/postgres/files/012_create_table_org_secrets.sql +++ /dev/null @@ -1,12 +0,0 @@ --- name: create-table-org-secrets - -CREATE TABLE IF NOT EXISTS orgsecrets ( - secret_id SERIAL PRIMARY KEY -,secret_namespace VARCHAR(50) -,secret_name VARCHAR(200) -,secret_type VARCHAR(50) -,secret_data BYTEA -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_namespace, secret_name) -); diff --git a/store/shared/migrate/postgres/files/013_add_column_builds_deploy_id.sql b/store/shared/migrate/postgres/files/013_add_column_builds_deploy_id.sql deleted file mode 100644 index b09a583256..0000000000 --- a/store/shared/migrate/postgres/files/013_add_column_builds_deploy_id.sql +++ /dev/null @@ -1,3 +0,0 @@ --- name: alter-table-builds-add-column-deploy-id - -ALTER TABLE builds ADD COLUMN build_deploy_id INTEGER NOT NULL DEFAULT 0; diff --git a/store/shared/migrate/postgres/files/015_create_table_refs.sql b/store/shared/migrate/postgres/files/015_create_table_refs.sql deleted file mode 100644 index dcd3ccc183..0000000000 --- a/store/shared/migrate/postgres/files/015_create_table_refs.sql +++ /dev/null @@ -1,16 +0,0 @@ --- name: create-table-latest - -CREATE TABLE IF NOT EXISTS latest ( - latest_repo_id INTEGER -,latest_build_id INTEGER -,latest_type VARCHAR(50) -,latest_name VARCHAR(500) -,latest_created INTEGER -,latest_updated INTEGER -,latest_deleted INTEGER -,PRIMARY KEY(latest_repo_id, latest_type, latest_name) -); - --- name: create-index-latest-repo - -CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id); diff --git a/store/shared/migrate/postgres/files/016_create_template_tables.sql b/store/shared/migrate/postgres/files/016_create_template_tables.sql deleted file mode 100644 index c08bb5a21a..0000000000 --- a/store/shared/migrate/postgres/files/016_create_template_tables.sql +++ /dev/null @@ -1,15 +0,0 @@ --- name: create-table-template - -CREATE TABLE IF NOT EXISTS templates ( - template_id SERIAL PRIMARY KEY - ,template_name TEXT - ,template_namespace VARCHAR(50) - ,template_data BYTEA - ,template_created INTEGER - ,template_updated INTEGER -,UNIQUE(template_name, template_namespace) -); - --- name: create-index-template-namespace - -CREATE INDEX IF NOT EXISTS ix_template_namespace ON templates (template_namespace); diff --git a/store/shared/migrate/postgres/files/017_add_columns_steps.sql b/store/shared/migrate/postgres/files/017_add_columns_steps.sql deleted file mode 100644 index de8c06006a..0000000000 --- a/store/shared/migrate/postgres/files/017_add_columns_steps.sql +++ /dev/null @@ -1,11 +0,0 @@ --- name: alter-table-steps-add-column-step-depends-on - -ALTER TABLE steps ADD COLUMN step_depends_on TEXT NOT NULL DEFAULT ''; - --- name: alter-table-steps-add-column-step-image - -ALTER TABLE steps ADD COLUMN step_image VARCHAR(1000) NOT NULL DEFAULT ''; - --- name: alter-table-steps-add-column-step-detached - -ALTER TABLE steps ADD COLUMN step_detached BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/store/shared/migrate/postgres/files/018_create_table_cards.sql b/store/shared/migrate/postgres/files/018_create_table_cards.sql deleted file mode 100644 index fd64924ae6..0000000000 --- a/store/shared/migrate/postgres/files/018_create_table_cards.sql +++ /dev/null @@ -1,16 +0,0 @@ --- name: create-table-cards - -CREATE TABLE IF NOT EXISTS cards ( - card_id SERIAL PRIMARY KEY - ,card_build INTEGER - ,card_stage INTEGER - ,card_step INTEGER - ,card_schema TEXT - ,card_data TEXT -); - --- name: create-index-cards-card_build -CREATE INDEX IF NOT EXISTS ix_cards_build ON cards (card_build); - --- name: create-index-cards-card_step -CREATE UNIQUE INDEX IF NOT EXISTS ix_cards_step ON cards (card_step); diff --git a/store/shared/migrate/postgres/files/019_amend_table_cards.sql b/store/shared/migrate/postgres/files/019_amend_table_cards.sql deleted file mode 100644 index b5391fa242..0000000000 --- a/store/shared/migrate/postgres/files/019_amend_table_cards.sql +++ /dev/null @@ -1,16 +0,0 @@ --- name: drop-table-cards - -DROP TABLE IF EXISTS cards; - --- name: alter-table-steps-add-column-step_schema - -ALTER TABLE steps - ADD COLUMN step_schema VARCHAR(2000) NOT NULL DEFAULT ''; - --- name: create-new-table-cards -CREATE TABLE IF NOT EXISTS cards -( - card_id SERIAL PRIMARY KEY, - card_data BYTEA, - FOREIGN KEY (card_id) REFERENCES steps (step_id) ON DELETE CASCADE -); \ No newline at end of file diff --git a/store/shared/migrate/sqlite/ddl.go b/store/shared/migrate/sqlite/ddl.go deleted file mode 100644 index 48b3d8b7da..0000000000 --- a/store/shared/migrate/sqlite/ddl.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite - -//go:generate togo ddl -package sqlite -dialect sqlite3 diff --git a/store/shared/migrate/sqlite/ddl_gen.go b/store/shared/migrate/sqlite/ddl_gen.go deleted file mode 100644 index ea5db1100f..0000000000 --- a/store/shared/migrate/sqlite/ddl_gen.go +++ /dev/null @@ -1,773 +0,0 @@ -package sqlite - -import ( - "database/sql" -) - -var migrations = []struct { - name string - stmt string -}{ - { - name: "create-table-users", - stmt: createTableUsers, - }, - { - name: "create-table-repos", - stmt: createTableRepos, - }, - { - name: "alter-table-repos-add-column-no-fork", - stmt: alterTableReposAddColumnNoFork, - }, - { - name: "alter-table-repos-add-column-no-pulls", - stmt: alterTableReposAddColumnNoPulls, - }, - { - name: "alter-table-repos-add-column-cancel-pulls", - stmt: alterTableReposAddColumnCancelPulls, - }, - { - name: "alter-table-repos-add-column-cancel-push", - stmt: alterTableReposAddColumnCancelPush, - }, - { - name: "alter-table-repos-add-column-throttle", - stmt: alterTableReposAddColumnThrottle, - }, - { - name: "alter-table-repos-add-column-cancel-running", - stmt: alterTableReposAddColumnCancelRunning, - }, - { - name: "create-table-perms", - stmt: createTablePerms, - }, - { - name: "create-index-perms-user", - stmt: createIndexPermsUser, - }, - { - name: "create-index-perms-repo", - stmt: createIndexPermsRepo, - }, - { - name: "create-table-builds", - stmt: createTableBuilds, - }, - { - name: "create-index-builds-repo", - stmt: createIndexBuildsRepo, - }, - { - name: "create-index-builds-author", - stmt: createIndexBuildsAuthor, - }, - { - name: "create-index-builds-sender", - stmt: createIndexBuildsSender, - }, - { - name: "create-index-builds-ref", - stmt: createIndexBuildsRef, - }, - { - name: "create-index-build-incomplete", - stmt: createIndexBuildIncomplete, - }, - { - name: "alter-table-builds-add-column-debug", - stmt: alterTableBuildsAddColumnDebug, - }, - { - name: "create-table-stages", - stmt: createTableStages, - }, - { - name: "create-index-stages-build", - stmt: createIndexStagesBuild, - }, - { - name: "create-index-stages-status", - stmt: createIndexStagesStatus, - }, - { - name: "alter-table-stages-add-column-limit-repos", - stmt: alterTableStagesAddColumnLimitRepos, - }, - { - name: "create-table-steps", - stmt: createTableSteps, - }, - { - name: "create-index-steps-stage", - stmt: createIndexStepsStage, - }, - { - name: "create-table-logs", - stmt: createTableLogs, - }, - { - name: "create-table-cron", - stmt: createTableCron, - }, - { - name: "create-index-cron-repo", - stmt: createIndexCronRepo, - }, - { - name: "create-index-cron-next", - stmt: createIndexCronNext, - }, - { - name: "create-table-secrets", - stmt: createTableSecrets, - }, - { - name: "create-index-secrets-repo", - stmt: createIndexSecretsRepo, - }, - { - name: "create-index-secrets-repo-name", - stmt: createIndexSecretsRepoName, - }, - { - name: "create-table-nodes", - stmt: createTableNodes, - }, - { - name: "alter-table-builds-add-column-cron", - stmt: alterTableBuildsAddColumnCron, - }, - { - name: "create-table-org-secrets", - stmt: createTableOrgSecrets, - }, - { - name: "alter-table-builds-add-column-deploy-id", - stmt: alterTableBuildsAddColumnDeployId, - }, - { - name: "create-table-latest", - stmt: createTableLatest, - }, - { - name: "create-index-latest-repo", - stmt: createIndexLatestRepo, - }, - { - name: "create-table-templates", - stmt: createTableTemplates, - }, - { - name: "create-index-template-namespace", - stmt: createIndexTemplateNamespace, - }, - { - name: "alter-table-steps-add-column-step-depends-on", - stmt: alterTableStepsAddColumnStepDependsOn, - }, - { - name: "alter-table-steps-add-column-step-image", - stmt: alterTableStepsAddColumnStepImage, - }, - { - name: "alter-table-steps-add-column-step-detached", - stmt: alterTableStepsAddColumnStepDetached, - }, - { - name: "create-table-cards", - stmt: createTableCards, - }, - { - name: "create-index-cards-card_build", - stmt: createIndexCardsCardbuild, - }, - { - name: "create-index-cards-card_step", - stmt: createIndexCardsCardstep, - }, - { - name: "drop-table-cards", - stmt: dropTableCards, - }, - { - name: "alter-table-steps-add-column-step_schema", - stmt: alterTableStepsAddColumnStepschema, - }, - { - name: "create-new-table-cards", - stmt: createNewTableCards, - }, -} - -// Migrate performs the database migration. If the migration fails -// and error is returned. -func Migrate(db *sql.DB) error { - if err := createTable(db); err != nil { - return err - } - completed, err := selectCompleted(db) - if err != nil && err != sql.ErrNoRows { - return err - } - for _, migration := range migrations { - if _, ok := completed[migration.name]; ok { - - continue - } - - if _, err := db.Exec(migration.stmt); err != nil { - return err - } - if err := insertMigration(db, migration.name); err != nil { - return err - } - - } - return nil -} - -func createTable(db *sql.DB) error { - _, err := db.Exec(migrationTableCreate) - return err -} - -func insertMigration(db *sql.DB, name string) error { - _, err := db.Exec(migrationInsert, name) - return err -} - -func selectCompleted(db *sql.DB) (map[string]struct{}, error) { - migrations := map[string]struct{}{} - rows, err := db.Query(migrationSelect) - if err != nil { - return nil, err - } - defer rows.Close() - for rows.Next() { - var name string - if err := rows.Scan(&name); err != nil { - return nil, err - } - migrations[name] = struct{}{} - } - return migrations, nil -} - -// -// migration table ddl and sql -// - -var migrationTableCreate = ` -CREATE TABLE IF NOT EXISTS migrations ( - name VARCHAR(255) -,UNIQUE(name) -) -` - -var migrationInsert = ` -INSERT INTO migrations (name) VALUES (?) -` - -var migrationSelect = ` -SELECT name FROM migrations -` - -// -// 001_create_table_user.sql -// - -var createTableUsers = ` -CREATE TABLE IF NOT EXISTS users ( - user_id INTEGER PRIMARY KEY AUTOINCREMENT -,user_login TEXT COLLATE NOCASE -,user_email TEXT -,user_admin BOOLEAN -,user_machine BOOLEAN -,user_active BOOLEAN -,user_avatar TEXT -,user_syncing BOOLEAN -,user_synced INTEGER -,user_created INTEGER -,user_updated INTEGER -,user_last_login INTEGER -,user_oauth_token TEXT -,user_oauth_refresh TEXT -,user_oauth_expiry INTEGER -,user_hash TEXT -,UNIQUE(user_login COLLATE NOCASE) -,UNIQUE(user_hash) -); -` - -// -// 002_create_table_repos.sql -// - -var createTableRepos = ` -CREATE TABLE IF NOT EXISTS repos ( - repo_id INTEGER PRIMARY KEY AUTOINCREMENT -,repo_uid TEXT -,repo_user_id INTEGER -,repo_namespace TEXT -,repo_name TEXT -,repo_slug TEXT -,repo_scm TEXT -,repo_clone_url TEXT -,repo_ssh_url TEXT -,repo_html_url TEXT -,repo_active BOOLEAN -,repo_private BOOLEAN -,repo_visibility TEXT -,repo_branch TEXT -,repo_counter INTEGER -,repo_config TEXT -,repo_timeout INTEGER -,repo_trusted BOOLEAN -,repo_protected BOOLEAN -,repo_synced INTEGER -,repo_created INTEGER -,repo_updated INTEGER -,repo_version INTEGER -,repo_signer TEXT -,repo_secret TEXT -,UNIQUE(repo_slug) -,UNIQUE(repo_uid) -); -` - -var alterTableReposAddColumnNoFork = ` -ALTER TABLE repos ADD COLUMN repo_no_forks BOOLEAN NOT NULL DEFAULT 0; -` - -var alterTableReposAddColumnNoPulls = ` -ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT 0; -` - -var alterTableReposAddColumnCancelPulls = ` -ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT 0; -` - -var alterTableReposAddColumnCancelPush = ` -ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT 0; -` - -var alterTableReposAddColumnThrottle = ` -ALTER TABLE repos ADD COLUMN repo_throttle INTEGER NOT NULL DEFAULT 0; -` - -var alterTableReposAddColumnCancelRunning = ` -ALTER TABLE repos ADD COLUMN repo_cancel_running BOOLEAN NOT NULL DEFAULT 0; -` - -// -// 003_create_table_perms.sql -// - -var createTablePerms = ` -CREATE TABLE IF NOT EXISTS perms ( - perm_user_id INTEGER -,perm_repo_uid TEXT -,perm_read BOOLEAN -,perm_write BOOLEAN -,perm_admin BOOLEAN -,perm_synced INTEGER -,perm_created INTEGER -,perm_updated INTEGER -,PRIMARY KEY(perm_user_id, perm_repo_uid) -); -` - -var createIndexPermsUser = ` -CREATE INDEX IF NOT EXISTS ix_perms_user ON perms (perm_user_id); -` - -var createIndexPermsRepo = ` -CREATE INDEX IF NOT EXISTS ix_perms_repo ON perms (perm_repo_uid); -` - -// -// 004_create_table_builds.sql -// - -var createTableBuilds = ` -CREATE TABLE IF NOT EXISTS builds ( - build_id INTEGER PRIMARY KEY AUTOINCREMENT -,build_repo_id INTEGER -,build_trigger TEXT -,build_number INTEGER -,build_parent INTEGER -,build_status TEXT -,build_error TEXT -,build_event TEXT -,build_action TEXT -,build_link TEXT -,build_timestamp INTEGER -,build_title TEXT -,build_message TEXT -,build_before TEXT -,build_after TEXT -,build_ref TEXT -,build_source_repo TEXT -,build_source TEXT -,build_target TEXT -,build_author TEXT -,build_author_name TEXT -,build_author_email TEXT -,build_author_avatar TEXT -,build_sender TEXT -,build_deploy TEXT -,build_params TEXT -,build_started INTEGER -,build_finished INTEGER -,build_created INTEGER -,build_updated INTEGER -,build_version INTEGER -,UNIQUE(build_repo_id, build_number) -); -` - -var createIndexBuildsRepo = ` -CREATE INDEX IF NOT EXISTS ix_build_repo ON builds (build_repo_id); -` - -var createIndexBuildsAuthor = ` -CREATE INDEX IF NOT EXISTS ix_build_author ON builds (build_author); -` - -var createIndexBuildsSender = ` -CREATE INDEX IF NOT EXISTS ix_build_sender ON builds (build_sender); -` - -var createIndexBuildsRef = ` -CREATE INDEX IF NOT EXISTS ix_build_ref ON builds (build_repo_id, build_ref); -` - -var createIndexBuildIncomplete = ` -CREATE INDEX IF NOT EXISTS ix_build_incomplete ON builds (build_status) -WHERE build_status IN ('pending', 'running'); -` - -var alterTableBuildsAddColumnDebug = ` -ALTER TABLE builds ADD COLUMN build_debug BOOLEAN NOT NULL DEFAULT 0; -` - -// -// 005_create_table_stages.sql -// - -var createTableStages = ` -CREATE TABLE IF NOT EXISTS stages ( - stage_id INTEGER PRIMARY KEY AUTOINCREMENT -,stage_repo_id INTEGER -,stage_build_id INTEGER -,stage_number INTEGER -,stage_kind TEXT -,stage_type TEXT -,stage_name TEXT -,stage_status TEXT -,stage_error TEXT -,stage_errignore BOOLEAN -,stage_exit_code INTEGER -,stage_limit INTEGER -,stage_os TEXT -,stage_arch TEXT -,stage_variant TEXT -,stage_kernel TEXT -,stage_machine TEXT -,stage_started INTEGER -,stage_stopped INTEGER -,stage_created INTEGER -,stage_updated INTEGER -,stage_version INTEGER -,stage_on_success BOOLEAN -,stage_on_failure BOOLEAN -,stage_depends_on TEXT -,stage_labels TEXT -,UNIQUE(stage_build_id, stage_number) -,FOREIGN KEY(stage_build_id) REFERENCES builds(build_id) ON DELETE CASCADE -); -` - -var createIndexStagesBuild = ` -CREATE INDEX IF NOT EXISTS ix_stages_build ON stages (stage_build_id); -` - -var createIndexStagesStatus = ` -CREATE INDEX IF NOT EXISTS ix_stage_in_progress ON stages (stage_status) -WHERE stage_status IN ('pending', 'running'); -` - -var alterTableStagesAddColumnLimitRepos = ` -ALTER TABLE stages ADD COLUMN stage_limit_repo INTEGER NOT NULL DEFAULT 0; -` - -// -// 006_create_table_steps.sql -// - -var createTableSteps = ` -CREATE TABLE IF NOT EXISTS steps ( - step_id INTEGER PRIMARY KEY AUTOINCREMENT -,step_stage_id INTEGER -,step_number INTEGER -,step_name TEXT -,step_status TEXT -,step_error TEXT -,step_errignore BOOLEAN -,step_exit_code INTEGER -,step_started INTEGER -,step_stopped INTEGER -,step_version INTEGER -,UNIQUE(step_stage_id, step_number) -,FOREIGN KEY(step_stage_id) REFERENCES stages(stage_id) ON DELETE CASCADE -); -` - -var createIndexStepsStage = ` -CREATE INDEX IF NOT EXISTS ix_steps_stage ON steps (step_stage_id); -` - -// -// 007_create_table_logs.sql -// - -var createTableLogs = ` -CREATE TABLE IF NOT EXISTS logs ( - log_id INTEGER PRIMARY KEY -,log_data BLOB -,FOREIGN KEY(log_id) REFERENCES steps(step_id) ON DELETE CASCADE -); -` - -// -// 008_create_table_cron.sql -// - -var createTableCron = ` -CREATE TABLE IF NOT EXISTS cron ( - cron_id INTEGER PRIMARY KEY AUTOINCREMENT -,cron_repo_id INTEGER -,cron_name TEXT -,cron_expr TEXT -,cron_next INTEGER -,cron_prev INTEGER -,cron_event TEXT -,cron_branch TEXT -,cron_target TEXT -,cron_disabled BOOLEAN -,cron_created INTEGER -,cron_updated INTEGER -,cron_version INTEGER -,UNIQUE(cron_repo_id, cron_name) -,FOREIGN KEY(cron_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); -` - -var createIndexCronRepo = ` -CREATE INDEX IF NOT EXISTS ix_cron_repo ON cron (cron_repo_id); -` - -var createIndexCronNext = ` -CREATE INDEX IF NOT EXISTS ix_cron_next ON cron (cron_next); -` - -// -// 009_create_table_secrets.sql -// - -var createTableSecrets = ` -CREATE TABLE IF NOT EXISTS secrets ( - secret_id INTEGER PRIMARY KEY AUTOINCREMENT -,secret_repo_id INTEGER -,secret_name TEXT -,secret_data BLOB -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_repo_id, secret_name) -,FOREIGN KEY(secret_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); -` - -var createIndexSecretsRepo = ` -CREATE INDEX IF NOT EXISTS ix_secret_repo ON secrets (secret_repo_id); -` - -var createIndexSecretsRepoName = ` -CREATE INDEX IF NOT EXISTS ix_secret_repo_name ON secrets (secret_repo_id, secret_name); -` - -// -// 010_create_table_nodes.sql -// - -var createTableNodes = ` -CREATE TABLE IF NOT EXISTS nodes ( - node_id INTEGER PRIMARY KEY AUTOINCREMENT -,node_uid TEXT -,node_provider TEXT -,node_state TEXT -,node_name TEXT -,node_image TEXT -,node_region TEXT -,node_size TEXT -,node_os TEXT -,node_arch TEXT -,node_kernel TEXT -,node_variant TEXT -,node_address TEXT -,node_capacity INTEGER -,node_filter TEXT -,node_labels TEXT -,node_error TEXT -,node_ca_key TEXT -,node_ca_cert TEXT -,node_tls_key TEXT -,node_tls_cert TEXT -,node_tls_name TEXT -,node_paused BOOLEAN -,node_protected BOOLEAN -,node_created INTEGER -,node_updated INTEGER -,node_pulled INTEGER - -,UNIQUE(node_name) -); -` - -// -// 011_add_column_builds_cron.sql -// - -var alterTableBuildsAddColumnCron = ` -ALTER TABLE builds ADD COLUMN build_cron TEXT NOT NULL DEFAULT ''; -` - -// -// 012_create_table_org_secrets.sql -// - -var createTableOrgSecrets = ` -CREATE TABLE IF NOT EXISTS orgsecrets ( - secret_id INTEGER PRIMARY KEY AUTOINCREMENT -,secret_namespace TEXT COLLATE NOCASE -,secret_name TEXT COLLATE NOCASE -,secret_type TEXT -,secret_data BLOB -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_namespace, secret_name) -); -` - -// -// 013_add_column_builds_deploy_id.sql -// - -var alterTableBuildsAddColumnDeployId = ` -ALTER TABLE builds ADD COLUMN build_deploy_id NUMBER NOT NULL DEFAULT 0; -` - -// -// 014_create_table_refs.sql -// - -var createTableLatest = ` -CREATE TABLE IF NOT EXISTS latest ( - latest_repo_id INTEGER -,latest_build_id INTEGER -,latest_type TEXT -- branch | tag | pull_request | promote -,latest_name TEXT -- master | v1.0.0, | 42 | production -,latest_created INTEGER -,latest_updated INTEGER -,latest_deleted INTEGER -,PRIMARY KEY(latest_repo_id, latest_type, latest_name) -); -` - -var createIndexLatestRepo = ` -CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id); -` - -// -// 015_create_template_tables.sql -// - -var createTableTemplates = ` -CREATE TABLE IF NOT EXISTS templates ( - template_id INTEGER PRIMARY KEY AUTOINCREMENT - ,template_name TEXT - ,template_namespace TEXT COLLATE NOCASE - ,template_data BLOB - ,template_created INTEGER - ,template_updated INTEGER - ,UNIQUE(template_name COLLATE NOCASE, template_namespace COLLATE NOCASE) -); -` - -var createIndexTemplateNamespace = ` -CREATE INDEX IF NOT EXISTS ix_template_namespace ON templates (template_namespace); -` - -// -// 016_add_columns_steps.sql -// - -var alterTableStepsAddColumnStepDependsOn = ` -ALTER TABLE steps ADD COLUMN step_depends_on TEXT NOT NULL DEFAULT ''; -` - -var alterTableStepsAddColumnStepImage = ` -ALTER TABLE steps ADD COLUMN step_image TEXT NOT NULL DEFAULT ''; -` - -var alterTableStepsAddColumnStepDetached = ` -ALTER TABLE steps ADD COLUMN step_detached BOOLEAN NOT NULL DEFAULT FALSE; -` - -// -// 017_create_table_cards.sql -// - -var createTableCards = ` -CREATE TABLE IF NOT EXISTS cards ( - card_id INTEGER PRIMARY KEY AUTOINCREMENT - ,card_build INTEGER - ,card_stage INTEGER - ,card_step INTEGER - ,card_schema TEXT - ,card_data TEXT -); -` - -var createIndexCardsCardbuild = ` -CREATE INDEX IF NOT EXISTS ix_cards_build ON cards (card_build); -` - -var createIndexCardsCardstep = ` -CREATE UNIQUE INDEX IF NOT EXISTS ix_cards_step ON cards (card_step); -` - -// -// 018_amend_table_cards.sql -// - -var dropTableCards = ` -DROP TABLE IF EXISTS cards; -` - -var alterTableStepsAddColumnStepschema = ` -ALTER TABLE steps - ADD COLUMN step_schema TEXT NOT NULL DEFAULT ''; -` - -var createNewTableCards = ` -CREATE TABLE IF NOT EXISTS cards -( - card_id INTEGER PRIMARY KEY, - card_data BLOB, - FOREIGN KEY (card_id) REFERENCES steps (step_id) ON DELETE CASCADE -); -` diff --git a/store/shared/migrate/sqlite/files/001_create_table_user.sql b/store/shared/migrate/sqlite/files/001_create_table_user.sql deleted file mode 100644 index 3e09b406fa..0000000000 --- a/store/shared/migrate/sqlite/files/001_create_table_user.sql +++ /dev/null @@ -1,22 +0,0 @@ --- name: create-table-users - -CREATE TABLE IF NOT EXISTS users ( - user_id INTEGER PRIMARY KEY AUTOINCREMENT -,user_login TEXT COLLATE NOCASE -,user_email TEXT -,user_admin BOOLEAN -,user_machine BOOLEAN -,user_active BOOLEAN -,user_avatar TEXT -,user_syncing BOOLEAN -,user_synced INTEGER -,user_created INTEGER -,user_updated INTEGER -,user_last_login INTEGER -,user_oauth_token TEXT -,user_oauth_refresh TEXT -,user_oauth_expiry INTEGER -,user_hash TEXT -,UNIQUE(user_login COLLATE NOCASE) -,UNIQUE(user_hash) -); diff --git a/store/shared/migrate/sqlite/files/002_create_table_repos.sql b/store/shared/migrate/sqlite/files/002_create_table_repos.sql deleted file mode 100644 index e17d2e400e..0000000000 --- a/store/shared/migrate/sqlite/files/002_create_table_repos.sql +++ /dev/null @@ -1,55 +0,0 @@ --- name: create-table-repos - -CREATE TABLE IF NOT EXISTS repos ( - repo_id INTEGER PRIMARY KEY AUTOINCREMENT -,repo_uid TEXT -,repo_user_id INTEGER -,repo_namespace TEXT -,repo_name TEXT -,repo_slug TEXT -,repo_scm TEXT -,repo_clone_url TEXT -,repo_ssh_url TEXT -,repo_html_url TEXT -,repo_active BOOLEAN -,repo_private BOOLEAN -,repo_visibility TEXT -,repo_branch TEXT -,repo_counter INTEGER -,repo_config TEXT -,repo_timeout INTEGER -,repo_trusted BOOLEAN -,repo_protected BOOLEAN -,repo_synced INTEGER -,repo_created INTEGER -,repo_updated INTEGER -,repo_version INTEGER -,repo_signer TEXT -,repo_secret TEXT -,UNIQUE(repo_slug) -,UNIQUE(repo_uid) -); - --- name: alter-table-repos-add-column-no-fork - -ALTER TABLE repos ADD COLUMN repo_no_forks BOOLEAN NOT NULL DEFAULT 0; - --- name: alter-table-repos-add-column-no-pulls - -ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT 0; - --- name: alter-table-repos-add-column-cancel-pulls - -ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT 0; - --- name: alter-table-repos-add-column-cancel-push - -ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT 0; - --- name: alter-table-repos-add-column-throttle - -ALTER TABLE repos ADD COLUMN repo_throttle INTEGER NOT NULL DEFAULT 0; - --- name: alter-table-repos-add-column-cancel-running - -ALTER TABLE repos ADD COLUMN repo_cancel_running BOOLEAN NOT NULL DEFAULT 0; \ No newline at end of file diff --git a/store/shared/migrate/sqlite/files/003_create_table_perms.sql b/store/shared/migrate/sqlite/files/003_create_table_perms.sql deleted file mode 100644 index f3f2557f6a..0000000000 --- a/store/shared/migrate/sqlite/files/003_create_table_perms.sql +++ /dev/null @@ -1,23 +0,0 @@ --- name: create-table-perms - -CREATE TABLE IF NOT EXISTS perms ( - perm_user_id INTEGER -,perm_repo_uid TEXT -,perm_read BOOLEAN -,perm_write BOOLEAN -,perm_admin BOOLEAN -,perm_synced INTEGER -,perm_created INTEGER -,perm_updated INTEGER -,PRIMARY KEY(perm_user_id, perm_repo_uid) ---,FOREIGN KEY(perm_user_id) REFERENCES users(user_id) ON DELETE CASCADE ---,FOREIGN KEY(perm_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-perms-user - -CREATE INDEX IF NOT EXISTS ix_perms_user ON perms (perm_user_id); - --- name: create-index-perms-repo - -CREATE INDEX IF NOT EXISTS ix_perms_repo ON perms (perm_repo_uid); diff --git a/store/shared/migrate/sqlite/files/004_create_table_builds.sql b/store/shared/migrate/sqlite/files/004_create_table_builds.sql deleted file mode 100644 index 9577025add..0000000000 --- a/store/shared/migrate/sqlite/files/004_create_table_builds.sql +++ /dev/null @@ -1,62 +0,0 @@ --- name: create-table-builds - -CREATE TABLE IF NOT EXISTS builds ( - build_id INTEGER PRIMARY KEY AUTOINCREMENT -,build_repo_id INTEGER -,build_trigger TEXT -,build_number INTEGER -,build_parent INTEGER -,build_status TEXT -,build_error TEXT -,build_event TEXT -,build_action TEXT -,build_link TEXT -,build_timestamp INTEGER -,build_title TEXT -,build_message TEXT -,build_before TEXT -,build_after TEXT -,build_ref TEXT -,build_source_repo TEXT -,build_source TEXT -,build_target TEXT -,build_author TEXT -,build_author_name TEXT -,build_author_email TEXT -,build_author_avatar TEXT -,build_sender TEXT -,build_deploy TEXT -,build_params TEXT -,build_started INTEGER -,build_finished INTEGER -,build_created INTEGER -,build_updated INTEGER -,build_version INTEGER -,UNIQUE(build_repo_id, build_number) ---,FOREIGN KEY(build_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-builds-repo - -CREATE INDEX IF NOT EXISTS ix_build_repo ON builds (build_repo_id); - --- name: create-index-builds-author - -CREATE INDEX IF NOT EXISTS ix_build_author ON builds (build_author); - --- name: create-index-builds-sender - -CREATE INDEX IF NOT EXISTS ix_build_sender ON builds (build_sender); - --- name: create-index-builds-ref - -CREATE INDEX IF NOT EXISTS ix_build_ref ON builds (build_repo_id, build_ref); - --- name: create-index-build-incomplete - -CREATE INDEX IF NOT EXISTS ix_build_incomplete ON builds (build_status) -WHERE build_status IN ('pending', 'running'); - --- name: alter-table-builds-add-column-debug - -ALTER TABLE builds ADD COLUMN build_debug BOOLEAN NOT NULL DEFAULT 0; diff --git a/store/shared/migrate/sqlite/files/005_create_table_stages.sql b/store/shared/migrate/sqlite/files/005_create_table_stages.sql deleted file mode 100644 index 2c5d1b8199..0000000000 --- a/store/shared/migrate/sqlite/files/005_create_table_stages.sql +++ /dev/null @@ -1,45 +0,0 @@ --- name: create-table-stages - -CREATE TABLE IF NOT EXISTS stages ( - stage_id INTEGER PRIMARY KEY AUTOINCREMENT -,stage_repo_id INTEGER -,stage_build_id INTEGER -,stage_number INTEGER -,stage_kind TEXT -,stage_type TEXT -,stage_name TEXT -,stage_status TEXT -,stage_error TEXT -,stage_errignore BOOLEAN -,stage_exit_code INTEGER -,stage_limit INTEGER -,stage_os TEXT -,stage_arch TEXT -,stage_variant TEXT -,stage_kernel TEXT -,stage_machine TEXT -,stage_started INTEGER -,stage_stopped INTEGER -,stage_created INTEGER -,stage_updated INTEGER -,stage_version INTEGER -,stage_on_success BOOLEAN -,stage_on_failure BOOLEAN -,stage_depends_on TEXT -,stage_labels TEXT -,UNIQUE(stage_build_id, stage_number) -,FOREIGN KEY(stage_build_id) REFERENCES builds(build_id) ON DELETE CASCADE -); - --- name: create-index-stages-build - -CREATE INDEX IF NOT EXISTS ix_stages_build ON stages (stage_build_id); - --- name: create-index-stages-status - -CREATE INDEX IF NOT EXISTS ix_stage_in_progress ON stages (stage_status) -WHERE stage_status IN ('pending', 'running'); - --- name: alter-table-stages-add-column-limit-repos - -ALTER TABLE stages ADD COLUMN stage_limit_repo INTEGER NOT NULL DEFAULT 0; diff --git a/store/shared/migrate/sqlite/files/006_create_table_steps.sql b/store/shared/migrate/sqlite/files/006_create_table_steps.sql deleted file mode 100644 index 3f5c757e12..0000000000 --- a/store/shared/migrate/sqlite/files/006_create_table_steps.sql +++ /dev/null @@ -1,21 +0,0 @@ --- name: create-table-steps - -CREATE TABLE IF NOT EXISTS steps ( - step_id INTEGER PRIMARY KEY AUTOINCREMENT -,step_stage_id INTEGER -,step_number INTEGER -,step_name TEXT -,step_status TEXT -,step_error TEXT -,step_errignore BOOLEAN -,step_exit_code INTEGER -,step_started INTEGER -,step_stopped INTEGER -,step_version INTEGER -,UNIQUE(step_stage_id, step_number) -,FOREIGN KEY(step_stage_id) REFERENCES stages(stage_id) ON DELETE CASCADE -); - --- name: create-index-steps-stage - -CREATE INDEX IF NOT EXISTS ix_steps_stage ON steps (step_stage_id); diff --git a/store/shared/migrate/sqlite/files/007_create_table_logs.sql b/store/shared/migrate/sqlite/files/007_create_table_logs.sql deleted file mode 100644 index 4c04d9fe68..0000000000 --- a/store/shared/migrate/sqlite/files/007_create_table_logs.sql +++ /dev/null @@ -1,7 +0,0 @@ --- name: create-table-logs - -CREATE TABLE IF NOT EXISTS logs ( - log_id INTEGER PRIMARY KEY -,log_data BLOB -,FOREIGN KEY(log_id) REFERENCES steps(step_id) ON DELETE CASCADE -); diff --git a/store/shared/migrate/sqlite/files/008_create_table_cron.sql b/store/shared/migrate/sqlite/files/008_create_table_cron.sql deleted file mode 100644 index 610a161811..0000000000 --- a/store/shared/migrate/sqlite/files/008_create_table_cron.sql +++ /dev/null @@ -1,27 +0,0 @@ --- name: create-table-cron - -CREATE TABLE IF NOT EXISTS cron ( - cron_id INTEGER PRIMARY KEY AUTOINCREMENT -,cron_repo_id INTEGER -,cron_name TEXT -,cron_expr TEXT -,cron_next INTEGER -,cron_prev INTEGER -,cron_event TEXT -,cron_branch TEXT -,cron_target TEXT -,cron_disabled BOOLEAN -,cron_created INTEGER -,cron_updated INTEGER -,cron_version INTEGER -,UNIQUE(cron_repo_id, cron_name) -,FOREIGN KEY(cron_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-cron-repo - -CREATE INDEX IF NOT EXISTS ix_cron_repo ON cron (cron_repo_id); - --- name: create-index-cron-next - -CREATE INDEX IF NOT EXISTS ix_cron_next ON cron (cron_next); diff --git a/store/shared/migrate/sqlite/files/009_create_table_secrets.sql b/store/shared/migrate/sqlite/files/009_create_table_secrets.sql deleted file mode 100644 index 3b3bd547d9..0000000000 --- a/store/shared/migrate/sqlite/files/009_create_table_secrets.sql +++ /dev/null @@ -1,20 +0,0 @@ --- name: create-table-secrets - -CREATE TABLE IF NOT EXISTS secrets ( - secret_id INTEGER PRIMARY KEY AUTOINCREMENT -,secret_repo_id INTEGER -,secret_name TEXT -,secret_data BLOB -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_repo_id, secret_name) -,FOREIGN KEY(secret_repo_id) REFERENCES repos(repo_id) ON DELETE CASCADE -); - --- name: create-index-secrets-repo - -CREATE INDEX IF NOT EXISTS ix_secret_repo ON secrets (secret_repo_id); - --- name: create-index-secrets-repo-name - -CREATE INDEX IF NOT EXISTS ix_secret_repo_name ON secrets (secret_repo_id, secret_name); diff --git a/store/shared/migrate/sqlite/files/010_create_table_nodes.sql b/store/shared/migrate/sqlite/files/010_create_table_nodes.sql deleted file mode 100644 index dcf919841e..0000000000 --- a/store/shared/migrate/sqlite/files/010_create_table_nodes.sql +++ /dev/null @@ -1,33 +0,0 @@ --- name: create-table-nodes - -CREATE TABLE IF NOT EXISTS nodes ( - node_id INTEGER PRIMARY KEY AUTOINCREMENT -,node_uid TEXT -,node_provider TEXT -,node_state TEXT -,node_name TEXT -,node_image TEXT -,node_region TEXT -,node_size TEXT -,node_os TEXT -,node_arch TEXT -,node_kernel TEXT -,node_variant TEXT -,node_address TEXT -,node_capacity INTEGER -,node_filter TEXT -,node_labels TEXT -,node_error TEXT -,node_ca_key TEXT -,node_ca_cert TEXT -,node_tls_key TEXT -,node_tls_cert TEXT -,node_tls_name TEXT -,node_paused BOOLEAN -,node_protected BOOLEAN -,node_created INTEGER -,node_updated INTEGER -,node_pulled INTEGER - -,UNIQUE(node_name) -); diff --git a/store/shared/migrate/sqlite/files/011_add_column_builds_cron.sql b/store/shared/migrate/sqlite/files/011_add_column_builds_cron.sql deleted file mode 100644 index ac9ffe6f0b..0000000000 --- a/store/shared/migrate/sqlite/files/011_add_column_builds_cron.sql +++ /dev/null @@ -1,3 +0,0 @@ --- name: alter-table-builds-add-column-cron - -ALTER TABLE builds ADD COLUMN build_cron TEXT NOT NULL DEFAULT ''; diff --git a/store/shared/migrate/sqlite/files/012_create_table_org_secrets.sql b/store/shared/migrate/sqlite/files/012_create_table_org_secrets.sql deleted file mode 100644 index 42808515cc..0000000000 --- a/store/shared/migrate/sqlite/files/012_create_table_org_secrets.sql +++ /dev/null @@ -1,12 +0,0 @@ --- name: create-table-org-secrets - -CREATE TABLE IF NOT EXISTS orgsecrets ( - secret_id INTEGER PRIMARY KEY AUTOINCREMENT -,secret_namespace TEXT COLLATE NOCASE -,secret_name TEXT COLLATE NOCASE -,secret_type TEXT -,secret_data BLOB -,secret_pull_request BOOLEAN -,secret_pull_request_push BOOLEAN -,UNIQUE(secret_namespace, secret_name) -); diff --git a/store/shared/migrate/sqlite/files/013_add_column_builds_deploy_id.sql b/store/shared/migrate/sqlite/files/013_add_column_builds_deploy_id.sql deleted file mode 100644 index 918c39b0fe..0000000000 --- a/store/shared/migrate/sqlite/files/013_add_column_builds_deploy_id.sql +++ /dev/null @@ -1,3 +0,0 @@ --- name: alter-table-builds-add-column-deploy-id - -ALTER TABLE builds ADD COLUMN build_deploy_id NUMBER NOT NULL DEFAULT 0; diff --git a/store/shared/migrate/sqlite/files/014_create_table_refs.sql b/store/shared/migrate/sqlite/files/014_create_table_refs.sql deleted file mode 100644 index 7a9f197a13..0000000000 --- a/store/shared/migrate/sqlite/files/014_create_table_refs.sql +++ /dev/null @@ -1,16 +0,0 @@ --- name: create-table-latest - -CREATE TABLE IF NOT EXISTS latest ( - latest_repo_id INTEGER -,latest_build_id INTEGER -,latest_type TEXT -- branch | tag | pull_request | promote -,latest_name TEXT -- master | v1.0.0, | 42 | production -,latest_created INTEGER -,latest_updated INTEGER -,latest_deleted INTEGER -,PRIMARY KEY(latest_repo_id, latest_type, latest_name) -); - --- name: create-index-latest-repo - -CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id); diff --git a/store/shared/migrate/sqlite/files/015_create_template_tables.sql b/store/shared/migrate/sqlite/files/015_create_template_tables.sql deleted file mode 100644 index bb73f96030..0000000000 --- a/store/shared/migrate/sqlite/files/015_create_template_tables.sql +++ /dev/null @@ -1,15 +0,0 @@ --- name: create-table-templates - -CREATE TABLE IF NOT EXISTS templates ( - template_id INTEGER PRIMARY KEY AUTOINCREMENT - ,template_name TEXT - ,template_namespace TEXT COLLATE NOCASE - ,template_data BLOB - ,template_created INTEGER - ,template_updated INTEGER - ,UNIQUE(template_name COLLATE NOCASE, template_namespace COLLATE NOCASE) -); - --- name: create-index-template-namespace - -CREATE INDEX IF NOT EXISTS ix_template_namespace ON templates (template_namespace); diff --git a/store/shared/migrate/sqlite/files/016_add_columns_steps.sql b/store/shared/migrate/sqlite/files/016_add_columns_steps.sql deleted file mode 100644 index 7e01996d90..0000000000 --- a/store/shared/migrate/sqlite/files/016_add_columns_steps.sql +++ /dev/null @@ -1,11 +0,0 @@ --- name: alter-table-steps-add-column-step-depends-on - -ALTER TABLE steps ADD COLUMN step_depends_on TEXT NOT NULL DEFAULT ''; - --- name: alter-table-steps-add-column-step-image - -ALTER TABLE steps ADD COLUMN step_image TEXT NOT NULL DEFAULT ''; - --- name: alter-table-steps-add-column-step-detached - -ALTER TABLE steps ADD COLUMN step_detached BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/store/shared/migrate/sqlite/files/017_create_table_cards.sql b/store/shared/migrate/sqlite/files/017_create_table_cards.sql deleted file mode 100644 index 2732b29b57..0000000000 --- a/store/shared/migrate/sqlite/files/017_create_table_cards.sql +++ /dev/null @@ -1,16 +0,0 @@ --- name: create-table-cards - -CREATE TABLE IF NOT EXISTS cards ( - card_id INTEGER PRIMARY KEY AUTOINCREMENT - ,card_build INTEGER - ,card_stage INTEGER - ,card_step INTEGER - ,card_schema TEXT - ,card_data TEXT -); - --- name: create-index-cards-card_build -CREATE INDEX IF NOT EXISTS ix_cards_build ON cards (card_build); - --- name: create-index-cards-card_step -CREATE UNIQUE INDEX IF NOT EXISTS ix_cards_step ON cards (card_step); diff --git a/store/shared/migrate/sqlite/files/018_amend_table_cards.sql b/store/shared/migrate/sqlite/files/018_amend_table_cards.sql deleted file mode 100644 index 34ba75237c..0000000000 --- a/store/shared/migrate/sqlite/files/018_amend_table_cards.sql +++ /dev/null @@ -1,16 +0,0 @@ --- name: drop-table-cards - -DROP TABLE IF EXISTS cards; - --- name: alter-table-steps-add-column-step_schema - -ALTER TABLE steps - ADD COLUMN step_schema TEXT NOT NULL DEFAULT ''; - --- name: create-new-table-cards -CREATE TABLE IF NOT EXISTS cards -( - card_id INTEGER PRIMARY KEY, - card_data BLOB, - FOREIGN KEY (card_id) REFERENCES steps (step_id) ON DELETE CASCADE -); \ No newline at end of file diff --git a/store/stage/scan.go b/store/stage/scan.go deleted file mode 100644 index 1d016da78d..0000000000 --- a/store/stage/scan.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stage - -import ( - "database/sql" - "encoding/json" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - - "github.com/jmoiron/sqlx/types" -) - -// helper function converts the Stage structure to a set -// of named query parameters. -func toParams(stage *core.Stage) map[string]interface{} { - return map[string]interface{}{ - "stage_id": stage.ID, - "stage_repo_id": stage.RepoID, - "stage_build_id": stage.BuildID, - "stage_number": stage.Number, - "stage_name": stage.Name, - "stage_kind": stage.Kind, - "stage_type": stage.Type, - "stage_status": stage.Status, - "stage_error": stage.Error, - "stage_errignore": stage.ErrIgnore, - "stage_exit_code": stage.ExitCode, - "stage_limit": stage.Limit, - "stage_limit_repo": stage.LimitRepo, - "stage_os": stage.OS, - "stage_arch": stage.Arch, - "stage_variant": stage.Variant, - "stage_kernel": stage.Kernel, - "stage_machine": stage.Machine, - "stage_started": stage.Started, - "stage_stopped": stage.Stopped, - "stage_created": stage.Created, - "stage_updated": stage.Updated, - "stage_version": stage.Version, - "stage_on_success": stage.OnSuccess, - "stage_on_failure": stage.OnFailure, - "stage_depends_on": encodeSlice(stage.DependsOn), - "stage_labels": encodeParams(stage.Labels), - } -} - -func encodeSlice(v []string) types.JSONText { - raw, _ := json.Marshal(v) - return types.JSONText(raw) -} - -func encodeParams(v map[string]string) types.JSONText { - raw, _ := json.Marshal(v) - return types.JSONText(raw) -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(scanner db.Scanner, dest *core.Stage) error { - depJSON := types.JSONText{} - labJSON := types.JSONText{} - err := scanner.Scan( - &dest.ID, - &dest.RepoID, - &dest.BuildID, - &dest.Number, - &dest.Name, - &dest.Kind, - &dest.Type, - &dest.Status, - &dest.Error, - &dest.ErrIgnore, - &dest.ExitCode, - &dest.Limit, - &dest.LimitRepo, - &dest.OS, - &dest.Arch, - &dest.Variant, - &dest.Kernel, - &dest.Machine, - &dest.Started, - &dest.Stopped, - &dest.Created, - &dest.Updated, - &dest.Version, - &dest.OnSuccess, - &dest.OnFailure, - &depJSON, - &labJSON, - ) - json.Unmarshal(depJSON, &dest.DependsOn) - json.Unmarshal(labJSON, &dest.Labels) - return err -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRowStep(scanner db.Scanner, stage *core.Stage, step *nullStep) error { - depJSON := types.JSONText{} - labJSON := types.JSONText{} - stepDepJSON := types.JSONText{} - err := scanner.Scan( - &stage.ID, - &stage.RepoID, - &stage.BuildID, - &stage.Number, - &stage.Name, - &stage.Kind, - &stage.Type, - &stage.Status, - &stage.Error, - &stage.ErrIgnore, - &stage.ExitCode, - &stage.Limit, - &stage.LimitRepo, - &stage.OS, - &stage.Arch, - &stage.Variant, - &stage.Kernel, - &stage.Machine, - &stage.Started, - &stage.Stopped, - &stage.Created, - &stage.Updated, - &stage.Version, - &stage.OnSuccess, - &stage.OnFailure, - &depJSON, - &labJSON, - &step.ID, - &step.StageID, - &step.Number, - &step.Name, - &step.Status, - &step.Error, - &step.ErrIgnore, - &step.ExitCode, - &step.Started, - &step.Stopped, - &step.Version, - &stepDepJSON, - &step.Image, - &step.Detached, - &step.Schema, - ) - json.Unmarshal(depJSON, &stage.DependsOn) - json.Unmarshal(labJSON, &stage.Labels) - json.Unmarshal(stepDepJSON, &step.DependsOn) - return err -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRows(rows *sql.Rows) ([]*core.Stage, error) { - defer rows.Close() - - stages := []*core.Stage{} - for rows.Next() { - stage := new(core.Stage) - err := scanRow(rows, stage) - if err != nil { - return nil, err - } - stages = append(stages, stage) - } - return stages, nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRowsWithSteps(rows *sql.Rows) ([]*core.Stage, error) { - defer rows.Close() - - stages := []*core.Stage{} - var curr *core.Stage - for rows.Next() { - stage := new(core.Stage) - step := new(nullStep) - err := scanRowStep(rows, stage, step) - if err != nil { - return nil, err - } - if curr == nil || curr.ID != stage.ID { - curr = stage - stages = append(stages, stage) - } - if step.ID.Int64 != 0 { - curr.Steps = append(curr.Steps, step.value()) - } - } - return stages, nil -} diff --git a/store/stage/stage.go b/store/stage/stage.go deleted file mode 100644 index fc69b69203..0000000000 --- a/store/stage/stage.go +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stage - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new StageStore. -func New(database *db.DB) core.StageStore { - return &stageStore{database} -} - -type stageStore struct { - db *db.DB -} - -func (s *stageStore) List(ctx context.Context, id int64) ([]*core.Stage, error) { - var out []*core.Stage - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{ - "stage_build_id": id, - } - stmt, args, err := binder.BindNamed(queryBuild, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *stageStore) ListState(ctx context.Context, state string) ([]*core.Stage, error) { - var out []*core.Stage - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{ - "stage_status": state, - } - query := queryState - // this is a workaround because mysql does not support - // partial or filtered indexes for low-cardinality values. - // For mysql we use a separate table to track pending and - // running jobs to avoid full table scans. - if (state == "pending" || state == "running") && - s.db.Driver() == db.Mysql { - query = queryStateMysql - } - stmt, args, err := binder.BindNamed(query, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *stageStore) ListSteps(ctx context.Context, id int64) ([]*core.Stage, error) { - var out []*core.Stage - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{ - "stage_build_id": id, - } - stmt, args, err := binder.BindNamed(queryNumberWithSteps, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRowsWithSteps(rows) - return err - }) - return out, err -} - -func (s *stageStore) ListIncomplete(ctx context.Context) ([]*core.Stage, error) { - var out []*core.Stage - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - stmt := queryUnfinished - // this is a workaround because mysql does not support - // partial or filtered indexes for low-cardinality values. - // For mysql we use a separate table to track pending and - // running jobs to avoid full table scans. - if s.db.Driver() == db.Mysql { - stmt = queryUnfinishedMysql - } - rows, err := queryer.Query(stmt) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *stageStore) Find(ctx context.Context, id int64) (*core.Stage, error) { - out := &core.Stage{ID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *stageStore) FindNumber(ctx context.Context, id int64, number int) (*core.Stage, error) { - out := &core.Stage{BuildID: id, Number: number} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryNumber, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *stageStore) Create(ctx context.Context, stage *core.Stage) error { - if s.db.Driver() == db.Postgres { - return s.createPostgres(ctx, stage) - } - return s.create(ctx, stage) -} - -func (s *stageStore) create(ctx context.Context, stage *core.Stage) error { - stage.Version = 1 - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(stage) - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - stage.ID, err = res.LastInsertId() - return err - }) -} - -func (s *stageStore) createPostgres(ctx context.Context, stage *core.Stage) error { - stage.Version = 1 - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(stage) - stmt, args, err := binder.BindNamed(stmtInsertPg, params) - if err != nil { - return err - } - return execer.QueryRow(stmt, args...).Scan(&stage.ID) - }) -} - -func (s *stageStore) Update(ctx context.Context, stage *core.Stage) error { - versionNew := stage.Version + 1 - versionOld := stage.Version - - err := s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(stage) - params["stage_version_old"] = versionOld - params["stage_version_new"] = versionNew - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - effected, err := res.RowsAffected() - if err != nil { - return err - } - if effected == 0 { - return db.ErrOptimisticLock - } - return nil - }) - if err == nil { - stage.Version = versionNew - } - return err -} - -const queryBase = ` -SELECT - stage_id -,stage_repo_id -,stage_build_id -,stage_number -,stage_name -,stage_kind -,stage_type -,stage_status -,stage_error -,stage_errignore -,stage_exit_code -,stage_limit -,stage_limit_repo -,stage_os -,stage_arch -,stage_variant -,stage_kernel -,stage_machine -,stage_started -,stage_stopped -,stage_created -,stage_updated -,stage_version -,stage_on_success -,stage_on_failure -,stage_depends_on -,stage_labels -FROM stages -` - -const queryKey = queryBase + ` -WHERE stage_id = :stage_id -` - -const queryState = queryBase + ` -WHERE stage_status = :stage_status -ORDER BY stage_id ASC -` - -const queryStateMysql = queryBase + ` -WHERE stage_id IN (SELECT stage_id FROM stages_unfinished) - AND stage_status = :stage_status -ORDER BY stage_id ASC -` - -const queryUnfinished = queryBase + ` -WHERE stage_status IN ('pending','running') -ORDER BY stage_id ASC -` - -const queryUnfinishedMysql = queryBase + ` -WHERE stage_id IN (SELECT stage_id FROM stages_unfinished) - AND stage_status IN ('pending','running') -ORDER BY stage_id ASC -` - -const queryBuild = queryBase + ` -WHERE stage_build_id = :stage_build_id -ORDER BY stage_number ASC -` - -const queryNumber = queryBase + ` -WHERE stage_build_id = :stage_build_id - AND stage_number = :stage_number -` - -const queryNumberWithSteps = ` -SELECT - stage_id -,stage_repo_id -,stage_build_id -,stage_number -,stage_name -,stage_kind -,stage_type -,stage_status -,stage_error -,stage_errignore -,stage_exit_code -,stage_limit -,stage_limit_repo -,stage_os -,stage_arch -,stage_variant -,stage_kernel -,stage_machine -,stage_started -,stage_stopped -,stage_created -,stage_updated -,stage_version -,stage_on_success -,stage_on_failure -,stage_depends_on -,stage_labels -,step_id -,step_stage_id -,step_number -,step_name -,step_status -,step_error -,step_errignore -,step_exit_code -,step_started -,step_stopped -,step_version -,step_depends_on -,step_image -,step_detached -,step_schema -FROM stages - LEFT JOIN steps - ON stages.stage_id=steps.step_stage_id - WHERE stages.stage_build_id = :stage_build_id -ORDER BY - stage_id ASC -,step_id ASC -` - -const stmtUpdate = ` -UPDATE stages -SET - stage_status = :stage_status -,stage_error = :stage_error -,stage_errignore = :stage_errignore -,stage_exit_code = :stage_exit_code -,stage_os = :stage_os -,stage_arch = :stage_arch -,stage_variant = :stage_variant -,stage_kernel = :stage_kernel -,stage_machine = :stage_machine -,stage_started = :stage_started -,stage_stopped = :stage_stopped -,stage_created = :stage_created -,stage_updated = :stage_updated -,stage_version = :stage_version_new -,stage_on_success = :stage_on_success -,stage_on_failure = :stage_on_failure -,stage_depends_on = :stage_depends_on -,stage_labels = :stage_labels -WHERE stage_id = :stage_id - AND stage_version = :stage_version_old -` - -const stmtInsert = ` -INSERT INTO stages ( - stage_repo_id -,stage_build_id -,stage_number -,stage_name -,stage_kind -,stage_type -,stage_status -,stage_error -,stage_errignore -,stage_exit_code -,stage_limit -,stage_limit_repo -,stage_os -,stage_arch -,stage_variant -,stage_kernel -,stage_machine -,stage_started -,stage_stopped -,stage_created -,stage_updated -,stage_version -,stage_on_success -,stage_on_failure -,stage_depends_on -,stage_labels -) VALUES ( - :stage_repo_id -,:stage_build_id -,:stage_number -,:stage_name -,:stage_kind -,:stage_type -,:stage_status -,:stage_error -,:stage_errignore -,:stage_exit_code -,:stage_limit -,:stage_limit_repo -,:stage_os -,:stage_arch -,:stage_variant -,:stage_kernel -,:stage_machine -,:stage_started -,:stage_stopped -,:stage_created -,:stage_updated -,:stage_version -,:stage_on_success -,:stage_on_failure -,:stage_depends_on -,:stage_labels -) -` - -const stmtInsertPg = stmtInsert + ` -RETURNING stage_id -` diff --git a/store/stage/stage_test.go b/store/stage/stage_test.go deleted file mode 100644 index d1305d9677..0000000000 --- a/store/stage/stage_test.go +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package stage - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/build" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/db/dbtest" -) - -var noContext = context.TODO() - -func TestStage(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - // seed with a dummy repository - arepo := &core.Repository{UID: "1", Slug: "octocat/hello-world"} - repos := repos.New(conn) - repos.Create(noContext, arepo) - - // seed with a dummy build - builds := build.New(conn) - abuild := &core.Build{Number: 1, RepoID: arepo.ID} - builds.Create(noContext, abuild, nil) - - store := New(conn).(*stageStore) - t.Run("Create", testStageCreate(store, abuild)) - t.Run("ListState", testStageListStatus(store, abuild)) -} - -func testStageCreate(store *stageStore, abuild *core.Build) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Stage{ - RepoID: 42, - BuildID: abuild.ID, - Number: 2, - Name: "clone", - Status: core.StatusRunning, - ExitCode: 0, - Started: 1522878684, - Stopped: 0, - } - err := store.Create(noContext, item) - if err != nil { - t.Error(err) - } - if item.ID == 0 { - t.Errorf("Want ID assigned, got %d", item.ID) - } - if item.Version == 0 { - t.Errorf("Want Version assigned, got %d", item.Version) - } - - t.Run("Find", testStageFind(store, item)) - t.Run("FindNumber", testStageFindNumber(store, item)) - t.Run("List", testStageList(store, item)) - t.Run("ListSteps", testStageListSteps(store, item)) - t.Run("Update", testStageUpdate(store, item)) - t.Run("Locking", testStageLocking(store, item)) - } -} - -func testStageFind(store *stageStore, stage *core.Stage) func(t *testing.T) { - return func(t *testing.T) { - result, err := store.Find(noContext, stage.ID) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testStage(result)) - } - } -} - -func testStageFindNumber(store *stageStore, stage *core.Stage) func(t *testing.T) { - return func(t *testing.T) { - result, err := store.FindNumber(noContext, stage.BuildID, stage.Number) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testStage(result)) - } - } -} - -func testStageList(store *stageStore, stage *core.Stage) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.List(noContext, stage.BuildID) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testStage(list[0])) - } - } -} - -func testStageListSteps(store *stageStore, stage *core.Stage) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.ListSteps(noContext, stage.BuildID) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testStage(list[0])) - } - } -} - -func testStageUpdate(store *stageStore, stage *core.Stage) func(t *testing.T) { - return func(t *testing.T) { - before := &core.Stage{ - ID: stage.ID, - RepoID: 42, - BuildID: stage.BuildID, - Number: stage.Number, - Name: "clone", - ExitCode: 255, - Started: 1522878684, - Stopped: 1522878690, - Status: core.StatusFailing, - Version: stage.Version, - } - err := store.Update(noContext, before) - if err != nil { - t.Error(err) - return - } - if got, want := before.Version, stage.Version+1; got != want { - t.Errorf("Want incremented version %d, got %d", want, got) - } - after, err := store.Find(noContext, before.ID) - if err != nil { - t.Error(err) - return - } - if got, want := after.Version, stage.Version+1; got != want { - t.Errorf("Want incremented version %d, got %d", want, got) - } - if got, want := after.ExitCode, before.ExitCode; got != want { - t.Errorf("Want updated ExitCode %v, got %v", want, got) - } - if got, want := after.Status, before.Status; got != want { - t.Errorf("Want updated Status %v, got %v", want, got) - } - if got, want := after.Stopped, before.Stopped; got != want { - t.Errorf("Want updated Stopped %v, got %v", want, got) - } - } -} - -func testStageLocking(store *stageStore, stage *core.Stage) func(t *testing.T) { - return func(t *testing.T) { - before := &core.Stage{ - ID: stage.ID, - RepoID: 42, - BuildID: stage.BuildID, - Number: stage.Number, - Name: "clone", - ExitCode: 255, - Started: 1522878684, - Stopped: 1522878690, - Status: core.StatusFailing, - Version: stage.Version - 1, - } - err := store.Update(noContext, before) - if err == nil { - t.Errorf("Want Optimistic Lock Error, got nil") - } else if err != db.ErrOptimisticLock { - t.Errorf("Want Optimistic Lock Error") - } - } -} - -func testStageListStatus(store *stageStore, abuild *core.Build) func(t *testing.T) { - return func(t *testing.T) { - _ = store.db.Update(func(execer db.Execer, binder db.Binder) error { - _, _ = execer.Exec("DELETE FROM stages_unfinished") - _, _ = execer.Exec("DELETE FROM stages") - return nil - }) - _ = store.Create(noContext, &core.Stage{Number: 1, BuildID: abuild.ID, Status: core.StatusPending}) - _ = store.Create(noContext, &core.Stage{Number: 2, BuildID: abuild.ID, Status: core.StatusRunning}) - _ = store.Create(noContext, &core.Stage{Number: 3, BuildID: abuild.ID, Status: core.StatusFailing}) - list, err := store.ListState(noContext, core.StatusPending) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } - if got, want := list[0].Status, core.StatusPending; got != want { - t.Errorf("Want status %s, got %s", want, got) - } - if store.db.Driver() == db.Mysql { - store.db.Update(func(execer db.Execer, binder db.Binder) error { - var count int - execer.QueryRow("SELECT count(*) FROM stages_unfinished").Scan(&count) - if count != 2 { - t.Errorf("Expect 2 items in stages_unfinished got %d", count) - } - execer.Exec("UPDATE stages SET stage_status ='success' WHERE stage_number=1") - execer.QueryRow("SELECT count(*) FROM stages_unfinished").Scan(&count) - if count != 1 { - t.Errorf("Expect 1 items in stages_unfinished got %d", count) - } - return nil - }) - } - } -} - -func testStage(item *core.Stage) func(t *testing.T) { - return func(t *testing.T) { - if got, want := item.Name, "clone"; got != want { - t.Errorf("Want Name %q, got %q", want, got) - } - if got, want := item.Status, core.StatusRunning; got != want { - t.Errorf("Want Status %q, got %q", want, got) - } - if got, want := item.Started, int64(1522878684); got != want { - t.Errorf("Want Started %d, got %d", want, got) - } - if got, want := item.RepoID, int64(42); got != want { - t.Errorf("Want RepoID %d, got %d", want, got) - } - } -} diff --git a/store/stage/type.go b/store/stage/type.go deleted file mode 100644 index e1d3d6839b..0000000000 --- a/store/stage/type.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stage - -import ( - "database/sql" - "encoding/json" - - "github.com/drone/drone/core" - "github.com/jmoiron/sqlx/types" -) - -type nullStep struct { - ID sql.NullInt64 - StageID sql.NullInt64 - Number sql.NullInt64 - Name sql.NullString - Status sql.NullString - Error sql.NullString - ErrIgnore sql.NullBool - ExitCode sql.NullInt64 - Started sql.NullInt64 - Stopped sql.NullInt64 - Version sql.NullInt64 - DependsOn types.JSONText - Image sql.NullString - Detached sql.NullBool - Schema sql.NullString -} - -func (s *nullStep) value() *core.Step { - var dependsOn []string - json.Unmarshal(s.DependsOn, &dependsOn) - - step := &core.Step{ - ID: s.ID.Int64, - StageID: s.StageID.Int64, - Number: int(s.Number.Int64), - Name: s.Name.String, - Status: s.Status.String, - Error: s.Error.String, - ErrIgnore: s.ErrIgnore.Bool, - ExitCode: int(s.ExitCode.Int64), - Started: s.Started.Int64, - Stopped: s.Stopped.Int64, - Version: s.Version.Int64, - DependsOn: dependsOn, - Image: s.Image.String, - Detached: s.Detached.Bool, - Schema: s.Schema.String, - } - - return step -} diff --git a/store/step/scan.go b/store/step/scan.go deleted file mode 100644 index 385b5ffd3e..0000000000 --- a/store/step/scan.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package step - -import ( - "database/sql" - "encoding/json" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - - "github.com/jmoiron/sqlx/types" -) - -// helper function converts the Step structure to a set -// of named query parameters. -func toParams(from *core.Step) map[string]interface{} { - return map[string]interface{}{ - "step_id": from.ID, - "step_stage_id": from.StageID, - "step_number": from.Number, - "step_name": from.Name, - "step_status": from.Status, - "step_error": from.Error, - "step_errignore": from.ErrIgnore, - "step_exit_code": from.ExitCode, - "step_started": from.Started, - "step_stopped": from.Stopped, - "step_version": from.Version, - "step_depends_on": encodeSlice(from.DependsOn), - "step_image": from.Image, - "step_detached": from.Detached, - "step_schema": from.Schema, - } -} - -func encodeSlice(v []string) types.JSONText { - raw, _ := json.Marshal(v) - return types.JSONText(raw) -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(scanner db.Scanner, dest *core.Step) error { - depJSON := types.JSONText{} - err := scanner.Scan( - &dest.ID, - &dest.StageID, - &dest.Number, - &dest.Name, - &dest.Status, - &dest.Error, - &dest.ErrIgnore, - &dest.ExitCode, - &dest.Started, - &dest.Stopped, - &dest.Version, - &depJSON, - &dest.Image, - &dest.Detached, - &dest.Schema, - ) - json.Unmarshal(depJSON, &dest.DependsOn) - return err -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRows(rows *sql.Rows) ([]*core.Step, error) { - defer rows.Close() - - steps := []*core.Step{} - for rows.Next() { - step := new(core.Step) - err := scanRow(rows, step) - if err != nil { - return nil, err - } - steps = append(steps, step) - } - return steps, nil -} diff --git a/store/step/step.go b/store/step/step.go deleted file mode 100644 index 8424c24664..0000000000 --- a/store/step/step.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package step - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new StepStore. -func New(db *db.DB) core.StepStore { - return &stepStore{db} -} - -type stepStore struct { - db *db.DB -} - -func (s *stepStore) List(ctx context.Context, id int64) ([]*core.Step, error) { - var out []*core.Step - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"step_stage_id": id} - stmt, args, err := binder.BindNamed(queryStage, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *stepStore) Find(ctx context.Context, id int64) (*core.Step, error) { - out := &core.Step{ID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *stepStore) FindNumber(ctx context.Context, id int64, number int) (*core.Step, error) { - out := &core.Step{StageID: id, Number: number} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := toParams(out) - query, args, err := binder.BindNamed(queryNumber, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *stepStore) Create(ctx context.Context, step *core.Step) error { - if s.db.Driver() == db.Postgres { - return s.createPostgres(ctx, step) - } - return s.create(ctx, step) -} - -func (s *stepStore) create(ctx context.Context, step *core.Step) error { - step.Version = 1 - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(step) - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - step.ID, err = res.LastInsertId() - return err - }) -} - -func (s *stepStore) createPostgres(ctx context.Context, step *core.Step) error { - step.Version = 1 - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(step) - stmt, args, err := binder.BindNamed(stmtInsertPg, params) - if err != nil { - return err - } - return execer.QueryRow(stmt, args...).Scan(&step.ID) - }) -} - -func (s *stepStore) Update(ctx context.Context, step *core.Step) error { - versionNew := step.Version + 1 - versionOld := step.Version - - err := s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := toParams(step) - params["step_version_old"] = versionOld - params["step_version_new"] = versionNew - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - effected, err := res.RowsAffected() - if err != nil { - return err - } - if effected == 0 { - return db.ErrOptimisticLock - } - return nil - }) - if err == nil { - step.Version = versionNew - } - return err -} - -const queryBase = ` -SELECT - step_id -,step_stage_id -,step_number -,step_name -,step_status -,step_error -,step_errignore -,step_exit_code -,step_started -,step_stopped -,step_version -,step_depends_on -,step_image -,step_detached -,step_schema -` - -const queryKey = queryBase + ` -FROM steps -WHERE step_id = :step_id -` - -const queryNumber = queryBase + ` -FROM steps -WHERE step_stage_id = :step_stage_id - AND step_number = :step_number -` - -const queryStage = queryBase + ` -FROM steps -WHERE step_stage_id = :step_stage_id -` - -const stmtUpdate = ` -UPDATE steps -SET - step_name = :step_name -,step_status = :step_status -,step_error = :step_error -,step_errignore = :step_errignore -,step_exit_code = :step_exit_code -,step_started = :step_started -,step_stopped = :step_stopped -,step_version = :step_version_new -,step_depends_on = :step_depends_on -,step_image = :step_image -,step_detached = :step_detached -,step_schema = :step_schema -WHERE step_id = :step_id - AND step_version = :step_version_old -` - -const stmtInsert = ` -INSERT INTO steps ( - step_stage_id -,step_number -,step_name -,step_status -,step_error -,step_errignore -,step_exit_code -,step_started -,step_stopped -,step_version -,step_depends_on -,step_image -,step_detached -,step_schema -) VALUES ( - :step_stage_id -,:step_number -,:step_name -,:step_status -,:step_error -,:step_errignore -,:step_exit_code -,:step_started -,:step_stopped -,:step_version -,:step_depends_on -,:step_image -,:step_detached -,:step_schema -) -` - -const stmtInsertPg = stmtInsert + ` -RETURNING step_id -` diff --git a/store/step/step_test.go b/store/step/step_test.go deleted file mode 100644 index 7f2b7a7d09..0000000000 --- a/store/step/step_test.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package step - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/build" - "github.com/drone/drone/store/repos" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/db/dbtest" -) - -var noContext = context.TODO() - -func TestStep(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - // seed with a dummy repository - arepo := &core.Repository{UID: "1", Slug: "octocat/hello-world"} - repos := repos.New(conn) - repos.Create(noContext, arepo) - - // seed with a dummy stage - stage := &core.Stage{Number: 1} - stages := []*core.Stage{stage} - - // seed with a dummy build - abuild := &core.Build{Number: 1, RepoID: arepo.ID} - builds := build.New(conn) - builds.Create(noContext, abuild, stages) - - store := New(conn).(*stepStore) - t.Run("Create", testStepCreate(store, stage)) -} - -func testStepCreate(store *stepStore, stage *core.Stage) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Step{ - StageID: stage.ID, - Number: 2, - Name: "clone", - Status: core.StatusRunning, - ExitCode: 0, - Started: 1522878684, - Stopped: 0, - DependsOn: []string{"backend", "frontend"}, - Image: "ubuntu", - Detached: false, - } - err := store.Create(noContext, item) - if err != nil { - t.Error(err) - } - if item.ID == 0 { - t.Errorf("Want ID assigned, got %d", item.ID) - } - if item.Version == 0 { - t.Errorf("Want Version assigned, got %d", item.Version) - } - - t.Run("Find", testStepFind(store, item)) - t.Run("FindNumber", testStepFindNumber(store, item)) - t.Run("List", testStepList(store, stage)) - t.Run("Update", testStepUpdate(store, item)) - t.Run("Locking", testStepLocking(store, item)) - } -} - -func testStepFind(store *stepStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - result, err := store.Find(noContext, step.ID) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testStep(result)) - } - } -} - -func testStepFindNumber(store *stepStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - result, err := store.FindNumber(noContext, step.StageID, step.Number) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testStep(result)) - } - } -} - -func testStepList(store *stepStore, stage *core.Stage) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.List(noContext, stage.ID) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testStep(list[0])) - } - } -} - -func testStepUpdate(store *stepStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - before := &core.Step{ - ID: step.ID, - StageID: step.StageID, - Number: 2, - Name: "clone", - ExitCode: 255, - Started: 1522878684, - Stopped: 1522878690, - Status: core.StatusFailing, - Version: step.Version, - } - err := store.Update(noContext, before) - if err != nil { - t.Error(err) - return - } - if got, want := before.Version, step.Version+1; got != want { - t.Errorf("Want incremented version %d, got %d", want, got) - } - after, err := store.Find(noContext, before.ID) - if err != nil { - t.Error(err) - return - } - if got, want := step.Version+1, after.Version; got != want { - t.Errorf("Want version incremented on update") - } - if got, want := after.ExitCode, before.ExitCode; got != want { - t.Errorf("Want updated ExitCode %v, got %v", want, got) - } - if got, want := after.Status, before.Status; got != want { - t.Errorf("Want updated Status %v, got %v", want, got) - } - if got, want := after.Stopped, before.Stopped; got != want { - t.Errorf("Want updated Stopped %v, got %v", want, got) - } - } -} - -func testStepLocking(store *stepStore, step *core.Step) func(t *testing.T) { - return func(t *testing.T) { - before := &core.Step{ - ID: step.ID, - StageID: step.StageID, - Number: 2, - Name: "clone", - ExitCode: 255, - Started: 1522878684, - Stopped: 1522878690, - Status: core.StatusFailing, - Version: step.Version - 1, - } - err := store.Update(noContext, before) - if err == nil { - t.Errorf("Want Optimistic Lock Error, got nil") - } else if err != db.ErrOptimisticLock { - t.Errorf("Want Optimistic Lock Error") - } - } -} - -func testStep(item *core.Step) func(t *testing.T) { - return func(t *testing.T) { - if got, want := item.Name, "clone"; got != want { - t.Errorf("Want Name %q, got %q", want, got) - } - if got, want := item.Number, 2; got != want { - t.Errorf("Want Name %d, got %d", want, got) - } - if got, want := item.Status, core.StatusRunning; got != want { - t.Errorf("Want Status %q, got %q", want, got) - } - if got, want := item.Started, int64(1522878684); got != want { - t.Errorf("Want Started %d, got %d", want, got) - } - } -} diff --git a/store/store.go b/store/store.go deleted file mode 100644 index 46800812a9..0000000000 --- a/store/store.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package store diff --git a/store/template/scan.go b/store/template/scan.go deleted file mode 100644 index e06ebbbad4..0000000000 --- a/store/template/scan.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "database/sql" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// helper function converts the Template structure to a set -// of named query parameters. -func toParams(template *core.Template) (map[string]interface{}, error) { - return map[string]interface{}{ - "template_id": template.Id, - "template_name": template.Name, - "template_namespace": template.Namespace, - "template_data": template.Data, - "template_created": template.Created, - "template_updated": template.Updated, - }, nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(scanner db.Scanner, dst *core.Template) error { - err := scanner.Scan( - &dst.Id, - &dst.Name, - &dst.Namespace, - &dst.Data, - &dst.Created, - &dst.Updated, - ) - if err != nil { - return err - } - return nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRows(rows *sql.Rows) ([]*core.Template, error) { - defer rows.Close() - - template := []*core.Template{} - for rows.Next() { - tem := new(core.Template) - err := scanRow(rows, tem) - if err != nil { - return nil, err - } - template = append(template, tem) - } - return template, nil -} diff --git a/store/template/template.go b/store/template/template.go deleted file mode 100644 index c0a330f5f3..0000000000 --- a/store/template/template.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package template - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -// New returns a new Template database store. -func New(db *db.DB) core.TemplateStore { - return &templateStore{ - db: db, - } -} - -type templateStore struct { - db *db.DB -} - -func (s *templateStore) List(ctx context.Context, namespace string) ([]*core.Template, error) { - var out []*core.Template - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"template_namespace": namespace} - stmt, args, err := binder.BindNamed(queryNamespace, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *templateStore) ListAll(ctx context.Context) ([]*core.Template, error) { - var out []*core.Template - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{} - stmt, args, err := binder.BindNamed(queryAll, params) - if err != nil { - return err - } - rows, err := queryer.Query(stmt, args...) - if err != nil { - return err - } - out, err = scanRows(rows) - return err - }) - return out, err -} - -func (s *templateStore) Find(ctx context.Context, id int64) (*core.Template, error) { - out := &core.Template{Id: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params, err := toParams(out) - if err != nil { - return err - } - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *templateStore) FindName(ctx context.Context, name string, namespace string) (*core.Template, error) { - out := &core.Template{Name: name, Namespace: namespace} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params, err := toParams(out) - if err != nil { - return err - } - - query, args, err := binder.BindNamed(queryName, params) - - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(row, out) - }) - return out, err -} - -func (s *templateStore) Create(ctx context.Context, template *core.Template) error { - if s.db.Driver() == db.Postgres { - return s.createPostgres(ctx, template) - } - return s.create(ctx, template) -} - -func (s *templateStore) create(ctx context.Context, template *core.Template) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(template) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - template.Id, err = res.LastInsertId() - return err - }) -} - -func (s *templateStore) createPostgres(ctx context.Context, template *core.Template) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(template) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtInsertPostgres, params) - if err != nil { - return err - } - return execer.QueryRow(stmt, args...).Scan(&template.Id) - }) -} - -func (s *templateStore) Update(ctx context.Context, template *core.Template) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(template) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -func (s *templateStore) Delete(ctx context.Context, template *core.Template) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(template) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtDelete, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -const queryKey = queryBase + ` -FROM templates -WHERE template_id = :template_id -LIMIT 1 -` - -const queryBase = ` -SELECT - template_id -,template_name -,template_namespace -,template_data -,template_created -,template_updated -` - -const queryAll = queryBase + ` -FROM templates -ORDER BY template_name -` - -const queryNamespace = queryBase + ` -FROM templates -WHERE template_namespace = :template_namespace -ORDER BY template_name -` - -const stmtInsert = ` -INSERT INTO templates ( - template_name -,template_namespace -,template_data -,template_created -,template_updated -) VALUES ( - :template_name -,:template_namespace -,:template_data -,:template_created -,:template_updated -) -` - -const stmtUpdate = ` -UPDATE templates SET -template_name = :template_name -,template_namespace = :template_namespace -,template_data = :template_data -,template_updated = :template_updated -WHERE template_id = :template_id -` - -const stmtDelete = ` -DELETE FROM templates -WHERE template_id = :template_id -` -const queryName = queryBase + ` -FROM templates -WHERE template_name = :template_name -AND template_namespace = :template_namespace -LIMIT 1 -` - -const stmtInsertPostgres = stmtInsert + ` -RETURNING template_id -` diff --git a/store/template/template_oss.go b/store/template/template_oss.go deleted file mode 100644 index e6ea289937..0000000000 --- a/store/template/template_oss.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package template - -import ( - "context" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" -) - -func New(db *db.DB) core.TemplateStore { - return new(noop) -} - -type noop struct{} - -func (noop) List(ctx context.Context, namespace string) ([]*core.Template, error) { - return nil, nil -} - -func (noop) ListAll(ctx context.Context) ([]*core.Template, error) { - return nil, nil -} - -func (noop) Find(ctx context.Context, id int64) (*core.Template, error) { - return nil, nil -} - -func (noop) FindName(ctx context.Context, name string, namespace string) (*core.Template, error) { - return nil, nil -} - -func (noop) Create(ctx context.Context, template *core.Template) error { - return nil -} - -func (noop) Update(ctx context.Context, template *core.Template) error { - return nil -} - -func (noop) Delete(ctx context.Context, template *core.Template) error { - return nil -} diff --git a/store/template/template_test.go b/store/template/template_test.go deleted file mode 100644 index cec90191ec..0000000000 --- a/store/template/template_test.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package template - -import ( - "context" - "database/sql" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db/dbtest" -) - -var noContext = context.TODO() - -func TestTemplate(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - store := New(conn).(*templateStore) - t.Run("TestTemplates", testTemplateCreate(store)) -} - -func testTemplateCreate(store *templateStore) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Template{ - Id: 1, - Name: "my_template", - Namespace: "my_org", - Data: "some_template_data", - Created: 1, - Updated: 2, - } - err := store.Create(noContext, item) - if err != nil { - t.Error(err) - } - if item.Id == 0 { - t.Errorf("Want template Id assigned, got %d", item.Id) - } - - t.Run("CreateSameNameDiffOrg", testCreateWithSameNameDiffOrg(store)) - t.Run("CreateSameNameSameOrgShouldError", testCreateSameNameSameOrgShouldError(store)) - t.Run("Find", testTemplateFind(store, item)) - t.Run("FindName", testTemplateFindName(store)) - t.Run("ListAll", testTemplateListAll(store)) - t.Run("List", testTemplateList(store)) - t.Run("Update", testTemplateUpdate(store)) - t.Run("Delete", testTemplateDelete(store)) - } -} - -func testCreateWithSameNameDiffOrg(store *templateStore) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Template{ - Id: 1, - Name: "my_template", - Namespace: "my_org2", - Data: "some_template_data", - Created: 1, - Updated: 2, - } - err := store.Create(noContext, item) - if err != nil { - t.Error(err) - } - if item.Id == 0 { - t.Errorf("Want template Id assigned, got %d", item.Id) - } - } -} - -func testCreateSameNameSameOrgShouldError(store *templateStore) func(t *testing.T) { - return func(t *testing.T) { - item := &core.Template{ - Id: 3, - Name: "my_template", - Namespace: "my_org2", - Data: "some_template_data", - Created: 1, - Updated: 2, - } - err := store.Create(noContext, item) - if err == nil { - t.Error(err) - } - } -} - -func testTemplateFind(store *templateStore, template *core.Template) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.Find(noContext, template.Id) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testTemplate(item)) - } - } -} - -func testTemplateFindName(store *templateStore) func(t *testing.T) { - return func(t *testing.T) { - item, err := store.FindName(noContext, "my_template", "my_org") - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testTemplate(item)) - } - } -} - -func testTemplate(item *core.Template) func(t *testing.T) { - return func(t *testing.T) { - if got, want := item.Name, "my_template"; got != want { - t.Errorf("Want template name %q, got %q", want, got) - } - if got, want := item.Data, "some_template_data"; got != want { - t.Errorf("Want template data %q, got %q", want, got) - } - if got, want := item.Namespace, "my_org"; got != want { - t.Errorf("Want template org %q, got %q", want, got) - } - } -} - -func testTemplate2(item *core.Template) func(t *testing.T) { - return func(t *testing.T) { - if got, want := item.Name, "my_template"; got != want { - t.Errorf("Want template name %q, got %q", want, got) - } - if got, want := item.Data, "some_template_data"; got != want { - t.Errorf("Want template data %q, got %q", want, got) - } - if got, want := item.Namespace, "my_org2"; got != want { - t.Errorf("Want template org %q, got %q", want, got) - } - } -} - -func testTemplateListAll(store *templateStore) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.ListAll(noContext) - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 2; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testTemplate(list[0])) - t.Run("Fields", testTemplate2(list[1])) - } - } -} - -func testTemplateList(store *templateStore) func(t *testing.T) { - return func(t *testing.T) { - list, err := store.List(noContext, "my_org") - if err != nil { - t.Error(err) - return - } - if got, want := len(list), 1; got != want { - t.Errorf("Want count %d, got %d", want, got) - } else { - t.Run("Fields", testTemplate(list[0])) - } - } -} - -func testTemplateUpdate(store *templateStore) func(t *testing.T) { - return func(t *testing.T) { - before, err := store.FindName(noContext, "my_template", "my_org") - if err != nil { - t.Error(err) - return - } - err = store.Update(noContext, before) - if err != nil { - t.Error(err) - return - } - after, err := store.Find(noContext, before.Id) - if err != nil { - t.Error(err) - return - } - if after == nil { - t.Fail() - } - } -} - -func testTemplateDelete(store *templateStore) func(t *testing.T) { - return func(t *testing.T) { - secret, err := store.FindName(noContext, "my_template", "my_org") - if err != nil { - t.Error(err) - return - } - err = store.Delete(noContext, secret) - if err != nil { - t.Error(err) - return - } - _, err = store.Find(noContext, secret.Id) - if got, want := sql.ErrNoRows, err; got != want { - t.Errorf("Want sql.ErrNoRows, got %v", got) - return - } - } -} diff --git a/store/user/scan.go b/store/user/scan.go deleted file mode 100644 index 015b1e3b68..0000000000 --- a/store/user/scan.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package user - -import ( - "database/sql" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/encrypt" -) - -// helper function converts the User structure to a set -// of named query parameters. -func toParams(encrypt encrypt.Encrypter, u *core.User) (map[string]interface{}, error) { - token, err := encrypt.Encrypt(u.Token) - if err != nil { - return nil, err - } - refresh, err := encrypt.Encrypt(u.Refresh) - if err != nil { - return nil, err - } - return map[string]interface{}{ - "user_id": u.ID, - "user_login": u.Login, - "user_email": u.Email, - "user_admin": u.Admin, - "user_machine": u.Machine, - "user_active": u.Active, - "user_avatar": u.Avatar, - "user_syncing": u.Syncing, - "user_synced": u.Synced, - "user_created": u.Created, - "user_updated": u.Updated, - "user_last_login": u.LastLogin, - "user_oauth_token": token, - "user_oauth_refresh": refresh, - "user_oauth_expiry": u.Expiry, - "user_hash": u.Hash, - }, nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRow(encrypt encrypt.Encrypter, scanner db.Scanner, dest *core.User) error { - var token, refresh []byte - err := scanner.Scan( - &dest.ID, - &dest.Login, - &dest.Email, - &dest.Admin, - &dest.Machine, - &dest.Active, - &dest.Avatar, - &dest.Syncing, - &dest.Synced, - &dest.Created, - &dest.Updated, - &dest.LastLogin, - &token, - &refresh, - &dest.Expiry, - &dest.Hash, - ) - if err != nil { - return err - } - dest.Token, err = encrypt.Decrypt(token) - if err != nil { - return err - } - dest.Refresh, err = encrypt.Decrypt(refresh) - if err != nil { - return err - } - return nil -} - -// helper function scans the sql.Row and copies the column -// values to the destination object. -func scanRows(encrypt encrypt.Encrypter, rows *sql.Rows) ([]*core.User, error) { - defer rows.Close() - - users := []*core.User{} - for rows.Next() { - user := new(core.User) - err := scanRow(encrypt, rows, user) - if err != nil { - return nil, err - } - users = append(users, user) - } - return users, nil -} diff --git a/store/user/user.go b/store/user/user.go deleted file mode 100644 index 32c51a5f20..0000000000 --- a/store/user/user.go +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package user - -import ( - "context" - "fmt" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db" - "github.com/drone/drone/store/shared/encrypt" -) - -// New returns a new UserStore. -func New(db *db.DB, enc encrypt.Encrypter) core.UserStore { - return &userStore{db, enc} -} - -type userStore struct { - db *db.DB - enc encrypt.Encrypter -} - -// Find returns a user from the datastore. -func (s *userStore) Find(ctx context.Context, id int64) (*core.User, error) { - out := &core.User{ID: id} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"user_id": id} - query, args, err := binder.BindNamed(queryKey, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(s.enc, row, out) - }) - return out, err -} - -// FindLogin returns a user from the datastore by username. -func (s *userStore) FindLogin(ctx context.Context, login string) (*core.User, error) { - out := &core.User{Login: login} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"user_login": login} - query, args, err := binder.BindNamed(queryLogin, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(s.enc, row, out) - }) - return out, err -} - -// FindToken returns a user from the datastore by token. -func (s *userStore) FindToken(ctx context.Context, token string) (*core.User, error) { - out := &core.User{Hash: token} - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"user_hash": token} - query, args, err := binder.BindNamed(queryToken, params) - if err != nil { - return err - } - row := queryer.QueryRow(query, args...) - return scanRow(s.enc, row, out) - }) - return out, err -} - -// List returns a list of users from the datastore. -func (s *userStore) List(ctx context.Context) ([]*core.User, error) { - var out []*core.User - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - rows, err := queryer.Query(queryAll) - if err != nil { - return err - } - out, err = scanRows(s.enc, rows) - return err - }) - return out, err -} - -// ListRange returns a list of users from the datastore. -func (s *userStore) ListRange(ctx context.Context, params core.UserParams) ([]*core.User, error) { - var out []*core.User - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - // this query breaks a rule and uses sprintf to inject parameters - // into the query. Normally this should be avoided, however, in this - // case the parameters are set by the internal system and can - // be considered safe. - query := queryRange - switch { - case params.Sort: - query = fmt.Sprintf(query, "user_login", params.Size, params.Page) - default: - query = fmt.Sprintf(query, "user_id", params.Size, params.Page) - } - rows, err := queryer.Query(query) - if err != nil { - return err - } - out, err = scanRows(s.enc, rows) - return err - }) - return out, err -} - -// Create persists a new user to the datastore. -func (s *userStore) Create(ctx context.Context, user *core.User) error { - if s.db.Driver() == db.Postgres { - return s.createPostgres(ctx, user) - } - return s.create(ctx, user) -} - -func (s *userStore) create(ctx context.Context, user *core.User) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, user) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtInsert, params) - if err != nil { - return err - } - res, err := execer.Exec(stmt, args...) - if err != nil { - return err - } - user.ID, err = res.LastInsertId() - return err - }) -} - -func (s *userStore) createPostgres(ctx context.Context, user *core.User) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, user) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtInsertPg, params) - if err != nil { - return err - } - return execer.QueryRow(stmt, args...).Scan(&user.ID) - }) -} - -// Update persists an updated user to the datastore. -func (s *userStore) Update(ctx context.Context, user *core.User) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params, err := toParams(s.enc, user) - if err != nil { - return err - } - stmt, args, err := binder.BindNamed(stmtUpdate, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -// Delete deletes a user from the datastore. -func (s *userStore) Delete(ctx context.Context, user *core.User) error { - return s.db.Lock(func(execer db.Execer, binder db.Binder) error { - params := map[string]interface{}{"user_id": user.ID} - stmt, args, err := binder.BindNamed(stmtDelete, params) - if err != nil { - return err - } - _, err = execer.Exec(stmt, args...) - return err - }) -} - -// Count returns a count of active users. -func (s *userStore) Count(ctx context.Context) (int64, error) { - var out int64 - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - return queryer.QueryRow(queryCount).Scan(&out) - }) - return out, err -} - -// Count returns a count of active human users. -func (s *userStore) CountHuman(ctx context.Context) (int64, error) { - var out int64 - err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { - params := map[string]interface{}{"user_machine": false} - stmt, args, err := binder.BindNamed(queryCountHuman, params) - if err != nil { - return err - } - return queryer.QueryRow(stmt, args...).Scan(&out) - }) - return out, err -} - -const queryCount = ` -SELECT COUNT(*) -FROM users -` - -const queryCountHuman = ` -SELECT COUNT(*) -FROM users -WHERE user_machine = :user_machine -` - -const queryBase = ` -SELECT - user_id -,user_login -,user_email -,user_admin -,user_machine -,user_active -,user_avatar -,user_syncing -,user_synced -,user_created -,user_updated -,user_last_login -,user_oauth_token -,user_oauth_refresh -,user_oauth_expiry -,user_hash -` - -const queryKey = queryBase + ` -FROM users -WHERE user_id = :user_id -` - -const queryLogin = queryBase + ` -FROM users -WHERE user_login = :user_login -` - -const queryToken = queryBase + ` -FROM users -WHERE user_hash = :user_hash -` - -const queryAll = queryBase + ` -FROM users -ORDER BY user_login -` - -const queryRange = queryBase + ` -FROM users -ORDER BY %s -LIMIT %d -OFFSET %d -` - -const stmtUpdate = ` -UPDATE users -SET - user_email = :user_email -,user_admin = :user_admin -,user_active = :user_active -,user_avatar = :user_avatar -,user_syncing = :user_syncing -,user_synced = :user_synced -,user_created = :user_created -,user_updated = :user_updated -,user_last_login = :user_last_login -,user_oauth_token = :user_oauth_token -,user_oauth_refresh = :user_oauth_refresh -,user_oauth_expiry = :user_oauth_expiry -,user_hash = :user_hash -WHERE user_id = :user_id -` - -const stmtDelete = ` -DELETE FROM users WHERE user_id = :user_id -` - -const stmtInsert = ` -INSERT INTO users ( - user_login -,user_email -,user_admin -,user_machine -,user_active -,user_avatar -,user_syncing -,user_synced -,user_created -,user_updated -,user_last_login -,user_oauth_token -,user_oauth_refresh -,user_oauth_expiry -,user_hash -) VALUES ( - :user_login -,:user_email -,:user_admin -,:user_machine -,:user_active -,:user_avatar -,:user_syncing -,:user_synced -,:user_created -,:user_updated -,:user_last_login -,:user_oauth_token -,:user_oauth_refresh -,:user_oauth_expiry -,:user_hash -) -` - -const stmtInsertPg = stmtInsert + ` -RETURNING user_id -` diff --git a/store/user/user_test.go b/store/user/user_test.go deleted file mode 100644 index 8c6b0c89e2..0000000000 --- a/store/user/user_test.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package user - -import ( - "context" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/store/shared/db/dbtest" - "github.com/drone/drone/store/shared/encrypt" -) - -var noContext = context.TODO() - -func TestUser(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - store := New(conn, nil).(*userStore) - store.enc, _ = encrypt.New("fb4b4d6267c8a5ce8231f8b186dbca92") - t.Run("Create", testUserCreate(store)) -} - -func testUserCreate(store *userStore) func(t *testing.T) { - return func(t *testing.T) { - user := &core.User{ - Login: "octocat", - Email: "octocat@github.com", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - Hash: "MjAxOC0wOC0xMVQxNTo1ODowN1o", - Token: "9595fe015ca9b98c41ebf4e7d4e004ee", - Refresh: "268ef49df64ea8ff79ef11e995d41aed", - } - err := store.Create(noContext, user) - if err != nil { - t.Error(err) - } - if user.ID == 0 { - t.Errorf("Want user ID assigned, got %d", user.ID) - } - - t.Run("Count", testUserCount(store)) - t.Run("Find", testUserFind(store, user)) - t.Run("FindLogin", testUserFindLogin(store)) - t.Run("FindToken", testUserFindToken(store)) - t.Run("List", testUserList(store)) - t.Run("Update", testUserUpdate(store, user)) - t.Run("Delete", testUserDelete(store, user)) - } -} - -func testUserCount(users *userStore) func(t *testing.T) { - return func(t *testing.T) { - count, err := users.Count(noContext) - if err != nil { - t.Error(err) - } - if got, want := count, int64(1); got != want { - t.Errorf("Want user table count %d, got %d", want, got) - } - - count, err = users.CountHuman(noContext) - if err != nil { - t.Error(err) - } - if got, want := count, int64(1); got != want { - t.Errorf("Want user table count %d for humans, got %d", want, got) - } - } -} - -func testUserFind(users *userStore, created *core.User) func(t *testing.T) { - return func(t *testing.T) { - user, err := users.Find(noContext, created.ID) - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testUser(user)) - } - } -} - -func testUserFindLogin(users *userStore) func(t *testing.T) { - return func(t *testing.T) { - user, err := users.FindLogin(noContext, "octocat") - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testUser(user)) - } - } -} - -func testUserFindToken(users *userStore) func(t *testing.T) { - return func(t *testing.T) { - user, err := users.FindToken(noContext, "MjAxOC0wOC0xMVQxNTo1ODowN1o") - if err != nil { - t.Error(err) - } else { - t.Run("Fields", testUser(user)) - } - } -} - -func testUserList(users *userStore) func(t *testing.T) { - return func(t *testing.T) { - users, err := users.List(noContext) - if err != nil { - t.Error(err) - return - } - if got, want := len(users), 1; got != want { - t.Errorf("Want user count %d, got %d", want, got) - } else { - t.Run("Fields", testUser(users[0])) - } - } -} - -func testUserUpdate(users *userStore, created *core.User) func(t *testing.T) { - return func(t *testing.T) { - user := &core.User{ - ID: created.ID, - Login: "octocat", - Email: "noreply@github.com", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - } - err := users.Update(noContext, user) - if err != nil { - t.Error(err) - return - } - updated, err := users.Find(noContext, user.ID) - if err != nil { - t.Error(err) - return - } - if got, want := updated.Email, user.Email; got != want { - t.Errorf("Want updated user Email %q, got %q", want, got) - } - } -} - -func testUserDelete(users *userStore, created *core.User) func(t *testing.T) { - return func(t *testing.T) { - count, _ := users.Count(noContext) - if got, want := count, int64(1); got != want { - t.Errorf("Want user table count %d, got %d", want, got) - return - } - - err := users.Delete(noContext, &core.User{ID: created.ID}) - if err != nil { - t.Error(err) - } - - count, _ = users.Count(noContext) - if got, want := count, int64(0); got != want { - t.Errorf("Want user table count %d, got %d", want, got) - return - } - } -} - -func testUser(user *core.User) func(t *testing.T) { - return func(t *testing.T) { - if got, want := user.Login, "octocat"; got != want { - t.Errorf("Want user Login %q, got %q", want, got) - } - if got, want := user.Email, "octocat@github.com"; got != want { - t.Errorf("Want user Email %q, got %q", want, got) - } - if got, want := user.Avatar, "https://avatars3.githubusercontent.com/u/583231?v=4"; got != want { - t.Errorf("Want user Avatar %q, got %q", want, got) - } - if got, want := user.Token, "9595fe015ca9b98c41ebf4e7d4e004ee"; got != want { - t.Errorf("Want user Access Token %q, got %q", want, got) - } - if got, want := user.Refresh, "268ef49df64ea8ff79ef11e995d41aed"; got != want { - t.Errorf("Want user Refresh Token %q, got %q", want, got) - } - } -} - -// The purpose of this unit test is to ensure that plaintext -// data can still be read from the database if encryption is -// added at a later time. -func TestUserCryptoCompat(t *testing.T) { - conn, err := dbtest.Connect() - if err != nil { - t.Error(err) - return - } - defer func() { - dbtest.Reset(conn) - dbtest.Disconnect(conn) - }() - - store := New(conn, nil).(*userStore) - store.enc, _ = encrypt.New("") - - item := &core.User{ - Login: "octocat", - Email: "octocat@github.com", - Avatar: "https://avatars3.githubusercontent.com/u/583231?v=4", - Hash: "MjAxOC0wOC0xMVQxNTo1ODowN1o", - Token: "9595fe015ca9b98c41ebf4e7d4e004ee", - Refresh: "268ef49df64ea8ff79ef11e995d41aed", - } - - // create the secret with the secret value stored as plaintext - err = store.Create(noContext, item) - if err != nil { - t.Error(err) - return - } - if item.ID == 0 { - t.Errorf("Want secret ID assigned, got %d", item.ID) - return - } - - // update the store to use encryption - store.enc, _ = encrypt.New("fb4b4d6267c8a5ce8231f8b186dbca92") - store.enc.(*encrypt.Aesgcm).Compat = true - - // fetch the secret from the database - got, err := store.Find(noContext, item.ID) - if err != nil { - t.Errorf("cannot retrieve user from database: %s", err) - } else { - t.Run("Fields", testUser(got)) - } -} diff --git a/trigger/change.go b/trigger/change.go deleted file mode 100644 index c477b70137..0000000000 --- a/trigger/change.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trigger - -// import ( -// "context" -// "regexp" -// "strconv" - -// "github.com/drone/drone/core" -// "github.com/drone/go-scm/scm" -// ) - -// func listChanges(client *scm.Client, repo *core.Repository, build *core.Build) ([]string, error) { -// switch build.Event { -// case core.EventPullRequest: -// return listChangesPullRequest(client, repo, build) -// case core.EventPush: -// return listChangesPush(client, repo, build) -// default: -// return nil, nil -// } -// } - -// func listChangesPullRequest(client *scm.Client, repo *core.Repository, build *core.Build) ([]string, error) { -// var paths []string -// pr, err := parsePullRequest(build.Ref) -// if err != nil { -// return nil, err -// } -// change, _, err := client.PullRequests.ListChanges(context.Background(), repo.Slug, pr, scm.ListOptions{}) -// if err == nil { -// for _, file := range change { -// paths = append(paths, file.Path) -// } -// } -// return paths, err -// } - -// func listChangesPush(client *scm.Client, repo *core.Repository, build *core.Build) ([]string, error) { -// var paths []string -// // TODO (bradrydzewski) some tag hooks provide the tag but do -// // not provide the sha, in which case we should use the ref -// // instead of the sha. -// change, _, err := client.Git.ListChanges(context.Background(), repo.Slug, build.After, scm.ListOptions{}) -// if err == nil { -// for _, file := range change { -// paths = append(paths, file.Path) -// } -// } -// return paths, err -// } - -// func parsePullRequest(ref string) (int, error) { -// return strconv.Atoi( -// pre.FindString(ref), -// ) -// } - -// var pre = regexp.MustCompile("\\d+") diff --git a/trigger/change_test.go b/trigger/change_test.go deleted file mode 100644 index 423fc23187..0000000000 --- a/trigger/change_test.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package trigger - -// import ( -// "testing" - -// "github.com/drone/drone/core" -// "github.com/drone/drone/mock" -// "github.com/drone/go-scm/scm" - -// "github.com/golang/mock/gomock" -// "github.com/google/go-cmp/cmp" -// ) - -// func Test_listChanges_None(t *testing.T) { -// controller := gomock.NewController(t) -// defer controller.Finish() - -// mockRepo := &core.Repository{ -// Slug: "octocat/hello-world", -// } -// mockBuild := &core.Build{ -// Event: core.EventTag, -// Ref: "refs/tags/v1.0.0", -// } -// paths, err := listChanges(nil, mockRepo, mockBuild) -// if err != nil { -// t.Error(err) -// } -// if len(paths) != 0 { -// t.Errorf("Expect empty changeset for Tag events") -// } -// } - -// func Test_listChanges_Push(t *testing.T) { -// controller := gomock.NewController(t) -// defer controller.Finish() - -// mockRepo := &core.Repository{ -// Slug: "octocat/hello-world", -// } -// mockBuild := &core.Build{ -// Event: core.EventPush, -// After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", -// } -// mockChanges := []*scm.Change{ -// {Path: "README.md"}, -// } - -// mockGit := mock.NewMockGitService(controller) -// mockGit.EXPECT().ListChanges(gomock.Any(), mockRepo.Slug, mockBuild.After, gomock.Any()).Return(mockChanges, nil, nil) - -// mockClient := new(scm.Client) -// mockClient.Git = mockGit - -// got, err := listChanges(mockClient, mockRepo, mockBuild) -// if err != nil { -// t.Error(err) -// } -// want := []string{"README.md"} -// if diff := cmp.Diff(got, want); diff != "" { -// t.Errorf(diff) -// } -// } - -// func Test_listChanges_PullRequest(t *testing.T) { -// controller := gomock.NewController(t) -// defer controller.Finish() - -// mockRepo := &core.Repository{ -// Slug: "octocat/hello-world", -// } -// mockBuild := &core.Build{ -// Event: core.EventPullRequest, -// Ref: "refs/pulls/12/head", -// } -// mockChanges := []*scm.Change{ -// {Path: "README.md"}, -// } - -// mockPR := mock.NewMockPullRequestService(controller) -// mockPR.EXPECT().ListChanges(gomock.Any(), mockRepo.Slug, 12, gomock.Any()).Return(mockChanges, nil, nil) - -// mockClient := new(scm.Client) -// mockClient.PullRequests = mockPR - -// got, err := listChanges(mockClient, mockRepo, mockBuild) -// if err != nil { -// t.Error(err) -// } -// want := []string{"README.md"} -// if diff := cmp.Diff(got, want); diff != "" { -// t.Errorf(diff) -// } -// } - -// func Test_listChanges_PullRequest_ParseError(t *testing.T) { -// controller := gomock.NewController(t) -// defer controller.Finish() - -// mockRepo := &core.Repository{ -// Slug: "octocat/hello-world", -// } -// mockBuild := &core.Build{ -// Event: core.EventPullRequest, -// Ref: "refs/pulls/foo/head", -// } -// _, err := listChanges(nil, mockRepo, mockBuild) -// if err == nil { -// t.Errorf("Expect error parsing invalid pull request number") -// } -// } - -// func Test_parsePullRequest(t *testing.T) { -// var tests = []struct { -// ref string -// num int -// }{ -// {"refs/pulls/1/merge", 1}, -// {"refs/pulls/12/merge", 12}, -// } -// for _, test := range tests { -// pr, err := parsePullRequest(test.ref) -// if err != nil { -// t.Error(err) -// } -// if got, want := pr, test.num; got != want { -// t.Errorf("Want pull request number %d, got %d", want, got) -// } -// } -// } diff --git a/trigger/cron/cron.go b/trigger/cron/cron.go deleted file mode 100644 index aa6a2da7d7..0000000000 --- a/trigger/cron/cron.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package cron - -import ( - "context" - "fmt" - "time" - - "github.com/drone/drone/core" - - "github.com/hashicorp/go-multierror" - "github.com/robfig/cron" - "github.com/sirupsen/logrus" -) - -// New returns a new Cron scheduler. -func New( - commits core.CommitService, - cron core.CronStore, - repos core.RepositoryStore, - users core.UserStore, - trigger core.Triggerer, -) *Scheduler { - return &Scheduler{ - commits: commits, - cron: cron, - repos: repos, - users: users, - trigger: trigger, - } -} - -// Scheduler defines a cron scheduler. -type Scheduler struct { - commits core.CommitService - cron core.CronStore - repos core.RepositoryStore - users core.UserStore - trigger core.Triggerer -} - -// Start starts the cron scheduler. -func (s *Scheduler) Start(ctx context.Context, dur time.Duration) error { - ticker := time.NewTicker(dur) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return nil - case <-ticker.C: - s.run(ctx) - } - } -} - -func (s *Scheduler) run(ctx context.Context) error { - var result error - - logrus.Debugln("cron: begin process pending jobs") - - defer func() { - if err := recover(); err != nil { - logger := logrus.WithField("error", err) - logger.Errorln("cron: unexpected panic") - } - }() - - now := time.Now() - jobs, err := s.cron.Ready(ctx, now.Unix()) - if err != nil { - logger := logrus.WithError(err) - logger.Error("cron: cannot list pending jobs") - return err - } - - logrus.Debugf("cron: found %d pending jobs", len(jobs)) - - for _, job := range jobs { - // jobs can be manually disabled in the user interface, - // and should be skipped. - if job.Disabled { - continue - } - - sched, err := cron.Parse(job.Expr) - if err != nil { - result = multierror.Append(result, err) - // this should never happen since we parse and verify - // the cron expression when the cron entry is created. - continue - } - - // calculate the next execution date. - job.Prev = job.Next - job.Next = sched.Next(now).Unix() - - logger := logrus.WithFields( - logrus.Fields{ - "repo": job.RepoID, - "cron": job.ID, - }, - ) - - err = s.cron.Update(ctx, job) - if err != nil { - logger := logrus.WithError(err) - logger.Warnln("cron: cannot re-schedule job") - result = multierror.Append(result, err) - continue - } - - repo, err := s.repos.Find(ctx, job.RepoID) - if err != nil { - logger := logrus.WithError(err) - logger.Warnln("cron: cannot find repository") - result = multierror.Append(result, err) - continue - } - - user, err := s.users.Find(ctx, repo.UserID) - if err != nil { - logger := logrus.WithError(err) - logger.Warnln("cron: cannot find repository owner") - result = multierror.Append(result, err) - continue - } - - if repo.Active == false { - logger.Traceln("cron: skip inactive repository") - continue - } - - // TODO(bradrydzewski) we may actually need to query the branch - // first to get the sha, and then query the commit. This works fine - // with github and gitlab, but may not work with other providers. - - commit, err := s.commits.FindRef(ctx, user, repo.Slug, job.Branch) - if err != nil { - logger.WithFields( - logrus.Fields{ - "error": err, - "repo": repo.Slug, - "branch": repo.Branch, - }).Warnln("cron: cannot find commit") - result = multierror.Append(result, err) - continue - } - - hook := &core.Hook{ - Trigger: core.TriggerCron, - Event: core.EventCron, - Link: commit.Link, - Timestamp: commit.Author.Date, - Message: commit.Message, - After: commit.Sha, - Ref: fmt.Sprintf("refs/heads/%s", job.Branch), - Target: job.Branch, - Author: commit.Author.Login, - AuthorName: commit.Author.Name, - AuthorEmail: commit.Author.Email, - AuthorAvatar: commit.Author.Avatar, - Cron: job.Name, - Sender: commit.Author.Login, - } - - logger.WithFields( - logrus.Fields{ - "cron": job.Name, - "repo": repo.Slug, - "branch": repo.Branch, - "sha": commit.Sha, - }).Warnln("cron: trigger build") - - _, err = s.trigger.Trigger(ctx, repo, hook) - if err != nil { - logger.WithFields( - logrus.Fields{ - "error": err, - "repo": repo.Slug, - "branch": repo.Branch, - "sha": commit.Sha, - }).Warnln("cron: cannot trigger build") - result = multierror.Append(result, err) - continue - } - } - - logrus.Debugf("cron: finished processing jobs") - return result -} diff --git a/trigger/cron/cron_oss.go b/trigger/cron/cron_oss.go deleted file mode 100644 index c87f9937fe..0000000000 --- a/trigger/cron/cron_oss.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build oss - -package cron - -import ( - "context" - "time" - - "github.com/drone/drone/core" -) - -// New returns a noop Cron scheduler. -func New( - core.CommitService, - core.CronStore, - core.RepositoryStore, - core.UserStore, - core.Triggerer, -) *Scheduler { - return &Scheduler{} -} - -// Schedule is a no-op cron scheduler. -type Scheduler struct{} - -// Start is a no-op. -func (Scheduler) Start(context.Context, time.Duration) error { - return nil -} diff --git a/trigger/cron/cron_test.go b/trigger/cron/cron_test.go deleted file mode 100644 index 2a6f20954d..0000000000 --- a/trigger/cron/cron_test.go +++ /dev/null @@ -1,489 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package cron - -import ( - "context" - "database/sql" - "io/ioutil" - "testing" - "time" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/hashicorp/go-multierror" - "github.com/sirupsen/logrus" -) - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -// TODO(bradrydzewski) test disabled cron jobs are skipped -// TODO(bradrydzewski) test to ensure panic does not exit program - -func TestCron(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - checkBuild := func(_ context.Context, _ *core.Repository, hook *core.Hook) { - ignoreHookFields := cmpopts.IgnoreFields(core.Hook{}, - "Source", "Before") - if diff := cmp.Diff(hook, dummyHook, ignoreHookFields); diff != "" { - t.Errorf(diff) - } - } - - before := time.Now().Unix() - checkCron := func(_ context.Context, cron *core.Cron) { - if got, want := cron.Prev, int64(2000000000); got != want { - t.Errorf("Expect Next copied to Prev") - } - if before > cron.Next { - t.Errorf("Expect Next is set to unix timestamp") - } - } - - mockTriggerer := mock.NewMockTriggerer(controller) - mockTriggerer.EXPECT().Trigger(gomock.Any(), dummyRepo, gomock.Any()).Do(checkBuild) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Find(gomock.Any(), dummyCron.RepoID).Return(dummyRepo, nil) - - mockCrons := mock.NewMockCronStore(controller) - mockCrons.EXPECT().Ready(gomock.Any(), gomock.Any()).Return(dummyCronList, nil) - mockCrons.EXPECT().Update(gomock.Any(), dummyCron).Do(checkCron) - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(dummyUser, nil) - - mockCommits := mock.NewMockCommitService(controller) - mockCommits.EXPECT().FindRef(gomock.Any(), dummyUser, dummyRepo.Slug, dummyRepo.Branch).Return(dummyCommit, nil) - - s := Scheduler{ - commits: mockCommits, - cron: mockCrons, - repos: mockRepos, - users: mockUsers, - trigger: mockTriggerer, - } - - err := s.run(noContext) - if err != nil { - t.Error(err) - } -} - -func TestCron_Cancel(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - s := new(Scheduler) - err := s.Start(ctx, time.Minute) - if err != nil { - t.Errorf("Expect cron scheduler exits when context is canceled") - } -} - -// This unit tests demonstrates that if an error is encountered -// when returning a list of ready cronjobs, the process exits -// immediately with an error message. -func TestCron_ErrorList(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockCrons := mock.NewMockCronStore(controller) - mockCrons.EXPECT().Ready(gomock.Any(), gomock.Any()).Return(dummyCronList, sql.ErrNoRows) - - s := Scheduler{ - commits: nil, - cron: mockCrons, - repos: nil, - trigger: nil, - users: nil, - } - - err := s.run(noContext) - if err == nil { - t.Errorf("Want error when the select cron query fails") - } -} - -// This unit tests demonstrates that if an error is encountered -// when parsing a cronjob, the system will continue processing -// cron jobs and return an aggregated list of errors. -func TestCron_ErrorCronParse(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockTriggerer := mock.NewMockTriggerer(controller) - mockTriggerer.EXPECT().Trigger(gomock.Any(), dummyRepo, gomock.Any()).Return(nil, nil).Times(1) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Find(gomock.Any(), dummyCron.RepoID).Return(dummyRepo, nil).Times(1) - - mockCrons := mock.NewMockCronStore(controller) - mockCrons.EXPECT().Ready(gomock.Any(), gomock.Any()).Return(dummyCronListInvalid, nil) - mockCrons.EXPECT().Update(gomock.Any(), dummyCron).Times(1) - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(dummyUser, nil).Times(1) - - mockCommits := mock.NewMockCommitService(controller) - mockCommits.EXPECT().FindRef(gomock.Any(), dummyUser, dummyRepo.Slug, dummyRepo.Branch).Return(dummyCommit, nil).Times(1) - - s := Scheduler{ - commits: mockCommits, - cron: mockCrons, - repos: mockRepos, - users: mockUsers, - trigger: mockTriggerer, - } - - err := s.run(noContext) - merr := err.(*multierror.Error) - if got, want := len(merr.Errors), 1; got != want { - t.Errorf("Want %d errors, got %d", want, got) - } -} - -// This unit tests demonstrates that if an error is encountered -// when finding the associated cron repository, the system will -// continue processing cron jobs and return an aggregated list of -// errors. -func TestCron_ErrorFindRepo(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockTriggerer := mock.NewMockTriggerer(controller) - mockTriggerer.EXPECT().Trigger(gomock.Any(), dummyRepo, gomock.Any()).Return(nil, nil).Times(1) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Find(gomock.Any(), dummyCron.RepoID).Return(dummyRepo, nil) - mockRepos.EXPECT().Find(gomock.Any(), dummyCron.RepoID).Return(nil, sql.ErrNoRows) - - mockCrons := mock.NewMockCronStore(controller) - mockCrons.EXPECT().Ready(gomock.Any(), gomock.Any()).Return(dummyCronListMultiple, nil) - mockCrons.EXPECT().Update(gomock.Any(), dummyCron).Times(2) - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(dummyUser, nil).Times(1) - - mockCommits := mock.NewMockCommitService(controller) - mockCommits.EXPECT().FindRef(gomock.Any(), dummyUser, dummyRepo.Slug, dummyRepo.Branch).Return(dummyCommit, nil).Times(1) - - s := Scheduler{ - commits: mockCommits, - cron: mockCrons, - repos: mockRepos, - users: mockUsers, - trigger: mockTriggerer, - } - - err := s.run(noContext) - merr := err.(*multierror.Error) - if got, want := len(merr.Errors), 1; got != want { - t.Errorf("Want %d errors, got %d", want, got) - } -} - -// This unit tests demonstrates that if an error is encountered -// when updating the next cron execution time, the system will -// continue processing cron jobs and return an aggregated list -// of errors. -func TestCron_ErrorUpdateCron(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockTriggerer := mock.NewMockTriggerer(controller) - mockTriggerer.EXPECT().Trigger(gomock.Any(), dummyRepo, gomock.Any()).Return(nil, nil).Times(1) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Find(gomock.Any(), dummyCron.RepoID).Return(dummyRepo, nil).Times(1) - - mockCrons := mock.NewMockCronStore(controller) - mockCrons.EXPECT().Ready(gomock.Any(), gomock.Any()).Return(dummyCronListMultiple, nil) - mockCrons.EXPECT().Update(gomock.Any(), dummyCron).Return(nil) - mockCrons.EXPECT().Update(gomock.Any(), dummyCron).Return(sql.ErrNoRows) - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(dummyUser, nil).Times(1) - - mockCommits := mock.NewMockCommitService(controller) - mockCommits.EXPECT().FindRef(gomock.Any(), dummyUser, dummyRepo.Slug, dummyRepo.Branch).Return(dummyCommit, nil).Times(1) - - s := Scheduler{ - commits: mockCommits, - cron: mockCrons, - repos: mockRepos, - users: mockUsers, - trigger: mockTriggerer, - } - - err := s.run(noContext) - merr := err.(*multierror.Error) - if got, want := len(merr.Errors), 1; got != want { - t.Errorf("Want %d errors, got %d", want, got) - } - if got, want := merr.Errors[0], sql.ErrNoRows; got != want { - t.Errorf("Want error %v, got %v", want, got) - } -} - -// This unit tests demonstrates that if an error is encountered -// when finding the repository owner in the database, the system -// will continue processing cron jobs and return an aggregated -// list of errors. -func TestCron_ErrorFindUser(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockTriggerer := mock.NewMockTriggerer(controller) - mockTriggerer.EXPECT().Trigger(gomock.Any(), dummyRepo, gomock.Any()).Return(nil, nil).Times(1) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Find(gomock.Any(), dummyCron.RepoID).Return(dummyRepo, nil).Times(2) - - mockCrons := mock.NewMockCronStore(controller) - mockCrons.EXPECT().Ready(gomock.Any(), gomock.Any()).Return(dummyCronListMultiple, nil) - mockCrons.EXPECT().Update(gomock.Any(), dummyCron).Times(2) - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(dummyUser, nil).Times(1) - mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(nil, sql.ErrNoRows).Times(1) - - mockCommits := mock.NewMockCommitService(controller) - mockCommits.EXPECT().FindRef(gomock.Any(), dummyUser, dummyRepo.Slug, dummyRepo.Branch).Return(dummyCommit, nil).Times(1) - - s := Scheduler{ - commits: mockCommits, - cron: mockCrons, - repos: mockRepos, - users: mockUsers, - trigger: mockTriggerer, - } - - err := s.run(noContext) - merr := err.(*multierror.Error) - if got, want := len(merr.Errors), 1; got != want { - t.Errorf("Want %d errors, got %d", want, got) - } - if got, want := merr.Errors[0], sql.ErrNoRows; got != want { - t.Errorf("Want error %v, got %v", want, got) - } -} - -// This unit tests demonstrates that if an error is encountered -// when communicating with the source code management system, the -// system will continue processing cron jobs and return an aggregated -// list of errors. -func TestCron_ErrorFindCommit(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockTriggerer := mock.NewMockTriggerer(controller) - mockTriggerer.EXPECT().Trigger(gomock.Any(), dummyRepo, gomock.Any()).Return(nil, nil).Times(1) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Find(gomock.Any(), dummyCron.RepoID).Return(dummyRepo, nil).Times(2) - - mockCrons := mock.NewMockCronStore(controller) - mockCrons.EXPECT().Ready(gomock.Any(), gomock.Any()).Return(dummyCronListMultiple, nil) - mockCrons.EXPECT().Update(gomock.Any(), dummyCron).Times(2) - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(dummyUser, nil).Times(2) - - mockCommits := mock.NewMockCommitService(controller) - mockCommits.EXPECT().FindRef(gomock.Any(), dummyUser, dummyRepo.Slug, dummyRepo.Branch).Return(dummyCommit, nil).Times(1) - mockCommits.EXPECT().FindRef(gomock.Any(), dummyUser, dummyRepo.Slug, dummyRepo.Branch).Return(nil, sql.ErrNoRows).Times(1) - - s := Scheduler{ - commits: mockCommits, - cron: mockCrons, - repos: mockRepos, - users: mockUsers, - trigger: mockTriggerer, - } - - err := s.run(noContext) - merr := err.(*multierror.Error) - if got, want := len(merr.Errors), 1; got != want { - t.Errorf("Want %d errors, got %d", want, got) - } - if got, want := merr.Errors[0], sql.ErrNoRows; got != want { - t.Errorf("Want error %v, got %v", want, got) - } -} - -// This unit tests demonstrates that if an error is encountered -// when triggering a build, the system will continue processing -// cron jobs and return an aggregated list of errors. -func TestCron_ErrorTrigger(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockTriggerer := mock.NewMockTriggerer(controller) - mockTriggerer.EXPECT().Trigger(gomock.Any(), dummyRepo, gomock.Any()).Return(nil, sql.ErrNoRows) - mockTriggerer.EXPECT().Trigger(gomock.Any(), dummyRepo, gomock.Any()).Return(nil, nil) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Find(gomock.Any(), dummyCron.RepoID).Return(dummyRepo, nil).Times(2) - - mockCrons := mock.NewMockCronStore(controller) - mockCrons.EXPECT().Ready(gomock.Any(), gomock.Any()).Return(dummyCronListMultiple, nil) - mockCrons.EXPECT().Update(gomock.Any(), dummyCron).Times(2) - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(dummyUser, nil).Times(2) - - mockCommits := mock.NewMockCommitService(controller) - mockCommits.EXPECT().FindRef(gomock.Any(), dummyUser, dummyRepo.Slug, dummyRepo.Branch).Return(dummyCommit, nil).Times(2) - - s := Scheduler{ - commits: mockCommits, - cron: mockCrons, - repos: mockRepos, - users: mockUsers, - trigger: mockTriggerer, - } - - err := s.run(noContext) - merr := err.(*multierror.Error) - if got, want := len(merr.Errors), 1; got != want { - t.Errorf("Want %d errors, got %d", want, got) - } - if got, want := merr.Errors[0], sql.ErrNoRows; got != want { - t.Errorf("Want error %v, got %v", want, got) - } -} - -var ( - noContext = context.Background() - - dummyUser = &core.User{ - Login: "octocat", - } - - dummyBuild = &core.Build{ - Number: dummyRepo.Counter, - RepoID: dummyRepo.ID, - Status: core.StatusPending, - Event: core.EventCron, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Timestamp: 1299283200, - Message: "first commit", - Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e", - After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Ref: "refs/heads/master", - Source: "master", - Target: "master", - Author: "octocat", - AuthorName: "The Octocat", - AuthorEmail: "octocat@hello-world.com", - AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231", - Sender: "octocat", - } - - dummyRepo = &core.Repository{ - ID: 1, - UID: "1296269", - UserID: 2, - Namespace: "octocat", - Name: "Hello-World", - Slug: "octocat/Hello-World", - SCM: "git", - HTTPURL: "https://github.com/octocat/Hello-World.git", - SSHURL: "git@github.com:octocat/Hello-World.git", - Link: "https://github.com/octocat/Hello-World", - Branch: "master", - Private: false, - Visibility: core.VisibilityPublic, - Active: true, - Counter: 42, - Signer: "g9dMChy22QutQM5lrpbe0yCR3f15t1gv", - Secret: "g9dMChy22QutQM5lrpbe0yCR3f15t1gv", - } - - dummyCron = &core.Cron{ - RepoID: dummyRepo.ID, - Name: "nightly", - Expr: "0 0 * * *", - Next: 2000000000, - Prev: 1000000000, - Branch: "master", - } - - dummyCronInvalid = &core.Cron{ - RepoID: dummyRepo.ID, - Name: "nightly", - Expr: "A B C D E", - Next: 2000000000, - Prev: 1000000000, - Branch: "master", - } - - dummyCronList = []*core.Cron{ - dummyCron, - } - - dummyCronListMultiple = []*core.Cron{ - dummyCron, - dummyCron, - } - - dummyCronListInvalid = []*core.Cron{ - dummyCronInvalid, - dummyCron, - } - - dummyHook = &core.Hook{ - Event: core.EventCron, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Timestamp: 1299283200, - Message: "first commit", - Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e", - After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Ref: "refs/heads/master", - Source: "master", - Target: "master", - Author: "octocat", - AuthorName: "The Octocat", - AuthorEmail: "octocat@hello-world.com", - AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231", - Sender: "octocat", - Cron: "nightly", - Trigger: "@cron", - } - - dummyCommit = &core.Commit{ - Sha: dummyHook.After, - Message: dummyHook.Message, - Link: dummyHook.Link, - Committer: &core.Committer{ - Name: dummyHook.AuthorName, - Email: dummyHook.AuthorEmail, - Login: dummyHook.Author, - Avatar: dummyHook.AuthorAvatar, - Date: dummyHook.Timestamp, - }, - Author: &core.Committer{ - Name: dummyHook.AuthorName, - Email: dummyHook.AuthorEmail, - Login: dummyHook.Author, - Avatar: dummyHook.AuthorAvatar, - Date: dummyHook.Timestamp, - }, - } - - ignoreBuildFields = cmpopts.IgnoreFields(core.Build{}, - "Created", "Updated") -) diff --git a/trigger/dag/dag.go b/trigger/dag/dag.go deleted file mode 100644 index 85aa9b21c8..0000000000 --- a/trigger/dag/dag.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// Copyright 2018 natessilva -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dag - -// Dag is a directed acyclic graph. -type Dag struct { - graph map[string]*Vertex -} - -// Vertex is a vertex in the graph. -type Vertex struct { - Name string - Skip bool - graph []string -} - -// New creates a new directed acyclic graph (dag) that can -// determinate if a stage has dependencies. -func New() *Dag { - return &Dag{ - graph: make(map[string]*Vertex), - } -} - -// Add establishes a dependency between two vertices in the graph. -func (d *Dag) Add(from string, to ...string) *Vertex { - vertex := new(Vertex) - vertex.Name = from - vertex.Skip = false - vertex.graph = to - d.graph[from] = vertex - return vertex -} - -// Get returns the vertex from the graph. -func (d *Dag) Get(name string) (*Vertex, bool) { - vertex, ok := d.graph[name] - return vertex, ok -} - -// Dependencies returns the direct dependencies accounting for -// skipped dependencies. -func (d *Dag) Dependencies(name string) []string { - vertex := d.graph[name] - return d.dependencies(vertex) -} - -// Ancestors returns the ancestors of the vertex. -func (d *Dag) Ancestors(name string) []*Vertex { - vertex := d.graph[name] - return d.ancestors(vertex) -} - -// DetectCycles returns true if cycles are detected in the graph. -func (d *Dag) DetectCycles() bool { - visited := make(map[string]bool) - recStack := make(map[string]bool) - - for vertex := range d.graph { - if !visited[vertex] { - if d.detectCycles(vertex, visited, recStack) { - return true - } - } - } - return false -} - -// helper function returns the list of ancestors for the vertex. -func (d *Dag) ancestors(parent *Vertex) []*Vertex { - if parent == nil { - return nil - } - var combined []*Vertex - for _, name := range parent.graph { - vertex, found := d.graph[name] - if !found { - continue - } - if !vertex.Skip { - combined = append(combined, vertex) - } - combined = append(combined, d.ancestors(vertex)...) - } - return combined -} - -// helper function returns the list of dependencies for the, -// vertex taking into account skipped dependencies. -func (d *Dag) dependencies(parent *Vertex) []string { - if parent == nil { - return nil - } - var combined []string - for _, name := range parent.graph { - vertex, found := d.graph[name] - if !found { - continue - } - if vertex.Skip { - // if the vertex is skipped we should move up the - // graph and check direct ancestors. - combined = append(combined, d.dependencies(vertex)...) - } else { - combined = append(combined, vertex.Name) - } - } - return combined -} - -// helper function returns true if the vertex is cyclical. -func (d *Dag) detectCycles(name string, visited, recStack map[string]bool) bool { - visited[name] = true - recStack[name] = true - - vertex, ok := d.graph[name] - if !ok { - return false - } - for _, v := range vertex.graph { - // only check cycles on a vertex one time - if !visited[v] { - if d.detectCycles(v, visited, recStack) { - return true - } - // if we've visited this vertex in this recursion - // stack, then we have a cycle - } else if recStack[v] { - return true - } - - } - recStack[name] = false - return false -} diff --git a/trigger/dag/dag_test.go b/trigger/dag/dag_test.go deleted file mode 100644 index c387955b9b..0000000000 --- a/trigger/dag/dag_test.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package dag - -import ( - "reflect" - "testing" -) - -func TestDag(t *testing.T) { - dag := New() - dag.Add("backend") - dag.Add("frontend") - dag.Add("notify", "backend", "frontend") - if dag.DetectCycles() { - t.Errorf("cycles detected") - } - - dag = New() - dag.Add("notify", "backend", "frontend") - if dag.DetectCycles() { - t.Errorf("cycles detected") - } - - dag = New() - dag.Add("backend", "frontend") - dag.Add("frontend", "backend") - dag.Add("notify", "backend", "frontend") - if dag.DetectCycles() == false { - t.Errorf("Expect cycles detected") - } - - dag = New() - dag.Add("backend", "backend") - dag.Add("frontend", "backend") - dag.Add("notify", "backend", "frontend") - if dag.DetectCycles() == false { - t.Errorf("Expect cycles detected") - } - - dag = New() - dag.Add("backend") - dag.Add("frontend") - dag.Add("notify", "backend", "frontend", "notify") - if dag.DetectCycles() == false { - t.Errorf("Expect cycles detected") - } -} - -func TestAncestors(t *testing.T) { - dag := New() - v := dag.Add("backend") - dag.Add("frontend", "backend") - dag.Add("notify", "frontend") - - ancestors := dag.Ancestors("frontend") - if got, want := len(ancestors), 1; got != want { - t.Errorf("Want %d ancestors, got %d", want, got) - } - if ancestors[0] != v { - t.Errorf("Unexpected ancestor") - } - - if v := dag.Ancestors("backend"); len(v) != 0 { - t.Errorf("Expect vertexes with no dependencies has zero ancestors") - } -} - -func TestAncestors_Skipped(t *testing.T) { - dag := New() - dag.Add("backend").Skip = true - dag.Add("frontend", "backend").Skip = true - dag.Add("notify", "frontend") - - if v := dag.Ancestors("frontend"); len(v) != 0 { - t.Errorf("Expect skipped vertexes excluded") - } - if v := dag.Ancestors("notify"); len(v) != 0 { - t.Errorf("Expect skipped vertexes excluded") - } -} - -func TestAncestors_NotFound(t *testing.T) { - dag := New() - dag.Add("backend") - dag.Add("frontend", "backend") - dag.Add("notify", "frontend") - if dag.DetectCycles() { - t.Errorf("cycles detected") - } - if v := dag.Ancestors("does-not-exist"); len(v) != 0 { - t.Errorf("Expect vertex not found does not panic") - } -} - -func TestAncestors_Malformed(t *testing.T) { - dag := New() - dag.Add("backend") - dag.Add("frontend", "does-not-exist") - dag.Add("notify", "frontend") - if dag.DetectCycles() { - t.Errorf("cycles detected") - } - if v := dag.Ancestors("frontend"); len(v) != 0 { - t.Errorf("Expect invalid dependency does not panic") - } -} - -func TestAncestors_Complex(t *testing.T) { - dag := New() - dag.Add("backend") - dag.Add("frontend") - dag.Add("publish", "backend", "frontend") - dag.Add("deploy", "publish") - last := dag.Add("notify", "deploy") - if dag.DetectCycles() { - t.Errorf("cycles detected") - } - - ancestors := dag.Ancestors("notify") - if got, want := len(ancestors), 4; got != want { - t.Errorf("Want %d ancestors, got %d", want, got) - return - } - for _, ancestor := range ancestors { - if ancestor == last { - t.Errorf("Unexpected ancestor") - } - } - - v, _ := dag.Get("publish") - v.Skip = true - ancestors = dag.Ancestors("notify") - if got, want := len(ancestors), 3; got != want { - t.Errorf("Want %d ancestors, got %d", want, got) - return - } -} - -func TestDependencies(t *testing.T) { - dag := New() - dag.Add("backend") - dag.Add("frontend") - dag.Add("publish", "backend", "frontend") - - if deps := dag.Dependencies("backend"); len(deps) != 0 { - t.Errorf("Expect zero dependencies") - } - if deps := dag.Dependencies("frontend"); len(deps) != 0 { - t.Errorf("Expect zero dependencies") - } - - got, want := dag.Dependencies("publish"), []string{"backend", "frontend"} - if !reflect.DeepEqual(got, want) { - t.Errorf("Unexpected dependencies, got %v", got) - } -} - -func TestDependencies_Skipped(t *testing.T) { - dag := New() - dag.Add("backend") - dag.Add("frontend").Skip = true - dag.Add("publish", "backend", "frontend") - - if deps := dag.Dependencies("backend"); len(deps) != 0 { - t.Errorf("Expect zero dependencies") - } - if deps := dag.Dependencies("frontend"); len(deps) != 0 { - t.Errorf("Expect zero dependencies") - } - - got, want := dag.Dependencies("publish"), []string{"backend"} - if !reflect.DeepEqual(got, want) { - t.Errorf("Unexpected dependencies, got %v", got) - } -} - -func TestDependencies_Complex(t *testing.T) { - dag := New() - dag.Add("clone") - dag.Add("backend") - dag.Add("frontend", "backend").Skip = true - dag.Add("publish", "frontend", "clone") - dag.Add("notify", "publish") - - if deps := dag.Dependencies("clone"); len(deps) != 0 { - t.Errorf("Expect zero dependencies for clone") - } - if deps := dag.Dependencies("backend"); len(deps) != 0 { - t.Errorf("Expect zero dependencies for backend") - } - - got, want := dag.Dependencies("frontend"), []string{"backend"} - if !reflect.DeepEqual(got, want) { - t.Errorf("Unexpected dependencies for frontend, got %v", got) - } - - got, want = dag.Dependencies("publish"), []string{"backend", "clone"} - if !reflect.DeepEqual(got, want) { - t.Errorf("Unexpected dependencies for publish, got %v", got) - } - - got, want = dag.Dependencies("notify"), []string{"publish"} - if !reflect.DeepEqual(got, want) { - t.Errorf("Unexpected dependencies for notify, got %v", got) - } -} diff --git a/trigger/skip.go b/trigger/skip.go deleted file mode 100644 index 2b62a4d1b4..0000000000 --- a/trigger/skip.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trigger - -import ( - "strings" - - "github.com/drone/drone-yaml/yaml" - "github.com/drone/drone/core" -) - -func skipBranch(document *yaml.Pipeline, branch string) bool { - return !document.Trigger.Branch.Match(branch) -} - -func skipRef(document *yaml.Pipeline, ref string) bool { - return !document.Trigger.Ref.Match(ref) -} - -func skipEvent(document *yaml.Pipeline, event string) bool { - return !document.Trigger.Event.Match(event) -} - -func skipAction(document *yaml.Pipeline, action string) bool { - return !document.Trigger.Action.Match(action) -} - -func skipInstance(document *yaml.Pipeline, instance string) bool { - return !document.Trigger.Instance.Match(instance) -} - -func skipTarget(document *yaml.Pipeline, env string) bool { - return !document.Trigger.Target.Match(env) -} - -func skipRepo(document *yaml.Pipeline, repo string) bool { - return !document.Trigger.Repo.Match(repo) -} - -func skipCron(document *yaml.Pipeline, cron string) bool { - return !document.Trigger.Cron.Match(cron) -} - -func skipMessage(hook *core.Hook) bool { - switch { - case hook.Event == core.EventTag: - return false - case hook.Event == core.EventCron: - return false - case hook.Event == core.EventCustom: - return false - case hook.Event == core.EventPromote: - return false - case hook.Event == core.EventRollback: - return false - case skipMessageEval(hook.Message): - return true - case skipMessageEval(hook.Title): - return true - default: - return false - } -} - -func skipMessageEval(str string) bool { - lower := strings.ToLower(str) - switch { - case strings.Contains(lower, "[ci skip]"), - strings.Contains(lower, "[skip ci]"), - strings.Contains(lower, "***no_ci***"): - return true - default: - return false - } -} - -// func skipPaths(document *config.Config, paths []string) bool { -// switch { -// // changed files are only returned for push and pull request -// // events. If the list of changed files is empty the system will -// // force-run all pipelines and pipeline steps -// case len(paths) == 0: -// return false -// // github returns a maximum of 300 changed files from the -// // api response. If there are 300+ changed files the system -// // will force-run all pipelines and pipeline steps. -// case len(paths) >= 300: -// return false -// default: -// return !document.Trigger.Paths.MatchAny(paths) -// } -// } diff --git a/trigger/skip_test.go b/trigger/skip_test.go deleted file mode 100644 index 0aa763e475..0000000000 --- a/trigger/skip_test.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -// +build !oss - -package trigger - -import ( - "testing" - - "github.com/drone/drone-yaml/yaml" - "github.com/drone/drone/core" -) - -func Test_skipBranch(t *testing.T) { - tests := []struct { - config string - branch string - want bool - }{ - { - config: "kind: pipeline\ntrigger: { }", - branch: "master", - want: false, - }, - { - config: "kind: pipeline\ntrigger: { branch: [ master ] }", - branch: "master", - want: false, - }, - { - config: "kind: pipeline\ntrigger: { branch: [ master ] }", - branch: "develop", - want: true, - }, - } - for i, test := range tests { - manifest, err := yaml.ParseString(test.config) - if err != nil { - t.Error(err) - } - pipeline := manifest.Resources[0].(*yaml.Pipeline) - got, want := skipBranch(pipeline, test.branch), test.want - if got != want { - t.Errorf("Want test %d to return %v", i, want) - } - } -} - -func Test_skipEvent(t *testing.T) { - tests := []struct { - config string - event string - want bool - }{ - { - config: "kind: pipeline\ntrigger: { }", - event: "push", - want: false, - }, - { - config: "kind: pipeline\ntrigger: { event: [ push ] }", - event: "push", - want: false, - }, - { - config: "kind: pipeline\ntrigger: { event: [ push ] }", - event: "pull_request", - want: true, - }, - } - for i, test := range tests { - manifest, err := yaml.ParseString(test.config) - if err != nil { - t.Error(err) - } - pipeline := manifest.Resources[0].(*yaml.Pipeline) - got, want := skipEvent(pipeline, test.event), test.want - if got != want { - t.Errorf("Want test %d to return %v", i, want) - } - } -} - -// func Test_skipPath(t *testing.T) { -// tests := []struct { -// config string -// paths []string -// want bool -// }{ -// { -// config: "trigger: { }", -// paths: []string{}, -// want: false, -// }, -// { -// config: "trigger: { }", -// paths: []string{"README.md"}, -// want: false, -// }, -// { -// config: "trigger: { paths: foo/* }", -// paths: []string{"foo/README"}, -// want: false, -// }, -// { -// config: "trigger: { paths: foo/* }", -// paths: []string{"bar/README"}, -// want: true, -// }, -// // if empty changeset, never skip the pipeline -// { -// config: "trigger: { paths: foo/* }", -// paths: []string{}, -// want: false, -// }, -// // if max changeset, never skip the pipeline -// { -// config: "trigger: { paths: foo/* }", -// paths: make([]string, 400), -// want: false, -// }, -// } -// for i, test := range tests { -// document, err := config.ParseString(test.config) -// if err != nil { -// t.Error(err) -// } -// got, want := skipPaths(document, test.paths), test.want -// if got != want { -// t.Errorf("Want test %d to return %v", i, want) -// } -// } -// } - -func Test_skipMessage(t *testing.T) { - tests := []struct { - event string - message string - title string - want bool - }{ - { - event: "push", - message: "update readme", - want: false, - }, - // skip when message contains [CI SKIP] - { - event: "push", - message: "update readme [CI SKIP]", - want: true, - }, - { - event: "pull_request", - message: "update readme [CI SKIP]", - want: true, - }, - // skip when title contains [CI SKIP] - - { - event: "push", - title: "update readme [CI SKIP]", - want: true, - }, - { - event: "pull_request", - title: "update readme [CI SKIP]", - want: true, - }, - // ignore [CI SKIP] when event is tag - { - event: "tag", - message: "update readme [CI SKIP]", - want: false, - }, - { - event: "tag", - title: "update readme [CI SKIP]", - want: false, - }, - { - event: "cron", - title: "update readme [CI SKIP]", - want: false, - }, - { - event: "cron", - title: "update readme [CI SKIP]", - want: false, - }, - { - event: "custom", - title: "update readme [CI SKIP]", - want: false, - }, - { - event: "custom", - title: "update readme [CI SKIP]", - want: false, - }, - { - event: "promote", - title: "update readme [CI SKIP]", - want: false, - }, - { - event: "promote", - title: "update readme [CI SKIP]", - want: false, - }, - { - event: "rollback", - title: "update readme [CI SKIP]", - want: false, - }, - { - event: "rollback", - title: "update readme [CI SKIP]", - want: false, - }, - } - for _, test := range tests { - hook := &core.Hook{ - Message: test.message, - Title: test.title, - Event: test.event, - } - got, want := skipMessage(hook), test.want - if got != want { - t.Errorf("Want { event: %q, message: %q, title: %q } to return %v", - test.event, test.message, test.title, want) - } - } -} - -func Test_skipMessageEval(t *testing.T) { - tests := []struct { - eval string - want bool - }{ - {"update readme", false}, - // test [CI SKIP] - {"foo [ci skip] bar", true}, - {"foo [CI SKIP] bar", true}, - {"foo [CI Skip] bar", true}, - {"foo [CI SKIP]", true}, - // test [SKIP CI] - {"foo [skip ci] bar", true}, - {"foo [SKIP CI] bar", true}, - {"foo [Skip CI] bar", true}, - {"foo [SKIP CI]", true}, - // test ***NO_CI*** - {"foo ***NO_CI*** bar", true}, - {"foo ***NO_CI*** bar", true}, - {"foo ***NO_CI*** bar", true}, - {"foo ***NO_CI***", true}, - } - for _, test := range tests { - got, want := skipMessageEval(test.eval), test.want - if got != want { - t.Errorf("Want %q to return %v, got %v", test.eval, want, got) - } - } -} diff --git a/trigger/trigger.go b/trigger/trigger.go deleted file mode 100644 index 164260d59d..0000000000 --- a/trigger/trigger.go +++ /dev/null @@ -1,643 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package trigger - -import ( - "context" - "runtime/debug" - "strings" - "time" - - "github.com/drone/drone-yaml/yaml" - "github.com/drone/drone-yaml/yaml/converter" - "github.com/drone/drone-yaml/yaml/linter" - "github.com/drone/drone-yaml/yaml/signer" - - "github.com/drone/drone/core" - "github.com/drone/drone/trigger/dag" - - "github.com/sirupsen/logrus" -) - -type triggerer struct { - canceler core.Canceler - config core.ConfigService - convert core.ConvertService - commits core.CommitService - status core.StatusService - builds core.BuildStore - sched core.Scheduler - repos core.RepositoryStore - users core.UserStore - validate core.ValidateService - hooks core.WebhookSender -} - -// New returns a new build triggerer. -func New( - canceler core.Canceler, - config core.ConfigService, - convert core.ConvertService, - commits core.CommitService, - status core.StatusService, - builds core.BuildStore, - sched core.Scheduler, - repos core.RepositoryStore, - users core.UserStore, - validate core.ValidateService, - hooks core.WebhookSender, -) core.Triggerer { - return &triggerer{ - canceler: canceler, - config: config, - convert: convert, - commits: commits, - status: status, - builds: builds, - sched: sched, - repos: repos, - users: users, - validate: validate, - hooks: hooks, - } -} - -func (t *triggerer) Trigger(ctx context.Context, repo *core.Repository, base *core.Hook) (*core.Build, error) { - logger := logrus.WithFields( - logrus.Fields{ - "repo": repo.Slug, - "ref": base.Ref, - "event": base.Event, - "commit": base.After, - }, - ) - - logger.Debugln("trigger: received") - defer func() { - // taking the paranoid approach to recover from - // a panic that should absolutely never happen. - if r := recover(); r != nil { - logger.Errorf("runner: unexpected panic: %s", r) - debug.PrintStack() - } - }() - - if skipMessage(base) { - logger.Infoln("trigger: skipping hook. found skip directive") - return nil, nil - } - if base.Event == core.EventPullRequest { - if repo.IgnorePulls { - logger.Infoln("trigger: skipping hook. project ignores pull requests") - return nil, nil - } - if repo.IgnoreForks && !strings.EqualFold(base.Fork, repo.Slug) { - logger.Infoln("trigger: skipping hook. project ignores forks") - return nil, nil - } - } - - user, err := t.users.Find(ctx, repo.UserID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot find repository owner") - return nil, err - } - - if user.Active == false { - logger.Infoln("trigger: skipping hook. repository owner is inactive") - return nil, nil - } - - // if the commit message is not included we should - // make an optional API call to the version control - // system to augment the available information. - if base.Message == "" && base.After != "" { - commit, err := t.commits.Find(ctx, user, repo.Slug, base.After) - if err == nil && commit != nil { - base.Message = commit.Message - if base.AuthorEmail == "" { - base.AuthorEmail = commit.Author.Email - } - if base.AuthorName == "" { - base.AuthorName = commit.Author.Name - } - if base.AuthorAvatar == "" { - base.AuthorAvatar = commit.Author.Avatar - } - } - } - - // // some tag hooks provide the tag but do not provide the sha. - // // this may be important if we want to fetch the .drone.yml - // if base.After == "" && base.Event == core.EventTag { - // tag, _, err := t.client.Git.FindTag(ctx, repo.Slug, base.Ref) - // if err != nil { - // logger.Error().Err(err). - // Msg("cannot find tag") - // return nil, err - // } - // base.After = tag.Sha - // } - - // TODO: do a better job of documenting this - // obj := base.After - // if len(obj) == 0 { - // if strings.HasPrefix(base.Ref, "refs/pull/") { - // obj = base.Target - // } else { - // obj = base.Ref - // } - // } - tmpBuild := &core.Build{ - RepoID: repo.ID, - Trigger: base.Trigger, - Parent: base.Parent, - Status: core.StatusPending, - Event: base.Event, - Action: base.Action, - Link: base.Link, - // Timestamp: base.Timestamp, - Title: base.Title, - Message: base.Message, - Before: base.Before, - After: base.After, - Ref: base.Ref, - Fork: base.Fork, - Source: base.Source, - Target: base.Target, - Author: base.Author, - AuthorName: base.AuthorName, - AuthorEmail: base.AuthorEmail, - AuthorAvatar: base.AuthorAvatar, - Params: base.Params, - Cron: base.Cron, - Deploy: base.Deployment, - DeployID: base.DeploymentID, - Debug: base.Debug, - Sender: base.Sender, - Created: time.Now().Unix(), - Updated: time.Now().Unix(), - } - req := &core.ConfigArgs{ - User: user, - Repo: repo, - Build: tmpBuild, - } - raw, err := t.config.Find(ctx, req) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot find yaml") - return nil, err - } - - raw, err = t.convert.Convert(ctx, &core.ConvertArgs{ - User: user, - Repo: repo, - Build: tmpBuild, - Config: raw, - }) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot convert yaml") - return t.createBuildError(ctx, repo, base, err.Error()) - } - - // this code is temporarily in place to detect and convert - // the legacy yaml configuration file to the new format. - raw.Data, err = converter.ConvertString(raw.Data, converter.Metadata{ - Filename: repo.Config, - URL: repo.Link, - Ref: base.Ref, - }) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot convert yaml") - return t.createBuildError(ctx, repo, base, err.Error()) - } - - manifest, err := yaml.ParseString(raw.Data) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot parse yaml") - return t.createBuildError(ctx, repo, base, err.Error()) - } - - verr := t.validate.Validate(ctx, &core.ValidateArgs{ - User: user, - Repo: repo, - Build: tmpBuild, - Config: raw, - }) - switch verr { - case core.ErrValidatorBlock: - logger.Debugln("trigger: yaml validation error: block pipeline") - case core.ErrValidatorSkip: - logger.Debugln("trigger: yaml validation error: skip pipeline") - return nil, nil - default: - if verr != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: yaml validation error") - return t.createBuildError(ctx, repo, base, verr.Error()) - } - } - - err = linter.Manifest(manifest, repo.Trusted) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: yaml linting error") - return t.createBuildError(ctx, repo, base, err.Error()) - } - - verified := true - if repo.Protected && base.Trigger == core.TriggerHook { - key := signer.KeyString(repo.Secret) - val := []byte(raw.Data) - verified, _ = signer.Verify(val, key) - } - // if pipeline validation failed with a block error, the - // pipeline verification should be set to false, which will - // force manual review and approval. - if verr == core.ErrValidatorBlock { - verified = false - } - - // var paths []string - // paths, err := listChanges(t.client, repo, base) - // if err != nil { - // logger.Warn().Err(err). - // Msg("cannot fetch changeset") - // } - - var matched []*yaml.Pipeline - var dag = dag.New() - for _, document := range manifest.Resources { - pipeline, ok := document.(*yaml.Pipeline) - if !ok { - continue - } - // TODO add repo - // TODO add instance - // TODO add target - // TODO add ref - name := pipeline.Name - if name == "" { - name = "default" - } - node := dag.Add(pipeline.Name, pipeline.DependsOn...) - node.Skip = true - - if skipBranch(pipeline, base.Target) { - logger = logger.WithField("pipeline", pipeline.Name) - logger.Infoln("trigger: skipping pipeline, does not match branch") - } else if skipEvent(pipeline, base.Event) { - logger = logger.WithField("pipeline", pipeline.Name) - logger.Infoln("trigger: skipping pipeline, does not match event") - } else if skipAction(pipeline, base.Action) { - logger = logger.WithField("pipeline", pipeline.Name).WithField("action", base.Action) - logger.Infoln("trigger: skipping pipeline, does not match action") - } else if skipRef(pipeline, base.Ref) { - logger = logger.WithField("pipeline", pipeline.Name) - logger.Infoln("trigger: skipping pipeline, does not match ref") - } else if skipRepo(pipeline, repo.Slug) { - logger = logger.WithField("pipeline", pipeline.Name) - logger.Infoln("trigger: skipping pipeline, does not match repo") - } else if skipTarget(pipeline, base.Deployment) { - logger = logger.WithField("pipeline", pipeline.Name) - logger.Infoln("trigger: skipping pipeline, does not match deploy target") - } else if skipCron(pipeline, base.Cron) { - logger = logger.WithField("pipeline", pipeline.Name) - logger.Infoln("trigger: skipping pipeline, does not match cron job") - } else { - matched = append(matched, pipeline) - node.Skip = false - } - } - - if dag.DetectCycles() { - return t.createBuildError(ctx, repo, base, "Error: Dependency cycle detected in Pipeline") - } - - if len(matched) == 0 { - logger.Infoln("trigger: skipping build, no matching pipelines") - return nil, nil - } - - repo, err = t.repos.Increment(ctx, repo) - if err != nil { - logger = logger.WithError(err) - logger.Errorln("trigger: cannot increment build sequence") - return nil, err - } - - build := &core.Build{ - RepoID: repo.ID, - Trigger: base.Trigger, - Number: repo.Counter, - Parent: base.Parent, - Status: core.StatusPending, - Event: base.Event, - Action: base.Action, - Link: base.Link, - // Timestamp: base.Timestamp, - Title: trunc(base.Title, 2000), - Message: trunc(base.Message, 2000), - Before: base.Before, - After: base.After, - Ref: base.Ref, - Fork: base.Fork, - Source: base.Source, - Target: base.Target, - Author: base.Author, - AuthorName: base.AuthorName, - AuthorEmail: base.AuthorEmail, - AuthorAvatar: base.AuthorAvatar, - Params: base.Params, - Deploy: base.Deployment, - DeployID: base.DeploymentID, - Debug: base.Debug, - Sender: base.Sender, - Cron: base.Cron, - Created: time.Now().Unix(), - Updated: time.Now().Unix(), - } - - stages := make([]*core.Stage, len(matched)) - for i, match := range matched { - onSuccess := match.Trigger.Status.Match(core.StatusPassing) - onFailure := match.Trigger.Status.Match(core.StatusFailing) - if len(match.Trigger.Status.Include)+len(match.Trigger.Status.Exclude) == 0 { - onFailure = false - } - - stage := &core.Stage{ - RepoID: repo.ID, - Number: i + 1, - Name: match.Name, - Kind: match.Kind, - Type: match.Type, - OS: match.Platform.OS, - Arch: match.Platform.Arch, - Variant: match.Platform.Variant, - Kernel: match.Platform.Version, - Limit: match.Concurrency.Limit, - LimitRepo: int(repo.Throttle), - Status: core.StatusWaiting, - DependsOn: match.DependsOn, - OnSuccess: onSuccess, - OnFailure: onFailure, - Labels: match.Node, - Created: time.Now().Unix(), - Updated: time.Now().Unix(), - } - if stage.Kind == "pipeline" && stage.Type == "" { - stage.Type = "docker" - } - if stage.OS == "" { - stage.OS = "linux" - } - if stage.Arch == "" { - stage.Arch = "amd64" - } - - if stage.Name == "" { - stage.Name = "default" - } - if verified == false { - stage.Status = core.StatusBlocked - } else if len(stage.DependsOn) == 0 { - stage.Status = core.StatusPending - } - stages[i] = stage - } - - for _, stage := range stages { - // here we re-work the dependencies for the stage to - // account for the fact that some steps may be skipped - // and may otherwise break the dependency chain. - stage.DependsOn = dag.Dependencies(stage.Name) - - // if the stage is pending dependencies, but those - // dependencies are skipped, the stage can be executed - // immediately. - if stage.Status == core.StatusWaiting && - len(stage.DependsOn) == 0 { - stage.Status = core.StatusPending - } - } - - err = t.builds.Create(ctx, build, stages) - if err != nil { - logger = logger.WithError(err) - logger.Errorln("trigger: cannot create build") - return nil, err - } - - err = t.status.Send(ctx, user, &core.StatusInput{ - Repo: repo, - Build: build, - }) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot create status") - } - - for _, stage := range stages { - if stage.Status != core.StatusPending { - continue - } - err = t.sched.Schedule(ctx, stage) - if err != nil { - logger = logger.WithError(err) - logger.Errorln("trigger: cannot enqueue build") - return nil, err - } - } - - payload := &core.WebhookData{ - Event: core.WebhookEventBuild, - Action: core.WebhookActionCreated, - User: user, - Repo: repo, - Build: build, - } - err = t.hooks.Send(ctx, payload) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot send webhook") - } - - if repo.CancelPush && build.Event == core.EventPush || - repo.CancelPulls && build.Event == core.EventPullRequest { - go t.canceler.CancelPending(ctx, repo, build) - } - - // err = t.hooks.SendEndpoint(ctx, payload, repo.Endpoints.Webhook) - // if err != nil { - // logger.Warn().Err(err). - // Int64("build", build.Number). - // Msg("cannot send user-defined webhook") - // } - - // // we should only synchronize the cronjob list on push - // // events to the default branch. - // if build.Event == core.EventPush && - // build.Target == repo.Branch { - // err = t.cron.Sync(ctx, repo, manifest) - // if err != nil { - // logger.Warn().Err(err). - // Msg("cannot sync cronjobs") - // } - // } - - return build, nil -} - -func trunc(s string, i int) string { - runes := []rune(s) - if len(runes) > i { - return string(runes[:i]) - } - return s -} - -func (t *triggerer) createBuildError(ctx context.Context, repo *core.Repository, base *core.Hook, message string) (*core.Build, error) { - logger := logrus.WithFields( - logrus.Fields{ - "repo": repo.Slug, - "ref": base.Ref, - "event": base.Event, - "commit": base.After, - }, - ) - - repo, err := t.repos.Increment(ctx, repo) - if err != nil { - return nil, err - } - - build := &core.Build{ - RepoID: repo.ID, - Number: repo.Counter, - Parent: base.Parent, - Status: core.StatusError, - Error: message, - Event: base.Event, - Action: base.Action, - Link: base.Link, - // Timestamp: base.Timestamp, - Title: base.Title, - Message: base.Message, - Before: base.Before, - After: base.After, - Ref: base.Ref, - Fork: base.Fork, - Source: base.Source, - Target: base.Target, - Author: base.Author, - AuthorName: base.AuthorName, - AuthorEmail: base.AuthorEmail, - AuthorAvatar: base.AuthorAvatar, - Deploy: base.Deployment, - DeployID: base.DeploymentID, - Debug: base.Debug, - Sender: base.Sender, - Created: time.Now().Unix(), - Updated: time.Now().Unix(), - Started: time.Now().Unix(), - Finished: time.Now().Unix(), - } - - err = t.builds.Create(ctx, build, nil) - if err != nil { - logger = logger.WithError(err) - logger.Errorln("trigger: cannot create build error") - return nil, err - } - - user, err := t.users.Find(ctx, repo.UserID) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot find repository owner") - return nil, err - } - - err = t.status.Send(ctx, user, &core.StatusInput{ - Repo: repo, - Build: build, - }) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot create status") - } - - payload := &core.WebhookData{ - Event: core.WebhookEventBuild, - Action: core.WebhookActionCreated, - User: user, - Repo: repo, - Build: build, - } - err = t.hooks.Send(ctx, payload) - if err != nil { - logger = logger.WithError(err) - logger.Warnln("trigger: cannot send webhook") - } - - return build, err -} - -// func shouldBlock(repo *core.Repository, build *core.Build) bool { -// switch { -// case repo.Hooks.Promote == core.HookBlock && build.Event == core.EventPromote: -// return true -// case repo.Hooks.Rollback == core.HookBlock && build.Event == core.EventRollback: -// return true -// case repo.Hooks.Deploy == core.HookBlock && build.Event == core.EventRollback: -// return true -// case repo.Hooks.Pull == core.HookBlock && build.Event == core.EventPullRequest: -// return true -// case repo.Hooks.Push == core.HookBlock && build.Event == core.EventPush: -// return true -// case repo.Hooks.Tags == core.HookBlock && build.Event == core.EventTag: -// return true -// case repo.Hooks.Forks == core.HookBlock && build.Fork != repo.Slug: -// return true -// default: -// return false -// } -// } - -// func skipHook(repo *core.Repository, build *core.Hook) bool { -// switch { -// case repo.Hooks.Promote == core.HookDisable && build.Event == core.EventPromote: -// return true -// case repo.Hooks.Rollback == core.HookDisable && build.Event == core.EventRollback: -// return true -// case repo.Hooks.Pull == core.HookDisable && build.Event == core.EventPullRequest: -// return true -// case repo.Hooks.Push == core.HookDisable && build.Event == core.EventPush: -// return true -// case repo.Hooks.Tags == core.HookDisable && build.Event == core.EventTag: -// return true -// default: -// return false -// } -// } - -// func skipFork(repo *core.Repository, build *core.Hook) bool { -// return repo.Hooks.Forks == core.HookDisable && build.Fork != repo.Slug -// } diff --git a/trigger/trigger_test.go b/trigger/trigger_test.go deleted file mode 100644 index 0e5e8b19e8..0000000000 --- a/trigger/trigger_test.go +++ /dev/null @@ -1,659 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package trigger - -import ( - "context" - "database/sql" - "io" - "io/ioutil" - "testing" - - "github.com/drone/drone/core" - "github.com/drone/drone/mock" - "github.com/sirupsen/logrus" - - "github.com/golang/mock/gomock" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -var noContext = context.Background() - -func init() { - logrus.SetOutput(ioutil.Discard) -} - -func TestTrigger(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - checkBuild := func(_ context.Context, build *core.Build, stages []*core.Stage) { - if diff := cmp.Diff(build, dummyBuild, ignoreBuildFields); diff != "" { - t.Errorf(diff) - } - if diff := cmp.Diff(stages, dummyStages, ignoreStageFields); diff != "" { - t.Errorf(diff) - } - } - - checkStatus := func(_ context.Context, _ *core.User, req *core.StatusInput) error { - if diff := cmp.Diff(req.Build, dummyBuild, ignoreBuildFields); diff != "" { - t.Errorf(diff) - } - if diff := cmp.Diff(req.Repo, dummyRepo, ignoreStageFields); diff != "" { - t.Errorf(diff) - } - return nil - } - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(dummyUser, nil) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(dummyRepo, nil) - - mockConfigService := mock.NewMockConfigService(controller) - mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYaml, nil) - - mockConvertService := mock.NewMockConvertService(controller) - mockConvertService.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(dummyYaml, nil) - - mockValidateService := mock.NewMockValidateService(controller) - mockValidateService.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(nil) - - mockStatus := mock.NewMockStatusService(controller) - mockStatus.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Do(checkStatus) - - mockQueue := mock.NewMockScheduler(controller) - mockQueue.EXPECT().Schedule(gomock.Any(), gomock.Any()).Return(nil) - - mockBuilds := mock.NewMockBuildStore(controller) - mockBuilds.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).Do(checkBuild).Return(nil) - - mockWebhooks := mock.NewMockWebhookSender(controller) - mockWebhooks.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil) - - triggerer := New( - nil, - mockConfigService, - mockConvertService, - nil, - mockStatus, - mockBuilds, - mockQueue, - mockRepos, - mockUsers, - mockValidateService, - mockWebhooks, - ) - - build, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - if err != nil { - t.Error(err) - return - } - if diff := cmp.Diff(build, dummyBuild, ignoreBuildFields); diff != "" { - t.Errorf(diff) - } -} - -// this test verifies that hook is ignored if the commit -// message includes the [CI SKIP] keyword. -func TestTrigger_SkipCI(t *testing.T) { - triggerer := New( - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - ) - dummyHookSkip := *dummyHook - dummyHookSkip.Message = "foo [CI SKIP] bar" - triggerer.Trigger(noContext, dummyRepo, &dummyHookSkip) -} - -// this test verifies that if the system cannot determine -// the repository owner, the function must exit with an error. -// The owner is required because we need an oauth token -// when fetching the configuration file. -func TestTrigger_NoOwner(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(nil, sql.ErrNoRows) - - triggerer := New( - nil, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - mockUsers, - nil, - nil, - ) - - _, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - if err != sql.ErrNoRows { - t.Errorf("Expect error when yaml not found") - } -} - -// this test verifies that if the system cannot fetch the yaml -// configuration file, the function must exit with an error. -func TestTrigger_MissingYaml(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil) - - mockConfigService := mock.NewMockConfigService(controller) - mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(nil, io.EOF) - - triggerer := New( - nil, - mockConfigService, - nil, - nil, - nil, - nil, - nil, - nil, - mockUsers, - nil, - nil, - ) - - _, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - if err == nil { - t.Errorf("Expect error when yaml not found") - } -} - -// this test verifies that if the system cannot parse the yaml -// configuration file, the function must exit with an error. -func TestTrigger_ErrorYaml(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - checkBuild := func(_ context.Context, build *core.Build, stages []*core.Stage) { - if diff := cmp.Diff(build, dummyErrorBuild, ignoreBuildFields); diff != "" { - t.Errorf(diff) - } - if diff := cmp.Diff(stages, []*core.Stage(nil), ignoreStageFields); diff != "" { - t.Errorf(diff) - } - } - - checkStatus := func(_ context.Context, _ *core.User, req *core.StatusInput) error { - if diff := cmp.Diff(req.Build, dummyErrorBuild, ignoreBuildFields); diff != "" { - t.Errorf(diff) - } - if diff := cmp.Diff(req.Repo, dummyRepo, ignoreStageFields); diff != "" { - t.Errorf(diff) - } - return nil - } - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil).MaxTimes(3) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(dummyRepo, nil) - - mockConfigService := mock.NewMockConfigService(controller) - mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYamlInvalid, nil) - - mockConvertService := mock.NewMockConvertService(controller) - mockConvertService.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(dummyYamlInvalid, nil) - - mockStatus := mock.NewMockStatusService(controller) - mockStatus.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Do(checkStatus) - - mockBuilds := mock.NewMockBuildStore(controller) - mockBuilds.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).Do(checkBuild).Return(nil) - - mockWebhooks := mock.NewMockWebhookSender(controller) - mockWebhooks.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil) - - triggerer := New( - nil, - mockConfigService, - mockConvertService, - nil, - mockStatus, - mockBuilds, - nil, - mockRepos, - mockUsers, - nil, - mockWebhooks, - ) - - build, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - if err != nil { - t.Error(err) - } - - if got, want := build.Status, core.StatusError; got != want { - t.Errorf("Want status %s, got %s", want, got) - } - if got, want := build.Error, "yaml: found unknown directive name"; got != want { - t.Errorf("Want error %s, got %s", want, got) - } - if build.Finished == 0 { - t.Errorf("Want non-zero finished time") - } -} - -// this test verifies that no build should be scheduled if the -// hook branch does not match the branches defined in the yaml. -func TestTrigger_SkipBranch(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil) - - mockConfigService := mock.NewMockConfigService(controller) - mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYamlSkipBranch, nil) - - mockConvertService := mock.NewMockConvertService(controller) - mockConvertService.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(dummyYamlSkipBranch, nil) - - mockValidateService := mock.NewMockValidateService(controller) - mockValidateService.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(nil) - - triggerer := New( - nil, - mockConfigService, - mockConvertService, - nil, - nil, - nil, - nil, - nil, - mockUsers, - mockValidateService, - nil, - ) - - _, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - if err != nil { - t.Errorf("Expect build silently skipped if branch does not match") - } -} - -// this test verifies that no build should be scheduled if the -// hook event does not match the events defined in the yaml. -func TestTrigger_SkipEvent(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil) - - mockConfigService := mock.NewMockConfigService(controller) - mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYamlSkipEvent, nil) - - mockConvertService := mock.NewMockConvertService(controller) - mockConvertService.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(dummyYamlSkipEvent, nil) - - mockValidateService := mock.NewMockValidateService(controller) - mockValidateService.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(nil) - - triggerer := New( - nil, - mockConfigService, - mockConvertService, - nil, - nil, - nil, - nil, - nil, - mockUsers, - mockValidateService, - nil, - ) - - _, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - if err != nil { - t.Errorf("Expect build silently skipped if event does not match") - } -} - -// this test verifies that no build should be scheduled if the -// hook action does not match the actions defined in the yaml. -func TestTrigger_SkipAction(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil) - - mockConfigService := mock.NewMockConfigService(controller) - mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYamlSkipAction, nil) - - mockConvertService := mock.NewMockConvertService(controller) - mockConvertService.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(dummyYamlSkipAction, nil) - - mockValidateService := mock.NewMockValidateService(controller) - mockValidateService.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(nil) - - triggerer := New( - nil, - mockConfigService, - mockConvertService, - nil, - nil, - nil, - nil, - nil, - mockUsers, - mockValidateService, - nil, - ) - - _, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - if err != nil { - t.Errorf("Expect build silently skipped if action does not match") - } -} - -// this test verifies that if the system cannot increment the -// build number, the function must exit with error and must not -// schedule a new build. -func TestTrigger_ErrorIncrement(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - mockUsers := mock.NewMockUserStore(controller) - mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil) - - mockRepos := mock.NewMockRepositoryStore(controller) - mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(nil, sql.ErrNoRows) - - mockConfigService := mock.NewMockConfigService(controller) - mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYaml, nil) - - mockConvertService := mock.NewMockConvertService(controller) - mockConvertService.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(dummyYaml, nil) - - mockValidateService := mock.NewMockValidateService(controller) - mockValidateService.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(nil) - - triggerer := New( - nil, - mockConfigService, - mockConvertService, - nil, - nil, - nil, - nil, - mockRepos, - mockUsers, - mockValidateService, - nil, - ) - - _, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - if err != sql.ErrNoRows { - t.Errorf("Expect error when unable to increment build sequence") - } -} - -func TestTrigger_ErrorCreate(t *testing.T) { - t.Skip() - // controller := gomock.NewController(t) - // defer controller.Finish() - - // mockUsers := mock.NewMockUserStore(controller) - // mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil) - - // mockTriggers := mock.NewMockTriggerStore(controller) - // mockTriggers.EXPECT().List(noContext, dummyRepo.ID).Return([]*core.Trigger{dummyTrigger}, nil) - - // mockRepos := mock.NewMockRepositoryStore(controller) - // mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(dummyRepo, nil) - - // mockContents := mock.NewMockContentService(controller) - // mockContents.EXPECT().Find(gomock.Any(), dummyRepo.Slug, dummyTrigger.Path, dummyHook.After).Return(dummyYaml, nil, nil) - // mockContents.EXPECT().Find(gomock.Any(), dummyRepo.Slug, dummySignature.Path, dummyHook.After).Return(dummySignature, nil, nil) - - // mockClient := new(scm.Client) - // mockClient.Contents = mockContents - - // mockBuilds := mock.NewMockBuildStore(controller) - // mockBuilds.EXPECT().Create(gomock.Any(), gomock.Any()).Return(sql.ErrNoRows) - - // triggerer := New( - // mockClient, - // mockBuilds, - // nil, - // mockRepos, - // mockTriggers, - // mockUsers, - // ) - - // builds, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - // if err != sql.ErrNoRows { - // t.Error("Expect error when persisting the build fails") - // } - // if got, want := len(builds), 0; got != want { - // t.Errorf("Got build count %d, want %d", got, want) - // } -} - -func TestTrigger_ErrorEnqueue(t *testing.T) { - t.Skip() - // controller := gomock.NewController(t) - // defer controller.Finish() - - // mockUsers := mock.NewMockUserStore(controller) - // mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil) - - // mockTriggers := mock.NewMockTriggerStore(controller) - // mockTriggers.EXPECT().List(noContext, dummyRepo.ID).Return([]*core.Trigger{dummyTrigger}, nil) - - // mockRepos := mock.NewMockRepositoryStore(controller) - // mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(dummyRepo, nil) - - // mockContents := mock.NewMockContentService(controller) - // mockContents.EXPECT().Find(gomock.Any(), dummyRepo.Slug, dummyTrigger.Path, dummyHook.After).Return(dummyYaml, nil, nil) - // mockContents.EXPECT().Find(gomock.Any(), dummyRepo.Slug, dummySignature.Path, dummyHook.After).Return(dummySignature, nil, nil) - - // mockClient := new(scm.Client) - // mockClient.Contents = mockContents - - // mockQueue := mock.NewMockQueue(controller) - // mockQueue.EXPECT().Push(gomock.Any(), gomock.Any()).Return(sql.ErrNoRows) - - // mockBuilds := mock.NewMockBuildStore(controller) - // mockBuilds.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil) - - // triggerer := New( - // mockClient, - // mockBuilds, - // mockQueue, - // mockRepos, - // mockTriggers, - // mockUsers, - // ) - - // builds, err := triggerer.Trigger(noContext, dummyRepo, dummyHook) - // if err != sql.ErrNoRows { - // t.Error("Expect error when enqueueing the build fails") - // } - // if got, want := len(builds), 0; got != want { - // t.Errorf("Got build count %d, want %d", got, want) - // } -} - -var ( - dummyHook = &core.Hook{ - Event: core.EventPush, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Timestamp: 1299283200, - Message: "first commit", - Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e", - After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Ref: "refs/heads/master", - Source: "master", - Target: "master", - Author: "octocat", - AuthorName: "The Octocat", - AuthorEmail: "octocat@hello-world.com", - AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231", - Sender: "octocat", - Action: "opened", - } - - dummyBuild = &core.Build{ - Number: dummyRepo.Counter, - RepoID: dummyRepo.ID, - Status: core.StatusPending, - Event: core.EventPush, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - // Timestamp: 1299283200, - Message: "first commit", - Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e", - After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Ref: "refs/heads/master", - Source: "master", - Target: "master", - Author: "octocat", - AuthorName: "The Octocat", - AuthorEmail: "octocat@hello-world.com", - AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231", - Sender: "octocat", - Action: "opened", - } - - dummyErrorBuild = &core.Build{ - Number: dummyRepo.Counter, - RepoID: dummyRepo.ID, - Status: core.StatusError, - Error: "yaml: found unknown directive name", - Event: core.EventPush, - Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - // Timestamp: 1299283200, - Message: "first commit", - Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e", - After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", - Ref: "refs/heads/master", - Source: "master", - Target: "master", - Author: "octocat", - AuthorName: "The Octocat", - AuthorEmail: "octocat@hello-world.com", - AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231", - Sender: "octocat", - Action: "opened", - } - - dummyRepo = &core.Repository{ - ID: 1, - UID: "1296269", - UserID: 2, - Namespace: "octocat", - Name: "Hello-World", - Slug: "octocat/Hello-World", - SCM: "git", - HTTPURL: "https://github.com/octocat/Hello-World.git", - SSHURL: "git@github.com:octocat/Hello-World.git", - Link: "https://github.com/octocat/Hello-World", - Branch: "master", - Private: false, - Visibility: core.VisibilityPublic, - Active: true, - Counter: 42, - Secret: "g9dMChy22QutQM5lrpbe0yCR3f15t1gv", - Signer: "g9dMChy22QutQM5lrpbe0yCR3f15t1gv", - Config: ".drone.yml", - } - - dummyStage = &core.Stage{ - Kind: "pipeline", - Type: "docker", - RepoID: 1, - Name: "default", - Number: 1, - OS: "linux", - Arch: "amd64", - OnSuccess: true, - OnFailure: false, - Status: core.StatusPending, - } - - dummyStages = []*core.Stage{ - dummyStage, - } - - dummyErrorStages = []*core.Stage{ - dummyStage, - } - - dummyUser = &core.User{ - ID: 2, - Login: "octocat", - Active: true, - } - - dummyYaml = &core.Config{ - Data: "kind: pipeline\nsteps: [ ]", - } - - dummyYamlInvalid = &core.Config{ - Data: "%ERROR", - } - - dummyYamlSkipBranch = &core.Config{ - Data: ` -kind: pipeline -trigger: - branch: - exclude: - - master`, - } - - dummyYamlSkipEvent = &core.Config{ - Data: ` -kind: pipeline -trigger: - event: - exclude: - - push`, - } - - dummyYamlSkipAction = &core.Config{ - Data: ` -kind: pipeline -trigger: - action: - exclude: - - opened`, - } - - ignoreBuildFields = cmpopts.IgnoreFields(core.Build{}, - "Created", "Updated", "Started", "Finished") - - ignoreStageFields = cmpopts.IgnoreFields(core.Stage{}, - "Created", "Updated") -) diff --git a/version/version.go b/version/version.go deleted file mode 100644 index 2e7391661e..0000000000 --- a/version/version.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019 Drone IO, Inc. -// Copyright 2016 The Linux Foundation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package version - -import "github.com/coreos/go-semver/semver" - -var ( - // GitRepository is the git repository that was compiled - GitRepository string - // GitCommit is the git commit that was compiled - GitCommit string - // VersionMajor is for an API incompatible changes. - VersionMajor int64 = 2 - // VersionMinor is for functionality in a backwards-compatible manner. - VersionMinor int64 = 20 - // VersionPatch is for backwards-compatible bug fixes. - VersionPatch int64 = 0 - // VersionPre indicates prerelease. - VersionPre = "" - // VersionDev indicates development branch. Releases will be empty string. - VersionDev string -) - -// Version is the specification version that the package types support. -var Version = semver.Version{ - Major: VersionMajor, - Minor: VersionMinor, - Patch: VersionPatch, - PreRelease: semver.PreRelease(VersionPre), - Metadata: VersionDev, -} diff --git a/version/version_test.go b/version/version_test.go deleted file mode 100644 index a69c29033e..0000000000 --- a/version/version_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019 Drone.IO Inc. All rights reserved. -// Use of this source code is governed by the Drone Non-Commercial License -// that can be found in the LICENSE file. - -//go:build !oss -// +build !oss - -package version - -import "testing" - -func TestVersion(t *testing.T) { - if got, want := Version.String(), "2.20.0"; got != want { - t.Errorf("Want version %s, got %s", want, got) - } -} diff --git a/web/README b/web/README deleted file mode 100644 index dca8bff9bd..0000000000 --- a/web/README +++ /dev/null @@ -1 +0,0 @@ -Future home of github.com/drone/drone-ui \ No newline at end of file