Compare commits

...

14 Commits

Author SHA1 Message Date
Oscar Hermoso e18d2424d0 📼 ensure that load balancer always sets model state for health checks (#32) 2024-10-25 10:39:11 +08:00
dependabot[bot] e1aac7ec1f 🐹 Bump github.com/hashicorp/terraform-plugin-framework-validators (#31)
Bumps the minor-patch group with 1 update: [github.com/hashicorp/terraform-plugin-framework-validators](https://github.com/hashicorp/terraform-plugin-framework-validators).


Updates `github.com/hashicorp/terraform-plugin-framework-validators` from 0.13.0 to 0.14.0
- [Release notes](https://github.com/hashicorp/terraform-plugin-framework-validators/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-framework-validators/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-framework-validators
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-18 09:50:08 +08:00
Oscar Hermoso 6c3520bbda ⭕ Retry if Servers, SSH Keys, or Load Balancer creation returns 500 response (#29)
* ⭕ retry on failed create of server, load balancer, and SSH key

* 🧹 sweepers for test resources

* 🧹 sweepers, but now they're working
2024-10-13 08:48:12 +08:00
dependabot[bot] 22ea042e33 Bump hashicorp/setup-terraform from 2 to 3 (#23)
Bumps [hashicorp/setup-terraform](https://github.com/hashicorp/setup-terraform) from 2 to 3.
- [Release notes](https://github.com/hashicorp/setup-terraform/releases)
- [Changelog](https://github.com/hashicorp/setup-terraform/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/setup-terraform/compare/v2...v3)

---
updated-dependencies:
- dependency-name: hashicorp/setup-terraform
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-13 08:47:41 +08:00
dependabot[bot] b63f052a9f Bump actions/checkout from 3 to 4 (#22)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-10 16:37:09 +08:00
Oscar Hermoso a5731e0a12 🧲 Fix README link to examples/README.md 2024-10-05 08:34:22 +08:00
Oscar Hermoso e5450d9f27 🐒 Fix SSH key logging, types (#28)
* 🐒 Fix SSH key logging, import type

* 💱 Ignore comments in go generate git diff
2024-10-04 13:10:14 +08:00
dependabot[bot] 53b1643f30 🐹 Bump github.com/oapi-codegen/oapi-codegen/v2 in the minor-patch group (#27)
Bumps the minor-patch group with 1 update: [github.com/oapi-codegen/oapi-codegen/v2](https://github.com/oapi-codegen/oapi-codegen).


Updates `github.com/oapi-codegen/oapi-codegen/v2` from 2.3.0 to 2.4.1
- [Release notes](https://github.com/oapi-codegen/oapi-codegen/releases)
- [Commits](https://github.com/oapi-codegen/oapi-codegen/compare/v2.3.0...v2.4.1)

---
updated-dependencies:
- dependency-name: github.com/oapi-codegen/oapi-codegen/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-04 12:15:54 +08:00
Oscar Hermoso 1084c27ab6 🥢 import ssh key by fingerprint, make tests faster (#26)
* 🥢 import ssh key by fingerprint, make tests faster

* ⌚ Wait for load balancer creation
2024-10-04 10:14:23 +08:00
dependabot[bot] ffcadb74b3 Bump actions/setup-go from 4 to 5 (#21)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 11:41:55 +08:00
Oscar Hermoso d1adea205b 🐛 Move cancel-in-progress to correct location 2024-09-27 08:53:27 +08:00
Oscar Hermoso 57ed8ff440 🐈 pass secrets to test.yml, add prefix to dependabot commit messages (#25)
* 🐈 Add commit message prefix to dependabot.yml

* 😶 Pass GitHub actions secrets from release.yml to test.yml
2024-09-27 08:50:17 +08:00
Oscar Hermoso 702aae5b2f 🐛 Fix release.yml depending on test.yml 2024-09-27 08:27:01 +08:00
Oscar Hermoso 9747587128 🧪 Bump terraform framework to v0.4.1 & test skipping go generate (#24)
* 🧪 Bump terraform framework to v0.4.1 & test skipping go generate

* ⏰ run go generate, update dependabot.yml
2024-09-27 08:22:01 +08:00
31 changed files with 826 additions and 230 deletions
+8
View File
@@ -0,0 +1,8 @@
TF_ACC=1
BINARYLANE_API_TOKEN=
# Uncomment to enable debug logging (will dump request bodies)
# TF_LOG=DEBUG
# Uncomment to disable the Go cache when testing, useful for flaky tests
# GOCACHE=off
+20
View File
@@ -1,10 +1,30 @@
version: 2
updates:
- package-ecosystem: 'github-actions'
commit-message:
prefix: '🐈 '
directory: '/'
schedule:
interval: 'weekly'
day: 'friday'
time: '06:00'
timezone: 'Australia/Perth'
groups:
minor-patch:
update-types:
- 'minor'
- 'patch'
- package-ecosystem: 'gomod'
commit-message:
prefix: '🐹 '
directory: '/'
schedule:
interval: 'weekly'
day: 'friday'
time: '06:00'
timezone: 'Australia/Perth'
groups:
minor-patch:
update-types:
- 'minor'
- 'patch'
+8 -2
View File
@@ -6,7 +6,7 @@ name: Release
on:
push:
tags:
- "v*"
- 'v*'
# Releases need permissions to read and write the repository contents.
# GitHub considers creating releases and uploading assets as writing contents.
@@ -14,7 +14,13 @@ permissions:
contents: write
jobs:
test:
uses: ./.github/workflows/test.yml
secrets:
BINARYLANE_API_TOKEN: ${{ secrets.BINARYLANE_API_TOKEN }}
goreleaser:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
@@ -23,7 +29,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
with:
go-version-file: "go.mod"
go-version-file: 'go.mod'
cache: true
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
-39
View File
@@ -1,39 +0,0 @@
name: Terraform Provider Tests
on:
pull_request:
paths:
- '.github/workflows/test.yaml'
- '**.go'
permissions:
# Permission for checking out code
contents: read
jobs:
acceptance:
name: Acceptance Tests
runs-on: ubuntu-latest
environment: test
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: 'go.mod'
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: '1.5.*'
terraform_wrapper: false
- run: go test -v -cover ./...
env:
TF_ACC: '1'
BINARYLANE_API_TOKEN: ${{ secrets.BINARYLANE_API_TOKEN }}
# unit:
# name: Unit Tests
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: actions/setup-go@v4
# with:
# go-version-file: 'go.mod'
# - run: go test -v -cover ./...
+61
View File
@@ -0,0 +1,61 @@
name: Terraform Provider Tests
on:
pull_request:
paths:
- '.github/workflows/test.yml'
- '**.go'
workflow_call:
secrets:
BINARYLANE_API_TOKEN:
required: true
permissions:
# Permission for checking out code
contents: read
jobs:
acceptance:
name: Acceptance Tests
runs-on: ubuntu-latest
environment: test
concurrency:
group: test
cancel-in-progress: true # would be nice if GH offered cancel-pending instead, https://github.com/orgs/community/discussions/63136
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: '1.5.*'
terraform_wrapper: false
- name: Check if generated files are up to date
run: |
go generate
git diff --color --exit-code --ignore-matching-lines='//'
if [ $? -ne 0 ]; then
echo "generated files are not up to date, please run 'go generate' and commit the changes" >&2
exit 1
fi
- name: Run tests
run: go test -v -cover ./internal/provider/...
env:
TF_ACC: '1'
BINARYLANE_API_TOKEN: ${{ secrets.BINARYLANE_API_TOKEN }}
- name: Run sweepers
if: failure()
run: go test -v ./internal/provider/... -sweep=all
env:
TF_ACC: '1'
BINARYLANE_API_TOKEN: ${{ secrets.BINARYLANE_API_TOKEN }}
# unit:
# name: Unit Tests
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: actions/setup-go@v5
# with:
# go-version-file: 'go.mod'
# - run: go test -v -cover ./...
+3
View File
@@ -0,0 +1,3 @@
{
"go.testEnvFile": "${workspaceFolder}/.env",
}
+10 -1
View File
@@ -35,7 +35,16 @@ terraform apply
```sh
TF_DEBUG=1 BINARYLANE_API_TOKEN=********* TF_ACC=1 go test -v ./...
cp .env.example .env
# Add your API token to .env
eval export $(cat .env)
go test -v ./internal/provider/...
```
To run Sweepers:
```sh
go test -v ./internal/provider/... -sweep=all
```
### Update modules
+4 -4
View File
@@ -1,6 +1,6 @@
# terraform-provider-binarylane
See the examples in the [examples directory](./examples/basic/README.md).
See the examples in the [examples directory](./examples/README.md).
```terraform
resource "binarylane_server" "example" {
@@ -44,7 +44,7 @@ Images:
```sh
curl -X GET "https://api.binarylane.com.au/v2/images?type=distribution&&page=1&per_page=200" \
-H "Authorization: Bearer **********" > tmp/images.json
-H "Authorization: Bearer $BINARYLANE_API_TOKEN" > tmp/images.json
jq '[ .images[] | .slug ] | sort' tmp/images.json
```
@@ -81,7 +81,7 @@ Regions:
```sh
curl -X GET "https://api.binarylane.com.au/v2/regions" \
-H "Authorization: Bearer **********"" > tmp/regions.json
-H "Authorization: Bearer $BINARYLANE_API_TOKEN" > tmp/regions.json
jq '[ .regions[] | .slug ] | sort' tmp/regions.json
```
@@ -103,7 +103,7 @@ jq '[ .regions[] | .slug ] | sort' tmp/regions.json
```sh
curl -X GET "https://api.binarylane.com.au/v2/sizes" \
-H "Authorization: Bearer **********"" > tmp/sizes.json
-H "Authorization: Bearer $BINARYLANE_API_TOKEN" > tmp/sizes.json
jq '[ .sizes[] | .slug ] | sort' tmp/sizes.json
```
+1
View File
@@ -22,5 +22,6 @@ description: |-
### Read-Only
- `default` (Boolean) Optional: If true this will be added to all new server installations (if we support SSH Key injection for the server's operating system).
- `fingerprint` (String) The fingerprint of the SSH key.
- `name` (String) A name to help you identify the key.
- `public_key` (String) The public key in OpenSSH "authorized_keys" format.
+4
View File
@@ -24,3 +24,7 @@ description: |-
- `default` (Boolean) Optional: If true this will be added to all new server installations (if we support SSH Key injection for the server's operating system).
- `id` (Number) The ID or fingerprint of the SSH Key to fetch.
### Read-Only
- `fingerprint` (String) The fingerprint of the SSH key.
+8 -9
View File
@@ -134,15 +134,14 @@ resource "binarylane_server_firewall_rules" "example" {
server_id = each.value.id
firewall_rules = [
# TODO: Due to a bug, this only works if you uncomment AFTER creating servers
# {
# description = "K3s supervisor and Kubernetes API Server"
# protocol = "tcp"
# source_addresses = ["0.0.0.0/0"]
# destination_addresses = local.server_ips
# destination_ports = ["6443"]
# action = "accept"
# },
{
description = "K3s supervisor and Kubernetes API Server"
protocol = "tcp"
source_addresses = ["0.0.0.0/0"]
destination_addresses = local.server_ips
destination_ports = ["6443"]
action = "accept"
},
{
description = "Flannel VXLAN"
protocol = "udp"
+1 -1
View File
@@ -8,4 +8,4 @@ package main
//go:generate mkdir -p internal/resources internal/data_sources
//go:generate go run github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework generate resources --input=./provider_code_spec.json --output=./internal/resources --package=resources
//go:generate go run github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework generate data-sources --input=./provider_code_spec.json --output=./internal/data_sources --package=data_sources
//go:generate tfplugindocs generate
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate
+15 -14
View File
@@ -1,18 +1,18 @@
module terraform-provider-binarylane
go 1.22.3
go 1.22.7
require (
github.com/deepmap/oapi-codegen v1.16.3
github.com/hashicorp/terraform-plugin-codegen-framework v0.4.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.19.4
github.com/hashicorp/terraform-plugin-framework v1.11.0
github.com/hashicorp/terraform-plugin-framework-validators v0.13.0
github.com/hashicorp/terraform-plugin-go v0.23.0
github.com/hashicorp/terraform-plugin-framework v1.12.0
github.com/hashicorp/terraform-plugin-framework-validators v0.14.0
github.com/hashicorp/terraform-plugin-go v0.24.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.10.0
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1
github.com/oapi-codegen/runtime v1.1.1
)
@@ -36,9 +36,9 @@ require (
github.com/fatih/color v1.17.0 // indirect
github.com/frankban/quicktest v1.14.6 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/getkin/kin-openapi v0.124.0 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/swag v0.22.8 // indirect
github.com/getkin/kin-openapi v0.127.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
@@ -58,14 +58,14 @@ require (
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.21.0 // indirect
github.com/hashicorp/terraform-json v0.22.1 // indirect
github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1 // indirect
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0 // indirect
github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 // indirect
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/invopop/yaml v0.2.0 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@@ -84,6 +84,7 @@ require (
github.com/posener/complete v1.2.3 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
@@ -106,8 +107,8 @@ require (
golang.org/x/text v0.18.0 // indirect
golang.org/x/tools v0.22.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect
google.golang.org/grpc v1.67.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+28 -27
View File
@@ -63,18 +63,18 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M=
github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY=
github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
@@ -139,20 +139,20 @@ github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVW
github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg=
github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec=
github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A=
github.com/hashicorp/terraform-plugin-codegen-framework v0.4.0 h1:xB96Rakulnn7ZxikDj8glCyojKpTbW+SGm6QxipoNxU=
github.com/hashicorp/terraform-plugin-codegen-framework v0.4.0/go.mod h1:nXlVjqHFBDr6EXnKTT9RCLmwnuTYj/3LE4ffkIdOtog=
github.com/hashicorp/terraform-plugin-codegen-framework v0.4.1 h1:eaI/3dsu2T5QAXbA+7N+B+UBj20GdtYnsRuYypKh3S4=
github.com/hashicorp/terraform-plugin-codegen-framework v0.4.1/go.mod h1:kpYM23L7NtcfaQdWAN0QFkV/lU0w16qJ2ddAPCI4zAg=
github.com/hashicorp/terraform-plugin-codegen-openapi v0.3.0 h1:IKpc337XKk50QyQPSxLrHwdqSo1E2XqCMxFkWsZcTvc=
github.com/hashicorp/terraform-plugin-codegen-openapi v0.3.0/go.mod h1:tT6wl80h7nsMBw+1yZRgJXi+Ys85PUai11weDqysvp4=
github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1 h1:7djpLeqtz0x6w01W4hckWwINJUnfXn2zGr/EhSC8rek=
github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1/go.mod h1:PQn6bDD8UWoAVJoHXqFk2i/RmLbeQBjbiP38i+E+YIw=
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0 h1:91dQG1A/DxP6vRz9GiytDTrZTXDbhHPvmpYnAyWA/Vw=
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0/go.mod h1:fywrEKpordQypmAjz/HIfm2LuNVmyJ6KDe8XT9GdJxQ=
github.com/hashicorp/terraform-plugin-docs v0.19.4 h1:G3Bgo7J22OMtegIgn8Cd/CaSeyEljqjH3G39w28JK4c=
github.com/hashicorp/terraform-plugin-docs v0.19.4/go.mod h1:4pLASsatTmRynVzsjEhbXZ6s7xBlUw/2Kt0zfrq8HxA=
github.com/hashicorp/terraform-plugin-framework v1.11.0 h1:M7+9zBArexHFXDx/pKTxjE6n/2UCXY6b8FIq9ZYhwfE=
github.com/hashicorp/terraform-plugin-framework v1.11.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM=
github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 h1:bxZfGo9DIUoLLtHMElsu+zwqI4IsMZQBRRy4iLzZJ8E=
github.com/hashicorp/terraform-plugin-framework-validators v0.13.0/go.mod h1:wGeI02gEhj9nPANU62F2jCaHjXulejm/X+af4PdZaNo=
github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co=
github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ=
github.com/hashicorp/terraform-plugin-framework v1.12.0 h1:7HKaueHPaikX5/7cbC1r9d1m12iYHY+FlNZEGxQ42CQ=
github.com/hashicorp/terraform-plugin-framework v1.12.0/go.mod h1:N/IOQ2uYjW60Jp39Cp3mw7I/OpC/GfZ0385R0YibmkE=
github.com/hashicorp/terraform-plugin-framework-validators v0.14.0 h1:3PCn9iyzdVOgHYOBmncpSSOxjQhCTYmc+PGvbdlqSaI=
github.com/hashicorp/terraform-plugin-framework-validators v0.14.0/go.mod h1:LwDKNdzxrDY/mHBrlC6aYfE2fQ3Dk3gaJD64vNiXvo4=
github.com/hashicorp/terraform-plugin-go v0.24.0 h1:2WpHhginCdVhFIrWHxDEg6RBn3YaWzR2o6qUeIEat2U=
github.com/hashicorp/terraform-plugin-go v0.24.0/go.mod h1:tUQ53lAsOyYSckFGEefGC5C8BAaO0ENqzFd3bQeuYQg=
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 h1:kJiWGx2kiQVo97Y5IOGR4EMcZ8DtMswHhUuFibsCQQE=
@@ -163,8 +163,8 @@ github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTV
github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM=
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
@@ -173,8 +173,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
@@ -221,8 +221,8 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0 h1:rICjNsHbPP1LttefanBPnwsSwl09SqhCO7Ee623qR84=
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0/go.mod h1:4k+cJeSq5ntkwlcpQSxLxICCxQzCL772o30PxdibRt4=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
@@ -264,6 +264,8 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/speakeasy-api/openapi-overlay v0.9.0 h1:Wrz6NO02cNlLzx1fB093lBlYxSI54VRhy1aSutx0PQg=
github.com/speakeasy-api/openapi-overlay v0.9.0/go.mod h1:f5FloQrHA7MsxYg9djzMD5h6dxrHjVVByWKh7an8TRc=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
@@ -393,10 +395,10 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -425,6 +427,5 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+49
View File
@@ -0,0 +1,49 @@
package binarylane
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httputil"
"github.com/deepmap/oapi-codegen/pkg/securityprovider"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
func NewClientWithAuth(endpoint string, token string) (*ClientWithResponses, error) {
if token == "" {
return nil, errors.New("missing or empty value for the Binary Lane API " +
"token. Set the `api_token` value in the configuration or use the " +
"BINARYLANE_API_TOKEN environment variable. If either is already set, " +
"ensure the value is not empty")
}
auth, err := securityprovider.NewSecurityProviderBearerToken(token)
if err != nil {
return nil, fmt.Errorf("failed to create API client with supplied API token: %w", err)
}
if endpoint == "" {
endpoint = "https://api.binarylane.com.au/v2"
}
client, err := NewClientWithResponses(
endpoint,
WithRequestEditorFn(func(ctx context.Context, req *http.Request) error {
dump, err := httputil.DumpRequestOut(req, true)
if err != nil {
return err
}
tflog.Debug(ctx, fmt.Sprintf("%q\n", dump))
return nil
}),
WithRequestEditorFn(auth.Intercept), // include auth AFTER the request logger
)
if err != nil {
return nil, fmt.Errorf("failed to create Binary Lane API client: %w", err)
}
return client, nil
}
+1 -1
View File
@@ -1,6 +1,6 @@
// Package binarylane provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT.
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT.
package binarylane
import (
+1
View File
@@ -39,4 +39,5 @@ cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.source_addresse
## Load Balancer
cat <<<$(jq '.components.schemas.CreateLoadBalancerRequest.properties.forwarding_rules += {"x-oapi-codegen-extra-tags": {"tfsdk": "forwarding_rules"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.ForwardingRule.properties.entry_protocol += {"x-oapi-codegen-extra-tags": {"tfsdk": "entry_protocol"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.LoadBalancer.properties.health_check += {"x-oapi-codegen-extra-tags": {"tfsdk": "health_check"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.HealthCheckProtocol |= del(.enum)' $OPENAPI_FILE) >$OPENAPI_FILE
+4 -1
View File
@@ -11845,7 +11845,10 @@
}
],
"description": "The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer.",
"readOnly": true
"readOnly": true,
"x-oapi-codegen-extra-tags": {
"tfsdk": "health_check"
}
},
"region": {
"allOf": [
+2 -2
View File
@@ -1,6 +1,6 @@
// Package binarylane provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT.
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT.
package binarylane
import (
@@ -1860,7 +1860,7 @@ type LoadBalancer struct {
ForwardingRules *[]ForwardingRule `json:"forwarding_rules,omitempty"`
// HealthCheck The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer.
HealthCheck *HealthCheck `json:"health_check,omitempty"`
HealthCheck *HealthCheck `json:"health_check,omitempty" tfsdk:"health_check"`
// Id The ID of the load balancer.
Id *int64 `json:"id,omitempty"`
+84 -11
View File
@@ -7,6 +7,7 @@ import (
"strconv"
"terraform-provider-binarylane/internal/binarylane"
"terraform-provider-binarylane/internal/resources"
"time"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
@@ -127,21 +128,50 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
tflog.Info(ctx, fmt.Sprintf("Creating Load Balancer: name=%s", data.Name.ValueString()))
lbResp, err := r.bc.client.PostLoadBalancersWithResponse(ctx, body)
if err != nil {
tflog.Info(ctx, fmt.Sprintf("Attempted to create new load balancer: request=%+v", body))
resp.Diagnostics.AddError(
fmt.Sprintf("Error sending request to create load balancer: name=%s", data.Name.ValueString()),
err.Error(),
)
return
const maxRetries = 3
var lbResp *binarylane.PostLoadBalancersResponse
retryLoop:
for i := 0; i < maxRetries; i++ {
var err error
lbResp, err = r.bc.client.PostLoadBalancersWithResponse(ctx, body)
if err != nil {
tflog.Info(ctx, fmt.Sprintf("Attempted to create new load balancer: request=%+v", body))
resp.Diagnostics.AddError(
fmt.Sprintf("Error sending request to create load balancer: name=%s", data.Name.ValueString()),
err.Error(),
)
return
}
switch lbResp.StatusCode() {
case http.StatusOK:
break retryLoop
case http.StatusInternalServerError:
if i < maxRetries-1 {
tflog.Warn(ctx, "Received 500 creating load balancer, retrying...")
time.Sleep(time.Second * 5)
continue
}
default:
tflog.Info(ctx, fmt.Sprintf("Attempted to create new load balancer: request=%+v", body))
resp.Diagnostics.AddError(
"Unexpected HTTP status code creating load balancer",
fmt.Sprintf("Received %s creating new load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body),
)
return
}
}
// Check if retries exceeded
if lbResp.StatusCode() != http.StatusOK {
tflog.Info(ctx, fmt.Sprintf("Attempted to create new load balancer: request=%+v", body))
resp.Diagnostics.AddError(
"Unexpected HTTP status code creating load balancer",
fmt.Sprintf("Received %s creating new load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body))
"Failed to create load balancer after retries",
fmt.Sprintf("Final status code: %d", lbResp.StatusCode()),
)
return
}
@@ -153,6 +183,49 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
// Wait for server to be ready
var createActionId int64
for _, action := range *lbResp.JSON200.Links.Actions {
if *action.Rel == "create_load_balancer" {
createActionId = *action.Id
break
}
}
if createActionId == 0 {
resp.Diagnostics.AddError(
"Unable to wait for load balancer to be created, links.actions with rel=create_load_balancer missing from response",
fmt.Sprintf("Received %s creating new load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body))
return
}
timeLimit := time.Now().Add(time.Duration(60 * time.Second))
for {
tflog.Info(ctx, "Waiting for load balancer to be ready...")
readyResp, err := r.bc.client.GetActionsActionIdWithResponse(ctx, createActionId)
if err != nil {
resp.Diagnostics.AddError("Error waiting for load balancer to be ready", err.Error())
return
}
if readyResp.StatusCode() == http.StatusOK && readyResp.JSON200.Action.CompletedAt != nil {
tflog.Info(ctx, "Load balancer is ready")
break
}
if time.Now().After(timeLimit) {
resp.Diagnostics.AddError(
"Timed out waiting for load balancer to be ready",
fmt.Sprintf(
"Timed out waiting for load balancer to be created, as `wait_for_create` was surpassed without "+
"recieving a `completed_at` in response: name=%s, status=%s, details: %s",
data.Name.ValueString(), readyResp.Status(), readyResp.Body,
),
)
return
}
tflog.Debug(ctx, fmt.Sprintf("Waiting for load balancer to be ready: name=%s, status=%s, details: %s", data.Name.ValueString(), readyResp.Status(), readyResp.Body))
time.Sleep(time.Second * 5)
}
}
func (r *loadBalancerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
@@ -1,8 +1,15 @@
package provider
import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"log"
"net/http"
"os"
"strings"
"terraform-provider-binarylane/internal/binarylane"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
@@ -18,7 +25,7 @@ func TestLoadBalancerResource(t *testing.T) {
}
password := base64.URLEncoding.EncodeToString(pw_bytes)
resource.Test(t, resource.TestCase{
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
@@ -84,3 +91,69 @@ data "binarylane_load_balancer" "test" {
},
})
}
func init() {
resource.AddTestSweepers("load_balancer", &resource.Sweeper{
Name: "load_balancer",
Dependencies: []string{"server"},
F: func(_ string) error {
endpoint := os.Getenv("BINARYLANE_API_ENDPOINT")
if endpoint == "" {
endpoint = "https://api.binarylane.com.au/v2"
}
token := os.Getenv("BINARYLANE_API_TOKEN")
client, err := binarylane.NewClientWithAuth(
endpoint,
token,
)
if err != nil {
return fmt.Errorf("Error creating Binary Lane API client: %w", err)
}
ctx := context.Background()
var page int32 = 1
perPage := int32(200)
var nextPage bool = true
for nextPage {
params := binarylane.GetLoadBalancersParams{
Page: &page,
PerPage: &perPage,
}
lbResp, err := client.GetLoadBalancersWithResponse(ctx, &params)
if err != nil {
return fmt.Errorf("Error getting load balancers for test sweep: %w", err)
}
if lbResp.StatusCode() != http.StatusOK {
return fmt.Errorf("Unexpected status code getting load balancers for test sweep: %s", lbResp.Body)
}
loadBalancers := *lbResp.JSON200.LoadBalancers
for _, lb := range loadBalancers {
if strings.HasPrefix(*lb.Name, "tf-test-") {
lbResp, err := client.DeleteLoadBalancersLoadBalancerIdWithResponse(ctx, *lb.Id)
if err != nil {
return fmt.Errorf("Error deleting load balancer %d for test sweep: %w", *lb.Id, err)
}
if lbResp.StatusCode() != http.StatusNoContent {
return fmt.Errorf("Unexpected status %d deleting load balancer %d in test sweep: %s", lbResp.StatusCode(), *lb.Id, lbResp.Body)
}
log.Println("Deleted load balancer during test sweep:", *lb.Id)
}
}
if lbResp.JSON200.Links == nil || lbResp.JSON200.Links.Pages == nil || lbResp.JSON200.Links.Pages.Next == nil {
nextPage = false
break
}
page++
}
return nil
},
})
}
+15 -53
View File
@@ -5,9 +5,7 @@ import (
"os"
"terraform-provider-binarylane/internal/binarylane"
"github.com/deepmap/oapi-codegen/pkg/securityprovider"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
@@ -67,60 +65,24 @@ func (p *binarylaneProvider) Configure(ctx context.Context, req provider.Configu
return
}
if config.Endpoint.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("api_endpoint"),
"Unknown Binary Lane API endpoint",
"The provider cannot create the Binary Lane API client as there is an unknown configuration value for the "+
"Binary Lane API endpoint. Either target apply the source of the value first, set the value statically in "+
"the configuration, or use the BINARYLANE_API_ENDPOINT environment variable.",
)
}
if config.Token.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("api_token"),
"Unknown Binary Lane token",
"The provider cannot create the Binary Lane API client as there is an unknown configuration value for the "+
"Binary Lane API token. Either target apply the source of the value first, set the value statically in the "+
"configuration, or use the BINARYLANE_API_TOKEN environment variable.",
)
}
if resp.Diagnostics.HasError() {
return
}
// Default values to environment variables, but override
// with Terraform configuration value if set.
endpoint := os.Getenv("BINARYLANE_API_ENDPOINT")
token := os.Getenv("BINARYLANE_API_TOKEN")
if !config.Endpoint.IsNull() {
endpoint = config.Endpoint.ValueString()
}
if !config.Token.IsNull() {
token = config.Token.ValueString()
}
endpoint := config.Endpoint.ValueString()
if endpoint == "" {
endpoint = "https://api.binarylane.com.au/v2"
}
if token == "" {
resp.Diagnostics.AddAttributeError(
path.Root("api_token"),
"Missing Binary Lane API token",
"The provider cannot create the Binary Lane API client as there is a missing or empty value for the Binary "+
"Lane API token. Set the token value in the configuration or use the BINARYLANE_API_TOKEN environment "+
"variable. If either is already set, ensure the value is not empty.",
)
}
if resp.Diagnostics.HasError() {
return
}
auth, err := securityprovider.NewSecurityProviderBearerToken(token)
if err != nil {
resp.Diagnostics.AddError("Failed to create security provider with supplied token", err.Error())
return
endpoint = os.Getenv("BINARYLANE_API_ENDPOINT")
if endpoint == "" {
endpoint = "https://api.binarylane.com.au/v2"
}
}
client, err := binarylane.NewClientWithResponses(endpoint, binarylane.WithRequestEditorFn(auth.Intercept))
token := config.Token.ValueString()
if token == "" {
token = os.Getenv("BINARYLANE_API_TOKEN")
}
client, err := binarylane.NewClientWithAuth(
endpoint,
token,
)
if err != nil {
resp.Diagnostics.AddError("Failed to create Binary Lane API client", err.Error())
return
@@ -15,7 +15,7 @@ func TestServerFirewallRulesResource(t *testing.T) {
rand.Read(pw_bytes)
password := base64.URLEncoding.EncodeToString(pw_bytes)
resource.Test(t, resource.TestCase{
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
+39 -9
View File
@@ -255,21 +255,51 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
data.Password = types.StringNull()
} else {
body.Password = data.Password.ValueStringPointer()
ctx = tflog.MaskMessageStrings(ctx, data.Password.String())
}
serverResp, err := r.bc.client.PostServersWithResponse(ctx, body)
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error creating server: name=%s", data.Name.ValueString()),
err.Error(),
)
return
const maxRetries = 3
var serverResp *binarylane.PostServersResponse
retryLoop:
for i := 0; i < maxRetries; i++ {
var err error
serverResp, err = r.bc.client.PostServersWithResponse(ctx, body)
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error creating server: name=%s", data.Name.ValueString()),
err.Error(),
)
return
}
switch serverResp.StatusCode() {
case http.StatusOK:
break retryLoop
case http.StatusInternalServerError:
if i < maxRetries-1 {
tflog.Warn(ctx, "Received 500 creating server, retrying...")
time.Sleep(time.Second * 5)
continue
}
default:
resp.Diagnostics.AddError(
"Unexpected HTTP status code creating server",
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), serverResp.Body),
)
return
}
}
// Check if retries exceeded
if serverResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code creating server",
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), serverResp.Body))
"Failed to create server after retries",
fmt.Sprintf("Final status code: %d", serverResp.StatusCode()),
)
return
}
+79 -1
View File
@@ -1,8 +1,15 @@
package provider
import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"log"
"net/http"
"os"
"strings"
"terraform-provider-binarylane/internal/binarylane"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
@@ -18,7 +25,7 @@ func TestServerResource(t *testing.T) {
}
password := base64.URLEncoding.EncodeToString(pw_bytes)
resource.Test(t, resource.TestCase{
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
@@ -109,3 +116,74 @@ echo "Hello World" > /var/tmp/output.txt
},
})
}
func init() {
resource.AddTestSweepers("server", &resource.Sweeper{
Name: "server",
F: func(_ string) error {
endpoint := os.Getenv("BINARYLANE_API_ENDPOINT")
if endpoint == "" {
endpoint = "https://api.binarylane.com.au/v2"
}
token := os.Getenv("BINARYLANE_API_TOKEN")
client, err := binarylane.NewClientWithAuth(
endpoint,
token,
)
if err != nil {
return fmt.Errorf("Error creating Binary Lane API client: %w", err)
}
ctx := context.Background()
var page int32 = 1
perPage := int32(200)
nextPage := true
for nextPage {
params := binarylane.GetServersParams{
Page: &page,
PerPage: &perPage,
}
serverResp, err := client.GetServersWithResponse(ctx, &params)
if err != nil {
return fmt.Errorf("Error getting servers for test sweep: %w", err)
}
if serverResp.StatusCode() != http.StatusOK {
return fmt.Errorf("Unexpected status code getting servers in test sweep: %s", serverResp.Body)
}
servers := *serverResp.JSON200.Servers
for _, s := range servers {
if strings.HasPrefix(*s.Name, "tf-test-") {
reason := "Terraform deletion"
params := binarylane.DeleteServersServerIdParams{
Reason: &reason,
}
serverResp, err := client.DeleteServersServerIdWithResponse(ctx, *s.Id, &params)
if err != nil {
return fmt.Errorf("Error deleting server %d during test sweep: %w", *s.Id, err)
}
if serverResp.StatusCode() != http.StatusNoContent {
return fmt.Errorf("Unexpected status %d deleting server %d in test sweep: %s", serverResp.StatusCode(), *s.Id, serverResp.Body)
}
log.Println("Deleted server during test sweep:", *s.Id)
}
}
if serverResp.JSON200.Links == nil || serverResp.JSON200.Links.Pages == nil || serverResp.JSON200.Links.Pages.Next == nil {
nextPage = false
break
}
page++
}
return nil
},
})
}
+13 -2
View File
@@ -7,6 +7,7 @@ import (
"terraform-provider-binarylane/internal/resources"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)
@@ -59,10 +60,20 @@ func (d *sshKeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequ
}
resp.Schema = *ds
// resp.Schema.Description = "TODO"
// Additional attributes
fingerprintDescription := "The fingerprint of the SSH key."
resp.Schema.Attributes["fingerprint"] = &schema.StringAttribute{
Description: fingerprintDescription,
MarkdownDescription: fingerprintDescription,
Optional: false,
Required: false,
Computed: true,
}
}
func (d *sshKeyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data resources.SshKeyModel
var data sshKeyModel
// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
@@ -88,11 +99,11 @@ func (d *sshKeyDataSource) Read(ctx context.Context, req datasource.ReadRequest,
return
}
// Example data value setting
data.Id = types.Int64Value(*sshResp.JSON200.SshKey.Id)
data.Default = types.BoolValue(*sshResp.JSON200.SshKey.Default)
data.Name = types.StringValue(*sshResp.JSON200.SshKey.Name)
data.PublicKey = types.StringValue(*sshResp.JSON200.SshKey.PublicKey)
data.Fingerprint = types.StringValue(*sshResp.JSON200.SshKey.Fingerprint)
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+133 -24
View File
@@ -4,8 +4,10 @@ import (
"context"
"fmt"
"net/http"
"strconv"
"terraform-provider-binarylane/internal/binarylane"
"terraform-provider-binarylane/internal/resources"
"time"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
@@ -14,13 +16,14 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &sshKeyResource{}
_ resource.ResourceWithConfigure = &sshKeyResource{}
_ resource.ResourceWithImportState = &serverResource{}
_ resource.ResourceWithImportState = &sshKeyResource{}
)
func NewSshKeyResource() resource.Resource {
@@ -31,6 +34,11 @@ type sshKeyResource struct {
bc *BinarylaneClient
}
type sshKeyModel struct {
resources.SshKeyModel
Fingerprint types.String `tfsdk:"fingerprint"`
}
func (d *sshKeyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
@@ -74,10 +82,20 @@ func (r *sshKeyResource) Schema(ctx context.Context, req resource.SchemaRequest,
MarkdownDescription: public_key.GetMarkdownDescription(),
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
}
// Additional attributes
fingerprintDescription := "The fingerprint of the SSH key."
resp.Schema.Attributes["fingerprint"] = &schema.StringAttribute{
Description: fingerprintDescription,
MarkdownDescription: fingerprintDescription,
Optional: false,
Required: false,
Computed: true,
}
}
func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data resources.SshKeyModel
var data sshKeyModel
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
@@ -87,28 +105,58 @@ func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest,
}
// Create API call logic
sshResp, err := r.bc.client.PostAccountKeysWithResponse(ctx, binarylane.SshKeyRequest{
Name: data.Name.ValueString(),
Default: data.Default.ValueBoolPointer(),
PublicKey: data.PublicKey.ValueString(),
})
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error creating SSH Key: name=%s", data.Name.ValueString()),
err.Error(),
)
return
const maxRetries = 3
var sshResp *binarylane.PostAccountKeysResponse
retryLoop:
for i := 0; i < maxRetries; i++ {
var err error
sshResp, err = r.bc.client.PostAccountKeysWithResponse(ctx, binarylane.SshKeyRequest{
Name: data.Name.ValueString(),
Default: data.Default.ValueBoolPointer(),
PublicKey: data.PublicKey.ValueString(),
})
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error creating SSH Key: name=%s", data.Name.ValueString()),
err.Error(),
)
return
}
switch sshResp.StatusCode() {
case http.StatusOK:
break retryLoop
case http.StatusInternalServerError:
if i < maxRetries-1 {
tflog.Warn(ctx, "Received 500 creating SSH key, retrying...")
time.Sleep(time.Second * 5)
continue
}
default:
resp.Diagnostics.AddError(
"Unexpected HTTP status code creating SSH key",
fmt.Sprintf("Received %s creating SSH key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
)
return
}
}
// Check if retries exceeded
if sshResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code creating server",
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
"Failed to create SSH key after retries",
fmt.Sprintf("Final status code: %d", sshResp.StatusCode()),
)
return
}
// Set data values
data.Id = types.Int64Value(*sshResp.JSON200.SshKey.Id)
data.Fingerprint = types.StringValue(*sshResp.JSON200.SshKey.Fingerprint)
if resp.Diagnostics.HasError() {
return
@@ -119,7 +167,7 @@ func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest,
}
func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data resources.SshKeyModel
var data sshKeyModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
@@ -141,8 +189,8 @@ func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, res
}
if sshResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code creating server",
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
"Unexpected HTTP status code getting SSH key",
fmt.Sprintf("Received %s getting SSH key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
)
return
}
@@ -151,14 +199,15 @@ func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, res
data.Id = types.Int64Value(*sshResp.JSON200.SshKey.Id)
data.Default = types.BoolValue(*sshResp.JSON200.SshKey.Default)
data.Name = types.StringValue(*sshResp.JSON200.SshKey.Name)
// data.PublicKey = types.StringValue(*sshResp.JSON200.SshKey.PublicKey) // don't set or it will force replacement every time
data.PublicKey = types.StringValue(*sshResp.JSON200.SshKey.PublicKey)
data.Fingerprint = types.StringValue(*sshResp.JSON200.SshKey.Fingerprint)
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
func (r *sshKeyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data resources.SshKeyModel
var data sshKeyModel
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
@@ -191,7 +240,7 @@ func (r *sshKeyResource) Update(ctx context.Context, req resource.UpdateRequest,
}
func (r *sshKeyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data resources.SshKeyModel
var data sshKeyModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
@@ -218,7 +267,67 @@ func (r *sshKeyResource) Delete(ctx context.Context, req resource.DeleteRequest,
}
}
func (r *sshKeyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// Retrieve import ID and save to id attribute
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
func (r *sshKeyResource) ImportState(
ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
) {
// Import by ID
id, err := strconv.ParseInt(req.ID, 10, 32)
if err == nil {
diags := resp.State.SetAttribute(ctx, path.Root("id"), int32(id))
resp.Diagnostics.Append(diags...)
return
}
// Import by name
var page int32 = 1
perPage := int32(200)
var sshKey binarylane.SshKey
var nextPage bool = true
for nextPage { // Need to paginate because the API does not support filtering by fingerprint
params := binarylane.GetAccountKeysParams{
Page: &page,
PerPage: &perPage,
}
sshResp, err := r.bc.client.GetAccountKeysWithResponse(ctx, &params)
if err != nil {
resp.Diagnostics.AddError(fmt.Sprintf("Error getting SSH key for import: fingerprint=%s", req.ID), err.Error())
return
}
if sshResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code getting SSH key for import",
fmt.Sprintf("Received %s getting SSH key for import: fingerprint=%s. Details: %s", sshResp.Status(), req.ID,
sshResp.Body))
return
}
sshKeys := sshResp.JSON200.SshKeys
for _, key := range sshKeys {
if *key.Fingerprint == req.ID {
sshKey = key
nextPage = false
break
}
}
if sshResp.JSON200.Links == nil || sshResp.JSON200.Links.Pages == nil || sshResp.JSON200.Links.Pages.Next == nil {
nextPage = false
break
}
page++
}
if sshKey.Id == nil {
resp.Diagnostics.AddError(
"Could not find SSH key by fingerprint",
fmt.Sprintf("Error finding SSH key: fingerprint=%s", req.ID),
)
return
}
diags := resp.State.SetAttribute(ctx, path.Root("id"), *sshKey.Id)
resp.Diagnostics.Append(diags...)
}
+112 -14
View File
@@ -1,24 +1,32 @@
package provider
import (
"context"
"crypto/ed25519"
"encoding/base64"
"fmt"
"log"
"net/http"
"os"
"strings"
"terraform-provider-binarylane/internal/binarylane"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
)
func TestSshKeyResource(t *testing.T) {
publicKey := GeneratePublicKey(t)
resource.Test(t, resource.TestCase{
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: providerConfig + `
resource "binarylane_ssh_key" "test" {
name = "tf_ssh_key_resource_test"
name = "tf-test-key-resource-test"
public_key = "` + publicKey + `"
}
@@ -30,38 +38,44 @@ data "binarylane_ssh_key" "test" {
`,
Check: resource.ComposeAggregateTestCheckFunc(
// Verify resource values
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf-test-key-resource-test"),
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "public_key", publicKey),
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "default", "false"),
resource.TestCheckResourceAttrSet("binarylane_ssh_key.test", "id"),
// Verify data source values
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "name", "tf-test-key-resource-test"),
resource.TestCheckResourceAttrSet("data.binarylane_ssh_key.test", "public_key"), // Ideally would check this is identical, but whitespace is not preserved
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "default", "false"),
resource.TestCheckResourceAttrSet("data.binarylane_ssh_key.test", "id"),
),
},
// ImportState testing
// TODO
// {
// ResourceName: "binarylane_ssh_key.test",
// ImportState: true,
// ImportStateVerify: true,
// ImportStateVerifyIgnore: []string{}, // nothing to ignore
// },
{
ResourceName: "binarylane_ssh_key.test",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{}, // nothing to ignore
},
{
ResourceName: "binarylane_ssh_key.test",
ImportStateIdFunc: ImportByFingerprint,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{}, // nothing to ignore
},
// TODO: Update and Read testing
// {
// Config: providerConfig + `
// resource "binarylane_ssh_key" "test" {
// name = "tf_ssh_key_resource_test"
// name = "tf-test-key-resource-test"
// public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"
// default = true
// }
// `,
// Check: resource.ComposeAggregateTestCheckFunc(
// // Verify resource values
// resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
// resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf-test-key-resource-test"),
// resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "default", "true"),
// ),
// },
@@ -73,7 +87,7 @@ data "binarylane_ssh_key" "test" {
func GeneratePublicKey(t *testing.T) string {
t.Helper()
_, pub, err := ed25519.GenerateKey(nil)
pub, _, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatalf("failed to generate key: %v", err)
}
@@ -81,3 +95,87 @@ func GeneratePublicKey(t *testing.T) string {
encoded := base64.StdEncoding.EncodeToString(pub)
return fmt.Sprintf("ssh-ed25519 %s test@company.internal", encoded)
}
func ImportByFingerprint(state *terraform.State) (fingerprint string, err error) {
resourceName := "binarylane_ssh_key.test"
var rawState map[string]string
for _, m := range state.Modules {
if len(m.Resources) > 0 {
if v, ok := m.Resources[resourceName]; ok {
rawState = v.Primary.Attributes
}
}
}
if rawState == nil {
return "", fmt.Errorf("resource not found: %s", resourceName)
}
return rawState["fingerprint"], nil
}
func init() {
resource.AddTestSweepers("ssh_key", &resource.Sweeper{
Name: "ssh_key",
F: func(_ string) error {
endpoint := os.Getenv("BINARYLANE_API_ENDPOINT")
if endpoint == "" {
endpoint = "https://api.binarylane.com.au/v2"
}
token := os.Getenv("BINARYLANE_API_TOKEN")
client, err := binarylane.NewClientWithAuth(
endpoint,
token,
)
if err != nil {
return fmt.Errorf("Error creating Binary Lane API client: %w", err)
}
ctx := context.Background()
var page int32 = 1
perPage := int32(200)
nextPage := true
for nextPage {
params := binarylane.GetAccountKeysParams{
Page: &page,
PerPage: &perPage,
}
keyResp, err := client.GetAccountKeysWithResponse(ctx, &params)
if err != nil {
return fmt.Errorf("Error getting SSH keys for test sweep: %w", err)
}
if keyResp.StatusCode() != http.StatusOK {
return fmt.Errorf("Unexpected status code getting SSH keys for test sweep: %s", keyResp.Body)
}
keys := keyResp.JSON200.SshKeys
for _, k := range keys {
if strings.HasPrefix(*k.Name, "tf-test-") {
keyResp, err := client.DeleteAccountKeysKeyIdWithResponse(ctx, int(*k.Id))
if err != nil {
return fmt.Errorf("Error deleting SSH key %d for test sweep: %w", *k.Id, err)
}
if keyResp.StatusCode() != http.StatusNoContent {
return fmt.Errorf("Unexpected status %d deleting SSH key %d for test sweep: %s", keyResp.StatusCode(), *k.Id, keyResp.Body)
}
log.Println("Deleted SSH key for test sweep:", *k.Id)
}
}
if keyResp.JSON200.Links == nil || keyResp.JSON200.Links.Pages == nil || keyResp.JSON200.Links.Pages.Next == nil {
nextPage = false
break
}
page++
}
return nil
},
})
}
+11
View File
@@ -0,0 +1,11 @@
package provider
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestMain(m *testing.M) {
resource.TestMain(m)
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func TestVpcResource(t *testing.T) {
resource.Test(t, resource.TestCase{
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
@@ -621,11 +621,19 @@ func (v FirewallRulesValue) String() string {
func (v FirewallRulesValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
var diags diag.Diagnostics
destinationAddressesVal, d := types.ListValue(types.StringType, v.DestinationAddresses.Elements())
var destinationAddressesVal basetypes.ListValue
switch {
case v.DestinationAddresses.IsUnknown():
destinationAddressesVal = types.ListUnknown(types.StringType)
case v.DestinationAddresses.IsNull():
destinationAddressesVal = types.ListNull(types.StringType)
default:
var d diag.Diagnostics
destinationAddressesVal, d = types.ListValue(types.StringType, v.DestinationAddresses.Elements())
diags.Append(d...)
}
diags.Append(d...)
if d.HasError() {
if diags.HasError() {
return types.ObjectUnknown(map[string]attr.Type{
"action": basetypes.StringType{},
"description": basetypes.StringType{},
@@ -642,11 +650,19 @@ func (v FirewallRulesValue) ToObjectValue(ctx context.Context) (basetypes.Object
}), diags
}
destinationPortsVal, d := types.ListValue(types.StringType, v.DestinationPorts.Elements())
var destinationPortsVal basetypes.ListValue
switch {
case v.DestinationPorts.IsUnknown():
destinationPortsVal = types.ListUnknown(types.StringType)
case v.DestinationPorts.IsNull():
destinationPortsVal = types.ListNull(types.StringType)
default:
var d diag.Diagnostics
destinationPortsVal, d = types.ListValue(types.StringType, v.DestinationPorts.Elements())
diags.Append(d...)
}
diags.Append(d...)
if d.HasError() {
if diags.HasError() {
return types.ObjectUnknown(map[string]attr.Type{
"action": basetypes.StringType{},
"description": basetypes.StringType{},
@@ -663,11 +679,19 @@ func (v FirewallRulesValue) ToObjectValue(ctx context.Context) (basetypes.Object
}), diags
}
sourceAddressesVal, d := types.ListValue(types.StringType, v.SourceAddresses.Elements())
var sourceAddressesVal basetypes.ListValue
switch {
case v.SourceAddresses.IsUnknown():
sourceAddressesVal = types.ListUnknown(types.StringType)
case v.SourceAddresses.IsNull():
sourceAddressesVal = types.ListNull(types.StringType)
default:
var d diag.Diagnostics
sourceAddressesVal, d = types.ListValue(types.StringType, v.SourceAddresses.Elements())
diags.Append(d...)
}
diags.Append(d...)
if d.HasError() {
if diags.HasError() {
return types.ObjectUnknown(map[string]attr.Type{
"action": basetypes.StringType{},
"description": basetypes.StringType{},