diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..34f5367 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @aiven/team-space-invaders diff --git a/.github/ISSUE_TEMPLATE/01_question.md b/.github/ISSUE_TEMPLATE/01_question.md index f26695a..1ad1483 100644 --- a/.github/ISSUE_TEMPLATE/01_question.md +++ b/.github/ISSUE_TEMPLATE/01_question.md @@ -3,11 +3,12 @@ name: ❓ Ask a question about: Got stuck or missing something from the docs? Ask away! --- -# What can we help you with? +# Fill the question form below and remove this heading + +## What can we help you with? -# Where would you expect to find this information? +## Where would you expect to find this information? - diff --git a/.github/ISSUE_TEMPLATE/02_bug.md b/.github/ISSUE_TEMPLATE/02_bug.md index 2f3099c..34c5987 100644 --- a/.github/ISSUE_TEMPLATE/02_bug.md +++ b/.github/ISSUE_TEMPLATE/02_bug.md @@ -3,15 +3,16 @@ name: 🐜 Report a bug about: Spotted a problem? Let us know --- -# What happened? +# Fill the bug report form below and remove this heading + +## What happened? -# What did you expect to happen? +## What did you expect to happen? -# What else do we need to know? +## What else do we need to know? - diff --git a/.github/ISSUE_TEMPLATE/03_feature.md b/.github/ISSUE_TEMPLATE/03_feature.md index cfdb2ff..519c0c0 100644 --- a/.github/ISSUE_TEMPLATE/03_feature.md +++ b/.github/ISSUE_TEMPLATE/03_feature.md @@ -3,14 +3,16 @@ name: 💡 Feature suggestion about: What would make this even better? --- -# What is currently missing? +# Fill the feature suggestion form below and remove this heading + +## What is currently missing? -# How could this be improved? +## How could this be improved? -# Is this a feature you would work on yourself? +## Is this a feature you would work on yourself? -* [ ] I plan to open a pull request for this feature +- [ ] I plan to open a pull request for this feature diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8e3cbd3..9e836cd 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,12 +1,15 @@ -# About this change - What it does + +# Fill the pull request form below and remove this heading + +## About this change - What it does + Resolves: #xxxxx -# Why this way +## Why this way - diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..342d272 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,39 @@ +name: CodeQL + +on: + pull_request: + branches: + - main + types: + - opened + - synchronize + - reopened + - labeled + - unlabeled + push: + branches: + - main + +permissions: + actions: read + contents: read + security-events: write + +jobs: + codeql: + runs-on: ubuntu-latest + if: > + (github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'skip workflows')) || + github.event_name == 'push' + strategy: + fail-fast: false + matrix: + language: + - go + steps: + - uses: actions/checkout@v3 + - uses: github/codeql-action/init@v2 + with: + languages: "${{ matrix.language }}" + - uses: github/codeql-action/autobuild@v2 + - uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..4e77c2a --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,31 @@ +name: Lint + +permissions: read-all + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + commitlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: wagoid/commitlint-github-action@v5 + trunk: + if: > + (github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'skip workflows')) || + github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: stable + - uses: trunk-io/trunk-action@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e5c96cf --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,26 @@ +name: Test + +permissions: read-all + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: {} + +jobs: + test: + if: > + (github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'skip workflows')) || + github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v4 + with: + go-version: stable + - uses: actions/checkout@v3 + - uses: arduino/setup-task@v1 + - run: task test diff --git a/.github/workflows/trunk-upgrade.yml b/.github/workflows/trunk-upgrade.yml new file mode 100644 index 0000000..15424d0 --- /dev/null +++ b/.github/workflows/trunk-upgrade.yml @@ -0,0 +1,23 @@ +name: Upgrade Trunk + +on: + schedule: + - cron: 0 8 * * 1-5 + workflow_dispatch: {} + +permissions: read-all + +jobs: + trunk_upgrade: + runs-on: ubuntu-latest + + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v5 + with: + go-version: stable + - uses: trunk-io/trunk-action/upgrade@v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d47e726 --- /dev/null +++ b/.gitignore @@ -0,0 +1,211 @@ +# Created by https://www.toptal.com/developers/gitignore/api/macos,linux,windows,go,goland+all,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,windows,go,goland+all,visualstudiocode + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### GoLand+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### GoLand+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,go,goland+all,visualstudiocode + +# Project specific +bin/ diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..3136f03 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,51 @@ +linters: + disable-all: true + enable: + - durationcheck + - errcheck + - errname + - errorlint + - forbidigo + - forcetypeassert + - funlen + - gocognit + - goconst + - gocyclo + - gofmt + - goimports + - gomnd + - gosec + - gosimple + - govet + - importas + - ineffassign + - lll + - makezero + - misspell + - nakedret + - nestif + - nilerr + - nlreturn + - prealloc + - revive + - staticcheck + - typecheck + - unconvert + - unused + - whitespace + - wsl + +linters-settings: + gocognit: + min-complexity: 25 + goconst: + min-len: 2 + min-occurrences: 2 + govet: + check-shadowing: true + lll: + line-length: 120 + tab-width: 4 + +issues: + exclude-use-default: false diff --git a/.trunk/.gitignore b/.trunk/.gitignore new file mode 100644 index 0000000..15966d0 --- /dev/null +++ b/.trunk/.gitignore @@ -0,0 +1,9 @@ +*out +*logs +*actions +*notifications +*tools +plugins +user_trunk.yaml +user.yaml +tmp diff --git a/.trunk/configs/.markdownlint.yaml b/.trunk/configs/.markdownlint.yaml new file mode 100644 index 0000000..fb94039 --- /dev/null +++ b/.trunk/configs/.markdownlint.yaml @@ -0,0 +1,10 @@ +# Autoformatter friendly markdownlint config (all formatting rules disabled) +default: true +blank_lines: false +bullet: false +html: false +indentation: false +line_length: false +spaces: false +url: false +whitespace: false diff --git a/.trunk/configs/.yamllint.yaml b/.trunk/configs/.yamllint.yaml new file mode 100644 index 0000000..4d44466 --- /dev/null +++ b/.trunk/configs/.yamllint.yaml @@ -0,0 +1,10 @@ +rules: + quoted-strings: + required: only-when-needed + extra-allowed: ["{|}"] + empty-values: + forbid-in-block-mappings: true + forbid-in-flow-mappings: true + key-duplicates: {} + octal-values: + forbid-implicit-octal: true diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml new file mode 100644 index 0000000..66890af --- /dev/null +++ b/.trunk/trunk.yaml @@ -0,0 +1,34 @@ +version: 0.1 +cli: + version: 1.19.0 +plugins: + sources: + - id: trunk + ref: v1.4.2 + uri: https://github.com/trunk-io/plugins +lint: + enabled: + - checkov@3.2.2 + - osv-scanner@1.6.2 + - trivy@0.48.3 + - trufflehog@3.66.2 + - actionlint@1.6.26 + - git-diff-check + - gitleaks@8.18.1 + - gofmt@1.20.4 + - golangci-lint@1.55.2 + - markdownlint@0.39.0 + - prettier@3.2.4 + - yamllint@1.33.0 +runtimes: + enabled: + - go@1.21.0 + - node@18.12.1 + - python@3.10.8 +actions: + disabled: + - trunk-announce + - trunk-check-pre-push + - trunk-fmt-pre-commit + enabled: + - trunk-upgrade-available diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index c08a552..a1f7299 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 379daf6..179cf44 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,33 +1,30 @@ -# Welcome! +# Contributing -Contributions are very welcome on {{ PROJECT }}. When contributing please keep this in mind: +## Welcome + +Contributions are very welcome on go-utils. When contributing please keep this in mind: - Open an issue to discuss new bigger features. - Write code consistent with the project style and make sure the tests are passing. - Stay in touch with us if we have follow up questions or requests for further changes. -# Development - -## Local Environment - - -## Tests - +## Development -## Static checking and Linting +Please refer to the `README.md` file of concrete utility for development and testing instructions. +### Static checking and Linting -## Manual testing +We use [Trunk.io](https://trunk.io/) for static checking and linting. Install it locally and you'll be good to go. +## Opening a PR -### Configuration - - -# Opening a PR - -- Commit messages should describe the changes, not the filenames. Win our admiration by following - the [excellent advice from Chris Beams](https://chris.beams.io/posts/git-commit/) when composing - commit messages. +- Commit messages should describe the changes, not the filenames. Win our admiration by following the + [excellent advice from Chris Beams](https://chris.beams.io/posts/git-commit/) when composing commit messages. - Choose a meaningful title for your pull request. - The pull request description should focus on what changed and why. - Check that the tests pass (and add test coverage for your changes if appropriate). + +### Commit Messages + +This project adheres to the [Conventional Commits](https://conventionalcommits.org/en/v1.0.0/) specification. +Please, make sure that your commit messages follow that specification. diff --git a/README.md b/README.md index 0c847bd..b0a234d 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,32 @@ -{{PROJECT_NAME}} -====================== -This is a template repository for creating open source repositories at Aiven. +# go-utils -Overview -======== +go-utils is a collection of utilities designed to assist in various tasks, each utility residing in its own dedicated +directory. These utilities are versatile tools developed to aid in a wide range of operations, making tasks more +efficient and streamlined. -Features -============ +Each utility within the repository is made to serve a specific purpose, ensuring a modular approach to problem-solving. -Setup -============ +## Utilities Overview -License -============ -{{PROJECT_NAME}} is licensed under the Apache license, version 2.0. Full license text is available in the [LICENSE](LICENSE) file. +- **selproj**: A tool to select an empty project's suffix from a list of projects using a prefix. + +## Setup + +To get started with `go-utils`, clone the repository and explore the individual utilities within their respective +directories. Each utility may have its own set of instructions or requirements, so be sure to review the `README.md` +file in each directory for specific setup and usage guidelines. + +See [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on how to contribute to the development of `go-utils`. + +## License + +go-utils is licensed under the Apache license, version 2.0. Full license text is available in the +[LICENSE](LICENSE) file. Please note that the project explicitly does not require a CLA (Contributor License Agreement) from its contributors. -Contact -============ -Bug reports and patches are very welcome, please post them as GitHub issues and pull requests at https://github.com/aiven/{{PROJECT_NAME}} . -To report any possible vulnerabilities or other serious issues please see our [security](SECURITY.md) policy. +## Contact + +Bug reports and patches are very welcome, please post them as GitHub issues and pull requests at +https://github.com/aiven/go-utils. To report any possible vulnerabilities or other serious issues please see our +[security](SECURITY.md) policy. diff --git a/SECURITY.md b/SECURITY.md index de5e4e5..4aae85e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,32 +2,31 @@ ## Supported Versions -We release patches for security vulnerabilities. Which versions are eligible -to receive such patches depend on the CVSS v3.0 Rating: +We release patches for security vulnerabilities. Which versions are eligible to receive such patches depend on the +CVSS v3.0 Rating: -| CVSS v3.0 | Supported Versions | -| --------- | ----------------------------------------- | -| 4.0-10.0 | Most recent release | +| CVSS v3.0 | Supported Versions | +| --------- | ------------------- | +| 4.0-10.0 | Most recent release | ## Reporting a Vulnerability -Please report (suspected) security vulnerabilities to our **[bug bounty -program](https://hackerone.com/aiven_ltd)**. You will receive a response from -us within 2 working days. If the issue is confirmed, we will release a patch as -soon as possible depending on impact and complexity. +Please report (suspected) security vulnerabilities to our **[bug bounty program](https://hackerone.com/aiven_ltd)**. +You will receive a response from us within 2 working days. If the issue is confirmed, we will release a patch as soon +as possible depending on impact and complexity. ## Qualifying Vulnerabilities -Any reproducible vulnerability that has a severe effect on the security or -privacy of our users is likely to be in scope for the program. +Any reproducible vulnerability that has a severe effect on the security or privacy of our users is likely to be in +scope for the program. We generally **aren't** interested in the following issues: -* Social engineering (e.g. phishing, vishing, smishing) attacks -* Brute force, DoS, text injection -* Missing best practices such as HTTP security headers (CSP, X-XSS, etc.), - email (SPF/DKIM/DMARC records), SSL/TLS configuration. -* Software version disclosure / Banner identification issues / Descriptive - error messages or headers (e.g. stack traces, application or server errors). -* Clickjacking on pages with no sensitive actions -* Theoretical vulnerabilities where you can't demonstrate a significant - security impact with a proof of concept. + +- Social engineering (e.g. phishing, vishing, smishing) attacks +- Brute force, DoS, text injection +- Missing best practices such as HTTP security headers (CSP, X-XSS, etc.), email (SPF/DKIM/DMARC records), SSL/TLS + configuration. +- Software version disclosure / Banner identification issues / Descriptive error messages or headers (e.g. stack + traces, application or server errors). +- Clickjacking on pages with no sensitive actions +- Theoretical vulnerabilities where you can't demonstrate a significant security impact with a proof of concept. diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..9bc81d3 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,21 @@ +version: 3 + +vars: + OUT_DIR: ../bin + SELPROJ_BIN: selproj + +tasks: + build: + cmds: + - task: build-selproj + build-selproj: + dir: selproj + cmds: + - go build -o {{ .OUT_DIR }}/{{ .SELPROJ_BIN }} + test: + cmds: + - task: test-selproj + test-selproj: + dir: selproj + cmds: + - go test -v ./... diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..2ef25a1 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,4 @@ +module.exports = { + extends: ["@commitlint/config-conventional"], + ignores: [(msg) => /Signed-off-by: dependabot\[bot]/m.test(msg)], +}; diff --git a/selproj/README.md b/selproj/README.md new file mode 100644 index 0000000..fcccf08 --- /dev/null +++ b/selproj/README.md @@ -0,0 +1,31 @@ +# selproj + +A tool to select an empty project's suffix from a list of projects using a prefix. + +## Usage + +```bash +go run main.go +``` + +The tool requires the following environment variables to be set: + +- `AIVEN_TOKEN` - Aiven authentication token +- `AIVEN_PROJECT_NAME_PREFIX` - A prefix to filter projects by + +## Example + +```text +$ AIVEN_TOKEN=... AIVEN_PROJECT_NAME_PREFIX=test go run -tags tools . +test-project-2 +``` + +## Development + +There are no dependencies besides Go itself. + +## Testing + +```bash +go test -v -tags tools ./... +``` diff --git a/selproj/client.go b/selproj/client.go new file mode 100644 index 0000000..fa37c57 --- /dev/null +++ b/selproj/client.go @@ -0,0 +1,41 @@ +// Package main is the main package of the selproj tool. +package main + +import ( + "context" + + "github.com/aiven/aiven-go-client/v2" +) + +// AivenClient defines the interface for Aiven client operations. +// It is implemented by aivenClientAdapter. +type AivenClient interface { + ProjectList(ctx context.Context) ([]*aiven.Project, error) + ServiceList(ctx context.Context, projectName string) ([]*aiven.Service, error) + VPCList(ctx context.Context, projectName string) ([]*aiven.VPC, error) +} + +var _ AivenClient = (*aivenClientAdapter)(nil) + +// NewAivenClientAdapter creates a new adapter for the given aiven.Client. +// This is needed because the aiven.Client does not implement an interface that is mockable. +func NewAivenClientAdapter(client *aiven.Client) AivenClient { + return &aivenClientAdapter{client: client} +} + +// aivenClientAdapter adapts aiven.Client to AivenClient. +type aivenClientAdapter struct { + client *aiven.Client +} + +func (a *aivenClientAdapter) ProjectList(ctx context.Context) ([]*aiven.Project, error) { + return a.client.Projects.List(ctx) +} + +func (a *aivenClientAdapter) ServiceList(ctx context.Context, projectName string) ([]*aiven.Service, error) { + return a.client.Services.List(ctx, projectName) +} + +func (a *aivenClientAdapter) VPCList(ctx context.Context, projectName string) ([]*aiven.VPC, error) { + return a.client.VPCs.List(ctx, projectName) +} diff --git a/selproj/go.mod b/selproj/go.mod new file mode 100644 index 0000000..c4395d4 --- /dev/null +++ b/selproj/go.mod @@ -0,0 +1,17 @@ +module github.com/aiven/terraform-provider-aiven/tools/selproj + +go 1.21 + +require ( + github.com/aiven/aiven-go-client/v2 v2.12.0 + github.com/stretchr/testify v1.8.4 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.5 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/selproj/go.sum b/selproj/go.sum new file mode 100644 index 0000000..e32c92e --- /dev/null +++ b/selproj/go.sum @@ -0,0 +1,47 @@ +github.com/aiven/aiven-go-client/v2 v2.12.0 h1:VxOfn61AtfLjy2B+DdXF2/7OHyyFZ1aQaIlE3TOyIsE= +github.com/aiven/aiven-go-client/v2 v2.12.0/go.mod h1:x0xhzxWEKAwKv0xY5FvECiI6tesWshcPHvjwl0B/1SU= +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/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= +github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/selproj/main.go b/selproj/main.go new file mode 100644 index 0000000..21fd712 --- /dev/null +++ b/selproj/main.go @@ -0,0 +1,38 @@ +// Package main is the main package of the selproj tool. +package main + +import ( + "context" + "fmt" + "os" + + "github.com/aiven/aiven-go-client/v2" +) + +// main is the entry point of the program. +func main() { + token, ok := os.LookupEnv("AIVEN_TOKEN") + if !ok { + panic("AIVEN_TOKEN env var not set") + } + + prefix, ok := os.LookupEnv("AIVEN_PROJECT_NAME_PREFIX") + if !ok { + panic("AIVEN_PROJECT_NAME_PREFIX env var not set") + } + + client, err := aiven.NewTokenClient(token, "aiven/selproj") + if err != nil { + panic(err) + } + + ctx := context.Background() + + selectedProject, err := selectProject(ctx, NewAivenClientAdapter(client), prefix) + if err != nil { + panic(err) + } + + //nolint:forbidigo // This is needed for the tool to work. + fmt.Println(selectedProject) +} diff --git a/selproj/mock_client.go b/selproj/mock_client.go new file mode 100644 index 0000000..51d57ad --- /dev/null +++ b/selproj/mock_client.go @@ -0,0 +1,33 @@ +// Package main is the main package of the selproj tool. +package main + +import ( + "context" + + "github.com/aiven/aiven-go-client/v2" + "github.com/stretchr/testify/mock" +) + +var _ AivenClient = (*mockAivenClient)(nil) + +type mockAivenClient struct { + mock.Mock +} + +func (m *mockAivenClient) ProjectList(ctx context.Context) ([]*aiven.Project, error) { + args := m.Called(ctx) + + return args.Get(0).([]*aiven.Project), args.Error(1) //nolint:forcetypeassert // This is a mock. +} + +func (m *mockAivenClient) ServiceList(ctx context.Context, projectName string) ([]*aiven.Service, error) { + args := m.Called(ctx, projectName) + + return args.Get(0).([]*aiven.Service), args.Error(1) //nolint:forcetypeassert // This is a mock. +} + +func (m *mockAivenClient) VPCList(ctx context.Context, projectName string) ([]*aiven.VPC, error) { + args := m.Called(ctx, projectName) + + return args.Get(0).([]*aiven.VPC), args.Error(1) //nolint:forcetypeassert // This is a mock. +} diff --git a/selproj/selectproject.go b/selproj/selectproject.go new file mode 100644 index 0000000..8f0d6da --- /dev/null +++ b/selproj/selectproject.go @@ -0,0 +1,47 @@ +// Package main is the main package of the selproj tool. +package main + +import ( + "context" + "errors" + "strings" +) + +// errNoSuitableProjectFound is the error returned when no suitable project is found. +var errNoSuitableProjectFound = errors.New("no suitable project found") + +// selectProject selects a project with the given prefix. +func selectProject(ctx context.Context, client AivenClient, prefix string) (string, error) { + projects, err := client.ProjectList(ctx) + if err != nil { + return "", err + } + + for _, project := range projects { + if !strings.HasPrefix(project.Name, prefix) { + continue + } + + services, err := client.ServiceList(ctx, project.Name) + if err != nil { + return "", err + } + + if len(services) != 0 { + continue + } + + vpcs, err := client.VPCList(ctx, project.Name) + if err != nil { + return "", err + } + + if len(vpcs) != 0 { + continue + } + + return strings.TrimPrefix(project.Name, prefix), nil + } + + return "", errNoSuitableProjectFound +} diff --git a/selproj/selectproject_test.go b/selproj/selectproject_test.go new file mode 100644 index 0000000..0ad2dcd --- /dev/null +++ b/selproj/selectproject_test.go @@ -0,0 +1,70 @@ +// Package main is the main package of the selproj tool. +package main + +import ( + "context" + "testing" + + "github.com/aiven/aiven-go-client/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +// testProjectNamePrefix is the prefix used for testing. +const testProjectNamePrefix = "test" + +// testServicesList is a list of services used for testing. +var testServicesList = []*aiven.Service{ + {Name: "test-service"}, +} + +// emptyServicesList is an empty list of services used for testing. +var emptyServicesList []*aiven.Service + +// setup sets up the test environment. +func setup() (context.Context, *mockAivenClient) { + ctx := context.Background() + + client := new(mockAivenClient) + client.On("VPCList", ctx, mock.Anything).Return([]*aiven.VPC{}, nil) + + return ctx, client +} + +// TestSelectProject_Basic tests selectProject. +func TestSelectProject_Basic(t *testing.T) { + ctx, client := setup() + + client.On("ProjectList", ctx).Return([]*aiven.Project{ + {Name: "test-project-1"}, + {Name: "other-project"}, + {Name: "test-project-2"}, + }, nil) + + client.On("ServiceList", ctx, "test-project-1").Return(testServicesList, nil) + client.On("ServiceList", ctx, "test-project-2").Return(emptyServicesList, nil) + + projectName, err := selectProject(ctx, client, testProjectNamePrefix) + + assert.NoError(t, err, "selectProject should not return an error") + assert.Equal( + t, "-project-2", projectName, "selectProject should return the correct project name", + ) +} + +// TestSelectProject_WrongPrefix tests selectProject with a wrong prefix. +func TestSelectProject_WrongPrefix(t *testing.T) { + ctx, client := setup() + + client.On("ProjectList", ctx).Return([]*aiven.Project{ + {Name: "mismatched-project-1"}, + {Name: "another-mismatched-project"}, + {Name: "mismatched-project-2"}, + }, nil) + + projectName, err := selectProject(ctx, client, testProjectNamePrefix) + + assert.Error(t, err, "selectProject should return an error for no suitable project found") + assert.Empty(t, projectName, "selectProject should not return any project name") + assert.Equal(t, errNoSuitableProjectFound, err, "Error message should match expected") +}