Compare commits

...

41 commits

Author SHA1 Message Date
635a9abf20
fix: disable shell color in runnerstats (#80)
Signed-off-by: marcel.henselin <marcel.henselin@stackit.cloud>

## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #80
Co-authored-by: marcel.henselin <marcel.henselin@stackit.cloud>
Co-committed-by: marcel.henselin <marcel.henselin@stackit.cloud>
2026-02-27 10:25:10 +00:00
07458c5677
feat: add runner stats (#79)
Signed-off-by: marcel.henselin <marcel.henselin@stackit.cloud>

## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #79
Co-authored-by: marcel.henselin <marcel.henselin@stackit.cloud>
Co-committed-by: marcel.henselin <marcel.henselin@stackit.cloud>
2026-02-27 10:20:02 +00:00
eb13630d2f
feat: test STACKIT runner (#78)
Signed-off-by: marcel.henselin <marcel.henselin@stackit.cloud>

## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #78
Co-authored-by: marcel.henselin <marcel.henselin@stackit.cloud>
Co-committed-by: marcel.henselin <marcel.henselin@stackit.cloud>
2026-02-27 10:08:09 +00:00
4a2819787d
fix: linting (#77)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #77
Co-authored-by: Andre Harms <andre.harms@stackit.cloud>
Co-committed-by: Andre Harms <andre.harms@stackit.cloud>
2026-02-19 08:54:34 +00:00
36eccc52c3
fix: null_ident (#76)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 9s
Publish / Publish provider (push) Successful in 34m58s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #76
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-17 17:18:40 +00:00
841e702b95
fix: encryption_fix (#75)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 12s
Publish / Publish provider (push) Successful in 33m28s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #75
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-17 09:42:46 +00:00
aba831cbdd
fix: some_fixes (#74)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 8s
Publish / Publish provider (push) Successful in 14m6s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #74
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-16 19:49:02 +00:00
89a24ce780
fix: try fix errors (#73)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 9s
Publish / Publish provider (push) Successful in 43m1s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #73
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-16 19:09:42 +00:00
f05e90c35a
fix: some more fix tests (#72)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 5s
Publish / Publish provider (push) Successful in 12m42s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #72
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-16 16:12:53 +00:00
7ee82366d7
fix: try fix errors (#71)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 11s
Publish / Publish provider (push) Successful in 35m24s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #71
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-16 13:40:05 +00:00
d5644ec27f
chore: #64 add system hardening with retry logic for client (#68)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 5s
Publish / Publish provider (push) Successful in 12m49s
- implement RetryRoundTripper

Refs: #64

Reviewed-on: #68
Reviewed-by: Marcel_Henselin <marcel.henselin@stackit.cloud>
Co-authored-by: Andre Harms <andre.harms@stackit.cloud>
Co-committed-by: Andre Harms <andre.harms@stackit.cloud>
2026-02-16 09:35:21 +00:00
20e9b3ca4c
fix: #66 non generic api error handling (#67)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #67
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-16 09:20:36 +00:00
43223f5d1f
fix: #63 sort user roles to prevent state change (#65)
fix: include recent api changes
Reviewed-on: #65
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-16 09:04:16 +00:00
452f73877f
fix: fix pgsql db state (#62)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 5s
Publish / Publish provider (push) Successful in 12m26s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #62
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-13 17:06:57 +00:00
55a0917a86
fix: fix sqlserverflexalpha (#61)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 5s
Publish / Publish provider (push) Successful in 12m55s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #61
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-13 16:23:42 +00:00
d90236b02e
chore: refactorings (#60)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 4s
Publish / Publish provider (push) Successful in 12m30s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #60
2026-02-13 15:49:32 +00:00
b1f8c8a4d9
fix: fix wrong order of params (#59)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 10s
Publish / Publish provider (push) Successful in 34m9s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #59
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-13 14:55:36 +00:00
e01ae1a920
fix: fix lintings (#58)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 5s
Publish / Publish provider (push) Successful in 12m24s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #58
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-13 14:27:14 +00:00
843fc46f54
fix: tests (#57)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #57
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-13 10:38:19 +00:00
10af1dbbba
fix: postgres_fixes (#54)
Some checks failed
CI Workflow / Code coverage report (pull_request) Has been skipped
Publish / Check GoReleaser config (push) Successful in 9s
Publish / Publish provider (push) Successful in 30m38s
CI Workflow / Check GoReleaser config (pull_request) Successful in 6s
CI Workflow / CI (pull_request) Failing after 22m26s
CI Workflow / Test readiness for publishing provider (pull_request) Successful in 37m30s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #54
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-13 08:15:21 +00:00
459120d3b3
fix: sqlserver return values mapping (#53)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 11s
Publish / Publish provider (push) Successful in 30m7s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #53
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-12 15:14:53 +00:00
82c654f3ba
fix: publisher - create versions file correctly (#52)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 16s
Publish / Publish provider (push) Successful in 43m11s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #52
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-12 12:03:14 +00:00
0c9ecfc670
fix: sqlserver beta fixes (#51)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 11s
Publish / Publish provider (push) Successful in 48m24s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #51
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-12 11:42:37 +00:00
131e1700bb
fix: change identity handling for user & database (#50)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #50
Reviewed-by: Marcel_Henselin <marcel.henselin@stackit.cloud>
Co-authored-by: Andre Harms <andre.harms@stackit.cloud>
Co-committed-by: Andre Harms <andre.harms@stackit.cloud>
2026-02-11 15:39:20 +00:00
86fc98461c
chore: add sqlserveralpha tests (#49)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #49
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-11 15:21:57 +00:00
ed7ff0f58e
chore: add tests (#48)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #48
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-11 14:20:41 +00:00
f2bffa9ece
chore: add_protocol (#47)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #47
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-11 09:07:38 +00:00
399e8ccb0c
feat: update sql server flex configuration for user and database (#46)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #46
Reviewed-by: Marcel_Henselin <marcel.henselin@stackit.cloud>
Co-authored-by: Andre Harms <andre.harms@stackit.cloud>
Co-committed-by: Andre Harms <andre.harms@stackit.cloud>
2026-02-11 09:03:31 +00:00
e21fe64326
feat: add_testing (#45)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 13s
Publish / Publish provider (push) Successful in 23m29s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #45
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-10 16:46:21 +00:00
4991897eca
fix: fix mapping tests (#44)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #44
Reviewed-by: Marcel_Henselin <marcel.henselin@stackit.cloud>
Co-authored-by: Andre Harms <andre.harms@stackit.cloud>
Co-committed-by: Andre Harms <andre.harms@stackit.cloud>
2026-02-10 16:06:10 +00:00
b737875c68
fix: fix README.md (#42)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #42
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
2026-02-10 13:32:15 +00:00
9dbf36dd35
chore: activate darwin and windows builds (#43)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #43
Co-authored-by: marcel.henselin <marcel.henselin@stackit.cloud>
Co-committed-by: marcel.henselin <marcel.henselin@stackit.cloud>
2026-02-10 13:31:42 +00:00
00a43dfb4c
fix: fix wrong identity handling in Read (#41)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 12s
Publish / Publish provider (push) Successful in 7m54s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #41
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-10 12:25:19 +00:00
b63526b065
chore: regenerate gen files (#40)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 13s
Publish / Publish provider (push) Successful in 19m5s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #40
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-10 09:56:54 +00:00
68e4c137f1
fix: fix build pipeline error (#38)
Some checks failed
Publish / Check GoReleaser config (push) Successful in 5s
Publish / Publish provider (push) Failing after 4m1s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #38
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-10 09:32:06 +00:00
6e23dab949
feat: create docs upload pipeline (#37)
Some checks failed
Publish / Check GoReleaser config (push) Successful in 14s
Publish / Publish provider (push) Failing after 15m24s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #37
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
2026-02-10 08:21:01 +00:00
de019908d2
chore: changed and refactored providers (#36)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Reviewed-on: #36
Reviewed-by: Marcel_Henselin <marcel.henselin@stackit.cloud>
Co-authored-by: Andre Harms <andre.harms@stackit.cloud>
Co-committed-by: Andre Harms <andre.harms@stackit.cloud>
2026-02-10 08:10:02 +00:00
b1b359f436
feat: refactor testing (#35)
Some checks failed
Publish / Check GoReleaser config (push) Successful in 5s
Publish / Publish provider (push) Failing after 3m54s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #35
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-09 07:28:01 +00:00
32e41d8b44
feat: testing (#34)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #34
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-06 09:57:51 +00:00
c22e758b2c
fix: sqlserver_beta (#33)
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #33
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-05 15:11:41 +00:00
581e45eb9c
feat: SQL server beta and templates refactoring (#32)
Some checks failed
Publish / Check GoReleaser config (push) Successful in 5s
Publish / Publish provider (push) Failing after 4m6s
## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #32
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
2026-02-05 09:55:32 +00:00
229 changed files with 31308 additions and 8110 deletions

1
.github/actions/acc_test/README.md vendored Normal file
View file

@ -0,0 +1 @@
# acceptance test action

114
.github/actions/acc_test/action.yaml vendored Normal file
View file

@ -0,0 +1,114 @@
name: Acceptance Testing
description: "Acceptance Testing pipeline"
inputs:
go-version:
description: "go version to install"
default: '1.25'
required: true
project_id:
description: "STACKIT project ID for tests"
required: true
region:
description: "STACKIT region for tests"
default: 'eu01'
required: true
service_account_json:
description: "STACKIT service account JSON file contents"
required: true
test_file:
description: "testfile to run"
default: ''
outputs:
random-number:
description: "Random number"
value: ${{ steps.random-number-generator.outputs.random-number }}
runs:
using: "composite"
steps:
- name: Random Number Generator
id: random-number-generator
run: echo "random-number=$(echo $RANDOM)" >> $GITHUB_OUTPUT
shell: bash
- name: Install needed tools
shell: bash
run: |
set -e
apt-get -y -qq update
apt-get -y -qq install jq python3 python3-pip python-is-python3 s3cmd git make wget
- name: Setup JAVA
uses: actions/setup-java@v5
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Install Go ${{ inputs.go-version }}
uses: actions/setup-go@v6
with:
go-version: ${{ inputs.go-version }}
check-latest: true
go-version-file: 'go.mod'
- name: Install go tools
shell: bash
run: |
set -e
go mod download
go install golang.org/x/tools/cmd/goimports@latest
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.7.2
go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@v0.24.0
- name: Prepare pkg_gen directory
shell: bash
run: |
go run cmd/main.go build -p
- name: Run acceptance test file
if: ${{ inputs.test_file != '' }}
shell: bash
run: |
echo "Running acceptance tests for the terraform provider"
echo "${STACKIT_SERVICE_ACCOUNT_JSON}" > ~/.service_account.json
cd stackit
TF_ACC=1 \
TF_ACC_PROJECT_ID=${TF_ACC_PROJECT_ID} \
TF_ACC_REGION=${TF_ACC_REGION} \
go test ${{ inputs.test_file }} -count=1 -timeout=30m
env:
STACKIT_SERVICE_ACCOUNT_JSON: ${{ inputs.service_account_json }}
TF_PROJECT_ID: ${{ inputs.project_id }}
TF_ACC_REGION: ${{ inputs.region }}
# TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL: ${{ secrets.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL }}
# TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN: ${{ secrets.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN }}
# TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID: ${{ secrets.TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID }}
# TF_ACC_TEST_PROJECT_PARENT_UUID: ${{ secrets.TF_ACC_TEST_PROJECT_PARENT_UUID }}
# TF_ACC_TEST_PROJECT_USER_EMAIL: ${{ secrets.TF_ACC_TEST_PROJECT_USER_EMAIL }}
- name: Run acceptance tests
if: ${{ inputs.test_file == '' }}
shell: bash
run: |
echo "Running acceptance tests for the terraform provider"
echo "${STACKIT_SERVICE_ACCOUNT_JSON}" > ~/.service_account.json
cd stackit
TF_ACC=1 \
TF_ACC_PROJECT_ID=${TF_ACC_PROJECT_ID} \
TF_ACC_REGION=${TF_ACC_REGION} \
go test ./... -count=1 -timeout=30m
env:
STACKIT_SERVICE_ACCOUNT_JSON: ${{ inputs.service_account_json }}
TF_PROJECT_ID: ${{ inputs.project_id }}
TF_ACC_REGION: ${{ inputs.region }}
# TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL: ${{ secrets.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL }}
# TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN: ${{ secrets.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN }}
# TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID: ${{ secrets.TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID }}
# TF_ACC_TEST_PROJECT_PARENT_UUID: ${{ secrets.TF_ACC_TEST_PROJECT_PARENT_UUID }}
# TF_ACC_TEST_PROJECT_USER_EMAIL: ${{ secrets.TF_ACC_TEST_PROJECT_USER_EMAIL }}

View file

@ -1,4 +1,3 @@
name: Build
description: "Build pipeline"
inputs:

View file

@ -0,0 +1,71 @@
name: 'Setup Go and cache dependencies'
author: 'Forgejo authors, Marcel S. Henselin'
description: |
Wrap the setup-go with improved dependency caching.
inputs:
username:
description: 'User for which to manage the dependency cache'
default: root
go-version:
description: "go version to install"
default: '1.25'
required: true
runs:
using: "composite"
steps:
- name: "Install zstd for faster caching"
shell: bash
run: |
apt-get update -qq
apt-get -q install -qq -y zstd
- name: "Set up Go using setup-go"
uses: https://code.forgejo.org/actions/setup-go@v6
id: go-version
with:
go-version: ${{ inputs.go-version }}
check-latest: true # Always check for the latest patch release
# go-version-file: "go.mod"
# do not cache dependencies, we do this manually
cache: false
- name: "Get go environment information"
shell: bash
id: go-environment
run: |
chmod 755 $HOME # ensure ${RUN_AS_USER} has permission when go is located in $HOME
export GOROOT="$(go env GOROOT)"
echo "modcache=$(su ${RUN_AS_USER} -c '${GOROOT}/bin/go env GOMODCACHE')" >> "$GITHUB_OUTPUT"
echo "cache=$(su ${RUN_AS_USER} -c '${GOROOT}/bin/go env GOCACHE')" >> "$GITHUB_OUTPUT"
env:
RUN_AS_USER: ${{ inputs.username }}
GO_VERSION: ${{ steps.go-version.outputs.go-version }}
- name: "Create cache folders with correct permissions (for non-root users)"
shell: bash
if: inputs.username != 'root'
# when the cache is restored, only the permissions of the last part are restored
# so assuming that /home/user exists and we are restoring /home/user/go/pkg/mod,
# both folders will have the correct permissions, but
# /home/user/go and /home/user/go/pkg might be owned by root
run: |
su ${RUN_AS_USER} -c 'mkdir -p "${MODCACHE_DIR}" "${CACHE_DIR}"'
env:
RUN_AS_USER: ${{ inputs.username }}
MODCACHE_DIR: ${{ steps.go-environment.outputs.modcache }}
CACHE_DIR: ${{ steps.go-environment.outputs.cache }}
- name: "Restore Go dependencies from cache or mark for later caching"
id: cache-deps
uses: https://code.forgejo.org/actions/cache@v5
with:
key: setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-${{ hashFiles('go.sum', 'go.mod') }}
restore-keys: |
setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-
setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-
path: |
${{ steps.go-environment.outputs.modcache }}
${{ steps.go-environment.outputs.cache }}

View file

@ -6,6 +6,11 @@ on:
- alpha
- main
workflow_dispatch:
schedule:
# every sunday at 00:00
# - cron: '0 0 * * 0'
# every day at 00:00
- cron: '0 0 * * *'
push:
branches:
- '!main'
@ -17,6 +22,39 @@ env:
CODE_COVERAGE_ARTIFACT_NAME: "code-coverage"
jobs:
runner_test:
name: "Test STACKIT runner"
runs-on: stackit-docker
steps:
- name: Install needed tools
run: |
apt-get -y -qq update
apt-get -y -qq install jq python3 python3-pip python-is-python3 s3cmd git make wget
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Install go tools
run: |
go install golang.org/x/tools/cmd/goimports@latest
go install github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework@latest
go install github.com/hashicorp/terraform-plugin-codegen-openapi/cmd/tfplugingen-openapi@latest
- name: Setup JAVA
uses: actions/setup-java@v5
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Checkout
uses: actions/checkout@v6
- name: Run build pkg directory
run: |
go run cmd/main.go build
publish_test:
name: "Test readiness for publishing provider"
needs: config
@ -99,14 +137,72 @@ jobs:
--gpgPubKeyFile=public_key.pem \
--version=${VERSION}
testing:
name: CI run tests
runs-on: ubuntu-latest
needs: config
env:
TF_ACC_PROJECT_ID: ${{ vars.TF_ACC_PROJECT_ID }}
TF_ACC_REGION: ${{ vars.TF_ACC_REGION }}
TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL: ${{ vars.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL }}
TF_ACC_SERVICE_ACCOUNT_FILE: "~/service_account.json"
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Build
uses: ./.github/actions/build
with:
go-version: ${{ env.GO_VERSION }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_wrapper: false
- name: Create service account json file
if: ${{ github.event_name == 'pull_request' }}
run: |
echo "${{ secrets.TF_ACC_SERVICE_ACCOUNT_JSON }}" >~/service_account.json
- name: Run go mod tidy
if: ${{ github.event_name == 'pull_request' }}
run: go mod tidy
- name: Testing
run: make test
- name: Acceptance Testing
env:
TF_ACC: "1"
if: ${{ github.event_name == 'pull_request' }}
run: make test-acceptance-tf
- name: Check coverage threshold
shell: bash
run: |
make coverage
COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
echo "Coverage: $COVERAGE%"
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Coverage is below 80%"
# exit 1
fi
- name: Archive code coverage results
uses: actions/upload-artifact@v4
with:
name: ${{ env.CODE_COVERAGE_ARTIFACT_NAME }}
path: "stackit/${{ env.CODE_COVERAGE_FILE_NAME }}"
main:
name: CI
if: ${{ github.event_name != 'schedule' }}
name: CI run build and linting
runs-on: ubuntu-latest
needs: config
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build
uses: ./.github/actions/build
@ -130,27 +226,45 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: v2.7
version: v2.9
args: --config=golang-ci.yaml --allow-parallel-runners --timeout=5m
continue-on-error: true
- name: Lint
- name: Linting
run: make lint
continue-on-error: true
- name: Test
run: make test
# - name: Testing
# run: make test
#
# - name: Acceptance Testing
# if: ${{ github.event_name == 'pull_request' }}
# run: make test-acceptance-tf
#
# - name: Check coverage threshold
# shell: bash
# run: |
# make coverage
# COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
# echo "Coverage: $COVERAGE%"
# if (( $(echo "$COVERAGE < 80" | bc -l) )); then
# echo "Coverage is below 80%"
# # exit 1
# fi
- name: Archive code coverage results
uses: actions/upload-artifact@v4
with:
name: ${{ env.CODE_COVERAGE_ARTIFACT_NAME }}
path: "stackit/${{ env.CODE_COVERAGE_FILE_NAME }}"
# - name: Archive code coverage results
# uses: actions/upload-artifact@v4
# with:
# name: ${{ env.CODE_COVERAGE_ARTIFACT_NAME }}
# path: "stackit/${{ env.CODE_COVERAGE_FILE_NAME }}"
config:
if: ${{ github.event_name != 'schedule' }}
name: Check GoReleaser config
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Check GoReleaser
uses: goreleaser/goreleaser-action@v6

View file

@ -130,3 +130,16 @@ jobs:
cd release/
s3cmd put --recursive v1 s3://terraform-provider-privatepreview/
s3cmd put --recursive .well-known s3://terraform-provider-privatepreview/
- name: Import SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DOCS_UPLOAD_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 0600 ~/.ssh/id_ed25519
- name: Upload docs via scp
run: |
set -e
ssh -o StrictHostKeyChecking=no ubuntu@${{ vars.DOCS_SERVER_IP }} 'rm -rf /srv/www/docs'
echo "${{ github.ref_name }}" >docs/_version.txt
scp -o StrictHostKeyChecking=no -r docs ubuntu@${{ vars.DOCS_SERVER_IP }}:/srv/www/

View file

@ -18,7 +18,7 @@ jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
# Allow goreleaser to access older tag information.
fetch-depth: 0

View file

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Self-hosted Renovate
uses: renovatebot/github-action@v41.0.0
with:

29
.github/workflows/runnerstats.yaml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Runner stats
on:
workflow_dispatch:
jobs:
stats-own:
name: "Get own runner stats"
runs-on: ubuntu-latest
steps:
- name: Install needed tools
run: |
apt-get -y -qq update
apt-get -y -qq install inxi
- name: Show stats
run: inxi -c 0
stats-stackit:
name: "Get STACKIT runner stats"
runs-on: stackit-docker
steps:
- name: Install needed tools
run: |
apt-get -y -qq update
apt-get -y -qq install inxi
- name: Show stats
run: inxi -c 0

View file

@ -7,21 +7,17 @@ on:
workflow_dispatch:
jobs:
main:
acc_test:
name: Acceptance Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install project tools and dependencies
run: make project-tools
- name: Run tests
run: |
make test-acceptance-tf TF_ACC_PROJECT_ID=$${{ secrets.TF_ACC_PROJECT_ID }} TF_ACC_ORGANIZATION_ID=$${{ secrets.TF_ACC_ORGANIZATION_ID }} TF_ACC_REGION="eu01"
env:
STACKIT_SERVICE_ACCOUNT_TOKEN: ${{ secrets.TF_ACC_SERVICE_ACCOUNT_TOKEN }}
TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL: ${{ secrets.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL }}
TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN: ${{ secrets.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN }}
TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID: ${{ secrets.TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID }}
TF_ACC_TEST_PROJECT_PARENT_UUID: ${{ secrets.TF_ACC_TEST_PROJECT_PARENT_UUID }}
TF_ACC_TEST_PROJECT_USER_EMAIL: ${{ secrets.TF_ACC_TEST_PROJECT_USER_EMAIL }}
uses: actions/checkout@v6
- name: Run Test
uses: ./.github/actions/acc_test
with:
go-version: ${{ env.GO_VERSION }}
project_id: ${{ vars.TEST_PROJECT_ID }}
region: 'eu01'
service_account_json: ${{ secrets.TF_ACC_SERVICE_ACCOUNT_JSON }}

2
.gitignore vendored
View file

@ -46,3 +46,5 @@ dist
pkg_gen
/release/
.env
**/.env

View file

@ -19,20 +19,20 @@ builds:
ldflags:
- '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}'
goos:
# - freebsd
# - windows
- freebsd
- windows
- linux
- darwin
goarch:
- amd64
# - '386'
# - arm
- '386'
- arm
- arm64
# ignore:
# - goos: darwin
# goarch: '386'
# - goos: windows
# goarch: arm
ignore:
- goos: darwin
goarch: '386'
- goos: windows
goarch: arm
binary: '{{ .ProjectName }}_v{{ .Version }}'
archives:
- formats: [ 'zip' ]

View file

@ -12,9 +12,10 @@ project-tools:
# LINT
lint-golangci-lint:
@echo "Linting with golangci-lint"
@$(SCRIPTS_BASE)/lint-golangci-lint.sh
@go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint run --fix --config golang-ci.yaml
lint-tf:
lint-tf:
@echo "Linting terraform files"
@terraform fmt -check -diff -recursive
@ -23,6 +24,7 @@ lint: lint-golangci-lint lint-tf
# DOCUMENTATION GENERATION
generate-docs:
@echo "Generating documentation with tfplugindocs"
@$(SCRIPTS_BASE)/tfplugindocs.sh
build:
@ -34,15 +36,16 @@ fmt:
@terraform fmt -diff -recursive
# TEST
.PHONY: test coverage
test:
@echo "Running tests for the terraform provider"
@cd $(ROOT_DIR)/stackit && go test ./... -count=1 -coverprofile=coverage.out && cd $(ROOT_DIR)
@cd $(ROOT_DIR)/stackit && go test -timeout 0 ./... -count=1 -coverprofile=../coverage.out && cd $(ROOT_DIR)
# Test coverage
coverage:
@echo ">> Creating test coverage report for the terraform provider"
@cd $(ROOT_DIR)/stackit && (go test ./... -count=1 -coverprofile=coverage.out || true) && cd $(ROOT_DIR)
@cd $(ROOT_DIR)/stackit && go tool cover -html=coverage.out -o coverage.html && cd $(ROOT_DIR)
@cd $(ROOT_DIR)/stackit && (go test -timeout 0 ./... -count=1 -coverprofile=../coverage.out || true) && cd $(ROOT_DIR)
@cd $(ROOT_DIR)/stackit && go tool cover -html=../coverage.out -o ../coverage.html && cd $(ROOT_DIR)
test-acceptance-tf:
@if [ -z $(TF_ACC_PROJECT_ID) ]; then echo "Input TF_ACC_PROJECT_ID missing"; exit 1; fi

View file

@ -1,15 +1,14 @@
<div align="center">
<br>
<img src=".github/images/stackit-logo.svg" alt="STACKIT logo" width="50%"/>
<br>
<br>
</div>
# STACKIT Terraform Provider
# STACKIT Terraform Provider <br />(PRIVATE PREVIEW)
[![Go Report Card](https://goreportcard.com/badge/github.com/stackitcloud/terraform-provider-stackit)](https://goreportcard.com/report/github.com/stackitcloud/terraform-provider-stackit) [![GitHub Release](https://img.shields.io/github/v/release/stackitcloud/terraform-provider-stackit)](https://registry.terraform.io/providers/stackitcloud/stackit/latest) ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/stackitcloud/terraform-provider-stackit) [![GitHub License](https://img.shields.io/github/license/stackitcloud/terraform-provider-stackit)](https://www.apache.org/licenses/LICENSE-2.0)
[![GitHub Release](https://img.shields.io/github/v/release/stackitcloud/terraform-provider-stackit)](https://registry.terraform.io/providers/stackitcloud/stackit/latest) ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/stackitcloud/terraform-provider-stackit) [![GitHub License](https://img.shields.io/github/license/stackitcloud/terraform-provider-stackit)](https://www.apache.org/licenses/LICENSE-2.0)
This project is the official [Terraform Provider](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs) for [STACKIT](https://www.stackit.de/en/), which allows you to manage STACKIT resources through Terraform.
This project is the **NOT** official [Terraform Provider](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs) for [STACKIT](https://www.stackit.de/en/)!
This a **private preview only**, which allows you to manage STACKIT resources through Terraform.
## Getting Started
@ -18,20 +17,22 @@ To install the [STACKIT Terraform Provider](https://registry.terraform.io/provid
```hcl
terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = "X.X.X"
stackitprivatepreview = {
source = "tfregistry.sysops.stackit.rocks/mhenselin/stackitprivatepreview"
version = "= 0.0.5-alpha"
}
}
}
provider "stackit" {
provider "stackitprivatepreview" {
# Configuration options
}
```
Check one of the examples in the [examples](examples/) folder.
<big font-size="3rem">TODO: revise the following sections</big>
## Authentication
To authenticate, you will need a [service account](https://docs.stackit.cloud/platform/access-and-identity/service-accounts/). Create it in the [STACKIT Portal](https://portal.stackit.cloud/) and assign the necessary permissions to it, e.g. `project.owner`. There are multiple ways to authenticate:

View file

@ -1,9 +1,13 @@
package build
import (
"bufio"
"bytes"
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io"
"log"
"log/slog"
@ -27,8 +31,6 @@ const (
GEN_REPO = "https://github.com/stackitcloud/stackit-sdk-generator.git"
)
var supportedVersions = []string{"alpha", "beta"}
type version struct {
verString string
major int
@ -36,17 +38,15 @@ type version struct {
}
type Builder struct {
SkipClone bool
SkipCleanup bool
SkipClone bool
SkipCleanup bool
PackagesOnly bool
}
func (b *Builder) Build() error {
slog.Info("Starting Builder")
slog.Info(" ... Checking needed commands available")
err := checkCommands([]string{"tfplugingen-framework", "tfplugingen-openapi"})
if err != nil {
return err
if b.PackagesOnly {
slog.Info(" >>> only generating pkg_gen <<<")
}
root, err := getRoot()
@ -58,13 +58,23 @@ func (b *Builder) Build() error {
}
slog.Info(" ... using root directory", "dir", *root)
if !b.SkipCleanup {
slog.Info("Cleaning up old generator directory")
err = os.RemoveAll(path.Join(*root, GEN_REPO_NAME))
if !b.PackagesOnly {
slog.Info(" ... Checking needed commands available")
err := checkCommands([]string{})
if err != nil {
return err
}
}
if !b.SkipCleanup {
slog.Info("Cleaning up old packages directory")
err = os.RemoveAll(path.Join(*root, "pkg_gen"))
if err != nil {
return err
}
}
if !b.SkipCleanup && !b.PackagesOnly {
slog.Info("Cleaning up old packages directory")
err = os.RemoveAll(path.Join(*root, "pkg_gen"))
if err != nil {
@ -101,7 +111,7 @@ func (b *Builder) Build() error {
}
slog.Info("Creating OAS dir")
err = os.MkdirAll(path.Join(genDir, "oas"), 0755)
err = os.MkdirAll(path.Join(genDir, "oas"), 0o755) //nolint:gosec // this dir is not sensitive, so we can use 0755
if err != nil {
return err
}
@ -148,7 +158,17 @@ func (b *Builder) Build() error {
if err = cmd.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
slog.Error("cmd.Wait", "code", exitErr.ExitCode(), "error", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"cmd.Wait",
"code",
exitErr.ExitCode(),
"error",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return fmt.Errorf("%s", stdErr.String())
}
if err != nil {
@ -182,7 +202,11 @@ func (b *Builder) Build() error {
}
slog.Info("Rearranging package directories")
err = os.MkdirAll(path.Join(*root, "pkg_gen"), 0755) // noqa:gosec
//nolint:gosec // this dir is not sensitive, so we can use 0755
err = os.MkdirAll(
path.Join(*root, "pkg_gen"),
0o755,
)
if err != nil {
return err
}
@ -192,40 +216,43 @@ func (b *Builder) Build() error {
return err
}
for _, item := range items {
if item.IsDir() {
slog.Info(" -> package", "name", item.Name())
tgtDir := path.Join(*root, "pkg_gen", item.Name())
if fileExists(tgtDir) {
delErr := os.RemoveAll(tgtDir)
if delErr != nil {
return delErr
}
}
err = os.Rename(path.Join(srcDir, item.Name()), tgtDir)
if err != nil {
return err
if !item.IsDir() {
continue
}
slog.Info(" -> package", "name", item.Name())
tgtDir := path.Join(*root, "pkg_gen", item.Name())
if fileExists(tgtDir) {
delErr := os.RemoveAll(tgtDir)
if delErr != nil {
return delErr
}
}
err = os.Rename(path.Join(srcDir, item.Name()), tgtDir)
if err != nil {
return err
}
}
slog.Info("Generating service boilerplate")
err = generateServiceFiles(*root, path.Join(*root, GEN_REPO_NAME))
if err != nil {
return err
}
if !b.PackagesOnly {
slog.Info("Generating service boilerplate")
err = generateServiceFiles(*root, path.Join(*root, GEN_REPO_NAME))
if err != nil {
return err
}
slog.Info("Copying all service files")
err = CopyDirectory(
path.Join(*root, "generated", "internal", "services"),
path.Join(*root, "stackit", "internal", "services"),
)
if err != nil {
return err
}
slog.Info("Copying all service files")
err = CopyDirectory(
path.Join(*root, "generated", "internal", "services"),
path.Join(*root, "stackit", "internal", "services"),
)
if err != nil {
return err
}
err = createBoilerplate(*root, path.Join(*root, "stackit", "internal", "services"))
if err != nil {
return err
err = createBoilerplate(*root, path.Join(*root, "stackit", "internal", "services"))
if err != nil {
return err
}
}
if !b.SkipCleanup {
@ -254,14 +281,17 @@ func (b *Builder) Build() error {
}
type templateData struct {
PackageName string
NameCamel string
NamePascal string
NameSnake string
PackageName string
PackageNameCamel string
PackageNamePascal string
NameCamel string
NamePascal string
NameSnake string
Fields []string
}
func fileExists(path string) bool {
_, err := os.Stat(path)
func fileExists(pathValue string) bool {
_, err := os.Stat(pathValue)
if os.IsNotExist(err) {
return false
}
@ -297,10 +327,22 @@ func createBoilerplate(rootFolder, folder string) error {
resourceName := res.Name()
dsFile := path.Join(folder, svc.Name(), res.Name(), "datasources_gen", fmt.Sprintf("%s_data_source_gen.go", res.Name()))
dsFile := path.Join(
folder,
svc.Name(),
res.Name(),
"datasources_gen",
fmt.Sprintf("%s_data_source_gen.go", res.Name()),
)
handleDS = fileExists(dsFile)
resFile := path.Join(folder, svc.Name(), res.Name(), "resources_gen", fmt.Sprintf("%s_resource_gen.go", res.Name()))
resFile := path.Join(
folder,
svc.Name(),
res.Name(),
"resources_gen",
fmt.Sprintf("%s_resource_gen.go", res.Name()),
)
handleRes = fileExists(resFile)
dsGoFile := path.Join(folder, svc.Name(), res.Name(), "datasource.go")
@ -310,21 +352,29 @@ func createBoilerplate(rootFolder, folder string) error {
foundRes = fileExists(resGoFile)
if handleDS && !foundDS {
slog.Info("Creating missing datasource.go", "service", svc.Name(), "resource", resourceName)
slog.Info(" creating missing datasource.go", "service", svc.Name(), "resource", resourceName)
if !ValidateSnakeCase(resourceName) {
return errors.New("resource name is invalid")
}
fields, tokenErr := getTokens(dsFile)
if tokenErr != nil {
return fmt.Errorf("error reading tokens: %w", tokenErr)
}
tplName := "data_source_scaffold.gotmpl"
err = writeTemplateToFile(
tplName,
path.Join(rootFolder, "tools", "templates", tplName),
path.Join(folder, svc.Name(), res.Name(), "datasource.go"),
path.Join(rootFolder, "cmd", "cmd", "build", "templates", tplName),
dsGoFile,
&templateData{
PackageName: svc.Name(),
NameCamel: ToCamelCase(resourceName),
NamePascal: ToPascalCase(resourceName),
NameSnake: resourceName,
PackageName: svc.Name(),
PackageNameCamel: ToCamelCase(svc.Name()),
PackageNamePascal: ToPascalCase(svc.Name()),
NameCamel: ToCamelCase(resourceName),
NamePascal: ToPascalCase(resourceName),
NameSnake: resourceName,
Fields: fields,
},
)
if err != nil {
@ -333,26 +383,58 @@ func createBoilerplate(rootFolder, folder string) error {
}
if handleRes && !foundRes {
slog.Info("Creating missing resource.go", "service", svc.Name(), "resource", resourceName)
slog.Info(" creating missing resource.go", "service", svc.Name(), "resource", resourceName)
if !ValidateSnakeCase(resourceName) {
return errors.New("resource name is invalid")
}
fields, tokenErr := getTokens(resFile)
if tokenErr != nil {
return fmt.Errorf("error reading tokens: %w", tokenErr)
}
tplName := "resource_scaffold.gotmpl"
err = writeTemplateToFile(
tplName,
path.Join(rootFolder, "tools", "templates", tplName),
path.Join(folder, svc.Name(), res.Name(), "resource.go"),
path.Join(rootFolder, "cmd", "cmd", "build", "templates", tplName),
resGoFile,
&templateData{
PackageName: svc.Name(),
NameCamel: ToCamelCase(resourceName),
NamePascal: ToPascalCase(resourceName),
NameSnake: resourceName,
PackageName: svc.Name(),
PackageNameCamel: ToCamelCase(svc.Name()),
PackageNamePascal: ToPascalCase(svc.Name()),
NameCamel: ToCamelCase(resourceName),
NamePascal: ToPascalCase(resourceName),
NameSnake: resourceName,
Fields: fields,
},
)
if err != nil {
return err
}
if !fileExists(path.Join(folder, svc.Name(), res.Name(), "functions.go")) {
slog.Info(" creating missing functions.go", "service", svc.Name(), "resource", resourceName)
if !ValidateSnakeCase(resourceName) {
return errors.New("resource name is invalid")
}
fncTplName := "functions_scaffold.gotmpl"
err = writeTemplateToFile(
fncTplName,
path.Join(rootFolder, "cmd", "cmd", "build", "templates", fncTplName),
path.Join(folder, svc.Name(), res.Name(), "functions.go"),
&templateData{
PackageName: svc.Name(),
PackageNameCamel: ToCamelCase(svc.Name()),
PackageNamePascal: ToPascalCase(svc.Name()),
NameCamel: ToCamelCase(resourceName),
NamePascal: ToPascalCase(resourceName),
NameSnake: resourceName,
},
)
if err != nil {
return err
}
}
}
}
}
@ -360,7 +442,7 @@ func createBoilerplate(rootFolder, folder string) error {
}
func ucfirst(s string) string {
if len(s) == 0 {
if s == "" {
return ""
}
return strings.ToUpper(s[:1]) + s[1:]
@ -395,8 +477,8 @@ func writeTemplateToFile(tplName, tplFile, outFile string, data *templateData) e
}
func generateServiceFiles(rootDir, generatorDir string) error {
// slog.Info("Generating specs folder")
err := os.MkdirAll(path.Join(rootDir, "generated", "specs"), 0755)
//nolint:gosec // this file is not sensitive, so we can use 0755
err := os.MkdirAll(path.Join(rootDir, "generated", "specs"), 0o755)
if err != nil {
return err
}
@ -434,7 +516,6 @@ func generateServiceFiles(rootDir, generatorDir string) error {
continue
}
// slog.Info("Checking spec", "name", spec.Name())
r := regexp.MustCompile(`^(.*)_config.yml$`)
matches := r.FindAllStringSubmatch(specFile.Name(), -1)
if matches != nil {
@ -450,27 +531,44 @@ func generateServiceFiles(rootDir, generatorDir string) error {
resource,
)
oasFile := path.Join(generatorDir, "oas", fmt.Sprintf("%s%s.json", service.Name(), svcVersion.Name()))
oasFile := path.Join(
generatorDir,
"oas",
fmt.Sprintf("%s%s.json", service.Name(), svcVersion.Name()),
)
if _, oasErr := os.Stat(oasFile); os.IsNotExist(oasErr) {
slog.Warn(" coulc not find matching oas", "svc", service.Name(), "version", svcVersion.Name())
slog.Warn(
" could not find matching oas",
"svc",
service.Name(),
"version",
svcVersion.Name(),
)
continue
}
scName := fmt.Sprintf("%s%s", service.Name(), svcVersion.Name())
scName = strings.ReplaceAll(scName, "-", "")
err = os.MkdirAll(path.Join(rootDir, "generated", "internal", "services", scName, resource), 0755)
//nolint:gosec // this file is not sensitive, so we can use 0755
err = os.MkdirAll(path.Join(rootDir, "generated", "internal", "services", scName, resource), 0o755)
if err != nil {
return err
}
// slog.Info("Generating openapi spec json")
specJsonFile := path.Join(rootDir, "generated", "specs", fmt.Sprintf("%s_%s_spec.json", scName, resource))
specJsonFile := path.Join(
rootDir,
"generated",
"specs",
fmt.Sprintf("%s_%s_spec.json", scName, resource),
)
var stdOut, stdErr bytes.Buffer
// noqa:gosec
// nolint:gosec // #nosec this command is not using any untrusted input, so we can ignore gosec warning
cmd := exec.Command(
"tfplugingen-openapi",
"go",
"run",
"github.com/hashicorp/terraform-plugin-codegen-openapi/cmd/tfplugingen-openapi",
"generate",
"--config",
path.Join(rootDir, "service_specs", service.Name(), svcVersion.Name(), fileName),
@ -497,11 +595,29 @@ func generateServiceFiles(rootDir, generatorDir string) error {
if err = cmd.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
slog.Error("tfplugingen-openapi generate", "code", exitErr.ExitCode(), "error", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-openapi generate",
"code",
exitErr.ExitCode(),
"error",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return fmt.Errorf("%s", stdErr.String())
}
if err != nil {
slog.Error("tfplugingen-openapi generate", "err", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-openapi generate",
"err",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return err
}
}
@ -509,18 +625,26 @@ func generateServiceFiles(rootDir, generatorDir string) error {
slog.Warn(" command output", "stdout", stdOut.String(), "stderr", stdErr.String())
}
// slog.Info("Creating terraform svc resource files folder")
tgtFolder := path.Join(rootDir, "generated", "internal", "services", scName, resource, "resources_gen")
err = os.MkdirAll(tgtFolder, 0755)
tgtFolder := path.Join(
rootDir,
"generated",
"internal",
"services",
scName,
resource,
"resources_gen",
)
//nolint:gosec // this file is not sensitive, so we can use 0755
err = os.MkdirAll(tgtFolder, 0o755)
if err != nil {
return err
}
// slog.Info("Generating terraform svc resource files")
// noqa:gosec
// nolint:gosec // #nosec this command is not using any untrusted input, so we can ignore gosec warning
cmd2 := exec.Command(
"tfplugingen-framework",
"go",
"run",
"github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework",
"generate",
"resources",
"--input",
@ -541,27 +665,53 @@ func generateServiceFiles(rootDir, generatorDir string) error {
if err = cmd2.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
slog.Error("tfplugingen-framework generate resources", "code", exitErr.ExitCode(), "error", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-framework generate resources",
"code",
exitErr.ExitCode(),
"error",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return fmt.Errorf("%s", stdErr.String())
}
if err != nil {
slog.Error("tfplugingen-framework generate resources", "err", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-framework generate resources",
"err",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return err
}
}
// slog.Info("Creating terraform svc datasource files folder")
tgtFolder = path.Join(rootDir, "generated", "internal", "services", scName, resource, "datasources_gen")
err = os.MkdirAll(tgtFolder, 0755)
tgtFolder = path.Join(
rootDir,
"generated",
"internal",
"services",
scName,
resource,
"datasources_gen",
)
//nolint:gosec // this directory is not sensitive, so we can use 0755
err = os.MkdirAll(tgtFolder, 0o755)
if err != nil {
return err
}
// slog.Info("Generating terraform svc resource files")
// noqa:gosec
// nolint:gosec // #nosec this command is not using any untrusted input, so we can ignore gosec warning
cmd3 := exec.Command(
"tfplugingen-framework",
"go",
"run",
"github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework",
"generate",
"data-sources",
"--input",
@ -583,14 +733,41 @@ func generateServiceFiles(rootDir, generatorDir string) error {
if err = cmd3.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
slog.Error("tfplugingen-framework generate data-sources", "code", exitErr.ExitCode(), "error", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-framework generate data-sources",
"code",
exitErr.ExitCode(),
"error",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return fmt.Errorf("%s", stdErr.String())
}
if err != nil {
slog.Error("tfplugingen-framework generate data-sources", "err", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-framework generate data-sources",
"err",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return err
}
}
tfAnoErr := handleTfTagForDatasourceFile(
path.Join(tgtFolder, fmt.Sprintf("%s_data_source_gen.go", resource)),
scName,
resource,
)
if tfAnoErr != nil {
return tfAnoErr
}
}
}
}
@ -598,6 +775,75 @@ func generateServiceFiles(rootDir, generatorDir string) error {
return nil
}
// handleTfTagForDatasourceFile replaces existing "id" with "stf_original_api_id"
func handleTfTagForDatasourceFile(filePath, service, resource string) error {
slog.Info(" handle terraform tag for datasource", "service", service, "resource", resource)
if !fileExists(filePath) {
slog.Warn(" could not find file, skipping", "path", filePath)
return nil
}
f, err := os.Open(filePath)
if err != nil {
return err
}
root, err := getRoot()
if err != nil {
//nolint:gocritic // in this case, we want to log the error and exit, as we cannot proceed without the root directory
log.Fatal(err)
}
tmp, err := os.CreateTemp(*root, "replace-*")
if err != nil {
return err
}
sc := bufio.NewScanner(f)
for sc.Scan() {
resLine, err := handleLine(sc.Text())
if err != nil {
return err
}
if _, err := tmp.WriteString(resLine + "\n"); err != nil {
return err
}
}
if scErr := sc.Err(); scErr != nil {
return scErr
}
if err := tmp.Close(); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
//nolint:gosec // path traversal is not a concern here
if err := os.Rename(tmp.Name(), filePath); err != nil {
log.Fatal(err)
}
return nil
}
func handleLine(line string) (string, error) {
schemaRegex := regexp.MustCompile(`(\s+")(id)(": schema.[a-zA-Z0-9]+Attribute{)`)
schemaMatches := schemaRegex.FindAllStringSubmatch(line, -1)
if schemaMatches != nil {
return fmt.Sprintf("%stf_original_api_id%s", schemaMatches[0][1], schemaMatches[0][3]), nil
}
modelRegex := regexp.MustCompile(`(\s+Id\s+types.[a-zA-Z0-9]+\s+.tfsdk:")(id)(".)`)
modelMatches := modelRegex.FindAllStringSubmatch(line, -1)
if modelMatches != nil {
return fmt.Sprintf("%stf_original_api_id%s", modelMatches[0][1], modelMatches[0][3]), nil
}
return line, nil
}
func checkCommands(commands []string) error {
for _, commandName := range commands {
if !commandExists(commandName) {
@ -639,13 +885,23 @@ func copyFile(src, dst string) (int64, error) {
if err != nil {
return 0, err
}
defer source.Close()
defer func(source *os.File) {
err := source.Close()
if err != nil {
slog.Error("copyFile", "err", err)
}
}(source)
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
defer func(destination *os.File) {
err := destination.Close()
if err != nil {
slog.Error("copyFile", "err", err)
}
}(destination)
nBytes, err := io.Copy(destination, source)
return nBytes, err
}
@ -656,10 +912,8 @@ func getOnlyLatest(m map[string]version) (map[string]version, error) {
item, ok := tmpMap[k]
if !ok {
tmpMap[k] = v
} else {
if item.major == v.major && item.minor < v.minor {
tmpMap[k] = v
}
} else if item.major == v.major && item.minor < v.minor {
tmpMap[k] = v
}
}
return tmpMap, nil
@ -673,18 +927,19 @@ func getVersions(dir string) (map[string]version, error) {
}
for _, entry := range children {
if entry.IsDir() {
versions, err := os.ReadDir(path.Join(dir, "services", entry.Name()))
if err != nil {
return nil, err
}
m, err2 := extractVersions(entry.Name(), versions)
if err2 != nil {
return m, err2
}
for k, v := range m {
res[k] = v
}
if !entry.IsDir() {
continue
}
versions, err := os.ReadDir(path.Join(dir, "services", entry.Name()))
if err != nil {
return nil, err
}
m, err2 := extractVersions(entry.Name(), versions)
if err2 != nil {
return m, err2
}
for k, v := range m {
res[k] = v
}
}
return res, nil
@ -693,20 +948,21 @@ func getVersions(dir string) (map[string]version, error) {
func extractVersions(service string, versionDirs []os.DirEntry) (map[string]version, error) {
res := make(map[string]version)
for _, vDir := range versionDirs {
if vDir.IsDir() {
r := regexp.MustCompile(`v([0-9]+)([a-z]+)([0-9]*)`)
matches := r.FindAllStringSubmatch(vDir.Name(), -1)
if matches == nil {
continue
}
svc, ver, err := handleVersion(service, matches[0])
if err != nil {
return nil, err
}
if !vDir.IsDir() {
continue
}
r := regexp.MustCompile(`v(\d+)([a-z]+)(\d*)`)
matches := r.FindAllStringSubmatch(vDir.Name(), -1)
if matches == nil {
continue
}
svc, ver, err := handleVersion(service, matches[0])
if err != nil {
return nil, err
}
if svc != nil && ver != nil {
res[*svc] = *ver
}
if svc != nil && ver != nil {
res[*svc] = *ver
}
}
return res, nil
@ -782,3 +1038,36 @@ func getRoot() (*string, error) {
lines := strings.Split(string(out), "\n")
return &lines[0], nil
}
func getTokens(fileName string) ([]string, error) {
fset := token.NewFileSet()
var result []string
node, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
if err != nil {
return nil, err
}
ast.Inspect(
node, func(n ast.Node) bool {
// Suche nach Typ-Deklarationen (structs)
ts, ok := n.(*ast.TypeSpec)
if ok {
if strings.Contains(ts.Name.Name, "Model") {
ast.Inspect(
ts, func(sn ast.Node) bool {
tts, tok := sn.(*ast.Field)
if tok {
result = append(result, tts.Names[0].String())
}
return true
},
)
}
}
return true
},
)
return result, nil
}

View file

@ -3,6 +3,7 @@ package build
import (
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"syscall"
@ -74,14 +75,24 @@ func Copy(srcFile, dstFile string) error {
return err
}
defer out.Close()
defer func(out *os.File) {
err := out.Close()
if err != nil {
slog.Error("failed to close file", slog.Any("err", err))
}
}(out)
in, err := os.Open(srcFile)
if err != nil {
return err
}
defer in.Close()
defer func(in *os.File) {
err := in.Close()
if err != nil {
slog.Error("error closing destination file", slog.Any("err", err))
}
}(in)
_, err = io.Copy(out, in)
if err != nil {

View file

@ -2,24 +2,38 @@ package {{.PackageName}}
import (
"context"
"fmt"
"net/http"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg/{{.PackageName}}"
{{.PackageName}}Pkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
{{.PackageName}}Gen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/{{.NameSnake}}/datasources_gen"
)
var _ datasource.DataSource = (*{{.NameCamel}}DataSource)(nil)
const errorPrefix = "[{{.PackageNamePascal}} - {{.NamePascal}}]"
func New{{.NamePascal}}DataSource() datasource.DataSource {
return &{{.NameCamel}}DataSource{}
}
type dsModel struct {
{{.PackageName}}Gen.{{.NamePascal}}Model
TfId types.String `tfsdk:"id"`
}
type {{.NameCamel}}DataSource struct{
client *{{.PackageName}}.APIClient
client *{{.PackageName}}Pkg.APIClient
providerData core.ProviderData
}
@ -29,10 +43,57 @@ func (d *{{.NameCamel}}DataSource) Metadata(_ context.Context, req datasource.Me
func (d *{{.NameCamel}}DataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = {{.PackageName}}Gen.{{.NamePascal}}DataSourceSchema(ctx)
resp.Schema.Attributes["id"] = schema.StringAttribute{
Computed: true,
Description: "The terraform internal identifier.",
MarkdownDescription: "The terraform internal identifier.",
}
}
// Configure adds the provider configured client to the data source.
func (d *{{.NameCamel}}DataSource) Configure(
ctx context.Context,
req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse,
) {
var ok bool
d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok {
return
}
apiClientConfigOptions := []config.ConfigurationOption{
config.WithCustomAuth(d.providerData.RoundTripper),
utils.UserAgentConfigOption(d.providerData.Version),
}
if d.providerData.{{.PackageNamePascal}}CustomEndpoint != "" {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithEndpoint(d.providerData.{{.PackageNamePascal}}CustomEndpoint),
)
} else {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithRegion(d.providerData.GetRegion()),
)
}
apiClient, err := {{.PackageName}}Pkg.NewAPIClient(apiClientConfigOptions...)
if err != nil {
resp.Diagnostics.AddError(
"Error configuring API client",
fmt.Sprintf(
"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
err,
),
)
return
}
d.client = apiClient
tflog.Info(ctx, fmt.Sprintf("%s client configured", errorPrefix))
}
func (d *{{.NameCamel}}DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data {{.PackageName}}Gen.{{.NameCamel}}Model
var data dsModel
// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
@ -41,11 +102,47 @@ func (d *{{.NameCamel}}DataSource) Read(ctx context.Context, req datasource.Read
return
}
// Todo: Read API call logic
ctx = core.InitProviderContext(ctx)
// Example data value setting
// data.Id = types.StringValue("example-id")
projectId := data.ProjectId.ValueString()
region := d.providerData.GetRegionWithOverride(data.Region)
{{.NameCamel}}Id := data.{{.NamePascal}}Id.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// TODO: implement needed fields
ctx = tflog.SetField(ctx, "{{.NameCamel}}_id", {{.NameCamel}}Id)
// TODO: refactor to correct implementation
{{.NameCamel}}Resp, err := d.client.Get{{.NamePascal}}Request(ctx, projectId, region, {{.NameCamel}}Id).Execute()
if err != nil {
utils.LogError(
ctx,
&resp.Diagnostics,
err,
"Reading {{.NameCamel}}",
fmt.Sprintf("{{.NameCamel}} with ID %q does not exist in project %q.", {{.NameCamel}}Id, projectId),
map[int]string{
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
},
)
resp.State.RemoveResource(ctx)
return
}
ctx = core.LogResponse(ctx)
data.TfId = utils.BuildInternalTerraformId(projectId, region, ..)
// TODO: fill remaining fields
{{- range .Fields }}
// data.{{.}} = types.Sometype(apiResponse.Get{{.}}())
{{- end -}}
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, fmt.Sprintf("%s read successful", errorPrefix))
}

View file

@ -0,0 +1,98 @@
package {{.PackageName}}
import (
"context"
"fmt"
"math"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
{{.PackageName}} "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
{{.PackageName}}ResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/instance/resources_gen"
)
func mapResponseToModel(
ctx context.Context,
resp *{{.PackageName}}.Get{{.NamePascal}}Response,
m *{{.PackageName}}ResGen.{{.NamePascal}}Model,
tfDiags diag.Diagnostics,
) error {
// TODO: complete and refactor
m.Id = types.StringValue(resp.GetId())
/*
sampleList, diags := types.ListValueFrom(ctx, types.StringType, resp.GetList())
tfDiags.Append(diags...)
if diags.HasError() {
return fmt.Errorf(
"error converting list response value",
)
}
sample, diags := {{.PackageName}}ResGen.NewSampleValue(
{{.PackageName}}ResGen.SampleValue{}.AttributeTypes(ctx),
map[string]attr.Value{
"field": types.StringValue(string(resp.GetField())),
},
)
tfDiags.Append(diags...)
if diags.HasError() {
return fmt.Errorf(
"error converting sample response value",
"sample",
types.StringValue(string(resp.GetField())),
)
}
m.Sample = sample
*/
return nil
}
func handleEncryption(
m *{{.PackageName}}ResGen.{{.NamePascal}}Model,
resp *{{.PackageName}}.Get{{.NamePascal}}Response,
) {{.PackageName}}ResGen.EncryptionValue {
if !resp.HasEncryption() ||
resp.Encryption == nil ||
resp.Encryption.KekKeyId == nil ||
resp.Encryption.KekKeyRingId == nil ||
resp.Encryption.KekKeyVersion == nil ||
resp.Encryption.ServiceAccount == nil {
if m.Encryption.IsNull() || m.Encryption.IsUnknown() {
return {{.PackageName}}ResGen.NewEncryptionValueNull()
}
return m.Encryption
}
enc := {{.PackageName}}ResGen.NewEncryptionValueNull()
if kVal, ok := resp.Encryption.GetKekKeyIdOk(); ok {
enc.KekKeyId = types.StringValue(kVal)
}
if kkVal, ok := resp.Encryption.GetKekKeyRingIdOk(); ok {
enc.KekKeyRingId = types.StringValue(kkVal)
}
if kkvVal, ok := resp.Encryption.GetKekKeyVersionOk(); ok {
enc.KekKeyVersion = types.StringValue(kkvVal)
}
if sa, ok := resp.Encryption.GetServiceAccountOk(); ok {
enc.ServiceAccount = types.StringValue(sa)
}
return enc
}
func toCreatePayload(
ctx context.Context,
model *{{.PackageName}}ResGen.{{.NamePascal}}Model,
) (*{{.PackageName}}.Create{{.NamePascal}}RequestPayload, error) {
if model == nil {
return nil, fmt.Errorf("nil model")
}
return &{{.PackageName}}.Create{{.NamePascal}}RequestPayload{
// TODO: fill fields
}, nil
}

View file

@ -2,15 +2,24 @@ package {{.PackageName}}
import (
"context"
_ "embed"
"fmt"
"strings"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
{{.PackageName}}Gen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/{{.NameSnake}}/resources_gen"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
{{.PackageName}}ResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/{{.NameSnake}}/resources_gen"
)
var (
@ -30,21 +39,43 @@ type {{.NameCamel}}Resource struct{
providerData core.ProviderData
}
type InstanceResourceIdentityModel struct {
// resourceModel represents the Terraform resource state
type resourceModel = {{.PackageName}}.{{.NamePascal}}Model
type {{.NamePascal}}ResourceIdentityModel struct {
ProjectID types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"`
InstanceID types.String `tfsdk:"instance_id"`
// TODO: implement further needed parts
{{.NamePascal}}ID types.String `tfsdk:"{{.NameSnake}}_id"`
}
// Metadata defines terraform resource name
func (r *{{.NameCamel}}Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_{{.PackageName}}_{{.NameSnake}}"
}
//go:embed planModifiers.yaml
var modifiersFileByte []byte
// Schema loads the schema from generated files and adds plan modifiers
func (r *{{.NameCamel}}Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = {{.PackageName}}Gen.{{.NamePascal}}ResourceSchema(ctx)
schema = {{.PackageName}}ResGen.{{.NamePascal}}ResourceSchema(ctx)
fields, err := {{.PackageName}}Utils.ReadModifiersConfig(modifiersFileByte)
if err != nil {
resp.Diagnostics.AddError("error during read modifiers config file", err.Error())
return
}
err = {{.PackageName}}Utils.AddPlanModifiersToResourceSchema(fields, &schema)
if err != nil {
resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
return
}
resp.Schema = schema
}
// IdentitySchema defines the identity schema
func (r *instanceResource) IdentitySchema(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
resp.IdentitySchema = identityschema.Schema{
Attributes: map[string]identityschema.Attribute{
@ -57,11 +88,11 @@ func (r *instanceResource) IdentitySchema(_ context.Context, _ resource.Identity
"instance_id": identityschema.StringAttribute{
RequiredForImport: true, // can be defaulted by the provider configuration
},
// TODO: implement remaining schema parts
},
}
}
// Configure adds the provider configured client to the resource.
func (r *{{.NameCamel}}Resource) Configure(
ctx context.Context,
@ -78,112 +109,26 @@ func (r *{{.NameCamel}}Resource) Configure(
config.WithCustomAuth(r.providerData.RoundTripper),
utils.UserAgentConfigOption(r.providerData.Version),
}
if r.providerData.PostgresFlexCustomEndpoint != "" {
apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(r.providerData.PostgresFlexCustomEndpoint))
if r.providerData.{{.PackageNamePascal}}CustomEndpoint != "" {
apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(r.providerData.{{.PackageName}}CustomEndpoint))
} else {
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(r.providerData.GetRegion()))
}
apiClient, err := {{.PackageName}}.NewAPIClient(apiClientConfigOptions...)
if err != nil {
resp.Diagnostics.AddError( "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
resp.Diagnostics.AddError(
"Error configuring API client",
fmt.Sprintf(
"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
err,
),
)
return
}
r.client = apiClient
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} client configured")
}
func (r *{{.NameCamel}}Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data {{.PackageName}}Gen.{{.NamePascal}}Model
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// TODO: Create API call logic
// Example data value setting
data.{{.NameCamel | ucfirst}}Id = types.StringValue("id-from-response")
// TODO: Set data returned by API in identity
identity := InstanceResourceIdentityModel{
ProjectID: types.StringValue(projectId),
Region: types.StringValue(region),
InstanceID: types.StringValue(instanceId),
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
if resp.Diagnostics.HasError() {
return
}
// TODO: implement wait handler if needed
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} created")
}
func (r *{{.NameCamel}}Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data {{.PackageName}}Gen.{{.NamePascal}}Model
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
// Read identity data
var identityData InstanceResourceIdentityModel
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
if resp.Diagnostics.HasError() {
return
}
if resp.Diagnostics.HasError() {
return
}
// Todo: Read API call logic
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} read")
}
func (r *{{.NameCamel}}Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data {{.PackageName}}Gen.{{.NamePascal}}Model
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Todo: Update API call logic
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} updated")
}
func (r *{{.NameCamel}}Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data {{.PackageName}}Gen.{{.NamePascal}}Model
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Todo: Delete API call logic
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} deleted")
}
// ModifyPlan implements resource.ResourceWithModifyPlan.
// Use the modifier to set the effective region in the current plan.
func (r *{{.NameCamel}}Resource) ModifyPlan(
@ -191,17 +136,21 @@ func (r *{{.NameCamel}}Resource) ModifyPlan(
req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse,
) { // nolint:gocritic // function signature required by Terraform
var configModel {{.PackageName}}Gen.{{.NamePascal}}Model
// skip initial empty configuration to avoid follow-up errors
if req.Config.Raw.IsNull() {
return
}
var configModel {{.PackageName}}ResGen.{{.NamePascal}}Model
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
if resp.Diagnostics.HasError() {
return
}
var planModel {{.PackageName}}Gen.{{.NamePascal}}Model
if req.Plan.Raw.IsNull() {
return
}
var planModel {{.PackageName}}ResGen.{{.NamePascal}}Model
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
if resp.Diagnostics.HasError() {
return
@ -218,6 +167,231 @@ func (r *{{.NameCamel}}Resource) ModifyPlan(
}
}
// Create creates a new resource
func (r *{{.NameCamel}}Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data {{.PackageName}}ResGen.{{.NamePascal}}Model
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := data.ProjectId.ValueString()
region := data.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// TODO: add remaining fields
// TODO: Create API call logic
/*
// Generate API request body from model
payload, err := toCreatePayload(ctx, &model)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating {{.NamePascal}}",
fmt.Sprintf("Creating API payload: %v", err),
)
return
}
// Create new {{.NamePascal}}
createResp, err := r.client.Create{{.NamePascal}}Request(
ctx,
projectId,
region,
).Create{{.NamePascal}}RequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating {{.NamePascal}}", fmt.Sprintf("Calling API: %v", err))
return
}
ctx = core.LogResponse(ctx)
{{.NamePascal}}Id := *createResp.Id
*/
// Example data value setting
data.{{.NameCamel | ucfirst}}Id = types.StringValue("id-from-response")
// TODO: Set data returned by API in identity
identity := {{.NamePascal}}ResourceIdentityModel{
ProjectID: types.StringValue(projectId),
Region: types.StringValue(region),
// TODO: add missing values
{{.NamePascal}}ID: types.StringValue({{.NamePascal}}Id),
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
if resp.Diagnostics.HasError() {
return
}
// TODO: implement wait handler if needed
/*
waitResp, err := wait.Create{{.NamePascal}}WaitHandler(
ctx,
r.client,
projectId,
{{.NamePascal}}Id,
region,
).SetSleepBeforeWait(
30 * time.Second,
).SetTimeout(
90 * time.Minute,
).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating {{.NamePascal}}",
fmt.Sprintf("{{.NamePascal}} creation waiting: %v", err),
)
return
}
if waitResp.Id == nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating {{.NamePascal}}",
"{{.NamePascal}} creation waiting: returned id is nil",
)
return
}
// Map response body to schema
err = mapResponseToModel(ctx, waitResp, &model, resp.Diagnostics)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating {{.NamePascal}}",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
*/
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} created")
}
func (r *{{.NameCamel}}Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data {{.PackageName}}ResGen.{{.NamePascal}}Model
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Read identity data
var identityData {{.NamePascal}}ResourceIdentityModel
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := identityData.ProjectID.ValueString()
region := identityData.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// TODO: Read API call logic
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
// TODO: Set data returned by API in identity
identity := {{.NamePascal}}ResourceIdentityModel{
ProjectID: types.StringValue(projectId),
Region: types.StringValue(region),
// InstanceID: types.StringValue(instanceId),
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} read")
}
func (r *{{.NameCamel}}Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data {{.PackageName}}ResGen.{{.NamePascal}}Model
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := data.ProjectId.ValueString()
region := data.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// TODO: Update API call logic
// TODO: Set data returned by API in identity
identity := {{.NamePascal}}ResourceIdentityModel{
ProjectID: types.StringValue(projectId),
Region: types.StringValue(region),
// TODO: add missing values
{{.NamePascal}}ID: types.StringValue({{.NamePascal}}Id),
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
if resp.Diagnostics.HasError() {
return
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} updated")
}
func (r *{{.NameCamel}}Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data {{.PackageName}}ResGen.{{.NamePascal}}Model
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Read identity data
var identityData {{.NamePascal}}ResourceIdentityModel
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := identityData.ProjectID.ValueString()
region := identityData.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// TODO: Delete API call logic
tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} deleted")
}
// ImportState imports a resource into the Terraform state on success.
// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
func (r *{{.NameCamel}}Resource) ImportState(
@ -227,7 +401,8 @@ func (r *{{.NameCamel}}Resource) ImportState(
) {
idParts := strings.Split(req.ID, core.Separator)
// Todo: Import logic
// TODO: Import logic
// TODO: fix len and parts itself
if len(idParts) < 2 || idParts[0] == "" || idParts[1] == "" {
core.LogAndAddError(
ctx, &resp.Diagnostics,

View file

@ -0,0 +1,47 @@
package utils
import (
"context"
"fmt"
{{.PackageName}} "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
)
func ConfigureClient(
ctx context.Context,
providerData *core.ProviderData,
diags *diag.Diagnostics,
) *{{.PackageName}}.APIClient {
apiClientConfigOptions := []config.ConfigurationOption{
config.WithCustomAuth(providerData.RoundTripper),
utils.UserAgentConfigOption(providerData.Version),
}
if providerData.{{.PackageName}}CustomEndpoint != "" {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithEndpoint(providerData.{{.PackageName}}CustomEndpoint),
)
} else {
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(providerData.GetRegion()))
}
apiClient, err := {{.PackageName}}.NewAPIClient(apiClientConfigOptions...)
if err != nil {
core.LogAndAddError(
ctx,
diags,
"Error configuring API client",
fmt.Sprintf(
"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
err,
),
)
return nil
}
return apiClient
}

View file

@ -0,0 +1,97 @@
package utils
import (
"context"
"os"
"reflect"
"testing"
"github.com/hashicorp/terraform-plugin-framework/diag"
sdkClients "github.com/stackitcloud/stackit-sdk-go/core/clients"
"github.com/stackitcloud/stackit-sdk-go/core/config"
{{.PackageName}} "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
)
const (
testVersion = "1.2.3"
testCustomEndpoint = "https://sqlserverflex-custom-endpoint.api.stackit.cloud"
)
func TestConfigureClient(t *testing.T) {
/* mock authentication by setting service account token env variable */
os.Clearenv()
err := os.Setenv(sdkClients.ServiceAccountToken, "mock-val")
if err != nil {
t.Errorf("error setting env variable: %v", err)
}
type args struct {
providerData *core.ProviderData
}
tests := []struct {
name string
args args
wantErr bool
expected *sqlserverflex.APIClient
}{
{
name: "default endpoint",
args: args{
providerData: &core.ProviderData{
Version: testVersion,
},
},
expected: func() *sqlserverflex.APIClient {
apiClient, err := sqlserverflex.NewAPIClient(
config.WithRegion("eu01"),
utils.UserAgentConfigOption(testVersion),
)
if err != nil {
t.Errorf("error configuring client: %v", err)
}
return apiClient
}(),
wantErr: false,
},
{
name: "custom endpoint",
args: args{
providerData: &core.ProviderData{
Version: testVersion,
SQLServerFlexCustomEndpoint: testCustomEndpoint,
},
},
expected: func() *sqlserverflex.APIClient {
apiClient, err := sqlserverflex.NewAPIClient(
utils.UserAgentConfigOption(testVersion),
config.WithEndpoint(testCustomEndpoint),
)
if err != nil {
t.Errorf("error configuring client: %v", err)
}
return apiClient
}(),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
ctx := context.Background()
diags := diag.Diagnostics{}
actual := ConfigureClient(ctx, tt.args.providerData, &diags)
if diags.HasError() != tt.wantErr {
t.Errorf("ConfigureClient() error = %v, want %v", diags.HasError(), tt.wantErr)
}
if !reflect.DeepEqual(actual, tt.expected) {
t.Errorf("ConfigureClient() = %v, want %v", actual, tt.expected)
}
},
)
}
}

View file

@ -2,22 +2,25 @@ package cmd
import (
"github.com/spf13/cobra"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/cmd/cmd/build"
)
var (
skipCleanup bool
skipClone bool
skipCleanup bool
skipClone bool
packagesOnly bool
)
var buildCmd = &cobra.Command{
Use: "build",
Short: "Build the necessary boilerplate",
Long: `...`,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
b := build.Builder{
SkipClone: skipClone,
SkipCleanup: skipCleanup,
SkipClone: skipClone,
SkipCleanup: skipCleanup,
PackagesOnly: packagesOnly,
}
return b.Build()
},
@ -27,7 +30,8 @@ func NewBuildCmd() *cobra.Command {
return buildCmd
}
func init() { // nolint: gochecknoinits
func init() { //nolint:gochecknoinits // This is the standard way to set up Cobra commands
buildCmd.Flags().BoolVarP(&skipCleanup, "skip-clean", "c", false, "Skip cleanup steps")
buildCmd.Flags().BoolVarP(&skipClone, "skip-clone", "g", false, "Skip cloning from git")
buildCmd.Flags().BoolVarP(&packagesOnly, "packages-only", "p", false, "Only generate packages")
}

114
cmd/cmd/examplesCmd.go Normal file
View file

@ -0,0 +1,114 @@
package cmd
import (
"fmt"
"os"
"path"
"github.com/spf13/cobra"
)
var examplesCmd = &cobra.Command{
Use: "examples",
Short: "create examples",
Long: `...`,
RunE: func(_ *cobra.Command, _ []string) error {
// filePathStr := "stackit/internal/services/postgresflexalpha/database/datasources_gen/database_data_source_gen.go"
//
// src, err := os.ReadFile(filePathStr)
// if err != nil {
// return err
//}
//
// i := interp.New(
// interp.Options{
// GoPath: "/home/henselinm/.asdf/installs/golang/1.25.6/packages",
// BuildTags: nil,
// Stdin: nil,
// Stdout: nil,
// Stderr: nil,
// Args: nil,
// Env: nil,
// SourcecodeFilesystem: nil,
// Unrestricted: false,
// },
//)
// err = i.Use(i.Symbols("github.com/hashicorp/terraform-plugin-framework-validators"))
// if err != nil {
// return err
//}
// err = i.Use(stdlib.Symbols)
// if err != nil {
// return err
//}
// _, err = i.Eval(string(src))
// if err != nil {
// return err
//}
//
// v, err := i.Eval("DatabaseDataSourceSchema")
// if err != nil {
// return err
//}
//
// bar := v.Interface().(func(string) string)
//
// r := bar("Kung")
// println(r)
//
// evalPath, err := i.EvalPath(filePathStr)
// if err != nil {
// return err
//}
//
// fmt.Printf("%+v\n", evalPath)
// _, err = i.Eval(`import "fmt"`)
// if err != nil {
// return err
//}
// _, err = i.Eval(`func Hallo() { fmt.Println("Hi!") }`)
// if err != nil {
// return err
//}
// v = i.Symbols("Hallo")
// fmt.Println(v)
return workServices()
},
}
func workServices() error {
startPath := path.Join("stackit", "internal", "services")
services, err := os.ReadDir(startPath)
if err != nil {
return err
}
for _, entry := range services {
if !entry.IsDir() {
continue
}
resources, err := os.ReadDir(path.Join(startPath, entry.Name()))
if err != nil {
return err
}
for _, res := range resources {
if !res.IsDir() {
continue
}
fmt.Println("Gefunden:", startPath, "subdir", entry.Name(), "resource", res.Name())
}
}
return nil
}
func NewExamplesCmd() *cobra.Command {
return examplesCmd
}
// func init() { // nolint: gochecknoinits
// examplesCmd.Flags().BoolVarP(&example, "example", "e", false, "example")
//}

148
cmd/cmd/getFieldsCmd.go Normal file
View file

@ -0,0 +1,148 @@
package cmd
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"path"
"path/filepath"
"strings"
"github.com/spf13/cobra"
)
var (
inFile string
svcName string
resName string
resType string
filePath string
)
var getFieldsCmd = &cobra.Command{
Use: "get-fields",
Short: "get fields from file",
Long: `...`,
PreRunE: func(_ *cobra.Command, _ []string) error {
typeStr := "data_source"
if resType != "resource" && resType != "datasource" {
return fmt.Errorf("--type can only be resource or datasource")
}
if resType == "resource" {
typeStr = resType
}
if inFile == "" && svcName == "" && resName == "" {
return fmt.Errorf("--infile or --service and --resource must be provided")
}
if inFile != "" {
if svcName != "" || resName != "" {
return fmt.Errorf("--infile is provided and excludes --service and --resource")
}
p, err := filepath.Abs(inFile)
if err != nil {
return err
}
filePath = p
return nil
}
if svcName != "" && resName == "" {
return fmt.Errorf("if --service is provided, you MUST also provide --resource")
}
if svcName == "" && resName != "" {
return fmt.Errorf("if --resource is provided, you MUST also provide --service")
}
p, err := filepath.Abs(
path.Join(
"stackit",
"internal",
"services",
svcName,
resName,
fmt.Sprintf("%ss_gen", resType),
fmt.Sprintf("%s_%s_gen.go", resName, typeStr),
),
)
if err != nil {
return err
}
filePath = p
//// Enum check
// switch format {
// case "json", "yaml":
//default:
// return fmt.Errorf("invalid --format: %s (want json|yaml)", format)
//}
return nil
},
RunE: func(_ *cobra.Command, _ []string) error {
return getFields(filePath)
},
}
func getFields(f string) error {
tokens, err := getTokens(f)
if err != nil {
return err
}
for _, item := range tokens {
fmt.Printf("%s \n", item)
}
return nil
}
func getTokens(fileName string) ([]string, error) {
fset := token.NewFileSet()
var result []string
node, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
if err != nil {
return nil, err
}
ast.Inspect(
node, func(n ast.Node) bool {
// Suche nach Typ-Deklarationen (structs)
ts, ok := n.(*ast.TypeSpec)
if ok {
if strings.Contains(ts.Name.Name, "Model") {
ast.Inspect(
ts, func(sn ast.Node) bool {
tts, tok := sn.(*ast.Field)
if tok {
result = append(result, tts.Names[0].String())
}
return true
},
)
}
}
return true
},
)
return result, nil
}
func NewGetFieldsCmd() *cobra.Command {
return getFieldsCmd
}
func init() { //nolint:gochecknoinits //this is the only way to add the command to the rootCmd
getFieldsCmd.Flags().StringVarP(&inFile, "infile", "i", "", "input filename incl path")
getFieldsCmd.Flags().StringVarP(&svcName, "service", "s", "", "service name")
getFieldsCmd.Flags().StringVarP(&resName, "resource", "r", "", "resource name")
getFieldsCmd.Flags().StringVarP(
&resType,
"type",
"t",
"resource",
"resource type (data-source or resource [default])",
)
}

View file

@ -35,36 +35,27 @@ type GpgPublicKey struct {
}
func (p *Provider) CreateArchitectureFiles() error {
// var namespace, provider, distPath, repoName, version, gpgFingerprint, gpgPubKeyFile, domain string
log.Println("* Creating architecture files in target directories")
// filename = terraform-provider-[provider]_0.0.1_darwin_amd64.zip - provider_name + version + target + architecture + .zip
// prefix := fmt.Sprintf("v1/providers/%s/%s/%s/", namespace, provider, version)
prefix := path.Join("v1", "providers", p.Namespace, p.Provider, p.Version)
// pathPrefix := fmt.Sprintf("release/%s", prefix)
pathPrefix := path.Join("release", prefix)
// urlPrefix := fmt.Sprintf("https://%s/%s", domain, prefix)
urlPrefix, err := url.JoinPath("https://", p.Domain, prefix)
if err != nil {
return fmt.Errorf("error creating base url: %w", err)
}
// download url = https://example.com/v1/providers/namespace/provider/0.0.1/download/terraform-provider_0.0.1_darwin_amd64.zip
downloadUrlPrefix, err := url.JoinPath(urlPrefix, "download")
if err != nil {
return fmt.Errorf("error crearting download url: %w", err)
}
downloadPathPrefix := path.Join(pathPrefix, "download")
// shasums url = https://example.com/v1/providers/namespace/provider/0.0.1/terraform-provider_0.0.1_SHA256SUMS
shasumsUrl, err := url.JoinPath(urlPrefix, fmt.Sprintf("%s_%s_SHA256SUMS", p.RepoName, p.Version))
if err != nil {
return fmt.Errorf("error creating shasums url: %w", err)
}
// shasums_signature_url = https://example.com/v1/providers/namespace/provider/0.0.1/terraform-provider_0.0.1_SHA256SUMS.sig
shasumsSigUrl := shasumsUrl + ".sig"
gpgAsciiPub, err := p.ReadGpgFile()
@ -94,7 +85,7 @@ func (p *Provider) CreateArchitectureFiles() error {
archFileName := path.Join(downloadPathPrefix, target, arch)
a := Architecture{
Protocols: []string{"5.1"},
Protocols: []string{"5.1", "6.0"},
OS: target,
Arch: arch,
FileName: sum.Path,
@ -116,33 +107,6 @@ func (p *Provider) CreateArchitectureFiles() error {
},
},
}
// var architectureTemplate = []byte(fmt.Sprintf(`
//{
// "protocols": [
// "4.0",
// "5.1",
// "6.0"
// ],
// "os": "%s",
// "arch": "%s",
// "filename": "%s",
// "download_url": "%s",
// "shasums_url": "%s",
// "shasums_signature_url": "%s",
// "shasum": "%s",
// "signing_keys": {
// "gpg_public_keys": [
// {
// "key_id": "%s",
// "ascii_armor": "%s",
// "trust_signature": "",
// "source": "",
// "source_url": ""
// }
// ]
// }
//}
//`, target, arch, fileName, downloadUrl, shasumsUrl, shasumsSigUrl, shasum, gpgFingerprint, gpgAsciiPub))
log.Printf(" - Arch file: %s", archFileName)
@ -160,8 +124,12 @@ func WriteArchitectureFile(filePath string, arch Architecture) error {
if err != nil {
return fmt.Errorf("error encoding data: %w", err)
}
err = os.WriteFile(filePath, jsonString, os.ModePerm)
//nolint:gosec // this file is not sensitive, so we can use os.ModePerm
err = os.WriteFile(
filePath,
jsonString,
os.ModePerm,
)
if err != nil {
return fmt.Errorf("error writing data: %w", err)
}

View file

@ -143,7 +143,7 @@ func (p *Provider) createVersionsFile() error {
// Build the versions file...
version := Version{
Version: p.Version,
Protocols: []string{"5.1"},
Protocols: []string{"5.1", "6.1"},
Platforms: nil,
}
for _, sum := range shasums {
@ -161,10 +161,12 @@ func (p *Provider) createVersionsFile() error {
target := fileNameSplit[2]
arch := fileNameSplit[3]
version.Platforms = append(version.Platforms, Platform{
OS: target,
Arch: arch,
})
version.Platforms = append(
version.Platforms, Platform{
OS: target,
Arch: arch,
},
)
}
data := Data{}
@ -206,16 +208,19 @@ func (p *Provider) CreateWellKnown() error {
log.Println("* Creating .well-known directory")
pathString := path.Join(p.RootPath, "release", ".well-known")
//nolint:gosec // this file is not sensitive, so we can use ModePerm
err := os.MkdirAll(pathString, os.ModePerm)
if err != nil && !errors.Is(err, fs.ErrExist) {
return fmt.Errorf("error creating '%s' dir: %w", pathString, err)
}
log.Println(" - Writing to .well-known/terraform.json file")
//nolint:gosec // this file is not sensitive, so we can use 0644
err = os.WriteFile(
fmt.Sprintf("%s/terraform.json", pathString),
[]byte(`{"providers.v1": "/v1/providers/"}`),
0644,
0o644,
)
if err != nil {
return err
@ -224,9 +229,10 @@ func (p *Provider) CreateWellKnown() error {
return nil
}
func CreateDir(path string) error {
log.Printf("* Creating %s directory", path)
err := os.MkdirAll(path, os.ModePerm)
func CreateDir(pathValue string) error {
log.Printf("* Creating %s directory", pathValue)
//nolint:gosec // this file is not sensitive, so we can use ModePerm
err := os.MkdirAll(pathValue, os.ModePerm)
if errors.Is(err, fs.ErrExist) {
return nil
}
@ -269,13 +275,23 @@ func CopyFile(src, dst string) (int64, error) {
if err != nil {
return 0, err
}
defer source.Close()
defer func(source *os.File) {
err := source.Close()
if err != nil {
slog.Error("error closing source file", slog.Any("err", err))
}
}(source)
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
defer func(destination *os.File) {
err := destination.Close()
if err != nil {
slog.Error("error closing destination file", slog.Any("err", err))
}
}(destination)
nBytes, err := io.Copy(destination, source)
return nBytes, err
}

View file

@ -0,0 +1,38 @@
{
log {
level debug
}
filesystem tf s3 {
bucket "terraform-provider-privatepreview"
region eu01
endpoint https://object.storage.eu01.onstackit.cloud
use_path_style
}
}
tfregistry.sysops.stackit.rocks {
encode zstd gzip
handle_path /docs/* {
root /srv/www
templates
@md {
file {path}
path *.md
}
rewrite @md /markdown.html
file_server {
browse
}
}
file_server {
fs tf
browse
}
}

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="de">
<head>
<title>Forwarding | Weiterleitung</title>
<meta http-equiv="refresh" content="0; URL=index.md">
</head>
<body>
<a href="index.md">Falls Sie nicht automatisch weitergeleitet werden, klicken Sie bitte hier.</a><br />
Sie gelangen dann auf unsere Hauptseite
</body>
</html>

View file

@ -0,0 +1,34 @@
---
page_title: STACKIT provider PrivatePreview
description: none
---
# provider
[Provider](docs/index.md)
## PostGreSQL alpha
### data sources
- [Flavor](docs/data-sources/postgresflexalpha_flavor.md)
- [Database](docs/data-sources/postgresflexalpha_database.md)
- [Instance](docs/data-sources/postgresflexalpha_instance.md)
- [Flavors](docs/data-sources/postgresflexalpha_flavors.md)
- [User](docs/data-sources/postgresflexalpha_user.md)
### resources
- [Database](docs/resources/postgresflexalpha_database.md)
- [Instance](docs/resources/postgresflexalpha_instance.md)
- [User](docs/resources/postgresflexalpha_user.md)
## SQL Server alpha
### data sources
- [Database](docs/data-sources/sqlserverflexalpha_database.md)
- [Version](docs/data-sources/sqlserverflexalpha_version.md)
- [User](docs/data-sources/sqlserverflexalpha_user.md)
- [Flavor](docs/data-sources/sqlserverflexalpha_flavor.md)
- [Instance](docs/data-sources/sqlserverflexalpha_instance.md)
### resources
- [Database](docs/resources/sqlserverflexalpha_database.md)
- [User](docs/resources/sqlserverflexalpha_user.md)
- [Instance](docs/resources/sqlserverflexalpha_instance.md)

View file

@ -0,0 +1,79 @@
<!DOCTYPE html>
{{ $mdFile := .OriginalReq.URL.Path | trimPrefix "/docs" }}
{{ $md := (include $mdFile | splitFrontMatter) }}
<html lang="en">
<head>
<title>{{$md.Meta.page_title}}</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="/docs/terraform-registry.css">
</head>
<body>
<h1>{{$md.Meta.page_title}}</h1>
<div class="provider-view">
<div class="provider-nav">
<nav class="bread-crumbs is-light" aria-label="Provider">
<div class="container is-widescreen">
<div class="level">
<ul class="provider-nav-breadcrumbs bread-crumbs-list">
<li class="bread-crumbs-item">
<a id="ember20" class="ember-view bread-crumbs-link" href="/">
Main
</a>
</li>
</ul>
</div>
</div>
</nav>
<nav class="block-border section-navbar section-header" aria-label="Provider details">
<div class="container">
<div class="columns is-vcentered">
<div class="column is-4">
<div class="provider-nav-info-header">
<div class="provider-overview-logo">
<span class="provider-logo">
<img class="github-image" src="https://avatars3.githubusercontent.com/stackitcloud" alt="stackitcloud">
</span>
</div>
<div class="provider-nav-info-origin">
<h1>PRIVATE PREVIEW</h1>
</div>
</div>
</div>
<div class="column is-8">
<ul class="nav-tabs-list nav-tabs tabs">
<li class="nav-tabs-item">
<a id="ember30" class="ember-view navbar-item" href="/">
Overview
</a>
</li>
</ul>
<div class="provider-nav-provision-wrapper">
<!----> </div>
</div>
</div>
</div>
</nav>
</div>
<div class="section block-border block-white section-content">
<div class="container">
<div class="columns columns-provider-docs">
<div class="column is-3 column-provider-docs-menu"></div>
<article id="provider-docs-content" class="column is-6 provider-docs-content">
<div class="markdown">
<div class="highlighted-code-wrapper">
{{markdown $md.Body}}
</div>
</div>
</article>
<div class="column is-3 column-provider-docs-menu"></div>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -22,16 +22,25 @@ type Platform struct {
}
type Data struct {
Id string `json:"id,omitempty"`
Versions []Version `json:"versions"`
}
func (d *Data) WriteToFile(filePath string) error {
// TODO: make it variable
d.Id = "tfregistry.sysops.stackit.rocks/mhenselin/stackitprivatepreview"
jsonString, err := json.Marshal(d)
if err != nil {
return fmt.Errorf("error encoding data: %w", err)
}
err = os.WriteFile(filePath, jsonString, os.ModePerm)
//nolint:gosec // this file is not sensitive, so we can use os.ModePerm
err = os.WriteFile(
filePath,
jsonString,
os.ModePerm,
)
if err != nil {
return fmt.Errorf("error writing data: %w", err)
}
@ -82,7 +91,13 @@ func (d *Data) LoadFromUrl(uri string) error {
if err != nil {
return err
}
defer os.Remove(file.Name()) // Clean up
defer func(name string) {
//nolint:gosec // The file path is generated by os.CreateTemp and is not user-controllable
err := os.Remove(name)
if err != nil {
slog.Error("failed to remove temporary file", slog.Any("err", err))
}
}(file.Name()) // Clean up
err = DownloadFile(
u.String(),
@ -119,20 +134,30 @@ func (v *Version) AddProtocol(p string) error {
// DownloadFile will download a url and store it in local filepath.
// It writes to the destination file as it downloads it, without
// loading the entire file into memory.
func DownloadFile(url string, filepath string) error {
func DownloadFile(urlValue, filepath string) error {
// Create the file
//nolint:gosec // path traversal is not a concern here, as the filepath is generated by us and not user input
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
defer func(out *os.File) {
err := out.Close()
if err != nil {
slog.Error("failed to close file", slog.Any("err", err))
}
}(out)
// Get the data
resp, err := http.Get(url)
//nolint:gosec,bodyclose // this is a controlled URL, not user input
resp, err := http.Get(urlValue)
if err != nil {
return err
}
defer resp.Body.Close()
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
// Write the body to file
_, err = io.Copy(out, resp.Body)

View file

@ -10,6 +10,7 @@ import (
"path/filepath"
"github.com/spf13/cobra"
publish2 "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/cmd/cmd/publish"
)
@ -28,20 +29,32 @@ var publishCmd = &cobra.Command{
Use: "publish",
Short: "Publish terraform provider",
Long: `...`,
RunE: func(_ *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
return publish()
},
}
func init() { // nolint: gochecknoinits
func init() { //nolint:gochecknoinits //this is the standard way to set up cobra commands
publishCmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace for the Terraform registry.")
publishCmd.Flags().StringVarP(&domain, "domain", "d", "", "Domain for the Terraform registry.")
publishCmd.Flags().StringVarP(&providerName, "providerName", "p", "", "ProviderName for the Terraform registry.")
publishCmd.Flags().StringVarP(&distPath, "distPath", "x", "dist", "Dist Path for the Terraform registry.")
publishCmd.Flags().StringVarP(&repoName, "repoName", "r", "", "RepoName for the Terraform registry.")
publishCmd.Flags().StringVarP(&version, "version", "v", "", "Version for the Terraform registry.")
publishCmd.Flags().StringVarP(&gpgFingerprint, "gpgFingerprint", "f", "", "GPG Fingerprint for the Terraform registry.")
publishCmd.Flags().StringVarP(&gpgPubKeyFile, "gpgPubKeyFile", "k", "", "GPG PubKey file name for the Terraform registry.")
publishCmd.Flags().StringVarP(
&gpgFingerprint,
"gpgFingerprint",
"f",
"",
"GPG Fingerprint for the Terraform registry.",
)
publishCmd.Flags().StringVarP(
&gpgPubKeyFile,
"gpgPubKeyFile",
"k",
"",
"GPG PubKey file name for the Terraform registry.",
)
err := publishCmd.MarkFlagRequired("namespace")
if err != nil {
@ -104,6 +117,7 @@ func publish() error {
// Create release dir - only the contents of this need to be uploaded to S3
log.Printf("* Creating release directory")
//nolint:gosec // this directory is not sensitive, so we can use 0750
err = os.MkdirAll(path.Join(p.RootPath, "release"), os.ModePerm)
if err != nil && !errors.Is(err, fs.ErrExist) {
return fmt.Errorf("error creating '%s' dir: %w", path.Join(p.RootPath, "release"), err)

View file

@ -5,8 +5,9 @@ import (
"log/slog"
"os"
"github.com/MatusOllah/slogcolor"
"github.com/SladkyCitron/slogcolor"
cc "github.com/ivanpirog/coloredcobra"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/cmd/cmd"
)
@ -28,6 +29,8 @@ func main() {
rootCmd.AddCommand(
cmd.NewBuildCmd(),
cmd.NewPublishCmd(),
cmd.NewGetFieldsCmd(),
cmd.NewExamplesCmd(),
)
err := rootCmd.Execute()

View file

@ -1,40 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_postgresflexalpha_database Data Source - stackitprivatepreview"
subcategory: ""
description: |-
Postgres Flex database resource schema. Must have a region specified in the provider configuration.
---
# stackitprivatepreview_postgresflexalpha_database (Data Source)
Postgres Flex database resource schema. Must have a `region` specified in the provider configuration.
## Example Usage
```terraform
data "stackitprivatepreview_postgresflexalpha_database" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `instance_id` (String) ID of the Postgres Flex instance.
- `project_id` (String) STACKIT project ID to which the instance is associated.
### Optional
- `database_id` (Number) Database ID.
- `name` (String) Database name.
- `region` (String) The resource region. If not defined, the provider region is used.
### Read-Only
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`instance_id`,`database_id`".
- `owner` (String) Username of the database owner.

View file

@ -1,43 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_postgresflexalpha_flavor Data Source - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_postgresflexalpha_flavor (Data Source)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `cpu` (Number) The cpu count of the instance.
- `node_type` (String) defines the nodeType it can be either single or replica
- `project_id` (String) The cpu count of the instance.
- `ram` (Number) The memory of the instance in Gibibyte.
- `region` (String) The flavor description.
- `storage_class` (String) The memory of the instance in Gibibyte.
### Read-Only
- `description` (String) The flavor description.
- `flavor_id` (String) The flavor id of the instance flavor.
- `id` (String) The terraform id of the instance flavor.
- `max_gb` (Number) maximum storage which can be ordered for the flavor in Gigabyte.
- `min_gb` (Number) minimum storage which is required to order in Gigabyte.
- `storage_classes` (Attributes List) (see [below for nested schema](#nestedatt--storage_classes))
<a id="nestedatt--storage_classes"></a>
### Nested Schema for `storage_classes`
Read-Only:
- `class` (String)
- `max_io_per_sec` (Number)
- `max_through_in_mb` (Number)

View file

@ -1,68 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_postgresflexalpha_flavors Data Source - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_postgresflexalpha_flavors (Data Source)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `project_id` (String) The STACKIT project ID.
- `region` (String) The region which should be addressed
### Optional
- `page` (Number) Number of the page of items list to be returned.
- `size` (Number) Number of items to be returned on each page.
- `sort` (String) Sorting of the flavors to be returned on each page.
### Read-Only
- `flavors` (Attributes List) List of flavors available for the project. (see [below for nested schema](#nestedatt--flavors))
- `pagination` (Attributes) (see [below for nested schema](#nestedatt--pagination))
<a id="nestedatt--flavors"></a>
### Nested Schema for `flavors`
Read-Only:
- `cpu` (Number) The cpu count of the instance.
- `description` (String) The flavor description.
- `id` (String) The id of the instance flavor.
- `max_gb` (Number) maximum storage which can be ordered for the flavor in Gigabyte.
- `memory` (Number) The memory of the instance in Gibibyte.
- `min_gb` (Number) minimum storage which is required to order in Gigabyte.
- `node_type` (String) defines the nodeType it can be either single or replica
- `storage_classes` (Attributes List) maximum storage which can be ordered for the flavor in Gigabyte. (see [below for nested schema](#nestedatt--flavors--storage_classes))
<a id="nestedatt--flavors--storage_classes"></a>
### Nested Schema for `flavors.storage_classes`
Read-Only:
- `class` (String)
- `max_io_per_sec` (Number)
- `max_through_in_mb` (Number)
<a id="nestedatt--pagination"></a>
### Nested Schema for `pagination`
Read-Only:
- `page` (Number)
- `size` (Number)
- `sort` (String)
- `total_pages` (Number)
- `total_rows` (Number)

View file

@ -1,87 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_postgresflexalpha_instance Data Source - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_postgresflexalpha_instance (Data Source)
## Example Usage
```terraform
data "stackitprivatepreview_postgresflexalpha_instance" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `instance_id` (String) The ID of the instance.
- `project_id` (String) The STACKIT project ID.
- `region` (String) The region which should be addressed
### Read-Only
- `acl` (List of String) List of IPV4 cidr.
- `backup_schedule` (String) The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.
- `connection_info` (Attributes) The DNS name and port in the instance overview (see [below for nested schema](#nestedatt--connection_info))
- `encryption` (Attributes) The configuration for instance's volume and backup storage encryption.
⚠︝ **Note:** This feature is in private preview. Supplying this object is only permitted for enabled accounts. If your account does not have access, the request will be rejected. (see [below for nested schema](#nestedatt--encryption))
- `flavor_id` (String) The id of the instance flavor.
- `id` (String) The ID of the instance.
- `is_deletable` (Boolean) Whether the instance can be deleted or not.
- `name` (String) The name of the instance.
- `network` (Attributes) The access configuration of the instance (see [below for nested schema](#nestedatt--network))
- `replicas` (Number) How many replicas the instance should have.
- `retention_days` (Number) How long backups are retained. The value can only be between 32 and 365 days.
- `status` (String) The current status of the instance.
- `storage` (Attributes) The object containing information about the storage size and class. (see [below for nested schema](#nestedatt--storage))
- `version` (String) The Postgres version used for the instance. See [Versions Endpoint](/documentation/postgres-flex-service/version/v3alpha1#tag/Version) for supported version parameters.
<a id="nestedatt--connection_info"></a>
### Nested Schema for `connection_info`
Read-Only:
- `host` (String) The host of the instance.
- `port` (Number) The port of the instance.
<a id="nestedatt--encryption"></a>
### Nested Schema for `encryption`
Read-Only:
- `kek_key_id` (String) The encryption-key key identifier
- `kek_key_ring_id` (String) The encryption-key keyring identifier
- `kek_key_version` (String) The encryption-key version
- `service_account` (String)
<a id="nestedatt--network"></a>
### Nested Schema for `network`
Read-Only:
- `access_scope` (String) The access scope of the instance. It defines if the instance is public or airgapped.
- `acl` (List of String) List of IPV4 cidr.
- `instance_address` (String)
- `router_address` (String)
<a id="nestedatt--storage"></a>
### Nested Schema for `storage`
Read-Only:
- `performance_class` (String) The storage class for the storage.
- `size` (Number) The storage size in Gigabytes.

View file

@ -1,44 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_postgresflexalpha_user Data Source - stackitprivatepreview"
subcategory: ""
description: |-
Postgres Flex user data source schema. Must have a region specified in the provider configuration.
---
# stackitprivatepreview_postgresflexalpha_user (Data Source)
Postgres Flex user data source schema. Must have a `region` specified in the provider configuration.
## Example Usage
```terraform
data "stackitprivatepreview_postgresflexalpha_user" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
user_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `instance_id` (String) ID of the PostgresFlex instance.
- `project_id` (String) STACKIT project ID to which the instance is associated.
- `user_id` (String) User ID.
### Optional
- `region` (String) The resource region. If not defined, the provider region is used.
### Read-Only
- `connection_string` (String) The connection string for the user to the instance.
- `host` (String) The host address for the user to connect to the instance.
- `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`region`,`instance_id`,`user_id`".
- `port` (Number) The port number for the user to connect to the instance.
- `roles` (Set of String) The roles assigned to the user.
- `status` (String) The current status of the user.
- `username` (String) The name of the user.

View file

@ -1,31 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_sqlserverflexalpha_database Data Source - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_sqlserverflexalpha_database (Data Source)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `database_name` (String) The name of the database.
- `instance_id` (String) The ID of the instance.
- `project_id` (String) The STACKIT project ID.
- `region` (String) The region which should be addressed
### Read-Only
- `collation_name` (String) The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.
- `compatibility_level` (Number) CompatibilityLevel of the Database.
- `id` (Number) The id of the database.
- `name` (String) The name of the database.
- `owner` (String) The owner of the database.

View file

@ -1,43 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_sqlserverflexalpha_flavor Data Source - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_sqlserverflexalpha_flavor (Data Source)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `cpu` (Number) The cpu count of the instance.
- `node_type` (String) defines the nodeType it can be either single or replica
- `project_id` (String) The cpu count of the instance.
- `ram` (Number) The memory of the instance in Gibibyte.
- `region` (String) The flavor description.
- `storage_class` (String) The memory of the instance in Gibibyte.
### Read-Only
- `description` (String) The flavor description.
- `flavor_id` (String) The flavor id of the instance flavor.
- `id` (String) The terraform id of the instance flavor.
- `max_gb` (Number) maximum storage which can be ordered for the flavor in Gigabyte.
- `min_gb` (Number) minimum storage which is required to order in Gigabyte.
- `storage_classes` (Attributes List) (see [below for nested schema](#nestedatt--storage_classes))
<a id="nestedatt--storage_classes"></a>
### Nested Schema for `storage_classes`
Read-Only:
- `class` (String)
- `max_io_per_sec` (Number)
- `max_through_in_mb` (Number)

View file

@ -1,77 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_sqlserverflexalpha_instance Data Source - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_sqlserverflexalpha_instance (Data Source)
## Example Usage
```terraform
data "stackitprivatepreview_sqlserverflexalpha_instance" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `instance_id` (String) The ID of the instance.
- `project_id` (String) The STACKIT project ID.
- `region` (String) The region which should be addressed
### Read-Only
- `backup_schedule` (String) The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.
- `edition` (String) Edition of the MSSQL server instance
- `encryption` (Attributes) this defines which key to use for storage encryption (see [below for nested schema](#nestedatt--encryption))
- `flavor_id` (String) The id of the instance flavor.
- `id` (String) The ID of the instance.
- `is_deletable` (Boolean) Whether the instance can be deleted or not.
- `name` (String) The name of the instance.
- `network` (Attributes) The access configuration of the instance (see [below for nested schema](#nestedatt--network))
- `replicas` (Number) How many replicas the instance should have.
- `retention_days` (Number) The days for how long the backup files should be stored before cleaned up. 30 to 365
- `status` (String)
- `storage` (Attributes) The object containing information about the storage size and class. (see [below for nested schema](#nestedatt--storage))
- `version` (String) The sqlserver version used for the instance.
<a id="nestedatt--encryption"></a>
### Nested Schema for `encryption`
Read-Only:
- `kek_key_id` (String) The key identifier
- `kek_key_ring_id` (String) The keyring identifier
- `kek_key_version` (String) The key version
- `service_account` (String)
<a id="nestedatt--network"></a>
### Nested Schema for `network`
Read-Only:
- `access_scope` (String) The network access scope of the instance
⚠️ **Note:** This feature is in private preview. Supplying this object is only permitted for enabled accounts. If your account does not have access, the request will be rejected.
- `acl` (List of String) List of IPV4 cidr.
- `instance_address` (String)
- `router_address` (String)
<a id="nestedatt--storage"></a>
### Nested Schema for `storage`
Read-Only:
- `class` (String) The storage class for the storage.
- `size` (Number) The storage size in Gigabytes.

View file

@ -1,44 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_sqlserverflexalpha_user Data Source - stackitprivatepreview"
subcategory: ""
description: |-
SQLServer Flex user data source schema. Must have a region specified in the provider configuration.
---
# stackitprivatepreview_sqlserverflexalpha_user (Data Source)
SQLServer Flex user data source schema. Must have a `region` specified in the provider configuration.
## Example Usage
```terraform
data "stackitprivatepreview_sqlserverflexalpha_user" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
user_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `instance_id` (String) ID of the SQLServer Flex instance.
- `project_id` (String) STACKIT project ID to which the instance is associated.
- `user_id` (Number) User ID.
### Optional
- `region` (String) The resource region. If not defined, the provider region is used.
### Read-Only
- `default_database` (String)
- `host` (String)
- `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`region`,`instance_id`,`user_id`".
- `port` (Number)
- `roles` (Set of String) Database access levels for the user.
- `status` (String)
- `username` (String) Username of the SQLServer Flex instance.

View file

@ -1,35 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_sqlserverflexalpha_version Data Source - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_sqlserverflexalpha_version (Data Source)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `project_id` (String) The STACKIT project ID.
- `region` (String) The region which should be addressed
### Read-Only
- `versions` (Attributes List) A list containing available sqlserver versions. (see [below for nested schema](#nestedatt--versions))
<a id="nestedatt--versions"></a>
### Nested Schema for `versions`
Read-Only:
- `beta` (Boolean) Flag if the version is a beta version. If set the version may contain bugs and is not fully tested.
- `deprecated` (String) Timestamp in RFC3339 format which says when the version will no longer be supported by STACKIT.
- `recommend` (Boolean) Flag if the version is recommend by the STACKIT Team.
- `version` (String) The sqlserver version used for the instance.

View file

@ -1,84 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview Provider"
description: |-
---
# stackitprivatepreview Provider
## Example Usage
```terraform
provider "stackitprivatepreview" {
default_region = "eu01"
}
# Authentication
# Token flow (scheduled for deprecation and will be removed on December 17, 2025)
provider "stackitprivatepreview" {
default_region = "eu01"
service_account_token = var.service_account_token
}
# Key flow
provider "stackitprivatepreview" {
default_region = "eu01"
service_account_key = var.service_account_key
private_key = var.private_key
}
# Key flow (using path)
provider "stackitprivatepreview" {
default_region = "eu01"
service_account_key_path = var.service_account_key_path
private_key_path = var.private_key_path
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Optional
- `authorization_custom_endpoint` (String) Custom endpoint for the Membership service
- `cdn_custom_endpoint` (String) Custom endpoint for the CDN service
- `credentials_path` (String) Path of JSON from where the credentials are read. Takes precedence over the env var `STACKIT_CREDENTIALS_PATH`. Default value is `~/.stackit/credentials.json`.
- `default_region` (String) Region will be used as the default location for regional services. Not all services require a region, some are global
- `dns_custom_endpoint` (String) Custom endpoint for the DNS service
- `enable_beta_resources` (Boolean) Enable beta resources. Default is false.
- `experiments` (List of String) Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: iam, routing-tables, network
- `git_custom_endpoint` (String) Custom endpoint for the Git service
- `iaas_custom_endpoint` (String) Custom endpoint for the IaaS service
- `kms_custom_endpoint` (String) Custom endpoint for the KMS service
- `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service
- `logme_custom_endpoint` (String) Custom endpoint for the LogMe service
- `mariadb_custom_endpoint` (String) Custom endpoint for the MariaDB service
- `modelserving_custom_endpoint` (String) Custom endpoint for the AI Model Serving service
- `mongodbflex_custom_endpoint` (String) Custom endpoint for the MongoDB Flex service
- `objectstorage_custom_endpoint` (String) Custom endpoint for the Object Storage service
- `observability_custom_endpoint` (String) Custom endpoint for the Observability service
- `opensearch_custom_endpoint` (String) Custom endpoint for the OpenSearch service
- `postgresflex_custom_endpoint` (String) Custom endpoint for the PostgresFlex service
- `private_key` (String) Private RSA key used for authentication, relevant for the key flow. It takes precedence over the private key that is included in the service account key.
- `private_key_path` (String) Path for the private RSA key used for authentication, relevant for the key flow. It takes precedence over the private key that is included in the service account key.
- `rabbitmq_custom_endpoint` (String) Custom endpoint for the RabbitMQ service
- `redis_custom_endpoint` (String) Custom endpoint for the Redis service
- `region` (String, Deprecated) Region will be used as the default location for regional services. Not all services require a region, some are global
- `resourcemanager_custom_endpoint` (String) Custom endpoint for the Resource Manager service
- `scf_custom_endpoint` (String) Custom endpoint for the Cloud Foundry (SCF) service
- `secretsmanager_custom_endpoint` (String) Custom endpoint for the Secrets Manager service
- `server_backup_custom_endpoint` (String) Custom endpoint for the Server Backup service
- `server_update_custom_endpoint` (String) Custom endpoint for the Server Update service
- `service_account_custom_endpoint` (String) Custom endpoint for the Service Account service
- `service_account_email` (String, Deprecated) Service account email. It can also be set using the environment variable STACKIT_SERVICE_ACCOUNT_EMAIL. It is required if you want to use the resource manager project resource.
- `service_account_key` (String) Service account key used for authentication. If set, the key flow will be used to authenticate all operations.
- `service_account_key_path` (String) Path for the service account key used for authentication. If set, the key flow will be used to authenticate all operations.
- `service_account_token` (String, Deprecated) Token used for authentication. If set, the token flow will be used to authenticate all operations.
- `service_enablement_custom_endpoint` (String) Custom endpoint for the Service Enablement API
- `ske_custom_endpoint` (String) Custom endpoint for the Kubernetes Engine (SKE) service
- `sqlserverflex_custom_endpoint` (String) Custom endpoint for the SQL Server Flex service
- `token_custom_endpoint` (String) Custom endpoint for the token API, which is used to request access tokens when using the key flow

View file

@ -1,47 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_postgresflexalpha_database Resource - stackitprivatepreview"
subcategory: ""
description: |-
Postgres Flex database resource schema. Must have a region specified in the provider configuration.
---
# stackitprivatepreview_postgresflexalpha_database (Resource)
Postgres Flex database resource schema. Must have a `region` specified in the provider configuration.
## Example Usage
```terraform
resource "stackitprivatepreview_postgresflexalpha_database" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "mydb"
owner = "myusername"
}
# Only use the import statement, if you want to import an existing postgresflex database
import {
to = stackitprivatepreview_postgresflexalpha_database.import-example
id = "${var.project_id},${var.region},${var.postgres_instance_id},${var.postgres_database_id}"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `instance_id` (String) ID of the Postgres Flex instance.
- `name` (String) Database name.
- `owner` (String) Username of the database owner.
- `project_id` (String) STACKIT project ID to which the instance is associated.
### Optional
- `region` (String) The resource region. If not defined, the provider region is used.
### Read-Only
- `database_id` (Number) Database ID.
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`instance_id`,`database_id`".

View file

@ -1,114 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_postgresflexalpha_instance Resource - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_postgresflexalpha_instance (Resource)
## Example Usage
```terraform
resource "stackitprivatepreview_postgresflexalpha_instance" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-instance"
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
backup_schedule = "00 00 * * *"
flavor = {
cpu = 2
ram = 4
}
replicas = 3
storage = {
class = "class"
size = 5
}
version = 14
}
# Only use the import statement, if you want to import an existing postgresflex instance
import {
to = stackitprivatepreview_postgresflexalpha_instance.import-example
id = "${var.project_id},${var.region},${var.postgres_instance_id}"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `backup_schedule` (String) The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.
- `flavor_id` (String) The id of the instance flavor.
- `name` (String) The name of the instance.
- `network` (Attributes) The access configuration of the instance (see [below for nested schema](#nestedatt--network))
- `replicas` (Number) How many replicas the instance should have.
- `retention_days` (Number) How long backups are retained. The value can only be between 32 and 365 days.
- `storage` (Attributes) The object containing information about the storage size and class. (see [below for nested schema](#nestedatt--storage))
- `version` (String) The Postgres version used for the instance. See [Versions Endpoint](/documentation/postgres-flex-service/version/v3alpha1#tag/Version) for supported version parameters.
### Optional
- `encryption` (Attributes) The configuration for instance's volume and backup storage encryption.
⚠︝ **Note:** This feature is in private preview. Supplying this object is only permitted for enabled accounts. If your account does not have access, the request will be rejected. (see [below for nested schema](#nestedatt--encryption))
- `instance_id` (String) The ID of the instance.
- `project_id` (String) The STACKIT project ID.
- `region` (String) The region which should be addressed
### Read-Only
- `acl` (List of String) List of IPV4 cidr.
- `connection_info` (Attributes) The DNS name and port in the instance overview (see [below for nested schema](#nestedatt--connection_info))
- `id` (String) The ID of the instance.
- `is_deletable` (Boolean) Whether the instance can be deleted or not.
- `status` (String) The current status of the instance.
<a id="nestedatt--network"></a>
### Nested Schema for `network`
Required:
- `acl` (List of String) List of IPV4 cidr.
Optional:
- `access_scope` (String) The access scope of the instance. It defines if the instance is public or airgapped.
Read-Only:
- `instance_address` (String)
- `router_address` (String)
<a id="nestedatt--storage"></a>
### Nested Schema for `storage`
Required:
- `performance_class` (String) The storage class for the storage.
- `size` (Number) The storage size in Gigabytes.
<a id="nestedatt--encryption"></a>
### Nested Schema for `encryption`
Required:
- `kek_key_id` (String) The encryption-key key identifier
- `kek_key_ring_id` (String) The encryption-key keyring identifier
- `kek_key_version` (String) The encryption-key version
- `service_account` (String)
<a id="nestedatt--connection_info"></a>
### Nested Schema for `connection_info`
Read-Only:
- `host` (String) The host of the instance.
- `port` (Number) The port of the instance.

View file

@ -1,52 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_postgresflexalpha_user Resource - stackitprivatepreview"
subcategory: ""
description: |-
Postgres Flex user resource schema. Must have a region specified in the provider configuration.
---
# stackitprivatepreview_postgresflexalpha_user (Resource)
Postgres Flex user resource schema. Must have a `region` specified in the provider configuration.
## Example Usage
```terraform
resource "stackitprivatepreview_postgresflexalpha_user" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
username = "username"
roles = ["role"]
}
# Only use the import statement, if you want to import an existing postgresflex user
import {
to = stackitprivatepreview_postgresflexalpha_user.import-example
id = "${var.project_id},${var.region},${var.postgres_instance_id},${var.user_id}"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `instance_id` (String) ID of the PostgresFlex instance.
- `project_id` (String) STACKIT project ID to which the instance is associated.
- `roles` (Set of String) Database access levels for the user. Possible values are: `login`, `createdb`, `createrole`.
- `username` (String) The name of the user.
### Optional
- `region` (String) The resource region. If not defined, the provider region is used.
### Read-Only
- `connection_string` (String) The connection string for the user to the instance.
- `host` (String) The host of the Postgres Flex instance.
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`instance_id`,`user_id`".
- `password` (String, Sensitive) The password for the user. This is only set upon creation.
- `port` (Number) The port of the Postgres Flex instance.
- `status` (String) The current status of the user.
- `user_id` (Number) User ID.

View file

@ -1,36 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_sqlserverflexalpha_database Resource - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_sqlserverflexalpha_database (Resource)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `name` (String) The name of the database.
- `owner` (String) The owner of the database.
### Optional
- `collation` (String) The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.
- `compatibility` (Number) CompatibilityLevel of the Database.
- `database_name` (String) The name of the database.
- `instance_id` (String) The ID of the instance.
- `project_id` (String) The STACKIT project ID.
- `region` (String) The region which should be addressed
### Read-Only
- `collation_name` (String) The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.
- `compatibility_level` (Number) CompatibilityLevel of the Database.
- `id` (Number) The id of the database.

View file

@ -1,103 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_sqlserverflexalpha_instance Resource - stackitprivatepreview"
subcategory: ""
description: |-
---
# stackitprivatepreview_sqlserverflexalpha_instance (Resource)
## Example Usage
```terraform
resource "stackitprivatepreview_sqlserverflexalpha_instance" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-instance"
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
backup_schedule = "00 00 * * *"
flavor = {
cpu = 4
ram = 16
}
storage = {
class = "class"
size = 5
}
version = 2022
}
# Only use the import statement, if you want to import an existing sqlserverflex instance
import {
to = stackitprivatepreview_sqlserverflexalpha_instance.import-example
id = "${var.project_id},${var.region},${var.sql_instance_id}"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `backup_schedule` (String) The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.
- `flavor_id` (String) The id of the instance flavor.
- `name` (String) The name of the instance.
- `network` (Attributes) the network configuration of the instance. (see [below for nested schema](#nestedatt--network))
- `retention_days` (Number) The days for how long the backup files should be stored before cleaned up. 30 to 365
- `storage` (Attributes) The object containing information about the storage size and class. (see [below for nested schema](#nestedatt--storage))
- `version` (String) The sqlserver version used for the instance.
### Optional
- `encryption` (Attributes) this defines which key to use for storage encryption (see [below for nested schema](#nestedatt--encryption))
- `instance_id` (String) The ID of the instance.
- `project_id` (String) The STACKIT project ID.
- `region` (String) The region which should be addressed
### Read-Only
- `edition` (String) Edition of the MSSQL server instance
- `id` (String) The ID of the instance.
- `is_deletable` (Boolean) Whether the instance can be deleted or not.
- `replicas` (Number) How many replicas the instance should have.
- `status` (String)
<a id="nestedatt--network"></a>
### Nested Schema for `network`
Required:
- `acl` (List of String) List of IPV4 cidr.
Optional:
- `access_scope` (String) The network access scope of the instance
⚠️ **Note:** This feature is in private preview. Supplying this object is only permitted for enabled accounts. If your account does not have access, the request will be rejected.
Read-Only:
- `instance_address` (String)
- `router_address` (String)
<a id="nestedatt--storage"></a>
### Nested Schema for `storage`
Required:
- `class` (String) The storage class for the storage.
- `size` (Number) The storage size in Gigabytes.
<a id="nestedatt--encryption"></a>
### Nested Schema for `encryption`
Required:
- `kek_key_id` (String) The key identifier
- `kek_key_ring_id` (String) The keyring identifier
- `kek_key_version` (String) The key version
- `service_account` (String)

View file

@ -1,52 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackitprivatepreview_sqlserverflexalpha_user Resource - stackitprivatepreview"
subcategory: ""
description: |-
SQLServer Flex user resource schema. Must have a region specified in the provider configuration.
---
# stackitprivatepreview_sqlserverflexalpha_user (Resource)
SQLServer Flex user resource schema. Must have a `region` specified in the provider configuration.
## Example Usage
```terraform
resource "stackitprivatepreview_sqlserverflexalpha_user" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
username = "username"
roles = ["role"]
}
# Only use the import statement, if you want to import an existing sqlserverflex user
import {
to = stackitprivatepreview_sqlserverflexalpha_user.import-example
id = "${var.project_id},${var.region},${var.sql_instance_id},${var.sql_user_id}"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `instance_id` (String) ID of the SQLServer Flex instance.
- `project_id` (String) STACKIT project ID to which the instance is associated.
- `roles` (Set of String) Database access levels for the user. The values for the default roles are: `##STACKIT_DatabaseManager##`, `##STACKIT_LoginManager##`, `##STACKIT_ProcessManager##`, `##STACKIT_ServerManager##`, `##STACKIT_SQLAgentManager##`, `##STACKIT_SQLAgentUser##`
- `username` (String) Username of the SQLServer Flex instance.
### Optional
- `region` (String)
### Read-Only
- `default_database` (String)
- `host` (String)
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`instance_id`,`user_id`".
- `password` (String, Sensitive) Password of the user account.
- `port` (Number)
- `status` (String)
- `user_id` (Number) User ID.

View file

@ -0,0 +1,8 @@
data "stackitprivatepreview_postgresflexalpha_flavor" "flavor" {
project_id = var.project_id
region = var.region
cpu = 4
ram = 16
node_type = "Single"
storage_class = "premium-perf2-stackit"
}

View file

@ -0,0 +1,8 @@
data "stackitprivatepreview_sqlserverflexalpha_flavor" "flavor" {
project_id = var.project_id
region = var.region
cpu = 4
ram = 16
node_type = "Single"
storage_class = "premium-perf2-stackit"
}

View file

@ -0,0 +1,5 @@
data "stackitprivatepreview_sqlserverflexbeta_database" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
database_name = "dbname"
}

View file

@ -0,0 +1,8 @@
data "stackitprivatepreview_sqlserverflexbeta_flavor" "flavor" {
project_id = var.project_id
region = var.region
cpu = 4
ram = 16
node_type = "Single"
storage_class = "premium-perf2-stackit"
}

View file

@ -0,0 +1,4 @@
data "stackitprivatepreview_sqlserverflexbeta_instance" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

View file

@ -2,14 +2,13 @@ provider "stackitprivatepreview" {
default_region = "eu01"
}
# Authentication
# Token flow (scheduled for deprecation and will be removed on December 17, 2025)
provider "stackitprivatepreview" {
default_region = "eu01"
service_account_token = var.service_account_token
default_region = "eu01"
service_account_key_path = "service_account.json"
}
# Authentication
# Key flow
provider "stackitprivatepreview" {
default_region = "eu01"
@ -23,4 +22,3 @@ provider "stackitprivatepreview" {
service_account_key_path = var.service_account_key_path
private_key_path = var.private_key_path
}

View file

@ -9,4 +9,14 @@ resource "stackitprivatepreview_postgresflexalpha_database" "example" {
import {
to = stackitprivatepreview_postgresflexalpha_database.import-example
id = "${var.project_id},${var.region},${var.postgres_instance_id},${var.postgres_database_id}"
}
}
import {
to = stackitprivatepreview_postgresflexalpha_database.import-example
identity = {
project_id = "project_id"
region = "region"
instance_id = "instance_id"
database_id = "database_id"
}
}

View file

@ -1,22 +1,39 @@
resource "stackitprivatepreview_postgresflexalpha_instance" "example" {
resource "stackitprivatepreview_postgresflexalpha_instance" "example-instance" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-instance"
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
backup_schedule = "00 00 * * *"
flavor = {
cpu = 2
ram = 4
}
replicas = 3
backup_schedule = "0 0 * * *"
retention_days = 30
flavor_id = "flavor.id"
replicas = 1
storage = {
class = "class"
size = 5
performance_class = "premium-perf2-stackit"
size = 10
}
version = 14
encryption = {
kek_key_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
kek_key_ring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
kek_key_version = 1
service_account = "service@account.email"
}
network = {
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
access_scope = "PUBLIC"
}
version = 17
}
# Only use the import statement, if you want to import an existing postgresflex instance
import {
to = stackitprivatepreview_postgresflexalpha_instance.import-example
id = "${var.project_id},${var.region},${var.postgres_instance_id}"
}
}
import {
to = stackitprivatepreview_postgresflexalpha_instance.import-example
identity = {
project_id = var.project_id
region = var.region
instance_id = var.postgres_instance_id
}
}

View file

@ -1,7 +1,7 @@
resource "stackitprivatepreview_postgresflexalpha_user" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
username = "username"
name = "username"
roles = ["role"]
}
@ -9,4 +9,14 @@ resource "stackitprivatepreview_postgresflexalpha_user" "example" {
import {
to = stackitprivatepreview_postgresflexalpha_user.import-example
id = "${var.project_id},${var.region},${var.postgres_instance_id},${var.user_id}"
}
}
import {
to = stackitprivatepreview_postgresflexalpha_user.import-example
identity = {
project_id = "project.id"
region = "region"
instance_id = "instance.id"
user_id = "user.id"
}
}

View file

@ -0,0 +1,24 @@
resource "stackitprivatepreview_sqlserverflexalpha_database" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
collation = ""
compatibility = "160"
name = ""
owner = ""
}
# Only use the import statement, if you want to import a existing sqlserverflex database
import {
to = stackitprivatepreview_sqlserverflexalpha_database.import-example
id = "${var.project_id},${var.region},${var.sql_instance_id},${var.sql_user_id}"
}
import {
to = stackitprivatepreview_sqlserverflexalpha_database.import-example
identity = {
project_id = "project.id"
region = "region"
instance_id = "instance.id"
database_id = "database.id"
}
}

View file

@ -0,0 +1,12 @@
resource "stackitprivatepreview_sqlserverflexalpha_user" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
username = "username"
roles = ["role"]
}
# Only use the import statement, if you want to import an existing sqlserverflex user
import {
to = stackitprivatepreview_sqlserverflexalpha_user.import-example
id = "${var.project_id},${var.region},${var.sql_instance_id},${var.sql_user_id}"
}

View file

@ -0,0 +1,76 @@
# without encryption and SNA
resource "stackitprivatepreview_sqlserverflexbeta_instance" "instance" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-instance"
backup_schedule = "0 3 * * *"
retention_days = 31
flavor_id = "flavor_id"
storage = {
class = "premium-perf2-stackit"
size = 50
}
version = 2022
network = {
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
access_scope = "SNA"
}
}
# without encryption and PUBLIC
resource "stackitprivatepreview_sqlserverflexbeta_instance" "instance" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-instance"
backup_schedule = "0 3 * * *"
retention_days = 31
flavor_id = "flavor_id"
storage = {
class = "premium-perf2-stackit"
size = 50
}
version = 2022
network = {
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
access_scope = "PUBLIC"
}
}
# with encryption and SNA
resource "stackitprivatepreview_sqlserverflexbeta_instance" "instance" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-instance"
backup_schedule = "0 3 * * *"
retention_days = 31
flavor_id = "flavor_id"
storage = {
class = "premium-perf2-stackit"
size = 50
}
version = 2022
encryption = {
kek_key_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
kek_key_ring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
kek_key_version = 1
service_account = "service_account@email"
}
network = {
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
access_scope = "SNA"
}
}
# Only use the import statement, if you want to import an existing sqlserverflex instance
import {
to = stackitprivatepreview_sqlserverflexalpha_instance.import-example
id = "${var.project_id},${var.region},${var.sql_instance_id}"
}
# import with identity
import {
to = stackitprivatepreview_sqlserverflexalpha_instance.import-example
identity = {
project_id = var.project_id
region = var.region
instance_id = var.sql_instance_id
}
}

View file

@ -0,0 +1,12 @@
resource "stackitprivatepreview_sqlserverflexalpha_user" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
username = "username"
roles = ["role"]
}
# Only use the import statement, if you want to import an existing sqlserverflex user
import {
to = stackitprivatepreview_sqlserverflexalpha_user.import-example
id = "${var.project_id},${var.region},${var.sql_instance_id},${var.sql_user_id}"
}

262
go.mod
View file

@ -2,10 +2,17 @@ module tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stac
go 1.25.6
require (
github.com/MatusOllah/slogcolor v1.7.0
github.com/SladkyCitron/slogcolor v1.8.0
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/golangci/golangci-lint/v2 v2.10.1
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/hashicorp/terraform-plugin-codegen-framework v0.4.1
github.com/hashicorp/terraform-plugin-codegen-openapi v0.3.0
github.com/hashicorp/terraform-plugin-docs v0.24.0
github.com/hashicorp/terraform-plugin-framework v1.17.0
github.com/hashicorp/terraform-plugin-framework-validators v0.19.0
github.com/hashicorp/terraform-plugin-go v0.29.0
@ -13,74 +20,283 @@ require (
github.com/hashicorp/terraform-plugin-testing v1.14.0
github.com/iancoleman/strcase v0.3.0
github.com/ivanpirog/coloredcobra v1.0.1
github.com/jarcoal/httpmock v1.4.1
github.com/joho/godotenv v1.5.1
github.com/ldez/go-git-cmd-wrapper/v2 v2.9.1
github.com/spf13/cobra v1.10.2
github.com/stackitcloud/stackit-sdk-go/core v0.21.0
github.com/stackitcloud/stackit-sdk-go/core v0.21.1
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.23-alpha
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.1
github.com/teambition/rrule-go v1.8.2
golang.org/x/tools v0.42.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
golang.org/x/telemetry v0.0.0-20260116145544-c6413dc483f5 // indirect
)
require github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
require (
4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
4d63.com/gochecknoglobals v0.2.2 // indirect
codeberg.org/chavacava/garif v0.2.0 // indirect
codeberg.org/polyfloyd/go-errorlint v1.9.0 // indirect
dario.cat/mergo v1.0.1 // indirect
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect
dev.gaijin.team/go/golib v0.6.0 // indirect
github.com/4meepo/tagalign v1.4.3 // indirect
github.com/Abirdcfly/dupword v0.1.7 // indirect
github.com/AdminBenni/iota-mixing v1.0.0 // indirect
github.com/AlwxSin/noinlineerr v1.0.5 // indirect
github.com/Antonboom/errname v1.1.1 // indirect
github.com/Antonboom/nilnil v1.1.1 // indirect
github.com/Antonboom/testifylint v1.6.4 // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/Djarvur/go-err113 v0.1.1 // indirect
github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/MirrexOne/unqueryvet v1.5.3 // indirect
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/alecthomas/chroma/v2 v2.23.1 // indirect
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
github.com/alexkohler/nakedret/v2 v2.0.6 // indirect
github.com/alexkohler/prealloc v1.0.2 // indirect
github.com/alfatraining/structtag v1.0.0 // indirect
github.com/alingse/asasalint v0.0.11 // indirect
github.com/alingse/nilnesserr v0.2.0 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/cloudflare/circl v1.6.2 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/ashanbrown/forbidigo/v2 v2.3.0 // indirect
github.com/ashanbrown/makezero/v2 v2.1.0 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/bkielbasa/cyclop v1.2.3 // indirect
github.com/blizzy78/varnamelen v0.8.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
github.com/bombsimon/wsl/v4 v4.7.0 // indirect
github.com/bombsimon/wsl/v5 v5.6.0 // indirect
github.com/breml/bidichk v0.3.3 // indirect
github.com/breml/errchkjson v0.4.1 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/butuzov/ireturn v0.4.0 // indirect
github.com/butuzov/mirror v1.3.0 // indirect
github.com/catenacyber/perfsprint v0.10.1 // indirect
github.com/ccojocar/zxcvbn-go v1.0.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charithe/durationcheck v0.0.11 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/charmbracelet/x/ansi v0.10.1 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/ckaznocha/intrange v0.3.1 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/curioswitch/go-reassign v0.3.0 // indirect
github.com/daixiang0/gci v0.13.7 // indirect
github.com/dave/dst v0.27.3 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/denis-tingaikin/go-header v0.5.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
github.com/ettle/strcase v0.2.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/firefart/nonamedreturns v1.0.6 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fzipp/gocyclo v0.6.0 // indirect
github.com/ghostiam/protogetter v0.3.20 // indirect
github.com/go-critic/go-critic v0.14.3 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
github.com/go-toolsmith/astcopy v1.1.0 // indirect
github.com/go-toolsmith/astequal v1.2.0 // indirect
github.com/go-toolsmith/astfmt v1.1.0 // indirect
github.com/go-toolsmith/astp v1.1.0 // indirect
github.com/go-toolsmith/strparse v1.1.0 // indirect
github.com/go-toolsmith/typep v1.1.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/godoc-lint/godoc-lint v0.11.2 // indirect
github.com/gofrs/flock v0.13.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golangci/asciicheck v0.5.0 // indirect
github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect
github.com/golangci/go-printf-func-name v0.1.1 // indirect
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect
github.com/golangci/golines v0.15.0 // indirect
github.com/golangci/misspell v0.8.0 // indirect
github.com/golangci/plugin-module-register v0.1.2 // indirect
github.com/golangci/revgrep v0.8.0 // indirect
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect
github.com/gordonklaus/ineffassign v0.2.0 // indirect
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
github.com/gostaticanalysis/comment v1.5.0 // indirect
github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect
github.com/gostaticanalysis/nilerr v0.1.2 // indirect
github.com/hashicorp/cli v1.1.7 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-cty v1.5.0 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.7.0 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.8.0 // indirect
github.com/hashicorp/hc-install v0.9.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hc-install v0.9.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/hcl/v2 v2.24.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.24.0 // indirect
github.com/hashicorp/terraform-exec v0.25.0 // indirect
github.com/hashicorp/terraform-json v0.27.2 // indirect
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1 // indirect
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0 // indirect
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.2 // indirect
github.com/hashicorp/terraform-registry-address v0.4.0 // indirect
github.com/hashicorp/terraform-svchost v0.2.0 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/jgautheron/goconst v1.8.2 // indirect
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
github.com/jjti/go-spancheck v0.6.5 // indirect
github.com/julz/importas v0.2.0 // indirect
github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect
github.com/kisielk/errcheck v1.9.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
github.com/kulti/thelper v0.7.1 // indirect
github.com/kunwardeep/paralleltest v1.0.15 // indirect
github.com/lasiar/canonicalheader v1.1.2 // indirect
github.com/ldez/exptostd v0.4.5 // indirect
github.com/ldez/gomoddirectives v0.8.0 // indirect
github.com/ldez/grignotin v0.10.1 // indirect
github.com/ldez/structtags v0.6.1 // indirect
github.com/ldez/tagliatelle v0.7.2 // indirect
github.com/ldez/usetesting v0.5.0 // indirect
github.com/leonklingele/grouper v1.1.2 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/macabu/inamedparam v0.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect
github.com/manuelarte/funcorder v0.5.0 // indirect
github.com/maratori/testableexamples v1.0.1 // indirect
github.com/maratori/testpackage v1.1.2 // indirect
github.com/matoous/godox v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mgechev/revive v1.14.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moricho/tparallel v0.3.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nishanths/exhaustive v0.12.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
github.com/nunnatsa/ginkgolinter v0.23.0 // indirect
github.com/oklog/run v1.2.0 // indirect
github.com/pb33f/libopenapi v0.15.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/quasilyte/go-ruleguard v0.4.5 // indirect
github.com/quasilyte/go-ruleguard/dsl v0.3.23 // indirect
github.com/quasilyte/gogrep v0.5.0 // indirect
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
github.com/raeperd/recvcheck v0.2.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/ryancurrah/gomodguard v1.4.1 // indirect
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect
github.com/securego/gosec/v2 v2.23.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/sivchari/containedctx v1.0.3 // indirect
github.com/sonatard/noctx v0.4.0 // indirect
github.com/sourcegraph/go-diff v0.7.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.12.0 // indirect
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/tetafro/godot v1.5.4 // indirect
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect
github.com/timonwong/loggercheck v0.11.0 // indirect
github.com/tomarrell/wrapcheck/v2 v2.12.0 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
github.com/ultraware/funlen v0.2.0 // indirect
github.com/ultraware/whitespace v0.2.0 // indirect
github.com/uudashr/gocognit v1.2.0 // indirect
github.com/uudashr/iface v1.4.1 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xen0n/gosmopolitan v1.3.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yagipy/maintidx v1.0.0 // indirect
github.com/yeya24/promlinter v0.3.0 // indirect
github.com/ykadowak/zerologlint v0.1.5 // indirect
github.com/yuin/goldmark v1.7.7 // indirect
github.com/yuin/goldmark-meta v1.1.0 // indirect
github.com/zclconf/go-cty v1.17.0 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/mod v0.32.0 // indirect
golang.org/x/net v0.49.0 // indirect
gitlab.com/bosi/decorder v0.4.2 // indirect
go-simpler.org/musttag v0.14.0 // indirect
go-simpler.org/sloglint v0.11.1 // indirect
go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
go.augendre.info/arangolint v0.4.0 // indirect
go.augendre.info/fatcontext v0.9.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
golang.org/x/tools v0.41.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
google.golang.org/grpc v1.78.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/grpc v1.79.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
honnef.co/go/tools v0.7.0 // indirect
mvdan.cc/gofumpt v0.9.2 // indirect
mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect
)
tool golang.org/x/tools/cmd/goimports

1103
go.sum

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,13 @@
version: "2"
run:
concurrency: 4
output:
formats:
text:
print-linter-name: true
print-issued-lines: true
colors: true
path: stdout
linters:
enable:
- bodyclose
@ -24,6 +31,11 @@ linters:
rules:
main:
list-mode: lax
allow:
- tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview
- github.com/hashicorp/terraform-plugin-framework
- github.com/hashicorp/terraform-plugin-log
- github.com/stackitcloud/stackit-sdk-go
deny:
- pkg: github.com/stretchr/testify
desc: Do not use a testing framework
@ -63,13 +75,18 @@ linters:
- name: empty-lines
- name: early-return
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
- tools/copy.go
- tools/main.go
- stackit-sdk-generator/
- generated/
- pkg_gen/
generated: lax
warn-unused: true
# Excluding configuration per-path, per-linter, per-text and per-source.
rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- gochecknoinits
formatters:
enable:
- gofmt
@ -77,10 +94,4 @@ formatters:
settings:
goimports:
local-prefixes:
- github.com/freiheit-com/nmww
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
- tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview

View file

@ -0,0 +1,11 @@
package testutil
import "testing"
func Equal[V comparable](t *testing.T, got, expected V) {
t.Helper()
if expected != got {
t.Errorf("assert equal failed:\ngot: %v \nexpected: %v", got, expected)
}
}

View file

@ -1,9 +1,6 @@
// Copyright (c) STACKIT
package testutil
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
@ -20,7 +17,8 @@ import (
const (
// Default location of credentials JSON
credentialsFilePath = ".stackit/credentials.json" //nolint:gosec // linter false positive
// credentialsFilePath = ".stackit/credentials.json" //nolint:gosec // linter false positive
serviceAccountFilePath = ".stackit/service_account.json"
)
var (
@ -29,7 +27,7 @@ var (
// CLI command executed to create a provider server to which the CLI can
// reattach.
TestAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"stackit": providerserver.NewProtocol6WithError(stackit.New("test-version")()),
"stackitprivatepreview": providerserver.NewProtocol6WithError(stackit.New("test-version")()),
}
// TestEphemeralAccProtoV6ProviderFactories is used to instantiate a provider during
@ -40,8 +38,8 @@ var (
// See the Terraform acceptance test documentation on ephemeral resources for more information:
// https://developer.hashicorp.com/terraform/plugin/testing/acceptance-tests/ephemeral-resources
TestEphemeralAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"stackit": providerserver.NewProtocol6WithError(stackit.New("test-version")()),
"echo": echoprovider.NewProviderServer(),
"stackitprivatepreview": providerserver.NewProtocol6WithError(stackit.New("test-version")()),
"echo": echoprovider.NewProviderServer(),
}
// E2ETestsEnabled checks if end-to-end tests should be run.
@ -52,6 +50,8 @@ var (
// ProjectId is the id of project used for tests
ProjectId = os.Getenv("TF_ACC_PROJECT_ID")
Region = os.Getenv("TF_ACC_REGION")
// ServiceAccountFile is the json file of the service account
ServiceAccountFile = os.Getenv("TF_ACC_SERVICE_ACCOUNT_FILE")
// ServerId is the id of a server used for some tests
ServerId = getenv("TF_ACC_SERVER_ID", "")
// TestProjectParentContainerID is the container id of the parent resource under which projects are created as part of the resource-manager acceptance tests
@ -97,26 +97,27 @@ var (
func ObservabilityProviderConfig() string {
if ObservabilityCustomEndpoint == "" {
return `provider "stackit" {
return `provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
observability_custom_endpoint = "%s"
}`,
ObservabilityCustomEndpoint,
)
}
func CdnProviderConfig() string {
if CdnCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
cdn_custom_endpoint = "%s"
enable_beta_resources = true
}`,
@ -126,10 +127,10 @@ func CdnProviderConfig() string {
func DnsProviderConfig() string {
if DnsCustomEndpoint == "" {
return `provider "stackit" {}`
return `provider "stackitprivatepreview" {}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
dns_custom_endpoint = "%s"
}`,
DnsCustomEndpoint,
@ -139,12 +140,12 @@ func DnsProviderConfig() string {
func IaaSProviderConfig() string {
if IaaSCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
iaas_custom_endpoint = "%s"
}`,
IaaSCustomEndpoint,
@ -154,13 +155,13 @@ func IaaSProviderConfig() string {
func IaaSProviderConfigWithBetaResourcesEnabled() string {
if IaaSCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
enable_beta_resources = true
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
enable_beta_resources = true
iaas_custom_endpoint = "%s"
}`,
@ -171,13 +172,13 @@ func IaaSProviderConfigWithBetaResourcesEnabled() string {
func IaaSProviderConfigWithExperiments() string {
if IaaSCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
experiments = [ "routing-tables", "network" ]
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
iaas_custom_endpoint = "%s"
experiments = [ "routing-tables", "network" ]
}`,
@ -188,12 +189,12 @@ func IaaSProviderConfigWithExperiments() string {
func KMSProviderConfig() string {
if KMSCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
kms_custom_endpoint = "%s"
}`,
KMSCustomEndpoint,
@ -203,12 +204,12 @@ func KMSProviderConfig() string {
func LoadBalancerProviderConfig() string {
if LoadBalancerCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
loadbalancer_custom_endpoint = "%s"
}`,
LoadBalancerCustomEndpoint,
@ -218,12 +219,12 @@ func LoadBalancerProviderConfig() string {
func LogMeProviderConfig() string {
if LogMeCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
logme_custom_endpoint = "%s"
}`,
LogMeCustomEndpoint,
@ -233,12 +234,12 @@ func LogMeProviderConfig() string {
func MariaDBProviderConfig() string {
if MariaDBCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
mariadb_custom_endpoint = "%s"
}`,
MariaDBCustomEndpoint,
@ -248,13 +249,13 @@ func MariaDBProviderConfig() string {
func ModelServingProviderConfig() string {
if ModelServingCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}
`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
modelserving_custom_endpoint = "%s"
}`,
ModelServingCustomEndpoint,
@ -264,12 +265,12 @@ func ModelServingProviderConfig() string {
func MongoDBFlexProviderConfig() string {
if MongoDBFlexCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
mongodbflex_custom_endpoint = "%s"
}`,
MongoDBFlexCustomEndpoint,
@ -279,12 +280,12 @@ func MongoDBFlexProviderConfig() string {
func ObjectStorageProviderConfig() string {
if ObjectStorageCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
objectstorage_custom_endpoint = "%s"
}`,
ObjectStorageCustomEndpoint,
@ -294,29 +295,32 @@ func ObjectStorageProviderConfig() string {
func OpenSearchProviderConfig() string {
if OpenSearchCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
opensearch_custom_endpoint = "%s"
}`,
OpenSearchCustomEndpoint,
)
}
func PostgresFlexProviderConfig() string {
func PostgresFlexProviderConfig(saFile string) string {
if PostgresFlexCustomEndpoint == "" {
return `
provider "stackit" {
return fmt.Sprintf(`
provider "stackitprivatepreview" {
default_region = "eu01"
}`
service_account_key_path = "%s"
}`, saFile)
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
service_account_key_path = "%s"
postgresflex_custom_endpoint = "%s"
}`,
saFile,
PostgresFlexCustomEndpoint,
)
}
@ -324,12 +328,12 @@ func PostgresFlexProviderConfig() string {
func RabbitMQProviderConfig() string {
if RabbitMQCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
rabbitmq_custom_endpoint = "%s"
}`,
RabbitMQCustomEndpoint,
@ -339,12 +343,12 @@ func RabbitMQProviderConfig() string {
func RedisProviderConfig() string {
if RedisCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
redis_custom_endpoint = "%s"
}`,
RedisCustomEndpoint,
@ -352,53 +356,56 @@ func RedisProviderConfig() string {
}
func ResourceManagerProviderConfig() string {
token := GetTestProjectServiceAccountToken("")
key := GetTestProjectServiceAccountJson("")
if ResourceManagerCustomEndpoint == "" || AuthorizationCustomEndpoint == "" {
return fmt.Sprintf(`
provider "stackit" {
service_account_token = "%s"
provider "stackitprivatepreview" {
service_account_key = "%s"
}`,
token,
key,
)
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
resourcemanager_custom_endpoint = "%s"
authorization_custom_endpoint = "%s"
service_account_token = "%s"
}`,
ResourceManagerCustomEndpoint,
AuthorizationCustomEndpoint,
token,
key,
)
}
func SecretsManagerProviderConfig() string {
if SecretsManagerCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
secretsmanager_custom_endpoint = "%s"
}`,
SecretsManagerCustomEndpoint,
)
}
func SQLServerFlexProviderConfig() string {
func SQLServerFlexProviderConfig(saFile string) string {
if SQLServerFlexCustomEndpoint == "" {
return `
provider "stackit" {
return fmt.Sprintf(`
provider "stackitprivatepreview" {
default_region = "eu01"
}`
service_account_key_path = "%s"
}`, saFile)
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
service_account_key_path = "%s"
sqlserverflex_custom_endpoint = "%s"
}`,
saFile,
SQLServerFlexCustomEndpoint,
)
}
@ -406,13 +413,13 @@ func SQLServerFlexProviderConfig() string {
func ServerBackupProviderConfig() string {
if ServerBackupCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
server_backup_custom_endpoint = "%s"
enable_beta_resources = true
}`,
@ -423,13 +430,13 @@ func ServerBackupProviderConfig() string {
func ServerUpdateProviderConfig() string {
if ServerUpdateCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
server_update_custom_endpoint = "%s"
enable_beta_resources = true
}`,
@ -440,12 +447,12 @@ func ServerUpdateProviderConfig() string {
func SKEProviderConfig() string {
if SKECustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
ske_custom_endpoint = "%s"
}`,
SKECustomEndpoint,
@ -455,13 +462,13 @@ func SKEProviderConfig() string {
func AuthorizationProviderConfig() string {
if AuthorizationCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
experiments = ["iam"]
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
authorization_custom_endpoint = "%s"
experiments = ["iam"]
}`,
@ -472,13 +479,13 @@ func AuthorizationProviderConfig() string {
func ServiceAccountProviderConfig() string {
if ServiceAccountCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
service_account_custom_endpoint = "%s"
enable_beta_resources = true
}`,
@ -489,13 +496,13 @@ func ServiceAccountProviderConfig() string {
func GitProviderConfig() string {
if GitCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
git_custom_endpoint = "%s"
enable_beta_resources = true
}`,
@ -506,12 +513,12 @@ func GitProviderConfig() string {
func ScfProviderConfig() string {
if ScfCustomEndpoint == "" {
return `
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
scf_custom_endpoint = "%s"
}`,
@ -526,11 +533,11 @@ func ResourceNameWithDateTime(name string) string {
return fmt.Sprintf("tf-acc-%s-%s", name, dateTimeTrimmed)
}
func GetTestProjectServiceAccountToken(path string) string {
func GetTestProjectServiceAccountJson(path string) string {
var err error
token, tokenSet := os.LookupEnv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN")
token, tokenSet := os.LookupEnv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_JSON")
if !tokenSet || token == "" {
token, err = readTestTokenFromCredentialsFile(path)
token, err = readTestServiceAccountJsonFromFile(path)
if err != nil {
return ""
}
@ -538,11 +545,53 @@ func GetTestProjectServiceAccountToken(path string) string {
return token
}
func readTestTokenFromCredentialsFile(path string) (string, error) {
//func GetTestProjectServiceAccountToken(path string) string {
// var err error
// token, tokenSet := os.LookupEnv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN")
// if !tokenSet || token == "" {
// token, err = readTestTokenFromCredentialsFile(path)
// if err != nil {
// return ""
// }
// }
// return token
//}
//
//func readTestTokenFromCredentialsFile(path string) (string, error) {
// if path == "" {
// customPath, customPathSet := os.LookupEnv("STACKIT_CREDENTIALS_PATH")
// if !customPathSet || customPath == "" {
// path = credentialsFilePath
// home, err := os.UserHomeDir()
// if err != nil {
// return "", fmt.Errorf("getting home directory: %w", err)
// }
// path = filepath.Join(home, path)
// } else {
// path = customPath
// }
// }
//
// credentialsRaw, err := os.ReadFile(path)
// if err != nil {
// return "", fmt.Errorf("opening file: %w", err)
// }
//
// var credentials struct {
// TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN string `json:"TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN"`
// }
// err = json.Unmarshal(credentialsRaw, &credentials)
// if err != nil {
// return "", fmt.Errorf("unmarshalling credentials: %w", err)
// }
// return credentials.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN, nil
//}
func readTestServiceAccountJsonFromFile(path string) (string, error) {
if path == "" {
customPath, customPathSet := os.LookupEnv("STACKIT_CREDENTIALS_PATH")
customPath, customPathSet := os.LookupEnv("STACKIT_SERVICE_ACCOUNT_PATH")
if !customPathSet || customPath == "" {
path = credentialsFilePath
path = serviceAccountFilePath
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("getting home directory: %w", err)
@ -557,15 +606,7 @@ func readTestTokenFromCredentialsFile(path string) (string, error) {
if err != nil {
return "", fmt.Errorf("opening file: %w", err)
}
var credentials struct {
TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN string `json:"TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN"`
}
err = json.Unmarshal(credentialsRaw, &credentials)
if err != nil {
return "", fmt.Errorf("unmarshalling credentials: %w", err)
}
return credentials.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN, nil
return string(credentialsRaw), nil
}
func getenv(key, defaultValue string) string {

View file

@ -0,0 +1,39 @@
package testutils
import (
"fmt"
"net/http"
"path/filepath"
"regexp"
"runtime"
"strings"
"github.com/jarcoal/httpmock"
)
func TestName() string {
pc, _, _, _ := runtime.Caller(1)
nameFull := runtime.FuncForPC(pc).Name()
nameEnd := filepath.Ext(nameFull)
name := strings.TrimPrefix(nameEnd, ".")
return name
}
func ActivateEnvironmentHttpMocks() {
httpmock.RegisterNoResponder(
func(req *http.Request) (*http.Response, error) {
return nil, fmt.Errorf("no responder found for %s %s, please check your http mocks", req.Method, req.URL)
},
)
httpmock.RegisterRegexpResponder(
"GET",
regexp.MustCompile(`^https://api\.bap\.microsoft\.com/providers/Microsoft\.BusinessAppPlatform/locations/(europe|unitedstates)/environmentLanguages\?api-version=2023-06-01$`),
func(_ *http.Request) (*http.Response, error) {
return httpmock.NewStringResponse(
http.StatusOK,
httpmock.File("../../services/languages/tests/datasource/Validate_Read/get_languages.json").String(),
), nil
},
)
}

View file

@ -0,0 +1,129 @@
package testutils
import (
"bytes"
"fmt"
"log"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"testing"
"text/template"
)
// GetHomeEnvVariableName Helper function to obtain the home directory on different systems.
// Based on os.UserHomeDir().
func GetHomeEnvVariableName() string {
env := "HOME"
switch runtime.GOOS {
case "windows":
env = "USERPROFILE"
case "plan9":
env = "home"
}
return env
}
// CreateTemporaryHome create temporary home and initialize the credentials file as well
func CreateTemporaryHome(createValidCredentialsFile bool, t *testing.T) string {
// create a temporary file
tempHome, err := os.MkdirTemp("", "tempHome")
if err != nil {
t.Fatalf("Failed to create temporary home directory: %v", err)
}
// create credentials file in temp directory
stackitFolder := path.Join(tempHome, ".stackit")
if err := os.Mkdir(stackitFolder, 0o750); err != nil {
t.Fatalf("Failed to create stackit folder: %v", err)
}
filePath := path.Join(stackitFolder, "credentials.json")
file, err := os.Create(filePath)
if err != nil {
t.Fatalf("Failed to create credentials file: %v", err)
}
defer func() {
if err := file.Close(); err != nil {
t.Fatalf("Error while closing the file: %v", err)
}
}()
// Define content, default = invalid token
token := "foo_token"
if createValidCredentialsFile {
token = GetTestProjectServiceAccountJson("")
}
if _, err = file.WriteString(token); err != nil {
t.Fatalf("Error writing to file: %v", err)
}
return tempHome
}
// SetTemporaryHome Function to overwrite the home folder
func SetTemporaryHome(tempHomePath string) {
env := GetHomeEnvVariableName()
if err := os.Setenv(env, tempHomePath); err != nil {
fmt.Printf("Error setting temporary home directory %v", err)
}
}
// CleanupTemporaryHome cleanup the temporary home and reset the environment variable
func CleanupTemporaryHome(tempHomePath string, t *testing.T) {
if err := os.RemoveAll(tempHomePath); err != nil {
t.Fatalf("Error cleaning up temporary folder: %v", err)
}
originalHomeDir, err := os.UserHomeDir()
if err != nil {
t.Fatalf("Failed to restore home directory back to normal: %v", err)
}
// revert back to original home folder
env := GetHomeEnvVariableName()
if err := os.Setenv(env, originalHomeDir); err != nil {
fmt.Printf("Error resetting temporary home directory %v", err)
}
}
func ucFirst(s string) string {
if s == "" {
return ""
}
return strings.ToUpper(s[:1]) + s[1:]
}
func StringFromTemplateMust(tplFile string, data any) string {
res, err := StringFromTemplate(tplFile, data)
if err != nil {
log.Fatalln(err)
}
return res
}
func StringFromTemplate(tplFile string, data any) (string, error) {
fn := template.FuncMap{
"ucfirst": ucFirst,
}
file := filepath.Base(tplFile)
tmpl, err := template.New(file).Funcs(fn).ParseFiles(tplFile)
if err != nil {
return "", err
}
tplBuf := &bytes.Buffer{}
err = tmpl.Execute(tplBuf, data)
if err != nil {
return "", err
}
return tplBuf.String(), nil
}
func ResStr(prefix, resource, name string) string {
return fmt.Sprintf("%s_%s.%s", prefix, resource, name)
}

View file

@ -0,0 +1,465 @@
package testutils
import (
"fmt"
"os"
)
var (
CdnCustomEndpoint = os.Getenv("TF_ACC_CDN_CUSTOM_ENDPOINT")
DnsCustomEndpoint = os.Getenv("TF_ACC_DNS_CUSTOM_ENDPOINT")
GitCustomEndpoint = os.Getenv("TF_ACC_GIT_CUSTOM_ENDPOINT")
IaaSCustomEndpoint = os.Getenv("TF_ACC_IAAS_CUSTOM_ENDPOINT")
KMSCustomEndpoint = os.Getenv("TF_ACC_KMS_CUSTOM_ENDPOINT")
LoadBalancerCustomEndpoint = os.Getenv("TF_ACC_LOADBALANCER_CUSTOM_ENDPOINT")
LogMeCustomEndpoint = os.Getenv("TF_ACC_LOGME_CUSTOM_ENDPOINT")
MariaDBCustomEndpoint = os.Getenv("TF_ACC_MARIADB_CUSTOM_ENDPOINT")
ModelServingCustomEndpoint = os.Getenv("TF_ACC_MODELSERVING_CUSTOM_ENDPOINT")
AuthorizationCustomEndpoint = os.Getenv("TF_ACC_authorization_custom_endpoint")
MongoDBFlexCustomEndpoint = os.Getenv("TF_ACC_MONGODBFLEX_CUSTOM_ENDPOINT")
OpenSearchCustomEndpoint = os.Getenv("TF_ACC_OPENSEARCH_CUSTOM_ENDPOINT")
ObservabilityCustomEndpoint = os.Getenv("TF_ACC_OBSERVABILITY_CUSTOM_ENDPOINT")
ObjectStorageCustomEndpoint = os.Getenv("TF_ACC_OBJECTSTORAGE_CUSTOM_ENDPOINT")
PostgresFlexCustomEndpoint = os.Getenv("TF_ACC_POSTGRESFLEX_CUSTOM_ENDPOINT")
RabbitMQCustomEndpoint = os.Getenv("TF_ACC_RABBITMQ_CUSTOM_ENDPOINT")
RedisCustomEndpoint = os.Getenv("TF_ACC_REDIS_CUSTOM_ENDPOINT")
ResourceManagerCustomEndpoint = os.Getenv("TF_ACC_RESOURCEMANAGER_CUSTOM_ENDPOINT")
ScfCustomEndpoint = os.Getenv("TF_ACC_SCF_CUSTOM_ENDPOINT")
SecretsManagerCustomEndpoint = os.Getenv("TF_ACC_SECRETSMANAGER_CUSTOM_ENDPOINT")
SQLServerFlexCustomEndpoint = os.Getenv("TF_ACC_SQLSERVERFLEX_CUSTOM_ENDPOINT")
ServerBackupCustomEndpoint = os.Getenv("TF_ACC_SERVER_BACKUP_CUSTOM_ENDPOINT")
ServerUpdateCustomEndpoint = os.Getenv("TF_ACC_SERVER_UPDATE_CUSTOM_ENDPOINT")
ServiceAccountCustomEndpoint = os.Getenv("TF_ACC_SERVICE_ACCOUNT_CUSTOM_ENDPOINT")
SKECustomEndpoint = os.Getenv("TF_ACC_SKE_CUSTOM_ENDPOINT")
)
func ObservabilityProviderConfig() string {
if ObservabilityCustomEndpoint == "" {
return `provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
observability_custom_endpoint = "%s"
}`,
ObservabilityCustomEndpoint,
)
}
func CdnProviderConfig() string {
if CdnCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
cdn_custom_endpoint = "%s"
enable_beta_resources = true
}`,
CdnCustomEndpoint,
)
}
func DnsProviderConfig() string {
if DnsCustomEndpoint == "" {
return `provider "stackitprivatepreview" {}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
dns_custom_endpoint = "%s"
}`,
DnsCustomEndpoint,
)
}
func IaaSProviderConfig() string {
if IaaSCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
iaas_custom_endpoint = "%s"
}`,
IaaSCustomEndpoint,
)
}
func IaaSProviderConfigWithBetaResourcesEnabled() string {
if IaaSCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
enable_beta_resources = true
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
enable_beta_resources = true
iaas_custom_endpoint = "%s"
}`,
IaaSCustomEndpoint,
)
}
func IaaSProviderConfigWithExperiments() string {
if IaaSCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
experiments = [ "routing-tables", "network" ]
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
iaas_custom_endpoint = "%s"
experiments = [ "routing-tables", "network" ]
}`,
IaaSCustomEndpoint,
)
}
func KMSProviderConfig() string {
if KMSCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
kms_custom_endpoint = "%s"
}`,
KMSCustomEndpoint,
)
}
func LoadBalancerProviderConfig() string {
if LoadBalancerCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
loadbalancer_custom_endpoint = "%s"
}`,
LoadBalancerCustomEndpoint,
)
}
func LogMeProviderConfig() string {
if LogMeCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
logme_custom_endpoint = "%s"
}`,
LogMeCustomEndpoint,
)
}
func MariaDBProviderConfig() string {
if MariaDBCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
mariadb_custom_endpoint = "%s"
}`,
MariaDBCustomEndpoint,
)
}
func ModelServingProviderConfig() string {
if ModelServingCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}
`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
modelserving_custom_endpoint = "%s"
}`,
ModelServingCustomEndpoint,
)
}
func MongoDBFlexProviderConfig() string {
if MongoDBFlexCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
mongodbflex_custom_endpoint = "%s"
}`,
MongoDBFlexCustomEndpoint,
)
}
func ObjectStorageProviderConfig() string {
if ObjectStorageCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
objectstorage_custom_endpoint = "%s"
}`,
ObjectStorageCustomEndpoint,
)
}
func OpenSearchProviderConfig() string {
if OpenSearchCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
opensearch_custom_endpoint = "%s"
}`,
OpenSearchCustomEndpoint,
)
}
func PostgresFlexProviderConfig(saFile string) string {
if PostgresFlexCustomEndpoint == "" {
return fmt.Sprintf(`
provider "stackitprivatepreview" {
default_region = "eu01"
service_account_key_path = "%s"
}`, saFile)
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
service_account_key_path = "%s"
postgresflex_custom_endpoint = "%s"
}`,
saFile,
PostgresFlexCustomEndpoint,
)
}
func RabbitMQProviderConfig() string {
if RabbitMQCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
rabbitmq_custom_endpoint = "%s"
}`,
RabbitMQCustomEndpoint,
)
}
func RedisProviderConfig() string {
if RedisCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
redis_custom_endpoint = "%s"
}`,
RedisCustomEndpoint,
)
}
func ResourceManagerProviderConfig() string {
key := GetTestProjectServiceAccountJson("")
if ResourceManagerCustomEndpoint == "" || AuthorizationCustomEndpoint == "" {
return fmt.Sprintf(`
provider "stackitprivatepreview" {
service_account_key = "%s"
}`,
key,
)
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
resourcemanager_custom_endpoint = "%s"
authorization_custom_endpoint = "%s"
service_account_token = "%s"
}`,
ResourceManagerCustomEndpoint,
AuthorizationCustomEndpoint,
key,
)
}
func SecretsManagerProviderConfig() string {
if SecretsManagerCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
secretsmanager_custom_endpoint = "%s"
}`,
SecretsManagerCustomEndpoint,
)
}
func SQLServerFlexProviderConfig(saFile string) string {
if SQLServerFlexCustomEndpoint == "" {
return fmt.Sprintf(`
provider "stackitprivatepreview" {
default_region = "eu01"
service_account_key_path = "%s"
}`, saFile)
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
service_account_key_path = "%s"
sqlserverflex_custom_endpoint = "%s"
}`,
saFile,
SQLServerFlexCustomEndpoint,
)
}
func ServerBackupProviderConfig() string {
if ServerBackupCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
server_backup_custom_endpoint = "%s"
enable_beta_resources = true
}`,
ServerBackupCustomEndpoint,
)
}
func ServerUpdateProviderConfig() string {
if ServerUpdateCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
server_update_custom_endpoint = "%s"
enable_beta_resources = true
}`,
ServerUpdateCustomEndpoint,
)
}
func SKEProviderConfig() string {
if SKECustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
ske_custom_endpoint = "%s"
}`,
SKECustomEndpoint,
)
}
func AuthorizationProviderConfig() string {
if AuthorizationCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
experiments = ["iam"]
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
authorization_custom_endpoint = "%s"
experiments = ["iam"]
}`,
AuthorizationCustomEndpoint,
)
}
func ServiceAccountProviderConfig() string {
if ServiceAccountCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
service_account_custom_endpoint = "%s"
enable_beta_resources = true
}`,
ServiceAccountCustomEndpoint,
)
}
func GitProviderConfig() string {
if GitCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
git_custom_endpoint = "%s"
enable_beta_resources = true
}`,
GitCustomEndpoint,
)
}
func ScfProviderConfig() string {
if ScfCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
default_region = "eu01"
scf_custom_endpoint = "%s"
}`,
ScfCustomEndpoint,
)
}

View file

@ -0,0 +1,219 @@
package testutils
import (
"fmt"
"log"
"log/slog"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-testing/config"
"github.com/hashicorp/terraform-plugin-testing/echoprovider"
"github.com/joho/godotenv"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit"
)
const (
// Default location of credentials JSON
// credentialsFilePath = ".stackit/credentials.json" //nolint:gosec // linter false positive
serviceAccountFilePath = ".stackit/service_account.json"
)
var (
// TestAccProtoV6ProviderFactories is used to instantiate a provider during
// acceptance testing. The factory function will be invoked for every Terraform
// CLI command executed to create a provider server to which the CLI can
// reattach.
TestAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"stackitprivatepreview": providerserver.NewProtocol6WithError(stackit.New("test-version")()),
}
// TestEphemeralAccProtoV6ProviderFactories is used to instantiate a provider during
// acceptance testing. The factory function will be invoked for every Terraform
// CLI command executed to create a provider server to which the CLI can
// reattach.
//
// See the Terraform acceptance test documentation on ephemeral resources for more information:
// https://developer.hashicorp.com/terraform/plugin/testing/acceptance-tests/ephemeral-resources
TestEphemeralAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"stackitprivatepreview": providerserver.NewProtocol6WithError(stackit.New("test-version")()),
"echo": echoprovider.NewProviderServer(),
}
// E2ETestsEnabled checks if end-to-end tests should be run.
// It is enabled when the TF_ACC environment variable is set to "1".
E2ETestsEnabled = os.Getenv("TF_ACC") == "1"
// OrganizationId is the id of organization used for tests
OrganizationId = os.Getenv("TF_ACC_ORGANIZATION_ID")
// ProjectId is the id of project used for tests
ProjectId = os.Getenv("TF_ACC_PROJECT_ID")
Region = os.Getenv("TF_ACC_REGION")
// ServiceAccountFile is the json file of the service account
ServiceAccountFile = os.Getenv("TF_ACC_SERVICE_ACCOUNT_FILE")
// ServerId is the id of a server used for some tests
ServerId = getenv("TF_ACC_SERVER_ID", "")
// TestProjectParentContainerID is the container id of the parent resource under which projects are created as part of the resource-manager acceptance tests
TestProjectParentContainerID = os.Getenv("TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID")
// TestProjectParentUUID is the uuid of the parent resource under which projects are created as part of the resource-manager acceptance tests
TestProjectParentUUID = os.Getenv("TF_ACC_TEST_PROJECT_PARENT_UUID")
// TestProjectServiceAccountEmail is the e-mail of a service account with admin permissions on the organization under which projects are created as part of the resource-manager acceptance tests
TestProjectServiceAccountEmail = os.Getenv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL")
// TestProjectUserEmail is the e-mail of a user for the project created as part of the resource-manager acceptance tests
// Default email: acc-test@sa.stackit.cloud
TestProjectUserEmail = getenv("TF_ACC_TEST_PROJECT_USER_EMAIL", "acc-test@sa.stackit.cloud")
// TestImageLocalFilePath is the local path to an image file used for image acceptance tests
TestImageLocalFilePath = getenv("TF_ACC_TEST_IMAGE_LOCAL_FILE_PATH", "default")
)
func Setup() {
root, err := getRoot()
if err != nil {
log.Fatalln(err)
}
err = godotenv.Load(fmt.Sprintf("%s/.env", *root))
if err != nil {
slog.Info("could not find .env file - not loading .env")
return
}
slog.Info("loaded .env file", "path", *root)
}
func getRoot() (*string, error) {
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
out, err := cmd.Output()
if err != nil {
return nil, err
}
lines := strings.Split(string(out), "\n")
return &lines[0], nil
}
func ResourceNameWithDateTime(name string) string {
dateTime := time.Now().Format(time.RFC3339)
// Remove timezone to have a smaller datetime
dateTimeTrimmed, _, _ := strings.Cut(dateTime, "+")
return fmt.Sprintf("tf-acc-%s-%s", name, dateTimeTrimmed)
}
func GetTestProjectServiceAccountJson(path string) string {
var err error
token, tokenSet := os.LookupEnv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_JSON")
if !tokenSet || token == "" {
token, err = readTestServiceAccountJsonFromFile(path)
if err != nil {
return ""
}
}
return token
}
// func GetTestProjectServiceAccountToken(path string) string {
// var err error
// token, tokenSet := os.LookupEnv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN")
// if !tokenSet || token == "" {
// token, err = readTestTokenFromCredentialsFile(path)
// if err != nil {
// return ""
// }
// }
// return token
//}
//
// func readTestTokenFromCredentialsFile(path string) (string, error) {
// if path == "" {
// customPath, customPathSet := os.LookupEnv("STACKIT_CREDENTIALS_PATH")
// if !customPathSet || customPath == "" {
// path = credentialsFilePath
// home, err := os.UserHomeDir()
// if err != nil {
// return "", fmt.Errorf("getting home directory: %w", err)
// }
// path = filepath.Join(home, path)
// } else {
// path = customPath
// }
// }
//
// credentialsRaw, err := os.ReadFile(path)
// if err != nil {
// return "", fmt.Errorf("opening file: %w", err)
// }
//
// var credentials struct {
// TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN string `json:"TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN"`
// }
// err = json.Unmarshal(credentialsRaw, &credentials)
// if err != nil {
// return "", fmt.Errorf("unmarshalling credentials: %w", err)
// }
// return credentials.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN, nil
//}
func readTestServiceAccountJsonFromFile(path string) (string, error) {
if path == "" {
customPath, customPathSet := os.LookupEnv("STACKIT_SERVICE_ACCOUNT_PATH")
if !customPathSet || customPath == "" {
path = serviceAccountFilePath
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("getting home directory: %w", err)
}
path = filepath.Join(home, path)
} else {
path = customPath
}
}
credentialsRaw, err := os.ReadFile(path)
if err != nil {
return "", fmt.Errorf("opening file: %w", err)
}
return string(credentialsRaw), nil
}
func getenv(key, defaultValue string) string {
val := os.Getenv(key)
if val == "" {
return defaultValue
}
return val
}
// CreateDefaultLocalFile is a helper for local_file_path. No real data is created
func CreateDefaultLocalFile() os.File {
// Define the file name and size
fileName := "test-512k.img"
size := 512 * 1024 // 512 KB
// Create the file
file, err := os.Create(fileName)
if err != nil {
panic(err)
}
// Seek to the desired position (512 KB)
_, err = file.Seek(int64(size), 0)
if err != nil {
panic(err)
}
return *file
}
func ConvertConfigVariable(variable config.Variable) string {
tmpByteArray, _ := variable.MarshalJSON()
// In case the variable is a string, the quotes should be removed
if tmpByteArray[0] == '"' && tmpByteArray[len(tmpByteArray)-1] == '"' {
result := string(tmpByteArray[1 : len(tmpByteArray)-1])
// Replace escaped quotes which where added MarshalJSON
rawString := strings.ReplaceAll(result, `\"`, `"`)
return rawString
}
return string(tmpByteArray)
}

View file

@ -0,0 +1,48 @@
package testutils
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/config"
)
func TestConvertConfigVariable(t *testing.T) {
tests := []struct {
name string
variable config.Variable
want string
}{
{
name: "string",
variable: config.StringVariable("test"),
want: "test",
},
{
name: "bool: true",
variable: config.BoolVariable(true),
want: "true",
},
{
name: "bool: false",
variable: config.BoolVariable(false),
want: "false",
},
{
name: "integer",
variable: config.IntegerVariable(10),
want: "10",
},
{
name: "quoted string",
variable: config.StringVariable(`instance =~ ".*"`),
want: `instance =~ ".*"`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ConvertConfigVariable(tt.variable); got != tt.want {
t.Errorf("ConvertConfigVariable() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -6,6 +6,7 @@ import (
"log"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit"
)

View file

@ -65,15 +65,15 @@ resource "stackitprivatepreview_postgresflexalpha_instance" "msh-sna-pe-example2
resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbadminuser" {
project_id = var.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example.instance_id
username = var.db_admin_username
roles = ["createdb", "login"]
name = var.db_admin_username
roles = ["createdb", "login", "login"]
# roles = ["createdb", "login", "createrole"]
}
resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbadminuser2" {
project_id = var.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example2.instance_id
username = var.db_admin_username
name = var.db_admin_username
roles = ["createdb", "login"]
# roles = ["createdb", "login", "createrole"]
}
@ -81,7 +81,7 @@ resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbadminuser2" {
resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" {
project_id = var.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example.instance_id
username = var.db_username
name = var.db_name
roles = ["login"]
# roles = ["createdb", "login", "createrole"]
}

View file

@ -1,5 +1,5 @@
data "stackitprivatepreview_sqlserverflexalpha_flavor" "sqlserver_flavor" {
data "stackitprivatepreview_sqlserverflexbeta_flavor" "sqlserver_flavor" {
project_id = var.project_id
region = "eu01"
cpu = 4
@ -9,5 +9,5 @@ data "stackitprivatepreview_sqlserverflexalpha_flavor" "sqlserver_flavor" {
}
output "sqlserver_flavor" {
value = data.stackitprivatepreview_sqlserverflexalpha_flavor.sqlserver_flavor.flavor_id
value = data.stackitprivatepreview_sqlserverflexbeta_flavor.sqlserver_flavor.flavor_id
}

View file

@ -18,15 +18,15 @@
# value = stackit_kms_key.key.key_id
# }
resource "stackitprivatepreview_sqlserverflexalpha_instance" "msh-sna-001" {
resource "stackitprivatepreview_sqlserverflexbeta_instance" "msh-beta-sna-001" {
project_id = var.project_id
name = "msh-sna-001"
name = "msh-beta-sna-001"
backup_schedule = "0 3 * * *"
retention_days = 31
flavor_id = data.stackitprivatepreview_sqlserverflexalpha_flavor.sqlserver_flavor.flavor_id
flavor_id = data.stackitprivatepreview_sqlserverflexbeta_flavor.sqlserver_flavor.flavor_id
storage = {
class = "premium-perf2-stackit"
size = 50
size = 10
}
version = 2022
encryption = {
@ -34,9 +34,11 @@ resource "stackitprivatepreview_sqlserverflexalpha_instance" "msh-sna-001" {
#keyring_id = stackit_kms_keyring.keyring.keyring_id
#key_version = 1
# key with scope public
kek_key_id = "fe039bcf-8d7b-431a-801d-9e81371a6b7b"
# kek_key_id = "fe039bcf-8d7b-431a-801d-9e81371a6b7b"
kek_key_id = "c6878f92-ce55-4b79-8236-ba9d001d7967" # msh-k-001
# key_id = var.key_id
kek_key_ring_id = var.keyring_id
# kek_key_ring_id = var.keyring_id
kek_key_ring_id = "0dea3f5f-9947-4dda-a9d3-18418832cefe" # msh-kr-sna01
kek_key_version = var.key_version
service_account = var.sa_email
}
@ -46,55 +48,16 @@ resource "stackitprivatepreview_sqlserverflexalpha_instance" "msh-sna-001" {
}
}
resource "stackitprivatepreview_sqlserverflexalpha_instance" "msh-nosna-001" {
project_id = var.project_id
name = "msh-nosna-001"
backup_schedule = "0 3 * * *"
retention_days = 31
flavor_id = data.stackitprivatepreview_sqlserverflexalpha_flavor.sqlserver_flavor.flavor_id
storage = {
class = "premium-perf2-stackit"
size = 50
}
version = 2022
# encryption = {
# #key_id = stackit_kms_key.key.key_id
# #keyring_id = stackit_kms_keyring.keyring.keyring_id
# #key_version = 1
# #key_id = var.key_id
# # key with scope public
# key_id = "fe039bcf-8d7b-431a-801d-9e81371a6b7b"
# keyring_id = var.keyring_id
# key_version = var.key_version
# service_account = var.sa_email
# }
network = {
acl = ["0.0.0.0/0", "193.148.160.0/19"]
access_scope = "PUBLIC"
}
resource "stackitprivatepreview_sqlserverflexbeta_user" "betauser" {
project_id = var.project_id
instance_id = stackitprivatepreview_sqlserverflexbeta_instance.msh-beta-sna-001.instance_id
username = "betauser"
roles = ["##STACKIT_DatabaseManager##", "##STACKIT_LoginManager##"]
}
# data "stackitprivatepreview_sqlserverflexalpha_instance" "test" {
# project_id = var.project_id
# instance_id = var.instance_id
# region = "eu01"
# }
# output "test" {
# value = data.stackitprivatepreview_sqlserverflexalpha_instance.test
# }
# resource "stackitprivatepreview_sqlserverflexalpha_user" "ptlsdbadminuser" {
# project_id = var.project_id
# instance_id = stackitprivatepreview_sqlserverflexalpha_instance.sqlsrv.instance_id
# username = var.db_admin_username
# roles = ["##STACKIT_LoginManager##", "##STACKIT_DatabaseManager##"]
# }
# resource "stackitprivatepreview_sqlserverflexalpha_user" "ptlsdbuser" {
# project_id = var.project_id
# instance_id = stackitprivatepreview_sqlserverflexalpha_instance.sqlsrv.instance_id
# username = var.db_username
# roles = ["##STACKIT_LoginManager##"]
# }
resource "stackitprivatepreview_sqlserverflexbeta_database" "betadb" {
project_id = var.project_id
instance_id = stackitprivatepreview_sqlserverflexbeta_instance.msh-beta-sna-001.instance_id
name = "mshtest002"
owner = stackitprivatepreview_sqlserverflexbeta_user.betauser.username
}

View file

@ -1,19 +0,0 @@
#!/usr/bin/env bash
# This script lints the SDK modules and the internal examples
# Pre-requisites: golangci-lint
set -eo pipefail
ROOT_DIR=$(git rev-parse --show-toplevel)
GOLANG_CI_YAML_PATH="${ROOT_DIR}/golang-ci.yaml"
GOLANG_CI_ARGS="--allow-parallel-runners --timeout=5m --config=${GOLANG_CI_YAML_PATH}"
if type -p golangci-lint >/dev/null; then
:
else
echo "golangci-lint not installed, unable to proceed."
exit 1
fi
cd ${ROOT_DIR}
golangci-lint run ${GOLANG_CI_ARGS}

View file

@ -17,11 +17,7 @@ elif [ "$action" = "tools" ]; then
go mod download
# go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.7.2
# go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@v0.21.0
go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@v0.24.0
go install golang.org/x/tools/cmd/goimports@v0.42.0
else
echo "Invalid action: '$action', please use $0 help for help"
fi

View file

@ -14,5 +14,5 @@ fi
mkdir -p ${ROOT_DIR}/docs
echo ">> Generating documentation"
tfplugindocs generate \
go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate \
--provider-name "stackitprivatepreview"

View file

@ -1,4 +1,3 @@
provider:
name: stackitprivatepreview

View file

@ -2,7 +2,7 @@ provider:
name: stackitprivatepreview
data_sources:
collation:
collations:
read:
path: /v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/collations
method: GET

View file

@ -1,13 +1,8 @@
provider:
name: stackitprivatepreview
resources:
database:
schema:
attributes:
aliases:
id: database_id
create:
path: /v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases
method: POST
@ -17,6 +12,10 @@ resources:
delete:
path: /v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases/{databaseName}
method: DELETE
schema:
attributes:
aliases:
id: databaseId
data_sources:
@ -26,9 +25,10 @@ data_sources:
method: GET
database:
attributes:
aliases:
id: database_id
read:
path: /v3alpha1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases/{databaseName}
method: GET
schema:
attributes:
aliases:
id: database_id

View file

@ -3,7 +3,7 @@ provider:
name: stackitprivatepreview
data_sources:
version:
versions:
read:
path: /v3alpha1/projects/{projectId}/regions/{region}/versions
method: GET

View file

@ -0,0 +1,13 @@
provider:
name: stackitprivatepreview
data_sources:
backups:
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/backups
method: GET
backup:
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/backups/{backupId}
method: GET

View file

@ -0,0 +1,8 @@
provider:
name: stackitprivatepreview
data_sources:
collation:
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/collations
method: GET

View file

@ -0,0 +1,33 @@
provider:
name: stackitprivatepreview
resources:
database:
schema:
attributes:
aliases:
databaseId: id
create:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases
method: POST
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases/{databaseName}
method: GET
delete:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases/{databaseName}
method: DELETE
data_sources:
databases:
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases
method: GET
database:
schema:
attributes:
aliases:
databaseId: id
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/databases/{databaseName}
method: GET

View file

@ -0,0 +1,9 @@
provider:
name: stackitprivatepreview
data_sources:
flavors:
read:
path: /v3beta1/projects/{projectId}/regions/{region}/flavors
method: GET

View file

@ -0,0 +1,28 @@
provider:
name: stackitprivatepreview
resources:
instance:
create:
path: /v3beta1/projects/{projectId}/regions/{region}/instances
method: POST
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}
method: GET
update:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}
method: PUT
delete:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}
method: DELETE
data_sources:
instances:
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances
method: GET
instance:
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}
method: GET

View file

@ -0,0 +1,24 @@
provider:
name: stackitprivatepreview
resources:
user:
create:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/users
method: POST
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/users/{userId}
method: GET
update:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/users/{userId}
method: PUT
delete:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/users/{userId}
method: DELETE
data_sources:
user:
read:
path: /v3beta1/projects/{projectId}/regions/{region}/instances/{instanceId}/users
method: GET

View file

@ -0,0 +1,9 @@
provider:
name: stackitprivatepreview
data_sources:
version:
read:
path: /v3beta1/projects/{projectId}/regions/{region}/versions
method: GET

View file

@ -11,6 +11,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
)

View file

@ -8,6 +8,7 @@ import (
"testing"
"github.com/hashicorp/terraform-plugin-framework/diag"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"github.com/google/go-cmp/cmp"

View file

@ -32,7 +32,7 @@ const (
type EphemeralProviderData struct {
ProviderData
PrivateKey string
PrivateKey string //nolint:gosec //this is a placeholder and not used in this code
PrivateKeyPath string
ServiceAccountKey string
ServiceAccountKeyPath string
@ -105,11 +105,13 @@ func DiagsToError(diags diag.Diagnostics) error {
diagsError := diags.Errors()
diagsStrings := make([]string, 0)
for _, diagnostic := range diagsError {
diagsStrings = append(diagsStrings, fmt.Sprintf(
"(%s) %s",
diagnostic.Summary(),
diagnostic.Detail(),
))
diagsStrings = append(
diagsStrings, fmt.Sprintf(
"(%s) %s",
diagnostic.Summary(),
diagnostic.Detail(),
),
)
}
return fmt.Errorf("%s", strings.Join(diagsStrings, ";"))
}
@ -136,14 +138,22 @@ func LogAndAddWarning(ctx context.Context, diags *diag.Diagnostics, summary, det
func LogAndAddWarningBeta(ctx context.Context, diags *diag.Diagnostics, name string, resourceType ResourceType) {
warnTitle := fmt.Sprintf("The %s %q is in beta", resourceType, name)
warnContent := fmt.Sprintf("The %s %q is in beta and may be subject to breaking changes in the future. Use with caution.", resourceType, name)
warnContent := fmt.Sprintf(
"The %s %q is in beta and may be subject to breaking changes in the future. Use with caution.",
resourceType,
name,
)
tflog.Warn(ctx, fmt.Sprintf("%s | %s", warnTitle, warnContent))
diags.AddWarning(warnTitle, warnContent)
}
func LogAndAddErrorBeta(ctx context.Context, diags *diag.Diagnostics, name string, resourceType ResourceType) {
errTitle := fmt.Sprintf("The %s %q is in beta and beta is not enabled", resourceType, name)
errContent := fmt.Sprintf(`The %s %q is in beta and the beta functionality is currently not enabled. To enable it, set the environment variable STACKIT_TF_ENABLE_BETA_RESOURCES to "true" or set the "enable_beta_resources" provider field to true.`, resourceType, name)
errContent := fmt.Sprintf(
`The %s %q is in beta and the beta functionality is currently not enabled. To enable it, set the environment variable STACKIT_TF_ENABLE_BETA_RESOURCES to "true" or set the "enable_beta_resources" provider field to true.`,
resourceType,
name,
)
tflog.Error(ctx, fmt.Sprintf("%s | %s", errTitle, errContent))
diags.AddError(errTitle, errContent)
}
@ -161,8 +171,10 @@ func LogResponse(ctx context.Context) context.Context {
traceId := runtime.GetTraceId(ctx)
ctx = tflog.SetField(ctx, "x-trace-id", traceId)
tflog.Info(ctx, "response data", map[string]interface{}{
"x-trace-id": traceId,
})
tflog.Info(
ctx, "response data", map[string]interface{}{
"x-trace-id": traceId,
},
)
return ctx
}

View file

@ -0,0 +1,237 @@
package core
import (
"context"
"crypto/rand"
"errors"
"fmt"
"math/big"
"net/http"
"time"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
const (
// backoffMultiplier is the factor by which the delay is multiplied for exponential backoff.
backoffMultiplier = 2
// jitterFactor is the divisor used to calculate jitter (e.g., half of the base delay).
jitterFactor = 2
)
var (
// ErrRequestFailedAfterRetries is returned when a request fails after all retry attempts.
ErrRequestFailedAfterRetries = errors.New("request failed after all retry attempts")
)
// RetryRoundTripper implements an http.RoundTripper that adds automatic retry logic for failed requests.
type RetryRoundTripper struct {
next http.RoundTripper
maxRetries int
initialDelay time.Duration
maxDelay time.Duration
perTryTimeout time.Duration
}
// NewRetryRoundTripper creates a new instance of the RetryRoundTripper with the specified configuration.
func NewRetryRoundTripper(
next http.RoundTripper,
maxRetries int,
initialDelay, maxDelay, perTryTimeout time.Duration,
) *RetryRoundTripper {
return &RetryRoundTripper{
next: next,
maxRetries: maxRetries,
initialDelay: initialDelay,
maxDelay: maxDelay,
perTryTimeout: perTryTimeout,
}
}
// RoundTrip executes the request and retries on failure.
func (rrt *RetryRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := rrt.executeRequest(req)
if !rrt.shouldRetry(resp, err) {
if err != nil {
return resp, fmt.Errorf("initial request failed, not retrying: %w", err)
}
return resp, nil
}
return rrt.retryLoop(req, resp, err)
}
// executeRequest performs a single HTTP request with a per-try timeout.
func (rrt *RetryRoundTripper) executeRequest(req *http.Request) (*http.Response, error) {
ctx, cancel := context.WithTimeout(req.Context(), rrt.perTryTimeout)
defer cancel()
resp, err := rrt.next.RoundTrip(req.WithContext(ctx))
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return resp, fmt.Errorf("per-try timeout of %v exceeded: %w", rrt.perTryTimeout, err)
}
return resp, fmt.Errorf("http roundtrip failed: %w", err)
}
return resp, nil
}
// retryLoop handles the retry logic for a failed request.
func (rrt *RetryRoundTripper) retryLoop(
req *http.Request,
initialResp *http.Response,
initialErr error,
) (*http.Response, error) {
var (
lastErr = initialErr
resp = initialResp
currentDelay = rrt.initialDelay
)
ctx := req.Context()
for attempt := 1; attempt <= rrt.maxRetries; attempt++ {
rrt.logRetryAttempt(ctx, attempt, currentDelay, lastErr)
waitDuration := rrt.calculateWaitDurationWithJitter(ctx, currentDelay)
if err := rrt.waitForDelay(ctx, waitDuration); err != nil {
return nil, err // Context was canceled during wait.
}
// Exponential backoff for the next potential retry.
currentDelay = rrt.updateCurrentDelay(currentDelay)
// Retry attempt.
resp, lastErr = rrt.executeRequest(req)
if !rrt.shouldRetry(resp, lastErr) {
if lastErr != nil {
return resp, fmt.Errorf("request failed on retry attempt %d: %w", attempt, lastErr)
}
return resp, nil
}
}
return nil, rrt.handleFinalError(ctx, resp, lastErr)
}
// logRetryAttempt logs the details of a retry attempt.
func (rrt *RetryRoundTripper) logRetryAttempt(
ctx context.Context,
attempt int,
delay time.Duration,
err error,
) {
tflog.Info(
ctx, "Request failed, retrying...", map[string]interface{}{
"attempt": attempt,
"max_attempts": rrt.maxRetries,
"delay": delay,
"error": err,
},
)
}
// updateCurrentDelay calculates the next delay for exponential backoff.
func (rrt *RetryRoundTripper) updateCurrentDelay(currentDelay time.Duration) time.Duration {
currentDelay *= backoffMultiplier
if currentDelay > rrt.maxDelay {
return rrt.maxDelay
}
return currentDelay
}
// handleFinalError constructs and returns the final error after all retries have been exhausted.
func (rrt *RetryRoundTripper) handleFinalError(
ctx context.Context,
resp *http.Response,
lastErr error,
) error {
if resp != nil {
if err := resp.Body.Close(); err != nil {
tflog.Warn(
ctx, "Failed to close response body", map[string]interface{}{
"error": err.Error(),
},
)
}
}
if lastErr != nil {
return fmt.Errorf("%w: %w", ErrRequestFailedAfterRetries, lastErr)
}
// This case occurs if shouldRetry was true due to a retryable status code,
// but all retries failed with similar status codes.
if resp != nil {
return fmt.Errorf(
"%w: last retry attempt failed with status code %d",
ErrRequestFailedAfterRetries,
resp.StatusCode,
)
}
return fmt.Errorf("%w: no response received", ErrRequestFailedAfterRetries)
}
// shouldRetry determines if a request should be retried based on the response or an error.
func (rrt *RetryRoundTripper) shouldRetry(resp *http.Response, err error) bool {
if err != nil {
return true
}
if resp != nil {
if resp.StatusCode == http.StatusBadGateway ||
resp.StatusCode == http.StatusServiceUnavailable ||
resp.StatusCode == http.StatusGatewayTimeout {
return true
}
}
return false
}
// calculateWaitDurationWithJitter calculates the backoff duration for the next retry,
// adding a random jitter to prevent thundering herd issues.
func (rrt *RetryRoundTripper) calculateWaitDurationWithJitter(
ctx context.Context,
baseDelay time.Duration,
) time.Duration {
if baseDelay <= 0 {
return 0
}
maxJitter := int64(baseDelay / jitterFactor)
if maxJitter <= 0 {
return baseDelay
}
random, err := rand.Int(rand.Reader, big.NewInt(maxJitter))
if err != nil {
tflog.Warn(
ctx, "Failed to generate random jitter, proceeding without it.", map[string]interface{}{
"error": err.Error(),
},
)
return baseDelay
}
jitter := time.Duration(random.Int64())
return baseDelay + jitter
}
// waitForDelay pauses execution for a given duration or until the context is canceled.
func (rrt *RetryRoundTripper) waitForDelay(ctx context.Context, delay time.Duration) error {
select {
case <-ctx.Done():
return fmt.Errorf("context canceled during backoff wait: %w", ctx.Err())
case <-time.After(delay):
return nil
}
}

View file

@ -0,0 +1,252 @@
package core
import (
"context"
"errors"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"time"
)
type mockRoundTripper struct {
roundTripFunc func(req *http.Request) (*http.Response, error)
callCount int32
}
func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
atomic.AddInt32(&m.callCount, 1)
return m.roundTripFunc(req)
}
func (m *mockRoundTripper) CallCount() int32 {
return atomic.LoadInt32(&m.callCount)
}
func TestRetryRoundTripper_RoundTrip(t *testing.T) {
t.Parallel()
testRetryConfig := func(next http.RoundTripper) *RetryRoundTripper {
return NewRetryRoundTripper(
next,
3,
1*time.Millisecond,
10*time.Millisecond,
50*time.Millisecond,
)
}
noRetryTests := []struct {
name string
mockStatusCode int
expectedStatusCode int
}{
{
name: "should succeed on the first try",
mockStatusCode: http.StatusOK,
expectedStatusCode: http.StatusOK,
},
{
name: "should not retry on a non-retryable status code like 400",
mockStatusCode: http.StatusBadRequest,
expectedStatusCode: http.StatusBadRequest,
},
}
for _, testCase := range noRetryTests {
t.Run(
testCase.name, func(t *testing.T) {
t.Parallel()
mock := &mockRoundTripper{
roundTripFunc: func(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: testCase.mockStatusCode,
Body: io.NopCloser(nil),
Request: req,
}, nil
},
}
tripper := testRetryConfig(mock)
req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
resp, err := tripper.RoundTrip(req)
if resp != nil {
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
t.Errorf("failed to close response body: %v", closeErr)
}
}()
}
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if resp.StatusCode != testCase.expectedStatusCode {
t.Fatalf("expected status code %d, got %d", testCase.expectedStatusCode, resp.StatusCode)
}
if mock.CallCount() != 1 {
t.Fatalf("expected 1 call, got %d", mock.CallCount())
}
},
)
}
t.Run(
"should retry on retryable status code (503) and eventually fail", func(t *testing.T) {
t.Parallel()
mock := &mockRoundTripper{
roundTripFunc: func(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusServiceUnavailable,
Body: io.NopCloser(nil),
Request: req,
}, nil
},
}
tripper := testRetryConfig(mock)
req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
resp, err := tripper.RoundTrip(req)
if resp != nil {
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
t.Errorf("failed to close response body: %v", closeErr)
}
}()
}
if err == nil {
t.Fatal("expected an error, but got nil")
}
expectedErrorMsg := "last retry attempt failed with status code 503"
if !strings.Contains(err.Error(), expectedErrorMsg) {
t.Fatalf("expected error to contain %q, got %q", expectedErrorMsg, err.Error())
}
if mock.CallCount() != 4 { // 1 initial + 3 retries
t.Fatalf("expected 4 calls, got %d", mock.CallCount())
}
},
)
t.Run(
"should succeed after one retry", func(t *testing.T) {
t.Parallel()
mock := &mockRoundTripper{}
mock.roundTripFunc = func(req *http.Request) (*http.Response, error) {
if mock.CallCount() < 2 {
return &http.Response{
StatusCode: http.StatusServiceUnavailable,
Body: io.NopCloser(nil),
Request: req,
}, nil
}
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(nil),
Request: req,
}, nil
}
tripper := testRetryConfig(mock)
req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
resp, err := tripper.RoundTrip(req)
if resp != nil {
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
t.Errorf("failed to close response body: %v", closeErr)
}
}()
}
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status code %d, got %d", http.StatusOK, resp.StatusCode)
}
if mock.CallCount() != 2 {
t.Fatalf("expected 2 calls, got %d", mock.CallCount())
}
},
)
t.Run(
"should retry on network error", func(t *testing.T) {
t.Parallel()
mockErr := errors.New("simulated network error")
mock := &mockRoundTripper{
roundTripFunc: func(_ *http.Request) (*http.Response, error) {
return nil, mockErr
},
}
tripper := testRetryConfig(mock)
req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
resp, err := tripper.RoundTrip(req)
if resp != nil {
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
t.Errorf("failed to close response body: %v", closeErr)
}
}()
}
if !errors.Is(err, mockErr) {
t.Fatalf("expected error to be %v, got %v", mockErr, err)
}
if mock.CallCount() != 4 { // 1 initial + 3 retries
t.Fatalf("expected 4 calls, got %d", mock.CallCount())
}
},
)
t.Run(
"should abort retries if the main context is canceled", func(t *testing.T) {
t.Parallel()
mock := &mockRoundTripper{
roundTripFunc: func(req *http.Request) (*http.Response, error) {
select {
case <-time.After(100 * time.Millisecond):
return nil, errors.New("this should not be returned")
case <-req.Context().Done():
return nil, req.Context().Err()
}
},
}
tripper := testRetryConfig(mock)
baseCtx := context.Background()
ctx, cancel := context.WithTimeout(baseCtx, 20*time.Millisecond)
defer cancel()
req := httptest.NewRequest(http.MethodGet, "/", http.NoBody).WithContext(ctx)
resp, err := tripper.RoundTrip(req)
if resp != nil {
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
t.Errorf("failed to close response body: %v", closeErr)
}
}()
}
if !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("expected error to be context.DeadlineExceeded, got %v", err)
}
if mock.CallCount() != 1 {
t.Fatalf("expected 1 call, got %d", mock.CallCount())
}
},
)
}

View file

@ -9,6 +9,7 @@ import (
"strings"
"github.com/hashicorp/terraform-plugin-framework/diag"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
)

View file

@ -7,6 +7,7 @@ import (
"testing"
"github.com/hashicorp/terraform-plugin-framework/diag"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
)

View file

@ -10,6 +10,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-log/tflog"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
)

Some files were not shown because too many files have changed in this diff Show more