forked from mirrors/terraform-provider-binarylane
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 90b94254f1 | |||
| 46117cf31d | |||
| 394638ff57 | |||
| 239e48e940 | |||
| 2221f8c853 | |||
| 95487045c4 | |||
| 7f5bae6b15 | |||
| 4ef9df6edf | |||
| 547763d639 | |||
| f5fecdd69c | |||
| cd85f67e28 | |||
| 50d06b967b | |||
| e6c50a470d | |||
| 2bc281d177 | |||
| 2a6ba27340 | |||
| fecd42d4a9 | |||
| c5a44b7694 | |||
| 2c639837ef | |||
| 200b2ee3a1 | |||
| a135bceee8 | |||
| 103f4cc2b0 | |||
| db948a103c | |||
| e18d2424d0 | |||
| e1aac7ec1f | |||
| 6c3520bbda | |||
| 22ea042e33 | |||
| b63f052a9f | |||
| a5731e0a12 | |||
| e5450d9f27 | |||
| 53b1643f30 | |||
| 1084c27ab6 | |||
| ffcadb74b3 | |||
| d1adea205b | |||
| 57ed8ff440 | |||
| 702aae5b2f | |||
| 9747587128 | |||
| 44772aa55d | |||
| 600e3f56e7 | |||
| 7a978731bb | |||
| 54dd00fa72 | |||
| e93659373d | |||
| dd3aaa2d75 | |||
| 6cafba5330 | |||
| 63632d3abb | |||
| d5f45788b2 | |||
| d0716f58af |
@@ -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
|
||||
@@ -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'
|
||||
|
||||
@@ -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,16 +29,16 @@ 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
|
||||
uses: crazy-max/ghaction-import-gpg@cb9bde2e2525e640591a934b1fd28eef1dcaf5e5 # v6.2.0
|
||||
id: import_gpg
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.PASSPHRASE }}
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0
|
||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||
with:
|
||||
args: release --clean
|
||||
env:
|
||||
|
||||
@@ -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 ./...
|
||||
+2
-3
@@ -27,7 +27,6 @@ go.work.sum
|
||||
# Misc
|
||||
tmp
|
||||
|
||||
# Normally, you wouldn't want to exclude lock files, but in this case, we're
|
||||
# using Terraform's lock file to store the provider version, so we need to
|
||||
# ignore it.
|
||||
# Normally, you wouldn't want to exclude lock files, but our examples are tested
|
||||
# with a locally built provider and won't be useful to others.
|
||||
.terraform.lock.hcl
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"go.testEnvFile": "${workspaceFolder}/.env",
|
||||
}
|
||||
+51
-10
@@ -35,7 +35,22 @@ terraform apply
|
||||
|
||||
|
||||
```sh
|
||||
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
|
||||
|
||||
```sh
|
||||
go get -u && go mod tidy && go generate
|
||||
```
|
||||
|
||||
### Adding resources/data sources to the provider
|
||||
@@ -70,9 +85,9 @@ Define the interfaces that the resource should implement:
|
||||
```diff
|
||||
- var _ resource.Resource = (*exampleResource)(nil)
|
||||
+ var (
|
||||
+ _ resource.Resource = &exampleResource{}
|
||||
+ _ resource.ResourceWithConfigure = &exampleResource{}
|
||||
+ // _ resource.ResourceWithImportState = &exampleResource{}
|
||||
+ _ resource.Resource = &exampleResource{}
|
||||
+ _ resource.ResourceWithConfigure = &exampleResource{}
|
||||
+ _ resource.ResourceWithImportState = &exampleResource{}
|
||||
+ )
|
||||
```
|
||||
|
||||
@@ -106,7 +121,7 @@ Add a `Configure` method to the resource:
|
||||
+ bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
+ if !ok {
|
||||
+ resp.Diagnostics.AddError(
|
||||
+ "Unexpected Data Source Configure Type",
|
||||
+ "Unexpected Resource Configure Type",
|
||||
+ fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData),
|
||||
+ )
|
||||
|
||||
@@ -117,7 +132,15 @@ Add a `Configure` method to the resource:
|
||||
+ }
|
||||
```
|
||||
|
||||
Add the new data source/resource to `provider.go`:
|
||||
```diff
|
||||
+ func (r *exampleResource) ImportState(
|
||||
+ ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
|
||||
+ ) {
|
||||
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
|
||||
+ }
|
||||
```
|
||||
|
||||
Add the new resource to `provider.go`:
|
||||
|
||||
```diff
|
||||
return []func() resource.Resource{
|
||||
@@ -158,7 +181,7 @@ Derive the data source model from the generated `resources.*Model`.
|
||||
Add a `Configure` method to the data source.
|
||||
|
||||
```diff
|
||||
+ func (d *vpcDataSource) Configure(
|
||||
+ func (d *exampleDataSource) Configure(
|
||||
+ _ context.Context,
|
||||
+ req datasource.ConfigureRequest,
|
||||
+ resp *datasource.ConfigureResponse,
|
||||
@@ -182,7 +205,7 @@ Add a `Configure` method to the data source.
|
||||
Use the generated schema to define the data source schema.
|
||||
|
||||
```diff
|
||||
func (d *vpcDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
func (d *exampleDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
- resp.Schema = schema.Schema{
|
||||
- Attributes: map[string]schema.Attribute{
|
||||
- "id": schema.StringAttribute{
|
||||
@@ -190,7 +213,25 @@ Use the generated schema to define the data source schema.
|
||||
- },
|
||||
- },
|
||||
- }
|
||||
+ resp.Schema = *convertResourceSchemaToDataSourceSchema(ctx, resources.ExampleResourceSchema(ctx))
|
||||
+ resp.Schema.Description = "TODO"
|
||||
+ ds, err := convertResourceSchemaToDataSourceSchema(
|
||||
+ resources.ExampleResourceSchema(ctx)
|
||||
+ AttributeConfig{
|
||||
+ RequiredAttributes: &[]string{"id"},
|
||||
+ },
|
||||
+ )
|
||||
+ if err != nil {
|
||||
+ resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
|
||||
+ return
|
||||
+ }
|
||||
+ resp.Schema = *ds
|
||||
+ // resp.Schema.Description = "TODO"
|
||||
}
|
||||
```
|
||||
|
||||
Add the data source to `provider.go`:
|
||||
|
||||
```diff
|
||||
return []func() datasource.DataSource{
|
||||
# ...
|
||||
+ NewExampleDataSource,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Oscar Hermoso
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,34 +1,81 @@
|
||||
# terraform-provider-binarylane
|
||||
|
||||
See the examples in the [examples directory](./examples/basic/main.tf).
|
||||
See the [documentation on the Terraform Registry](https://registry.terraform.io/providers/oscarhermoso/binarylane/latest), or see [examples in the `examples` directory](./examples/README.md).
|
||||
|
||||
```terraform
|
||||
resource "binarylane_server" "example" {
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
```
|
||||
|
||||
## WIP
|
||||
## Progress
|
||||
|
||||
If somehow you use this in production I would be pretty impressed.
|
||||
Planned features:
|
||||
|
||||
- [x] Create/delete a server when runing locally
|
||||
- [x] [Publish to Terraform Registry](https://registry.terraform.io/providers/oscarhermoso/binarylane/latest/docs/resources/server)
|
||||
- [ ] (and maybe OpenTofu?)
|
||||
- [ ] Deploy the rest of the owl
|
||||
- [x] Servers
|
||||
- [x] Firewall Rules
|
||||
- [x] Cloud Init
|
||||
- [x] Updating server properties
|
||||
- [ ] Customised Backups
|
||||
- [ ] Software
|
||||
- [ ] IPV6
|
||||
- [ ] Alerts
|
||||
- [x] SSH Keys
|
||||
- [x] Virtual Private Cloud
|
||||
- [x] Load Balancers
|
||||
- [ ] Images
|
||||
- [ ] DNS
|
||||
- [x] Docs
|
||||
- [x] Generated docs
|
||||
- [ ] Review & fill out remaining descriptions
|
||||
- [ ] Include examples in docs
|
||||
- [x] Examples
|
||||
- [x] Basic
|
||||
- [x] Cloud Init
|
||||
- [ ] NixOS
|
||||
- [ ] Virtual Private Cloud
|
||||
- [ ] Kubernetes
|
||||
|
||||
## Parameters
|
||||
## Server parameters
|
||||
|
||||
Images:
|
||||
### Regions
|
||||
|
||||
<details>
|
||||
<summary>curl for regions</summary>
|
||||
|
||||
```sh
|
||||
curl -X GET "https://api.binarylane.com.au/v2/regions" \
|
||||
-H "Authorization: Bearer $BINARYLANE_API_TOKEN" > tmp/regions.json
|
||||
|
||||
jq '[ .regions[] | .slug ] | sort' tmp/regions.json
|
||||
```
|
||||
</details>
|
||||
|
||||
```json
|
||||
[
|
||||
"bne",
|
||||
"mel",
|
||||
"per",
|
||||
"sin",
|
||||
"syd"
|
||||
]
|
||||
```
|
||||
|
||||
### Images
|
||||
|
||||
<details>
|
||||
<summary>curl for images</summary>
|
||||
|
||||
```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
|
||||
```
|
||||
</details>
|
||||
|
||||
```json
|
||||
[
|
||||
@@ -58,33 +105,18 @@ jq '[ .images[] | .slug ] | sort' tmp/images.json
|
||||
]
|
||||
```
|
||||
|
||||
Regions:
|
||||
### Sizes
|
||||
|
||||
```sh
|
||||
curl -X GET "https://api.binarylane.com.au/v2/regions" \
|
||||
-H "Authorization: Bearer **********"" > tmp/regions.json
|
||||
|
||||
jq '[ .regions[] | .slug ] | sort' tmp/regions.json
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
"bne",
|
||||
"mel",
|
||||
"per",
|
||||
"sin",
|
||||
"syd"
|
||||
]
|
||||
```
|
||||
|
||||
Sizes (not all sizes are available in all regions):
|
||||
<details>
|
||||
<summary>curl for sizes</summary>
|
||||
|
||||
```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
|
||||
```
|
||||
</details>
|
||||
|
||||
```json
|
||||
[
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_load_balancer Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
|
||||
---
|
||||
|
||||
# binarylane_load_balancer (Data Source)
|
||||
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "binarylane_load_balancer" "example" {
|
||||
id = 123456
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `id` (Number) The ID of the load balancer to fetch.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `forwarding_rules` (Attributes List) The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule. (see [below for nested schema](#nestedatt--forwarding_rules))
|
||||
- `health_check` (Object) The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules. (see [below for nested schema](#nestedatt--health_check))
|
||||
- `name` (String) The hostname of the load balancer.
|
||||
- `region` (String) Leave null to create an anycast load balancer.
|
||||
- `server_ids` (List of Number) A list of server IDs to assign to this load balancer.
|
||||
|
||||
<a id="nestedatt--forwarding_rules"></a>
|
||||
### Nested Schema for `forwarding_rules`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `entry_protocol` (String) The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
|
||||
|
||||
|
||||
<a id="nestedatt--health_check"></a>
|
||||
### Nested Schema for `health_check`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `path` (String)
|
||||
- `protocol` (String)
|
||||
+19
-10
@@ -3,14 +3,20 @@
|
||||
page_title: "binarylane_server Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
Provides a Binary Lane Server data source. This can be used to read existing server details.
|
||||
---
|
||||
|
||||
# binarylane_server (Data Source)
|
||||
|
||||
TODO
|
||||
Provides a Binary Lane Server data source. This can be used to read existing server details.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "binarylane_server" "example" {
|
||||
id = 123456
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
@@ -19,16 +25,19 @@ TODO
|
||||
|
||||
- `id` (Number) The ID of the server to fetch.
|
||||
|
||||
### Optional
|
||||
|
||||
- `backups` (Boolean) If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.
|
||||
- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.
|
||||
- `password` (String) If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.
|
||||
- `user_data` (String) If provided this will be used to initialise the new server. This must be left null if the Image does not support UserData, see DistributionInfo.Features for more information.
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `backups` (Boolean) If `true`, the server will be backed up twice per day. By default, backups are disabled.
|
||||
- `disk` (Number) The amount of storage in GB assigned to the server.
|
||||
- `image` (String) The slug of the selected operating system.
|
||||
- `memory` (Number) The amount of memory in MB assigned to the server.
|
||||
- `name` (String) The hostname of your server, such as vps01.yourcompany.com.
|
||||
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature
|
||||
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.
|
||||
- `private_ipv4_addresses` (List of String) The private IPv4 addresses assigned to the server.
|
||||
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
|
||||
- `region` (String) The slug of the selected region.
|
||||
- `size` (String) The slug of the selected size.
|
||||
- `ssh_keys` (List of Number) This is a list of SSH key ids that were added to the server during creation.
|
||||
- `user_data` (String) A script or cloud-config YAML file to configure the server.
|
||||
- `vpc_id` (Number) ID of the Virtual Private Cloud (VPC) the server is connected to.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_server_firewall_rules Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
|
||||
---
|
||||
|
||||
# binarylane_server_firewall_rules (Data Source)
|
||||
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "binarylane_server_firewall_rules" "example" {
|
||||
server_id = 123456
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `server_id` (Number) The ID of the server for which firewall rules should be listed.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `firewall_rules` (Attributes List) A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules. (see [below for nested schema](#nestedatt--firewall_rules))
|
||||
|
||||
<a id="nestedatt--firewall_rules"></a>
|
||||
### Nested Schema for `firewall_rules`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `action` (String) The action to take when there is a match on this rule.
|
||||
- `description` (String) A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. "Block access to RDP" or "Allow access from HQ".
|
||||
- `destination_addresses` (List of String) The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
- `destination_ports` (List of String) The destination ports to match for this rule. Leave null or empty to match on all ports.
|
||||
- `protocol` (String) The protocol to match for this rule.
|
||||
- `source_addresses` (List of String) The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
@@ -3,24 +3,31 @@
|
||||
page_title: "binarylane_ssh_key Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
|
||||
---
|
||||
|
||||
# binarylane_ssh_key (Data Source)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "binarylane_ssh_key" "example" {
|
||||
id = 123456
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Optional
|
||||
### Required
|
||||
|
||||
- `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
|
||||
|
||||
- `default` (Boolean) If `true`, this SSH key will be included on all new server installations (if the operating system supports SSH key injection).
|
||||
- `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.
|
||||
|
||||
@@ -3,23 +3,29 @@
|
||||
page_title: "binarylane_vpc Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
|
||||
---
|
||||
|
||||
# binarylane_vpc (Data Source)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "binarylane_vpc" "example" {
|
||||
id = 123456
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Optional
|
||||
### Required
|
||||
|
||||
- `id` (Number) The target vpc id.
|
||||
- `ip_range` (String) A private address range that you select during creation, such as the default value of 10.240.0.0/16. Because the virtual network is dedicated to your use, you may use whatever IP address range you like.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `ip_range` (String) A private address range that you select during creation, such as the default value of 10.240.0.0/16. Because the virtual network is dedicated to your use, you may use whatever IP address range you like.
|
||||
- `name` (String) A name to help identify this VPC.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_vpc_route_entries Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
|
||||
---
|
||||
|
||||
# binarylane_vpc_route_entries (Data Source)
|
||||
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "binarylane_vpc_route_entries" "example" {
|
||||
vpc_id = 123456
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `vpc_id` (Number) The target vpc id.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `route_entries` (Attributes List) The route entries that control how network traffic is directed through the VPC environment. (see [below for nested schema](#nestedatt--route_entries))
|
||||
|
||||
<a id="nestedatt--route_entries"></a>
|
||||
### Nested Schema for `route_entries`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String) An optional description for the route.
|
||||
- `destination` (String) The destination address for this route entry. This may be in CIDR format.
|
||||
- `router` (String) The server that will receive traffic sent to the destination property in this VPC.
|
||||
@@ -10,7 +10,31 @@ description: |-
|
||||
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
|
||||
# Create a new server
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example-basic"
|
||||
region = "per" # or "syd", "mel", "bne", "sin"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 1
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_load_balancer Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
|
||||
---
|
||||
|
||||
# binarylane_load_balancer (Resource)
|
||||
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "binarylane_server" "example" {
|
||||
count = 2
|
||||
# ...
|
||||
}
|
||||
|
||||
resource "binarylane_load_balancer" "example" {
|
||||
name = "tf-example-lb"
|
||||
forwarding_rules = [{ entry_protocol = "https" }]
|
||||
server_ids = [
|
||||
binarylane_server.example.0.id,
|
||||
binarylane_server.example.1.id
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) The hostname of the load balancer.
|
||||
|
||||
### Optional
|
||||
|
||||
- `forwarding_rules` (Attributes List) The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule. (see [below for nested schema](#nestedatt--forwarding_rules))
|
||||
- `health_check` (Attributes) The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules. (see [below for nested schema](#nestedatt--health_check))
|
||||
- `region` (String) Leave null to create an anycast load balancer.
|
||||
- `server_ids` (List of Number) A list of server IDs to assign to this load balancer.
|
||||
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (Number) The ID of the load balancer to fetch.
|
||||
|
||||
<a id="nestedatt--forwarding_rules"></a>
|
||||
### Nested Schema for `forwarding_rules`
|
||||
|
||||
Required:
|
||||
|
||||
- `entry_protocol` (String) The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
|
||||
|
||||
|
||||
<a id="nestedatt--health_check"></a>
|
||||
### Nested Schema for `health_check`
|
||||
|
||||
Optional:
|
||||
|
||||
- `path` (String) Leave null to accept the default '/' path.
|
||||
- `protocol` (String) Leave null to accept the default HTTP protocol.
|
||||
|
||||
|
||||
<a id="nestedatt--timeouts"></a>
|
||||
### Nested Schema for `timeouts`
|
||||
|
||||
Optional:
|
||||
|
||||
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_load_balancer.example "<name or id>"
|
||||
```
|
||||
+57
-10
@@ -3,33 +3,80 @@
|
||||
page_title: "binarylane_server Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Provides a Binary Lane Server resource. This can be used to create and delete servers.
|
||||
|
||||
---
|
||||
|
||||
# binarylane_server (Resource)
|
||||
|
||||
Provides a Binary Lane Server resource. This can be used to create and delete servers.
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example"
|
||||
region = "per" # or "syd", "mel", "bne", "sin"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
public_ipv4_count = 1
|
||||
|
||||
# Accepts a cloud-init script or cloud-config YAML file to configure the server
|
||||
# See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
|
||||
user_data = file("./init.sh")
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `image` (String) The slug of the selected operating system.
|
||||
- `image` (String) The slug of the selected operating system, such as `debian-12`. You can fetch a full list of images from the BinaryLane API.
|
||||
- `public_ipv4_count` (Number) The number of public IPv4 addresses to assign to the server.
|
||||
- `region` (String) The slug of the selected region.
|
||||
- `size` (String) The slug of the selected size.
|
||||
|
||||
### Optional
|
||||
|
||||
- `backups` (Boolean) If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.
|
||||
- `id` (Number) The ID of the server to fetch.
|
||||
- `backups` (Boolean) If `true` this will enable two daily backups for the server. By default, backups are disabled.
|
||||
- `disk` (Number) The total storage in GB for this server. Leave null to accept the default for the size Valid values:
|
||||
- must be a multiple of 5
|
||||
- \> 60 GB must be a multiple of 10
|
||||
- \> 200 GB must be a multiple of 100
|
||||
- `memory` (Number) The total memory in MB for this server. Leave null to accept the default size. Valid values:
|
||||
- must be a multiple of 128
|
||||
- \> 2048 MB must be a multiple of 1024
|
||||
- \> 16384 MB must be a multiple of 2048
|
||||
- \> 24576 MB must be a multiple of 4096
|
||||
- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.
|
||||
- `password` (String, Sensitive) If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.
|
||||
- `password` (String, Sensitive) If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions. If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.
|
||||
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.
|
||||
- `source_and_destination_check` (Boolean) This attribute can only be set if your server also has a `vpc_id` attribute set. When enabled (which is `true` by default), your server will only be able to send or receive packets that are directly addressed to one of the IP addresses associated with the Cloud Server. Generally, this is desirable behaviour because it prevents IP conflicts and other hard-to-diagnose networking faults due to incorrect network configuration. When `source_and_destination_check` is `false`, your Cloud Server will be able to send and receive packets addressed to any server. This is typically used when you want to use your Cloud Server as a VPN endpoint, a NAT server to provide internet access, or IP forwarding.
|
||||
- `ssh_keys` (List of Number) This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.
|
||||
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
|
||||
- `user_data` (String) A script or cloud-config YAML file to configure the server. Can only be specified if the OS image supports UserData (i.e. not Windows). See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (Number) The ID of the server to fetch.
|
||||
- `password_change_supported` (Boolean) If this is true then the `password` attribute can be changed with Terraform. If this is false then the `password` attribute can only be replaced with a null/empty value, which will clear the root/administrator password allowing the password to be changed via the web console.
|
||||
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature
|
||||
- `private_ipv4_addresses` (List of String) The private IPv4 addresses assigned to the server.
|
||||
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
|
||||
- `public_ipv4_count` (Number) The number of public IPv4 addresses to assign to the server. If this is not provided, the server will be created with the default number of public IPv4 addresses.
|
||||
- `user_data` (String) If provided this will be used to initialise the new server. This must be left null if the Image does not support UserData, see DistributionInfo.Features for more information.
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
- `wait_for_create` (Number) The number of seconds to wait for the server to be created, after which, a timeout error will be reported. If `wait_seconds` is left empty or set to 0, Terraform will succeed without waiting for the server creation to complete.
|
||||
|
||||
<a id="nestedatt--timeouts"></a>
|
||||
### Nested Schema for `timeouts`
|
||||
|
||||
Optional:
|
||||
|
||||
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
|
||||
- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_server.example "<server name or id>"
|
||||
```
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_server_firewall_rules Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
|
||||
---
|
||||
|
||||
# binarylane_server_firewall_rules (Resource)
|
||||
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "binarylane_server" "example" {
|
||||
# ...
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "example" {
|
||||
server_id = binarylane_server.example.id
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow SSH"
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.example.private_ipv4_addresses.0]
|
||||
destination_ports = ["22"]
|
||||
action = "accept"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `firewall_rules` (Attributes List) A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules. (see [below for nested schema](#nestedatt--firewall_rules))
|
||||
- `server_id` (Number) The ID of the server for which firewall rules should be listed.
|
||||
|
||||
<a id="nestedatt--firewall_rules"></a>
|
||||
### Nested Schema for `firewall_rules`
|
||||
|
||||
Required:
|
||||
|
||||
- `action` (String) The action to take when there is a match on this rule.
|
||||
- `destination_addresses` (List of String) The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
- `protocol` (String) The protocol to match for this rule.
|
||||
- `source_addresses` (List of String) The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
|
||||
Optional:
|
||||
|
||||
- `description` (String) A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. "Block access to RDP" or "Allow access from HQ".
|
||||
- `destination_ports` (List of String) The destination ports to match for this rule. Leave null or empty to match on all ports.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_server_firewall_rules.example "<server name or id>"
|
||||
```
|
||||
@@ -3,14 +3,28 @@
|
||||
page_title: "binarylane_ssh_key Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
|
||||
---
|
||||
|
||||
# binarylane_ssh_key (Resource)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "binarylane_ssh_key" "example" {
|
||||
name = "tf-example"
|
||||
|
||||
# Generate with: ssh-keygen -t ed25519 -C "name@example.com"
|
||||
public_key = file("~/.ssh/id_ed25519.pub")
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
# ...
|
||||
ssh_keys = [binarylane_ssh_key.example.id]
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
@@ -22,5 +36,17 @@ TODO
|
||||
|
||||
### Optional
|
||||
|
||||
- `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.
|
||||
- `default` (Boolean) If `true`, this SSH key will be included on all new server installations (if the operating system supports SSH key injection). Default is `false`.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `fingerprint` (String) The fingerprint of the SSH key.
|
||||
- `id` (Number) The numeric ID of the SSH key.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_ssh_key.example "<SSH Key ID or fingerprint>"
|
||||
```
|
||||
|
||||
+26
-3
@@ -3,14 +3,26 @@
|
||||
page_title: "binarylane_vpc Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
|
||||
---
|
||||
|
||||
# binarylane_vpc (Resource)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "binarylane_vpc" "example" {
|
||||
name = "tf-example-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
# ...
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
@@ -21,5 +33,16 @@ TODO
|
||||
|
||||
### Optional
|
||||
|
||||
- `id` (Number) The target vpc id.
|
||||
- `ip_range` (String) A private address range that you select during creation, such as the default value of 10.240.0.0/16. Because the virtual network is dedicated to your use, you may use whatever IP address range you like.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (Number) The target vpc id.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_vpc.example "<vpc_id>"
|
||||
```
|
||||
|
||||
@@ -3,14 +3,42 @@
|
||||
page_title: "binarylane_vpc_route_entries Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
|
||||
---
|
||||
|
||||
# binarylane_vpc_route_entries (Resource)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "binarylane_vpc" "example" {
|
||||
name = "tf-example-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "web" {
|
||||
# ...
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "example" {
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "NAT"
|
||||
destination = "0.0.0.0/0"
|
||||
router = binarylane_server.web.private_ipv4_addresses.0
|
||||
},
|
||||
{
|
||||
description = "VPN"
|
||||
destination = "192.168.1.0/24"
|
||||
router = binarylane_server.vpn.private_ipv4_addresses.0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
@@ -34,3 +62,11 @@ Required:
|
||||
Optional:
|
||||
|
||||
- `description` (String) An optional description for the route.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_vpc_route_entries.example "<vpc_id>"
|
||||
```
|
||||
|
||||
+3
-2
@@ -5,5 +5,6 @@ In order of increasing complexity, these examples show how to use the provider.
|
||||
- [./basic](./basic/main.tf) - A basic example that creates a server.
|
||||
- [./ssh](./ssh/main.tf) - Create a server and registers an SSH key as a global default.
|
||||
- [./cloud-init](./cloud-init/main.tf) - Create a server with a cloud-init configuration.
|
||||
- [./nix](./nix/main.tf) - Create a server with a NixOS configuration.
|
||||
- [./vpc](./vpc/main.tf) - Create a server with a Virtual Private Cloud.
|
||||
- [./nix](./nix/main.tf) - WIP - Create a server with a NixOS configuration.
|
||||
- [./vpc](./vpc/main.tf) - WIP - Create a server with a Virtual Private Cloud.
|
||||
- [./k3s](./k3s/main.tf) - WIP - Create Kubernetes cluster using k3s.
|
||||
|
||||
@@ -7,15 +7,14 @@ terraform {
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example"
|
||||
region = "per" # or "syd", "mel", "bne", "sin"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
|
||||
# password will be generated automatically and mailed to the account holder
|
||||
name = "tf-example-basic"
|
||||
region = "per" # or "syd", "mel", "bne", "sin"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
public_ipv4_count = 1
|
||||
# Password will be generated automatically and mailed to the account holder
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ terraform {
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com"
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com.au/v2"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#cloud-config
|
||||
bootcmd:
|
||||
# By default, the provider won't wait for the server installation to complete.
|
||||
#
|
||||
# If you want Terraform to wait until the server has fully provisioned, set
|
||||
# the `wait_for_create` attribute on the `binarylane_server` resource.
|
||||
#
|
||||
# If you have additional installation steps that need to be awaited, define
|
||||
# these in the `bootcmd` section and they will also be awaited.
|
||||
#
|
||||
|
||||
@@ -6,16 +6,13 @@ terraform {
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
provider "binarylane" {}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-cloud-init-example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
wait_for_create = 300 # 5 mins
|
||||
name = "tf-cloud-init-example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
data "binarylane_load_balancer" "example" {
|
||||
id = 123456
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
data "binarylane_server" "example" {
|
||||
id = 123456
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
data "binarylane_server_firewall_rules" "example" {
|
||||
server_id = 123456
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
data "binarylane_ssh_key" "example" {
|
||||
id = 123456
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
data "binarylane_vpc" "example" {
|
||||
id = 123456
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
data "binarylane_vpc_route_entries" "example" {
|
||||
vpc_id = 123456
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Ignore transient lock info files created by terraform apply
|
||||
.terraform.tfstate.lock.info
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# .kube/config contains credentials
|
||||
.kube
|
||||
@@ -0,0 +1,15 @@
|
||||
# WIP - examples/k8s
|
||||
|
||||
This WIP example shows how to create a Kubernetes cluster on Binary Lane.
|
||||
|
||||
## Prior art
|
||||
|
||||
- [MartinHodges/create_k8s](https://github.com/MartinHodges/create_k8s) repo and the [medium article](https://medium.com/@martin.hodges/creating-a-kubernetes-cluster-from-scratch-in-1-hour-using-automation-a25e387be547) that goes with it.
|
||||
- https://github.com/inscapist/terraform-k3s-private-cloud
|
||||
- https://github.com/schnerring/schnerring.github.io/blob/07d06fb40e3d01f2483a739f516ac4d711a5742c/content/blog/use-terraform-to-deploy-an-azure-kubernetes-service-aks-cluster-traefik-2-cert-manager-and-lets-encrypt-certificates/index.md
|
||||
|
||||
## Remaining TODOs
|
||||
|
||||
- [ ] Consider rewriting module to use `microk8s` instead of `k3s`
|
||||
- [ ] Use `kustomize` instead of `helm`
|
||||
- [ ] Use the `binarylane_load_balancer` resource instead of `kubectl port-forward` (or use the load balancer in another example)
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash -x
|
||||
exec >/tmp/k3s-agent-join-debug.log 2>&1
|
||||
|
||||
export K3S_TOKEN="${cluster_token}"
|
||||
export K3S_URL="https://${cluster_server}:6443"
|
||||
|
||||
curl -sfL https://get.k3s.io | sh -s - agent \
|
||||
--node-name="$(hostname -f)"
|
||||
|
||||
unset K3S_TOKEN
|
||||
unset K3S_URL
|
||||
|
||||
# Disable firewall recommended
|
||||
ufw disable
|
||||
|
||||
echo "K3s Node Join Completed"
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash -x
|
||||
exec >/tmp/k3s-server-install-debug.log 2>&1
|
||||
|
||||
export INSTALL_K3S_NAME="${cluster_id}"
|
||||
export K3S_TOKEN="${cluster_token}"
|
||||
export K3S_KUBECONFIG_MODE="644"
|
||||
|
||||
curl -sfL https://get.k3s.io | sh -s - server \
|
||||
--node-name="$(hostname -f)"
|
||||
# --disable-cloud-controller \
|
||||
# --disable servicelb \
|
||||
# --disable local-storage \
|
||||
# --disable traefik
|
||||
|
||||
unset INSTALL_K3S_NAME
|
||||
unset K3S_TOKEN
|
||||
unset K3S_KUBECONFIG_MODE
|
||||
|
||||
echo "Installing Helm"
|
||||
curl -sfL https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
|
||||
|
||||
# Disable firewall recommended
|
||||
ufw disable
|
||||
|
||||
echo "K3s Setup Completed"
|
||||
@@ -0,0 +1,176 @@
|
||||
locals {
|
||||
cluster_id = "tf-example-k8s"
|
||||
custom_domain = "tf-example-k8s.internal"
|
||||
}
|
||||
|
||||
resource "random_password" "binarylane" {
|
||||
length = 18
|
||||
}
|
||||
|
||||
resource "random_password" "cluster_token" {
|
||||
length = 64
|
||||
special = false
|
||||
}
|
||||
|
||||
# SSH Key
|
||||
# -------
|
||||
resource "binarylane_ssh_key" "example" {
|
||||
name = "tf-example-k8s"
|
||||
public_key = file("~/.ssh/id_ed25519.pub")
|
||||
}
|
||||
|
||||
# Virtual Private Cloud
|
||||
# ---------------------
|
||||
resource "binarylane_vpc" "example" {
|
||||
name = local.cluster_id
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
# k3s Servers
|
||||
# -----------
|
||||
data "cloudinit_config" "server" {
|
||||
gzip = false
|
||||
base64_encode = false
|
||||
|
||||
part {
|
||||
content_type = "text/x-shellscript"
|
||||
content = templatefile("${path.module}/k3s-server-install.sh", {
|
||||
cluster_id = local.cluster_id,
|
||||
cluster_token = random_password.cluster_token.result,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resource "binarylane_server" "server" {
|
||||
count = 1
|
||||
|
||||
name = "${local.cluster_id}-server-${count.index + 1}"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
password = random_password.binarylane.result
|
||||
ssh_keys = [binarylane_ssh_key.example.id]
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
user_data = sensitive(data.cloudinit_config.server.rendered)
|
||||
}
|
||||
|
||||
data "external" "kubeconfig" {
|
||||
program = [
|
||||
"/usr/bin/ssh",
|
||||
"-o UserKnownHostsFile=/dev/null",
|
||||
"-o StrictHostKeyChecking=no",
|
||||
"root@${binarylane_server.server.0.permalink}",
|
||||
"echo '{\"kubeconfig\":\"'$(cat /etc/rancher/k3s/k3s.yaml | base64)'\"}'",
|
||||
]
|
||||
}
|
||||
|
||||
resource "local_sensitive_file" "kubeconfig" {
|
||||
depends_on = [binarylane_server.server]
|
||||
|
||||
content = replace(
|
||||
base64decode(
|
||||
replace(data.external.kubeconfig.result.kubeconfig, " ", "")
|
||||
),
|
||||
"server: https://127.0.0.1:6443",
|
||||
"server: https://${binarylane_server.server.0.permalink}:6443",
|
||||
)
|
||||
filename = "${path.module}/.kube/config"
|
||||
file_permission = "0600"
|
||||
}
|
||||
|
||||
# k3s Agents
|
||||
# ----------
|
||||
data "cloudinit_config" "agent" {
|
||||
gzip = false
|
||||
base64_encode = false
|
||||
|
||||
part {
|
||||
content_type = "text/x-shellscript"
|
||||
content = templatefile("${path.module}/k3s-agent-join.sh", {
|
||||
cluster_token = random_password.cluster_token.result,
|
||||
cluster_server = binarylane_server.server.0.private_ipv4_addresses.0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resource "binarylane_server" "agent" {
|
||||
count = 2
|
||||
|
||||
name = "${local.cluster_id}-agent-${count.index + 1}"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
password = random_password.binarylane.result
|
||||
ssh_keys = [binarylane_ssh_key.example.id]
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
user_data = sensitive(data.cloudinit_config.agent.rendered)
|
||||
}
|
||||
|
||||
# Virtual Private Cloud Routing
|
||||
# -----------------------------
|
||||
resource "binarylane_vpc_route_entries" "example" {
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
route_entries = [
|
||||
# {
|
||||
# description = "NAT"
|
||||
# destination = "0.0.0.0/0"
|
||||
# router = binarylane_server.gateway.private_ipv4_addresses.0
|
||||
# }
|
||||
]
|
||||
}
|
||||
|
||||
locals {
|
||||
agent_ips = flatten([for _, a in binarylane_server.agent : a.private_ipv4_addresses])
|
||||
server_ips = flatten([for _, s in binarylane_server.server : s.private_ipv4_addresses])
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "example" {
|
||||
for_each = { for server in concat(binarylane_server.server, binarylane_server.agent) : server.name => server }
|
||||
|
||||
server_id = each.value.id
|
||||
|
||||
firewall_rules = [
|
||||
{
|
||||
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"
|
||||
source_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_ports = ["8472"]
|
||||
action = "accept"
|
||||
},
|
||||
{
|
||||
description = "Kubelet metrics"
|
||||
protocol = "tcp"
|
||||
source_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_ports = ["10250"]
|
||||
action = "accept"
|
||||
},
|
||||
{
|
||||
description = "SSH"
|
||||
protocol = "udp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_ports = ["22"]
|
||||
action = "accept"
|
||||
},
|
||||
{
|
||||
description = "HTTP"
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_ports = ["80"]
|
||||
action = "accept"
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
resource "kubectl_manifest" "hello" {
|
||||
yaml_body = <<YAML
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: nginx-hello
|
||||
labels:
|
||||
app: nginx-hello
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx-hello
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx-hello
|
||||
spec:
|
||||
containers:
|
||||
- image: nginxdemos/hello
|
||||
name: nginx-hello
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
YAML
|
||||
}
|
||||
|
||||
resource "kubectl_manifest" "hello_service" {
|
||||
yaml_body = <<YAML
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-hello
|
||||
labels:
|
||||
app: nginx-hello
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: nginx-hello
|
||||
ports:
|
||||
- port: 80
|
||||
YAML
|
||||
}
|
||||
|
||||
resource "kubectl_manifest" "hello_ingress" {
|
||||
yaml_body = <<YAML
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx-hello
|
||||
spec:
|
||||
rules:
|
||||
- host: "*.bnr.la"
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-hello
|
||||
port:
|
||||
number: 80
|
||||
YAML
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
kubectl = {
|
||||
source = "alekc/kubectl"
|
||||
version = ">= 2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {}
|
||||
|
||||
provider "helm" {
|
||||
kubernetes {
|
||||
host = "${binarylane_server.server.0.permalink}:6443"
|
||||
config_path = local_sensitive_file.kubeconfig.filename
|
||||
insecure = true # TODO
|
||||
}
|
||||
}
|
||||
|
||||
provider "kubectl" {
|
||||
host = "${binarylane_server.server.0.permalink}:6443"
|
||||
config_path = local_sensitive_file.kubeconfig.filename
|
||||
insecure = true # TODO
|
||||
}
|
||||
+7
-10
@@ -6,16 +6,13 @@ terraform {
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
provider "binarylane" {}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-nix-example"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
wait_for_create = 300 # 5 mins
|
||||
name = "tf-nix-example"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
|
||||
# Create a new server
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example-basic"
|
||||
region = "per" # or "syd", "mel", "bne", "sin"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 1
|
||||
# ...
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import binarylane_load_balancer.example "<name or id>"
|
||||
@@ -0,0 +1,13 @@
|
||||
resource "binarylane_server" "example" {
|
||||
count = 2
|
||||
# ...
|
||||
}
|
||||
|
||||
resource "binarylane_load_balancer" "example" {
|
||||
name = "tf-example-lb"
|
||||
forwarding_rules = [{ entry_protocol = "https" }]
|
||||
server_ids = [
|
||||
binarylane_server.example.0.id,
|
||||
binarylane_server.example.1.id
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import binarylane_server.example "<server name or id>"
|
||||
@@ -0,0 +1,11 @@
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example"
|
||||
region = "per" # or "syd", "mel", "bne", "sin"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
public_ipv4_count = 1
|
||||
|
||||
# Accepts a cloud-init script or cloud-config YAML file to configure the server
|
||||
# See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
|
||||
user_data = file("./init.sh")
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import binarylane_server_firewall_rules.example "<server name or id>"
|
||||
@@ -0,0 +1,17 @@
|
||||
resource "binarylane_server" "example" {
|
||||
# ...
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "example" {
|
||||
server_id = binarylane_server.example.id
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow SSH"
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.example.private_ipv4_addresses.0]
|
||||
destination_ports = ["22"]
|
||||
action = "accept"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import binarylane_ssh_key.example "<SSH Key ID or fingerprint>"
|
||||
@@ -0,0 +1,11 @@
|
||||
resource "binarylane_ssh_key" "example" {
|
||||
name = "tf-example"
|
||||
|
||||
# Generate with: ssh-keygen -t ed25519 -C "name@example.com"
|
||||
public_key = file("~/.ssh/id_ed25519.pub")
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
# ...
|
||||
ssh_keys = [binarylane_ssh_key.example.id]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import binarylane_vpc.example "<vpc_id>"
|
||||
@@ -0,0 +1,9 @@
|
||||
resource "binarylane_vpc" "example" {
|
||||
name = "tf-example-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
# ...
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
terraform import binarylane_vpc_route_entries.example "<vpc_id>"
|
||||
@@ -0,0 +1,25 @@
|
||||
resource "binarylane_vpc" "example" {
|
||||
name = "tf-example-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "web" {
|
||||
# ...
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "example" {
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "NAT"
|
||||
destination = "0.0.0.0/0"
|
||||
router = binarylane_server.web.private_ipv4_addresses.0
|
||||
},
|
||||
{
|
||||
description = "VPN"
|
||||
destination = "192.168.1.0/24"
|
||||
router = binarylane_server.vpn.private_ipv4_addresses.0
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
-24
@@ -1,24 +0,0 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/oscarhermoso/binarylane" {
|
||||
version = "0.1.1"
|
||||
hashes = [
|
||||
"h1:GvTza7TvwgMCegUJ/q+kmteckZ8Sc/lqsIUK2UCxxAU=",
|
||||
"zh:025de19988848b2e6b8dacd61b7e99274641f32ea9a9e1a67f7dad006630916f",
|
||||
"zh:0b5ce83392db3e7693a8c9a71b9c1eb60983c43f758f8a2623a6b64778e683b9",
|
||||
"zh:0bf621429caa7c4468e2996f6c517178761b0492f8611603efd912797ef4da7c",
|
||||
"zh:1c4db19c124efecde6910a7e9ecbdb5f3f9042685457a3828ca0c5a43da9f43d",
|
||||
"zh:30fe222bc7cdfadfe3872d443a8f6db957711fa3961f62601a9b974bda995637",
|
||||
"zh:3b21a79e2d69d76364a043319bd54c86080eee3016f11b73f821f22629e8b0ef",
|
||||
"zh:3bbc5ea741c85267ff25bd6db1c0863548f2da2c2277bfbf30955080282fa4f0",
|
||||
"zh:414887df5094afe07ce13d521c57e161301437f8891876d9d480409ffc51e5e2",
|
||||
"zh:594827a9be3de7ab4fe634be3d933c5c5a0de665293feab34037da6af5355bf6",
|
||||
"zh:7d73d2ca49b8a6d6f8fe23cf0adfc1fae17fcbb2b1f0a6fb7eef0b3cca5995e1",
|
||||
"zh:ad0b99751f3ee17630edeed6bec065defcbb08abd377b09169a7826d0f5896ef",
|
||||
"zh:c4c1ed1f41a238b50a802efc2e8fd025d5e3a1ebd0a6a0b3d84cde1b0a7eda4a",
|
||||
"zh:cfb3b91d255258583d41382f3adea82af76afff7ba607daff739b951a4a27012",
|
||||
"zh:d865a4a7886c8354c0636d005904ef090dfad2ea2f5ef27b1ecad2e2dcd9964b",
|
||||
"zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597",
|
||||
]
|
||||
}
|
||||
+26
-15
@@ -6,25 +6,36 @@ terraform {
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
provider "binarylane" {}
|
||||
|
||||
/**
|
||||
* This example uses the public key in the git repository, but your SSH key will
|
||||
* be different. You can use the following command to generate a new key pair:
|
||||
*
|
||||
* ssh-keygen -t ed25519 -C "your_email@example.com"
|
||||
*
|
||||
* Then, update the "public_key" attribute to point to the location of your
|
||||
* generated public key.
|
||||
*/
|
||||
resource "binarylane_ssh_key" "example" {
|
||||
name = "test-user"
|
||||
public_key = file("./id_ed25519.pub") # Likely "~/.ssh/id_ed25519.pub"
|
||||
default = true # Currently, the server will only register SSH keys that are added as global defaults
|
||||
}
|
||||
name = "tf-example-ssh"
|
||||
|
||||
public_key = file("./id_ed25519.pub") # Change to "~/.ssh/id_ed25519.pub"
|
||||
|
||||
# You also have the option to register an SSH key as a global default, for any
|
||||
# new VM but it is not the recommended approach.
|
||||
# default = false
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
name = "tf-example-ssh"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
ssh_keys = [binarylane_ssh_key.example.id]
|
||||
public_ipv4_count = 1
|
||||
|
||||
depends_on = [binarylane_ssh_key.example] # Wait for SSH key to be created as a global default
|
||||
|
||||
# TODO: Extend provider with "ssh_key_id" option for explicit registraton of keys
|
||||
# If you are using a global default SSH key, you need to explicitly wait for
|
||||
# it to be created before creating the server.
|
||||
# depends_on = [binarylane_ssh_key.example]
|
||||
}
|
||||
|
||||
+11
-13
@@ -6,19 +6,16 @@ terraform {
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
provider "binarylane" {}
|
||||
|
||||
resource "binarylane_vpc" "example" {
|
||||
name = "tf-example"
|
||||
name = "tf-example-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
# Web & NAT server
|
||||
resource "binarylane_server" "web" {
|
||||
name = "tf-vpc-example-web"
|
||||
name = "tf-example-vpc-web"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
@@ -28,7 +25,7 @@ resource "binarylane_server" "web" {
|
||||
|
||||
# Database server
|
||||
resource "binarylane_server" "db" {
|
||||
name = "tf-vpc-example-db"
|
||||
name = "tf-example-vpc-db"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
@@ -38,12 +35,13 @@ resource "binarylane_server" "db" {
|
||||
|
||||
# VPN server
|
||||
resource "binarylane_server" "vpn" {
|
||||
name = "tf-vpc-example-vpn"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
name = "tf-example-vpc-vpn"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
source_and_destination_check = false
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "example" {
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
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-docs v0.20.1
|
||||
github.com/hashicorp/terraform-plugin-framework v1.13.0
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.25.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/hashicorp/terraform-plugin-testing v1.11.0
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1
|
||||
github.com/oapi-codegen/runtime v1.1.1
|
||||
)
|
||||
|
||||
@@ -29,16 +30,16 @@ require (
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/fatih/color v1.18.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
|
||||
@@ -49,23 +50,23 @@ require (
|
||||
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.0 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/hc-install v0.8.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 // indirect
|
||||
github.com/hashicorp/hc-install v0.9.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.23.0 // indirect
|
||||
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-sdk/v2 v2.34.0 // indirect
|
||||
github.com/hashicorp/terraform-json v0.23.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.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
|
||||
@@ -77,13 +78,14 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/oklog/run v1.0.0 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/onsi/gomega v1.33.1 // indirect
|
||||
github.com/pb33f/libopenapi v0.15.0 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
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
|
||||
@@ -93,22 +95,22 @@ require (
|
||||
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/yuin/goldmark v1.7.1 // indirect
|
||||
github.com/yuin/goldmark v1.7.7 // indirect
|
||||
github.com/yuin/goldmark-meta v1.1.0 // indirect
|
||||
github.com/zclconf/go-cty v1.15.0 // indirect
|
||||
go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.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-20240730163845-b1a4ccb954bf // indirect
|
||||
google.golang.org/grpc v1.64.1 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/grpc v1.67.1 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -30,8 +30,8 @@ github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xW
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
@@ -55,26 +55,26 @@ github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5ql
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
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=
|
||||
@@ -120,8 +120,8 @@ github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A=
|
||||
github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI=
|
||||
github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog=
|
||||
github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
@@ -129,42 +129,44 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hc-install v0.8.0 h1:LdpZeXkZYMQhoKPCecJHlKvUkQFixN/nvyR1CdfOLjI=
|
||||
github.com/hashicorp/hc-install v0.8.0/go.mod h1:+MwJYjDfCruSD/udvBmRB22Nlkwwkwf5sAB6uTIhSaU=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/hashicorp/hc-install v0.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6eLhghE=
|
||||
github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
|
||||
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-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI=
|
||||
github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c=
|
||||
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-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-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.20.1 h1:Fq7E/HrU8kuZu3hNliZGwloFWSYfWEOWnylFhYQIoys=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.20.1/go.mod h1:Yz6HoK7/EgzSrHPB9J/lWFzwl9/xep2OPnc5jaJDV90=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ532Bh2BobF4H+yBiYJ/scw=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU=
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0 h1:I/N0g/eLZ1ZkLZXUQ0oRSXa8YG/EF0CEuQP1wXdrzKw=
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0/go.mod h1:t339KhmxnaF4SzdpxmqW8HnQBHVGYazwtfxU0qCs4eE=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0 h1:O9QqGoYDzQT7lwTXUsZEtgabeWW96zUBh47Smn2lkFA=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0/go.mod h1:Bh89/hNmqsEWug4/XWKYBwtnw3tbz5BAy1L1OgvbIaY=
|
||||
github.com/hashicorp/terraform-plugin-go v0.25.0 h1:oi13cx7xXA6QciMcpcFi/rwA974rdTxjqEhXJjbAyks=
|
||||
github.com/hashicorp/terraform-plugin-go v0.25.0/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw=
|
||||
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=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0/go.mod h1:sl/UoabMc37HA6ICVMmGO+/0wofkVIRxf+BMb/dnoIg=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.10.0 h1:2+tmRNhvnfE4Bs8rB6v58S/VpqzGC6RCh9Y8ujdn+aw=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.10.0/go.mod h1:iWRW3+loP33WMch2P/TEyCxxct/ZEcCGMquSLSCVsrc=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 h1:wyKCCtn6pBBL46c1uIIBNUOWlNfYXfXpVo16iDyLp8Y=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0/go.mod h1:B0Al8NyYVr8Mp/KLwssKXG1RqnTk7FySqSn4fRuLNgw=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.11.0 h1:MeDT5W3YHbONJt2aPQyaBsgQeAIckwPX41EUHXEn29A=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.11.0/go.mod h1:WNAHQ3DcgV/0J+B15WTE6hDvxcUdkPPpnB1FR3M910U=
|
||||
github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI=
|
||||
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 +175,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,12 +223,12 @@ 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.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
@@ -254,8 +256,8 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
@@ -264,6 +266,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=
|
||||
@@ -300,8 +304,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
|
||||
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
|
||||
github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
|
||||
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
|
||||
github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
|
||||
@@ -315,14 +319,14 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -333,14 +337,14 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -366,8 +370,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
@@ -378,8 +382,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@@ -393,10 +397,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-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
||||
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/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=
|
||||
@@ -405,8 +409,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -425,6 +429,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=
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package binarylane
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
|
||||
"github.com/deepmap/oapi-codegen/pkg/securityprovider"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
func NewClientWithDefaultConfig() (*ClientWithResponses, error) {
|
||||
return NewClientWithConfig("", "")
|
||||
}
|
||||
|
||||
func NewClientWithConfig(endpoint string, token string) (*ClientWithResponses, error) {
|
||||
if endpoint == "" {
|
||||
endpoint = os.Getenv("BINARYLANE_API_ENDPOINT")
|
||||
if endpoint == "" {
|
||||
endpoint = "https://api.binarylane.com.au/v2"
|
||||
}
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
token = os.Getenv("BINARYLANE_API_TOKEN")
|
||||
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
|
||||
}
|
||||
+144
-886
File diff suppressed because it is too large
Load Diff
@@ -4,20 +4,46 @@ OPENAPI_FILE=$(dirname "$0")/openapi.json
|
||||
|
||||
# Fetch the latest OpenAPI spec
|
||||
curl https://api.binarylane.com.au/reference/openapi.json --output $OPENAPI_FILE
|
||||
# Remove the /v2 prefix from the paths
|
||||
cat <<<$(jq '.paths |= with_entries(.key |= sub("/v2/"; "/"))' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
# Set the base URL with a /v2 suffix
|
||||
|
||||
# Move the /v2 prefix to the base URL and remove it from the paths
|
||||
cat <<<$(jq '.servers[0].url = "https://api.binarylane.com.au/v2"' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths |= with_entries(.key |= sub("/v2/"; "/"))' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
# Terraform can't handle oneOf types, so we need to replace them with basic types
|
||||
cat <<<$(jq '.components.schemas.CreateServerRequest.properties.image |= del(.oneOf) + {type:"string"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.CreateServerRequest.properties.ssh_keys.items |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.Rebuild.properties.image |= del(.oneOf) + {type:"string"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.ImageOptions.properties.ssh_keys.items |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.ChangeImage.properties.image |= del(.oneOf) + {type:"string"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].delete.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].put.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].get.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].delete.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].delete.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
# Remove the "/paths/{image_id}" path because its duplicated by "/images/{image_id_or_slug}"
|
||||
cat <<<$(jq 'del(.paths."/images/{image_id}")' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
# Add x-oapi-codegen-extra-tags so Go structs can be reflected
|
||||
|
||||
# Add x-oapi-codegen-extra-tags so structs can be reflected
|
||||
|
||||
## RouteEntryRequest
|
||||
cat <<<$(jq '.components.schemas.RouteEntryRequest.properties.destination += {"x-oapi-codegen-extra-tags": {"tfsdk": "destination"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.RouteEntryRequest.properties.description += {"x-oapi-codegen-extra-tags": {"tfsdk": "description"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.RouteEntryRequest.properties.router += {"x-oapi-codegen-extra-tags": {"tfsdk": "router"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
## AdvancedFirewallRule
|
||||
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.description += {"x-oapi-codegen-extra-tags": {"tfsdk": "description"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.protocol += {"x-oapi-codegen-extra-tags": {"tfsdk": "protocol"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.action += {"x-oapi-codegen-extra-tags": {"tfsdk": "action"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.destination_addresses += {"x-oapi-codegen-extra-tags": {"tfsdk": "destination_addresses"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.destination_ports += {"x-oapi-codegen-extra-tags": {"tfsdk": "destination_ports"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.source_addresses += {"x-oapi-codegen-extra-tags": {"tfsdk": "source_addresses"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
## 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
|
||||
|
||||
# Edit description here because it's hard to override nested schema properties
|
||||
cat <<<$(jq '.components.schemas.ForwardingRule.properties.entry_protocol.description = "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\"."' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
+139
-814
File diff suppressed because it is too large
Load Diff
@@ -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 (
|
||||
@@ -230,13 +230,6 @@ const (
|
||||
EnableIpv6TypeEnableIpv6 EnableIpv6Type = "enable_ipv6"
|
||||
)
|
||||
|
||||
// Defines values for HealthCheckProtocol.
|
||||
const (
|
||||
HealthCheckProtocolBoth HealthCheckProtocol = "both"
|
||||
HealthCheckProtocolHttp HealthCheckProtocol = "http"
|
||||
HealthCheckProtocolHttps HealthCheckProtocol = "https"
|
||||
)
|
||||
|
||||
// Defines values for ImageQueryType.
|
||||
const (
|
||||
ImageQueryTypeBackup ImageQueryType = "backup"
|
||||
@@ -265,8 +258,8 @@ const (
|
||||
|
||||
// Defines values for LoadBalancerRuleProtocol.
|
||||
const (
|
||||
LoadBalancerRuleProtocolHttp LoadBalancerRuleProtocol = "http"
|
||||
LoadBalancerRuleProtocolHttps LoadBalancerRuleProtocol = "https"
|
||||
Http LoadBalancerRuleProtocol = "http"
|
||||
Https LoadBalancerRuleProtocol = "https"
|
||||
)
|
||||
|
||||
// Defines values for LoadBalancerStatus.
|
||||
@@ -428,6 +421,7 @@ const (
|
||||
PcI440fx4point2 VmMachineType = "pc_i440fx_4point2"
|
||||
PcI440fx5point0 VmMachineType = "pc_i440fx_5point0"
|
||||
PcI440fx5point1 VmMachineType = "pc_i440fx_5point1"
|
||||
PcI440fx7point2 VmMachineType = "pc_i440fx_7point2"
|
||||
)
|
||||
|
||||
// Account defines model for Account.
|
||||
@@ -499,7 +493,7 @@ type Action struct {
|
||||
// ResourceType The resource type (if any) associated with this action.
|
||||
ResourceType *ResourceType `json:"resource_type"`
|
||||
|
||||
// ResultData Returned information from a completed action. For example: a successful completed 'ping' action will have the ping value in ms in this field.
|
||||
// ResultData Returned information from a completed action. For example: a successful completed 'uptime' action will have the uptime value in this field.
|
||||
ResultData *string `json:"result_data"`
|
||||
|
||||
// StartedAt The timestamp in ISO8601 format of when processing of this action started.
|
||||
@@ -593,22 +587,22 @@ type AdvancedFeature string
|
||||
// AdvancedFirewallRule defines model for AdvancedFirewallRule.
|
||||
type AdvancedFirewallRule struct {
|
||||
// Action The action to take when there is a match on this rule.
|
||||
Action AdvancedFirewallRuleAction `json:"action"`
|
||||
Action AdvancedFirewallRuleAction `json:"action" tfsdk:"action"`
|
||||
|
||||
// Description A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. "Block access to RDP" or "Allow access from HQ".
|
||||
Description *string `json:"description"`
|
||||
Description *string `json:"description" tfsdk:"description"`
|
||||
|
||||
// DestinationAddresses The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
DestinationAddresses []string `json:"destination_addresses"`
|
||||
DestinationAddresses []string `json:"destination_addresses" tfsdk:"destination_addresses"`
|
||||
|
||||
// DestinationPorts The destination ports to match for this rule. Leave null or empty to match on all ports.
|
||||
DestinationPorts *[]string `json:"destination_ports"`
|
||||
DestinationPorts *[]string `json:"destination_ports" tfsdk:"destination_ports"`
|
||||
|
||||
// Protocol The protocol to match for this rule.
|
||||
Protocol AdvancedFirewallRuleProtocol `json:"protocol"`
|
||||
Protocol AdvancedFirewallRuleProtocol `json:"protocol" tfsdk:"protocol"`
|
||||
|
||||
// SourceAddresses The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
SourceAddresses []string `json:"source_addresses"`
|
||||
SourceAddresses []string `json:"source_addresses" tfsdk:"source_addresses"`
|
||||
}
|
||||
|
||||
// AdvancedFirewallRuleAction
|
||||
@@ -721,7 +715,7 @@ type BackupInfo struct {
|
||||
// BackupDisks A list of the individual disks that make up this backup.
|
||||
BackupDisks *[]BackupDisk `json:"backup_disks,omitempty"`
|
||||
|
||||
// Iso If this is true the backup is an ISO image and cannot be restored. ISO images may only be attached for use as a boot disk or an additional disk.
|
||||
// Iso If this is true the backup is an ISO image and cannot be restored or downloaded. ISO images may only be attached for use as a boot disk or an additional disk.
|
||||
Iso *bool `json:"iso,omitempty"`
|
||||
|
||||
// Locked If this is true the backup is locked and cannot be replaced.
|
||||
@@ -861,23 +855,12 @@ type ChangeBackupScheduleType string
|
||||
// ChangeImage defines model for ChangeImage.
|
||||
type ChangeImage struct {
|
||||
// Image The slug or ID of the selected image. What type of image is permitted here varies based on the server action.
|
||||
Image *ChangeImage_Image `json:"image"`
|
||||
Image *string `json:"image"`
|
||||
|
||||
// Options Additional options for the server configuration after the image has been changed.
|
||||
Options *ImageOptions `json:"options"`
|
||||
}
|
||||
|
||||
// ChangeImageImage0 defines model for .
|
||||
type ChangeImageImage0 = int
|
||||
|
||||
// ChangeImageImage1 defines model for .
|
||||
type ChangeImageImage1 = string
|
||||
|
||||
// ChangeImage_Image The slug or ID of the selected image. What type of image is permitted here varies based on the server action.
|
||||
type ChangeImage_Image struct {
|
||||
union json.RawMessage
|
||||
}
|
||||
|
||||
// ChangeIpv6 Enable or Disable IPv6 for a Server
|
||||
type ChangeIpv6 struct {
|
||||
// Enabled The desired enabled status of IPv6.
|
||||
@@ -1131,7 +1114,7 @@ type ConsoleResponse struct {
|
||||
// CreateLoadBalancerRequest defines model for CreateLoadBalancerRequest.
|
||||
type CreateLoadBalancerRequest struct {
|
||||
// ForwardingRules The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule.
|
||||
ForwardingRules *[]ForwardingRule `json:"forwarding_rules"`
|
||||
ForwardingRules *[]ForwardingRule `json:"forwarding_rules" tfsdk:"forwarding_rules"`
|
||||
|
||||
// HealthCheck The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules.
|
||||
HealthCheck *HealthCheck `json:"health_check"`
|
||||
@@ -1185,7 +1168,7 @@ type CreateServerRequest struct {
|
||||
Size string `json:"size"`
|
||||
|
||||
// SshKeys This may be either the SSH keys Ids or fingerprints. If this is null or not provided any SSH keys that have been marked as default will be deployed (if the operating system supports SSH keys). Submit an empty array to disable deployment of default keys.
|
||||
SshKeys *[]CreateServerRequest_SshKeys_Item `json:"ssh_keys"`
|
||||
SshKeys *[]int `json:"ssh_keys"`
|
||||
|
||||
// UserData If provided this will be used to initialise the new server. This must be left null if the Image does not support UserData, see DistributionInfo.Features for more information.
|
||||
UserData *string `json:"user_data"`
|
||||
@@ -1194,17 +1177,6 @@ type CreateServerRequest struct {
|
||||
VpcId *int64 `json:"vpc_id"`
|
||||
}
|
||||
|
||||
// CreateServerRequestSshKeys0 defines model for .
|
||||
type CreateServerRequestSshKeys0 = int
|
||||
|
||||
// CreateServerRequestSshKeys1 defines model for .
|
||||
type CreateServerRequestSshKeys1 = string
|
||||
|
||||
// CreateServerRequest_SshKeys_Item defines model for CreateServerRequest.ssh_keys.Item.
|
||||
type CreateServerRequest_SshKeys_Item struct {
|
||||
union json.RawMessage
|
||||
}
|
||||
|
||||
// CreateServerResponse defines model for CreateServerResponse.
|
||||
type CreateServerResponse struct {
|
||||
Links *ActionsLinks `json:"links,omitempty"`
|
||||
@@ -1375,6 +1347,9 @@ type Domain struct {
|
||||
// CurrentNameservers The current authoritative name servers for this domain.
|
||||
CurrentNameservers *[]string `json:"current_nameservers,omitempty"`
|
||||
|
||||
// Id The ID of this domain.
|
||||
Id *int64 `json:"id,omitempty"`
|
||||
|
||||
// Name The name of the domain.
|
||||
Name *string `json:"name,omitempty"`
|
||||
|
||||
@@ -1528,25 +1503,10 @@ type EnableIpv6 struct {
|
||||
// EnableIpv6Type defines model for EnableIpv6.Type.
|
||||
type EnableIpv6Type string
|
||||
|
||||
// FailoverIpsRequest defines model for FailoverIpsRequest.
|
||||
type FailoverIpsRequest struct {
|
||||
// FailoverIps The list of IP Failover addresses to assign to this server. This overwrites the current list, so any current IP Failover addresses that are omitted will be removed from the server.
|
||||
FailoverIps []string `json:"failover_ips"`
|
||||
}
|
||||
|
||||
// FailoverIpsResponse defines model for FailoverIpsResponse.
|
||||
type FailoverIpsResponse struct {
|
||||
FailoverIps *[]string `json:"failover_ips,omitempty"`
|
||||
Links *Links `json:"links"`
|
||||
|
||||
// Meta Contains metadata about the response, currently this includes the total number of items.
|
||||
Meta Meta `json:"meta"`
|
||||
}
|
||||
|
||||
// ForwardingRule defines model for ForwardingRule.
|
||||
type ForwardingRule struct {
|
||||
// EntryProtocol The protocol that traffic must match for this load balancer to forward traffic according to this rule.
|
||||
EntryProtocol LoadBalancerRuleProtocol `json:"entry_protocol"`
|
||||
// EntryProtocol The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
|
||||
EntryProtocol LoadBalancerRuleProtocol `json:"entry_protocol" tfsdk:"entry_protocol"`
|
||||
}
|
||||
|
||||
// ForwardingRulesRequest defines model for ForwardingRulesRequest.
|
||||
@@ -1570,7 +1530,7 @@ type HealthCheck struct {
|
||||
// | http | The health check will be performed via HTTP. |
|
||||
// | https | The health check will be performed via HTTPS. |
|
||||
// | both | The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol. |
|
||||
type HealthCheckProtocol string
|
||||
type HealthCheckProtocol = string
|
||||
|
||||
// Host defines model for Host.
|
||||
type Host struct {
|
||||
@@ -1677,23 +1637,12 @@ type ImageOptions struct {
|
||||
// SshKeys This may be either the existing SSH Keys IDs or fingerprints.
|
||||
// If this is null or not provided any SSH keys that have been marked as default will be deployed (if the operating system supports SSH Keys).
|
||||
// Submit an empty array to disable deployment of default keys.
|
||||
SshKeys *[]ImageOptions_SshKeys_Item `json:"ssh_keys"`
|
||||
SshKeys *[]int `json:"ssh_keys"`
|
||||
|
||||
// UserData If provided this will be used to initialise the new server. This must be left null if the Image does not support UserData, see DistributionInfo.Features for more information.
|
||||
UserData *string `json:"user_data"`
|
||||
}
|
||||
|
||||
// ImageOptionsSshKeys0 defines model for .
|
||||
type ImageOptionsSshKeys0 = int
|
||||
|
||||
// ImageOptionsSshKeys1 defines model for .
|
||||
type ImageOptionsSshKeys1 = string
|
||||
|
||||
// ImageOptions_SshKeys_Item defines model for ImageOptions.ssh_keys.Item.
|
||||
type ImageOptions_SshKeys_Item struct {
|
||||
union json.RawMessage
|
||||
}
|
||||
|
||||
// ImageQueryType
|
||||
// | Value | Description |
|
||||
// | ----- | ----------- |
|
||||
@@ -1875,7 +1824,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"`
|
||||
@@ -2191,24 +2140,13 @@ type RebootType string
|
||||
// Rebuild Rebuild an Existing Server
|
||||
type Rebuild struct {
|
||||
// Image The Operating System ID or slug or Backup image ID to use as a base for the rebuild.
|
||||
Image *Rebuild_Image `json:"image"`
|
||||
Image *string `json:"image"`
|
||||
|
||||
// Options Additional options. Leaving this entirely null or any of the properties included null will use the defaults from the existing server.
|
||||
Options *ImageOptions `json:"options"`
|
||||
Type RebuildType `json:"type"`
|
||||
}
|
||||
|
||||
// RebuildImage0 defines model for .
|
||||
type RebuildImage0 = int
|
||||
|
||||
// RebuildImage1 defines model for .
|
||||
type RebuildImage1 = string
|
||||
|
||||
// Rebuild_Image The Operating System ID or slug or Backup image ID to use as a base for the rebuild.
|
||||
type Rebuild_Image struct {
|
||||
union json.RawMessage
|
||||
}
|
||||
|
||||
// RebuildType defines model for Rebuild.Type.
|
||||
type RebuildType string
|
||||
|
||||
@@ -3077,6 +3015,7 @@ type VideoDevice string
|
||||
// | pc_i440fx_4point2 | PC I440 FX 4.2 |
|
||||
// | pc_i440fx_5point0 | PC I440 FX 5.0 |
|
||||
// | pc_i440fx_5point1 | PC I440 FX 5.1 |
|
||||
// | pc_i440fx_7point2 | PC I440 FX 7.2 |
|
||||
type VmMachineType string
|
||||
|
||||
// Vpc defines model for Vpc.
|
||||
@@ -3187,6 +3126,18 @@ type GetDomainsParams struct {
|
||||
// PostDomainsJSONBody defines parameters for PostDomains.
|
||||
type PostDomainsJSONBody = DomainRequest
|
||||
|
||||
// DeleteDomainsDomainNameParamsDomainName0 defines parameters for DeleteDomainsDomainName.
|
||||
type DeleteDomainsDomainNameParamsDomainName0 = int
|
||||
|
||||
// DeleteDomainsDomainNameParamsDomainName1 defines parameters for DeleteDomainsDomainName.
|
||||
type DeleteDomainsDomainNameParamsDomainName1 = string
|
||||
|
||||
// GetDomainsDomainNameParamsDomainName0 defines parameters for GetDomainsDomainName.
|
||||
type GetDomainsDomainNameParamsDomainName0 = int
|
||||
|
||||
// GetDomainsDomainNameParamsDomainName1 defines parameters for GetDomainsDomainName.
|
||||
type GetDomainsDomainNameParamsDomainName1 = string
|
||||
|
||||
// GetDomainsDomainNameRecordsParams defines parameters for GetDomainsDomainNameRecords.
|
||||
type GetDomainsDomainNameRecordsParams struct {
|
||||
// Type
|
||||
@@ -3214,32 +3165,41 @@ type GetDomainsDomainNameRecordsParams struct {
|
||||
PerPage *int32 `form:"per_page,omitempty" json:"per_page,omitempty"`
|
||||
}
|
||||
|
||||
// GetDomainsDomainNameRecordsParamsDomainName0 defines parameters for GetDomainsDomainNameRecords.
|
||||
type GetDomainsDomainNameRecordsParamsDomainName0 = int
|
||||
|
||||
// GetDomainsDomainNameRecordsParamsDomainName1 defines parameters for GetDomainsDomainNameRecords.
|
||||
type GetDomainsDomainNameRecordsParamsDomainName1 = string
|
||||
|
||||
// PostDomainsDomainNameRecordsJSONBody defines parameters for PostDomainsDomainNameRecords.
|
||||
type PostDomainsDomainNameRecordsJSONBody = DomainRecordRequest
|
||||
|
||||
// PostDomainsDomainNameRecordsParamsDomainName0 defines parameters for PostDomainsDomainNameRecords.
|
||||
type PostDomainsDomainNameRecordsParamsDomainName0 = int
|
||||
|
||||
// PostDomainsDomainNameRecordsParamsDomainName1 defines parameters for PostDomainsDomainNameRecords.
|
||||
type PostDomainsDomainNameRecordsParamsDomainName1 = string
|
||||
|
||||
// DeleteDomainsDomainNameRecordsRecordIdParamsDomainName0 defines parameters for DeleteDomainsDomainNameRecordsRecordId.
|
||||
type DeleteDomainsDomainNameRecordsRecordIdParamsDomainName0 = int
|
||||
|
||||
// DeleteDomainsDomainNameRecordsRecordIdParamsDomainName1 defines parameters for DeleteDomainsDomainNameRecordsRecordId.
|
||||
type DeleteDomainsDomainNameRecordsRecordIdParamsDomainName1 = string
|
||||
|
||||
// GetDomainsDomainNameRecordsRecordIdParamsDomainName0 defines parameters for GetDomainsDomainNameRecordsRecordId.
|
||||
type GetDomainsDomainNameRecordsRecordIdParamsDomainName0 = int
|
||||
|
||||
// GetDomainsDomainNameRecordsRecordIdParamsDomainName1 defines parameters for GetDomainsDomainNameRecordsRecordId.
|
||||
type GetDomainsDomainNameRecordsRecordIdParamsDomainName1 = string
|
||||
|
||||
// PutDomainsDomainNameRecordsRecordIdJSONBody defines parameters for PutDomainsDomainNameRecordsRecordId.
|
||||
type PutDomainsDomainNameRecordsRecordIdJSONBody = DomainRecordRequest
|
||||
|
||||
// GetFailoverIpsServerIdParams defines parameters for GetFailoverIpsServerId.
|
||||
type GetFailoverIpsServerIdParams struct {
|
||||
// Page The selected page. Page numbering starts at 1
|
||||
Page *int32 `form:"page,omitempty" json:"page,omitempty"`
|
||||
// PutDomainsDomainNameRecordsRecordIdParamsDomainName0 defines parameters for PutDomainsDomainNameRecordsRecordId.
|
||||
type PutDomainsDomainNameRecordsRecordIdParamsDomainName0 = int
|
||||
|
||||
// PerPage The number of results to show per page.
|
||||
PerPage *int32 `form:"per_page,omitempty" json:"per_page,omitempty"`
|
||||
}
|
||||
|
||||
// PostFailoverIpsServerIdJSONBody defines parameters for PostFailoverIpsServerId.
|
||||
type PostFailoverIpsServerIdJSONBody = FailoverIpsRequest
|
||||
|
||||
// GetFailoverIpsServerIdAvailableParams defines parameters for GetFailoverIpsServerIdAvailable.
|
||||
type GetFailoverIpsServerIdAvailableParams struct {
|
||||
// Page The selected page. Page numbering starts at 1
|
||||
Page *int32 `form:"page,omitempty" json:"page,omitempty"`
|
||||
|
||||
// PerPage The number of results to show per page.
|
||||
PerPage *int32 `form:"per_page,omitempty" json:"per_page,omitempty"`
|
||||
}
|
||||
// PutDomainsDomainNameRecordsRecordIdParamsDomainName1 defines parameters for PutDomainsDomainNameRecordsRecordId.
|
||||
type PutDomainsDomainNameRecordsRecordIdParamsDomainName1 = string
|
||||
|
||||
// GetImagesParams defines parameters for GetImages.
|
||||
type GetImagesParams struct {
|
||||
@@ -3528,9 +3488,6 @@ type PostDomainsDomainNameRecordsJSONRequestBody = PostDomainsDomainNameRecordsJ
|
||||
// PutDomainsDomainNameRecordsRecordIdJSONRequestBody defines body for PutDomainsDomainNameRecordsRecordId for application/json ContentType.
|
||||
type PutDomainsDomainNameRecordsRecordIdJSONRequestBody = PutDomainsDomainNameRecordsRecordIdJSONBody
|
||||
|
||||
// PostFailoverIpsServerIdJSONRequestBody defines body for PostFailoverIpsServerId for application/json ContentType.
|
||||
type PostFailoverIpsServerIdJSONRequestBody = PostFailoverIpsServerIdJSONBody
|
||||
|
||||
// PostLoadBalancersJSONRequestBody defines body for PostLoadBalancers for application/json ContentType.
|
||||
type PostLoadBalancersJSONRequestBody = PostLoadBalancersJSONBody
|
||||
|
||||
@@ -3958,192 +3915,6 @@ func (a ValidationProblemDetails) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(object)
|
||||
}
|
||||
|
||||
// AsChangeImageImage0 returns the union data inside the ChangeImage_Image as a ChangeImageImage0
|
||||
func (t ChangeImage_Image) AsChangeImageImage0() (ChangeImageImage0, error) {
|
||||
var body ChangeImageImage0
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromChangeImageImage0 overwrites any union data inside the ChangeImage_Image as the provided ChangeImageImage0
|
||||
func (t *ChangeImage_Image) FromChangeImageImage0(v ChangeImageImage0) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeChangeImageImage0 performs a merge with any union data inside the ChangeImage_Image, using the provided ChangeImageImage0
|
||||
func (t *ChangeImage_Image) MergeChangeImageImage0(v ChangeImageImage0) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
// AsChangeImageImage1 returns the union data inside the ChangeImage_Image as a ChangeImageImage1
|
||||
func (t ChangeImage_Image) AsChangeImageImage1() (ChangeImageImage1, error) {
|
||||
var body ChangeImageImage1
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromChangeImageImage1 overwrites any union data inside the ChangeImage_Image as the provided ChangeImageImage1
|
||||
func (t *ChangeImage_Image) FromChangeImageImage1(v ChangeImageImage1) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeChangeImageImage1 performs a merge with any union data inside the ChangeImage_Image, using the provided ChangeImageImage1
|
||||
func (t *ChangeImage_Image) MergeChangeImageImage1(v ChangeImageImage1) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
func (t ChangeImage_Image) MarshalJSON() ([]byte, error) {
|
||||
b, err := t.union.MarshalJSON()
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (t *ChangeImage_Image) UnmarshalJSON(b []byte) error {
|
||||
err := t.union.UnmarshalJSON(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// AsCreateServerRequestSshKeys0 returns the union data inside the CreateServerRequest_SshKeys_Item as a CreateServerRequestSshKeys0
|
||||
func (t CreateServerRequest_SshKeys_Item) AsCreateServerRequestSshKeys0() (CreateServerRequestSshKeys0, error) {
|
||||
var body CreateServerRequestSshKeys0
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromCreateServerRequestSshKeys0 overwrites any union data inside the CreateServerRequest_SshKeys_Item as the provided CreateServerRequestSshKeys0
|
||||
func (t *CreateServerRequest_SshKeys_Item) FromCreateServerRequestSshKeys0(v CreateServerRequestSshKeys0) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeCreateServerRequestSshKeys0 performs a merge with any union data inside the CreateServerRequest_SshKeys_Item, using the provided CreateServerRequestSshKeys0
|
||||
func (t *CreateServerRequest_SshKeys_Item) MergeCreateServerRequestSshKeys0(v CreateServerRequestSshKeys0) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
// AsCreateServerRequestSshKeys1 returns the union data inside the CreateServerRequest_SshKeys_Item as a CreateServerRequestSshKeys1
|
||||
func (t CreateServerRequest_SshKeys_Item) AsCreateServerRequestSshKeys1() (CreateServerRequestSshKeys1, error) {
|
||||
var body CreateServerRequestSshKeys1
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromCreateServerRequestSshKeys1 overwrites any union data inside the CreateServerRequest_SshKeys_Item as the provided CreateServerRequestSshKeys1
|
||||
func (t *CreateServerRequest_SshKeys_Item) FromCreateServerRequestSshKeys1(v CreateServerRequestSshKeys1) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeCreateServerRequestSshKeys1 performs a merge with any union data inside the CreateServerRequest_SshKeys_Item, using the provided CreateServerRequestSshKeys1
|
||||
func (t *CreateServerRequest_SshKeys_Item) MergeCreateServerRequestSshKeys1(v CreateServerRequestSshKeys1) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
func (t CreateServerRequest_SshKeys_Item) MarshalJSON() ([]byte, error) {
|
||||
b, err := t.union.MarshalJSON()
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (t *CreateServerRequest_SshKeys_Item) UnmarshalJSON(b []byte) error {
|
||||
err := t.union.UnmarshalJSON(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// AsImageOptionsSshKeys0 returns the union data inside the ImageOptions_SshKeys_Item as a ImageOptionsSshKeys0
|
||||
func (t ImageOptions_SshKeys_Item) AsImageOptionsSshKeys0() (ImageOptionsSshKeys0, error) {
|
||||
var body ImageOptionsSshKeys0
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromImageOptionsSshKeys0 overwrites any union data inside the ImageOptions_SshKeys_Item as the provided ImageOptionsSshKeys0
|
||||
func (t *ImageOptions_SshKeys_Item) FromImageOptionsSshKeys0(v ImageOptionsSshKeys0) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeImageOptionsSshKeys0 performs a merge with any union data inside the ImageOptions_SshKeys_Item, using the provided ImageOptionsSshKeys0
|
||||
func (t *ImageOptions_SshKeys_Item) MergeImageOptionsSshKeys0(v ImageOptionsSshKeys0) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
// AsImageOptionsSshKeys1 returns the union data inside the ImageOptions_SshKeys_Item as a ImageOptionsSshKeys1
|
||||
func (t ImageOptions_SshKeys_Item) AsImageOptionsSshKeys1() (ImageOptionsSshKeys1, error) {
|
||||
var body ImageOptionsSshKeys1
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromImageOptionsSshKeys1 overwrites any union data inside the ImageOptions_SshKeys_Item as the provided ImageOptionsSshKeys1
|
||||
func (t *ImageOptions_SshKeys_Item) FromImageOptionsSshKeys1(v ImageOptionsSshKeys1) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeImageOptionsSshKeys1 performs a merge with any union data inside the ImageOptions_SshKeys_Item, using the provided ImageOptionsSshKeys1
|
||||
func (t *ImageOptions_SshKeys_Item) MergeImageOptionsSshKeys1(v ImageOptionsSshKeys1) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
func (t ImageOptions_SshKeys_Item) MarshalJSON() ([]byte, error) {
|
||||
b, err := t.union.MarshalJSON()
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (t *ImageOptions_SshKeys_Item) UnmarshalJSON(b []byte) error {
|
||||
err := t.union.UnmarshalJSON(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// AsNetworkNetmask0 returns the union data inside the Network_Netmask as a NetworkNetmask0
|
||||
func (t Network_Netmask) AsNetworkNetmask0() (NetworkNetmask0, error) {
|
||||
var body NetworkNetmask0
|
||||
@@ -4206,68 +3977,6 @@ func (t *Network_Netmask) UnmarshalJSON(b []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// AsRebuildImage0 returns the union data inside the Rebuild_Image as a RebuildImage0
|
||||
func (t Rebuild_Image) AsRebuildImage0() (RebuildImage0, error) {
|
||||
var body RebuildImage0
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromRebuildImage0 overwrites any union data inside the Rebuild_Image as the provided RebuildImage0
|
||||
func (t *Rebuild_Image) FromRebuildImage0(v RebuildImage0) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeRebuildImage0 performs a merge with any union data inside the Rebuild_Image, using the provided RebuildImage0
|
||||
func (t *Rebuild_Image) MergeRebuildImage0(v RebuildImage0) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
// AsRebuildImage1 returns the union data inside the Rebuild_Image as a RebuildImage1
|
||||
func (t Rebuild_Image) AsRebuildImage1() (RebuildImage1, error) {
|
||||
var body RebuildImage1
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromRebuildImage1 overwrites any union data inside the Rebuild_Image as the provided RebuildImage1
|
||||
func (t *Rebuild_Image) FromRebuildImage1(v RebuildImage1) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeRebuildImage1 performs a merge with any union data inside the Rebuild_Image, using the provided RebuildImage1
|
||||
func (t *Rebuild_Image) MergeRebuildImage1(v RebuildImage1) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
func (t Rebuild_Image) MarshalJSON() ([]byte, error) {
|
||||
b, err := t.union.MarshalJSON()
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (t *Rebuild_Image) UnmarshalJSON(b []byte) error {
|
||||
err := t.union.UnmarshalJSON(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// AsRestoreImage0 returns the union data inside the Restore_Image as a RestoreImage0
|
||||
func (t Restore_Image) AsRestoreImage0() (RestoreImage0, error) {
|
||||
var body RestoreImage0
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSource = &loadBalancerDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &loadBalancerDataSource{}
|
||||
)
|
||||
|
||||
func NewLoadBalancerDataSource() datasource.DataSource {
|
||||
return &loadBalancerDataSource{}
|
||||
}
|
||||
|
||||
type loadBalancerDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type loadBalancerDataSourceModel struct {
|
||||
resources.LoadBalancerModel
|
||||
}
|
||||
|
||||
func (d *loadBalancerDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_load_balancer"
|
||||
}
|
||||
|
||||
func (d *loadBalancerDataSource) Configure(
|
||||
_ context.Context,
|
||||
req datasource.ConfigureRequest,
|
||||
resp *datasource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Data Source Configure Type",
|
||||
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData))
|
||||
return
|
||||
}
|
||||
|
||||
d.bc = &bc
|
||||
}
|
||||
|
||||
func (d *loadBalancerDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(
|
||||
resources.LoadBalancerResourceSchema(ctx),
|
||||
AttributeConfig{
|
||||
RequiredAttributes: &[]string{"id"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
|
||||
return
|
||||
}
|
||||
resp.Schema = *ds
|
||||
// resp.Schema.Description = "TODO"
|
||||
}
|
||||
|
||||
func (d *loadBalancerDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data loadBalancerDataSourceModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
lbResp, err := d.bc.client.GetLoadBalancersLoadBalancerIdWithResponse(ctx, data.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading load balancer: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if lbResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading load balancer",
|
||||
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags := setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
"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"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &loadBalancerResource{}
|
||||
_ resource.ResourceWithConfigure = &loadBalancerResource{}
|
||||
_ resource.ResourceWithImportState = &loadBalancerResource{}
|
||||
)
|
||||
|
||||
func NewLoadBalancerResource() resource.Resource {
|
||||
return &loadBalancerResource{}
|
||||
}
|
||||
|
||||
type loadBalancerResource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type loadBalancerResourceModel struct {
|
||||
resources.LoadBalancerModel
|
||||
Timeouts timeouts.Value `tfsdk:"timeouts"`
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_load_balancer"
|
||||
}
|
||||
|
||||
func (d *loadBalancerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData),
|
||||
)
|
||||
return
|
||||
}
|
||||
d.bc = &bc
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.LoadBalancerResourceSchema(ctx)
|
||||
// resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
id := resp.Schema.Attributes["id"]
|
||||
resp.Schema.Attributes["id"] = &schema.Int64Attribute{
|
||||
Description: id.GetDescription(),
|
||||
MarkdownDescription: id.GetMarkdownDescription(),
|
||||
// read only
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.Int64{
|
||||
int64planmodifier.UseStateForUnknown(),
|
||||
},
|
||||
}
|
||||
|
||||
region := resp.Schema.Attributes["region"]
|
||||
resp.Schema.Attributes["region"] = &schema.StringAttribute{
|
||||
Description: region.GetDescription(),
|
||||
MarkdownDescription: region.GetMarkdownDescription(),
|
||||
Optional: true,
|
||||
Computed: false, // region is not computed, defined at creation
|
||||
PlanModifiers: []planmodifier.String{ // region is not allowed to be changed
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
}
|
||||
|
||||
resp.Schema.Attributes["timeouts"] =
|
||||
timeouts.Attributes(ctx, timeouts.Opts{
|
||||
Create: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data loadBalancerResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Create API call logic
|
||||
forwardingRules := []binarylane.ForwardingRule{}
|
||||
diags := data.LoadBalancerModel.ForwardingRules.ElementsAs(ctx, &forwardingRules, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
serverIds := []int64{}
|
||||
diags = data.ServerIds.ElementsAs(ctx, &serverIds, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
body := binarylane.CreateLoadBalancerRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
ForwardingRules: &forwardingRules,
|
||||
ServerIds: &serverIds,
|
||||
}
|
||||
|
||||
if data.HealthCheck.Path.ValueStringPointer() != nil || data.HealthCheck.Protocol.ValueStringPointer() != nil {
|
||||
body.HealthCheck = &binarylane.HealthCheck{
|
||||
Path: data.HealthCheck.Path.ValueStringPointer(),
|
||||
Protocol: data.HealthCheck.Protocol.ValueStringPointer(),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if lbResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected 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
|
||||
}
|
||||
|
||||
diags = setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
createTimeout, diags := data.Timeouts.Create(ctx, 20*time.Minute)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
ctx, cancel := context.WithTimeout(ctx, createTimeout)
|
||||
defer cancel()
|
||||
var lastReadyResp *binarylane.GetActionsActionIdResponse
|
||||
|
||||
retryLoop:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if lastReadyResp == nil {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError(
|
||||
"Timed out waiting for load balancer to be created",
|
||||
fmt.Sprintf(
|
||||
"Timed out waiting for load balancer %s to be created, last response was status=%s, body: %s",
|
||||
data.Name.ValueString(),
|
||||
lastReadyResp.Status(),
|
||||
lastReadyResp.Body,
|
||||
),
|
||||
)
|
||||
return
|
||||
default:
|
||||
readyResp, err := r.bc.client.GetActionsActionIdWithResponse(ctx, createActionId)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for load balancer to be created", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if readyResp.StatusCode() == http.StatusOK && readyResp.JSON200.Action.CompletedAt != nil {
|
||||
break retryLoop
|
||||
}
|
||||
|
||||
lastReadyResp = readyResp
|
||||
tflog.Debug(ctx, fmt.Sprintf("Waiting for load balancer to be created: 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) {
|
||||
var data loadBalancerResourceModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
lbResp, err := r.bc.client.GetLoadBalancersLoadBalancerIdWithResponse(ctx, data.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading load balancer: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if lbResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading load balancer",
|
||||
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags := setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data loadBalancerResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
forwardingRules := &[]binarylane.ForwardingRule{}
|
||||
diags := data.LoadBalancerModel.ForwardingRules.ElementsAs(ctx, forwardingRules, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
serverIds := &[]int64{}
|
||||
diags = data.ServerIds.ElementsAs(ctx, serverIds, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, fmt.Sprintf("Updating Load Balancer: name=%s", data.Name.ValueString()))
|
||||
|
||||
body := binarylane.UpdateLoadBalancerRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
ForwardingRules: forwardingRules,
|
||||
HealthCheck: &binarylane.HealthCheck{
|
||||
Path: data.HealthCheck.Path.ValueStringPointer(),
|
||||
Protocol: data.HealthCheck.Protocol.ValueStringPointer(),
|
||||
},
|
||||
ServerIds: serverIds,
|
||||
}
|
||||
|
||||
lbResp, err := r.bc.client.PutLoadBalancersLoadBalancerIdWithResponse(ctx, data.Id.ValueInt64(), body)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading load balancer: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if lbResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading load balancer",
|
||||
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags = setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data loadBalancerResourceModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete API call logic
|
||||
tflog.Debug(ctx, fmt.Sprintf("Deleting Load Balancer: name=%s", data.Name.ValueString()))
|
||||
|
||||
lbResp, err := r.bc.client.DeleteLoadBalancersLoadBalancerIdWithResponse(ctx, data.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error deleting load balancer: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if lbResp.StatusCode() != http.StatusNoContent {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code deleting load balancer",
|
||||
fmt.Sprintf("Received %s deleting load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) 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 loadBalancer binarylane.LoadBalancer
|
||||
var nextPage bool = true
|
||||
|
||||
for nextPage { // Need to paginate because the API does not support filtering by name
|
||||
params := binarylane.GetLoadBalancersParams{
|
||||
Page: &page,
|
||||
PerPage: &perPage,
|
||||
}
|
||||
|
||||
lbResp, err := r.bc.client.GetLoadBalancersWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Error getting load balancer for import: name=%s", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if lbResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code getting load balancer for import",
|
||||
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), req.ID,
|
||||
lbResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
loadBalancers := *lbResp.JSON200.LoadBalancers
|
||||
for _, lb := range loadBalancers {
|
||||
if *lb.Name == req.ID {
|
||||
loadBalancer = lb
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if lbResp.JSON200.Links == nil || lbResp.JSON200.Links.Pages == nil || lbResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
|
||||
if loadBalancer.Id == nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Could not find load balancer by name",
|
||||
fmt.Sprintf("Error finding load balancer: name=%s", req.ID),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("id"), *loadBalancer.Id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
|
||||
func setLoadBalancerModelState(ctx context.Context, data *resources.LoadBalancerModel, lb *binarylane.LoadBalancer) diag.Diagnostics {
|
||||
var diags, diag diag.Diagnostics
|
||||
|
||||
data.Id = types.Int64Value(*lb.Id)
|
||||
data.Name = types.StringValue(*lb.Name)
|
||||
|
||||
if lb.Region == nil {
|
||||
data.Region = types.StringNull()
|
||||
} else {
|
||||
data.Region = types.StringValue(*lb.Region.Slug)
|
||||
}
|
||||
|
||||
data.ServerIds, diags = types.ListValueFrom(ctx, types.Int64Type, lb.ServerIds)
|
||||
|
||||
if lb.HealthCheck == nil {
|
||||
data.HealthCheck = resources.NewHealthCheckValueNull()
|
||||
} else {
|
||||
data.HealthCheck, diag = resources.NewHealthCheckValue(
|
||||
resources.HealthCheckValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"path": types.StringValue(*lb.HealthCheck.Path),
|
||||
"protocol": types.StringValue(*lb.HealthCheck.Protocol),
|
||||
},
|
||||
)
|
||||
diags.Append(diag...)
|
||||
}
|
||||
|
||||
data.ForwardingRules, diag = types.ListValueFrom(ctx,
|
||||
types.ObjectType{AttrTypes: resources.ForwardingRulesValue{}.AttributeTypes(ctx)},
|
||||
lb.ForwardingRules,
|
||||
)
|
||||
diags.Append(diag...)
|
||||
|
||||
return diags
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestLoadBalancerResource(t *testing.T) {
|
||||
// Must assign a password to the server or Binary Lane will send emails
|
||||
pw_bytes := make([]byte, 12)
|
||||
_, err := rand.Read(pw_bytes)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to generate password: %s", err)
|
||||
return
|
||||
}
|
||||
password := base64.URLEncoding.EncodeToString(pw_bytes)
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
count = 2
|
||||
name = "tf-test-lb-server-${count.index}"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
|
||||
resource "binarylane_load_balancer" "test" {
|
||||
name = "tf-test-lb"
|
||||
server_ids = [binarylane_server.test.0.id, binarylane_server.test.1.id]
|
||||
forwarding_rules = [{ entry_protocol = "http" }]
|
||||
}
|
||||
|
||||
data "binarylane_load_balancer" "test" {
|
||||
depends_on = [binarylane_load_balancer.test]
|
||||
|
||||
id = binarylane_load_balancer.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttrSet("binarylane_load_balancer.test", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "name", "tf-test-lb"),
|
||||
resource.TestCheckNoResourceAttr("binarylane_load_balancer.test", "region"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "server_ids.#", "2"),
|
||||
resource.TestCheckResourceAttrPair("binarylane_load_balancer.test", "server_ids.0", "binarylane_server.test.0", "id"),
|
||||
resource.TestCheckResourceAttrPair("binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "http"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "id", "binarylane_load_balancer.test", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "name", "tf-test-lb"),
|
||||
resource.TestCheckNoResourceAttr("data.binarylane_load_balancer.test", "region"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "server_ids.#", "2"),
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "server_ids.0", "binarylane_server.test.0", "id"),
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "http"),
|
||||
),
|
||||
},
|
||||
// Test import by ID
|
||||
{
|
||||
ResourceName: "binarylane_load_balancer.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
// Test import by name
|
||||
{
|
||||
ResourceName: "binarylane_load_balancer.test",
|
||||
ImportState: true,
|
||||
ImportStateId: "tf-test-lb",
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("load_balancer", &resource.Sweeper{
|
||||
Name: "load_balancer",
|
||||
Dependencies: []string{"server"},
|
||||
F: func(_ string) error {
|
||||
client, err := binarylane.NewClientWithDefaultConfig()
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
listResp, err := client.GetLoadBalancersWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting load balancers for test sweep: %w", err)
|
||||
}
|
||||
|
||||
if listResp.StatusCode() != http.StatusOK {
|
||||
return fmt.Errorf("Unexpected status code getting load balancers for test sweep: %s", listResp.Body)
|
||||
}
|
||||
|
||||
loadBalancers := *listResp.JSON200.LoadBalancers
|
||||
for _, lb := range loadBalancers {
|
||||
if strings.HasPrefix(*lb.Name, "tf-test-") {
|
||||
deleteResp, 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 deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting load balancer %d in test sweep: %s", deleteResp.StatusCode(), *lb.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted load balancer during test sweep:", *lb.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -2,12 +2,9 @@ package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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"
|
||||
@@ -25,8 +22,7 @@ func New(version string) func() provider.Provider {
|
||||
}
|
||||
|
||||
type BinarylaneClient struct {
|
||||
endpoint string
|
||||
client *binarylane.ClientWithResponses
|
||||
client *binarylane.ClientWithResponses
|
||||
}
|
||||
|
||||
type binarylaneProvider struct {
|
||||
@@ -67,68 +63,17 @@ 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()
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
client, err := binarylane.NewClientWithResponses(endpoint, binarylane.WithRequestEditorFn(auth.Intercept))
|
||||
client, err := binarylane.NewClientWithConfig(
|
||||
config.Endpoint.ValueString(),
|
||||
config.Token.ValueString(),
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to create Binary Lane API client", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
binarylaneClient := BinarylaneClient{
|
||||
endpoint: endpoint,
|
||||
client: client,
|
||||
client: client,
|
||||
}
|
||||
|
||||
resp.DataSourceData = binarylaneClient
|
||||
@@ -143,16 +88,21 @@ func (p *binarylaneProvider) Metadata(_ context.Context, _ provider.MetadataRequ
|
||||
func (p *binarylaneProvider) DataSources(_ context.Context) []func() datasource.DataSource {
|
||||
return []func() datasource.DataSource{
|
||||
NewServerDataSource,
|
||||
NewServerFirewallRulesDataSource,
|
||||
NewSshKeyDataSource,
|
||||
NewVpcDataSource,
|
||||
NewVpcRouteEntriesDataSource,
|
||||
NewLoadBalancerDataSource,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *binarylaneProvider) Resources(_ context.Context) []func() resource.Resource {
|
||||
return []func() resource.Resource{
|
||||
NewServerResource,
|
||||
NewServerFirewallRulesResource,
|
||||
NewSshKeyResource,
|
||||
NewVpcResource,
|
||||
NewVpcRouteEntriesResource,
|
||||
NewLoadBalancerResource,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ provider "binarylane" {
|
||||
# api_token = "test123"
|
||||
# api_endpoint = "https://test.binarylane.internal.au/v2"
|
||||
}
|
||||
|
||||
# End of provider config
|
||||
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
d_schema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
@@ -25,6 +25,15 @@ type serverDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type serverDataModel struct {
|
||||
resources.ServerModel
|
||||
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
|
||||
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
|
||||
Permalink types.String `tfsdk:"permalink"`
|
||||
Memory types.Int32 `tfsdk:"memory"`
|
||||
Disk types.Int32 `tfsdk:"disk"`
|
||||
}
|
||||
|
||||
func (d *serverDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
@@ -48,78 +57,204 @@ func (d *serverDataSource) Metadata(ctx context.Context, req datasource.Metadata
|
||||
}
|
||||
|
||||
func (d *serverDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(resources.ServerResourceSchema(ctx))
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(
|
||||
resources.ServerResourceSchema(ctx),
|
||||
AttributeConfig{
|
||||
RequiredAttributes: &[]string{"id"},
|
||||
ExcludedAttributes: &[]string{"password"},
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
|
||||
return
|
||||
}
|
||||
resp.Schema = *ds
|
||||
resp.Schema.Description = "TODO"
|
||||
resp.Schema.Description = "Provides a Binary Lane Server data source. This can be used to read existing server details."
|
||||
|
||||
// Overrides
|
||||
id := resp.Schema.Attributes["id"]
|
||||
resp.Schema.Attributes["id"] = d_schema.Int64Attribute{
|
||||
resp.Schema.Attributes["id"] = schema.Int64Attribute{
|
||||
Description: id.GetDescription(),
|
||||
MarkdownDescription: id.GetMarkdownDescription(),
|
||||
Required: true, // ID is required to find the server
|
||||
}
|
||||
|
||||
nameDescription := "The hostname of your server, such as vps01.yourcompany.com."
|
||||
resp.Schema.Attributes["name"] = &schema.StringAttribute{
|
||||
Description: nameDescription,
|
||||
MarkdownDescription: nameDescription,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
backupsDescription := "If `true`, the server will be backed up twice per day. By default, backups are disabled."
|
||||
resp.Schema.Attributes["backups"] = &schema.BoolAttribute{
|
||||
Description: backupsDescription,
|
||||
MarkdownDescription: backupsDescription,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
sshKeysDescription := "This is a list of SSH key ids that were added to the server during creation."
|
||||
resp.Schema.Attributes["ssh_keys"] = &schema.ListAttribute{
|
||||
ElementType: types.Int64Type,
|
||||
Description: sshKeysDescription,
|
||||
MarkdownDescription: sshKeysDescription,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
userDataDescription := "A script or cloud-config YAML file to configure the server."
|
||||
resp.Schema.Attributes["user_data"] = &schema.StringAttribute{
|
||||
Description: userDataDescription,
|
||||
MarkdownDescription: userDataDescription,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
vpcIdDescription := "ID of the Virtual Private Cloud (VPC) the server is connected to."
|
||||
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
|
||||
Description: vpcIdDescription,
|
||||
MarkdownDescription: vpcIdDescription,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
// Additional attributes
|
||||
resp.Schema.Attributes["permalink"] = &schema.StringAttribute{
|
||||
Description: "A randomly generated two-word identifier assigned to servers in regions that support this feature",
|
||||
MarkdownDescription: "A randomly generated two-word identifier assigned to servers in regions that support this feature",
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
publicIpv4AddressesDescription := "The public IPv4 addresses assigned to the server."
|
||||
resp.Schema.Attributes["public_ipv4_addresses"] = &schema.ListAttribute{
|
||||
Description: publicIpv4AddressesDescription,
|
||||
MarkdownDescription: publicIpv4AddressesDescription,
|
||||
ElementType: types.StringType,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
privateIpv4AddressesDescription := "The private IPv4 addresses assigned to the server."
|
||||
resp.Schema.Attributes["private_ipv4_addresses"] = &schema.ListAttribute{
|
||||
Description: privateIpv4AddressesDescription,
|
||||
MarkdownDescription: privateIpv4AddressesDescription,
|
||||
ElementType: types.StringType,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
memoryDescription := "The amount of memory in MB assigned to the server."
|
||||
resp.Schema.Attributes["memory"] = &schema.Int32Attribute{
|
||||
Description: memoryDescription,
|
||||
MarkdownDescription: memoryDescription,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
diskDescription := "The amount of storage in GB assigned to the server."
|
||||
resp.Schema.Attributes["disk"] = &schema.Int32Attribute{
|
||||
Description: diskDescription,
|
||||
MarkdownDescription: diskDescription,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data resources.ServerModel
|
||||
var data serverDataModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
diags := req.Config.Get(ctx, &data)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Use go routines here to make requests concurrently
|
||||
|
||||
// Read server
|
||||
serverResp, err := d.bc.client.GetServersServerIdWithResponse(ctx, data.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading server: name=%s", data.Id.String()),
|
||||
fmt.Sprintf("Error reading server: name=%s, id=%s", data.Name.ValueString(), data.Id.String()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Unexpected HTTP status code reading server: name=%s", data.Id.String()),
|
||||
fmt.Sprintf("Unexpected HTTP status code reading server: %s", serverResp.Body),
|
||||
fmt.Sprintf("Unexpected HTTP status code reading server: name=%s, id=%s", data.Name.ValueString(), data.Id.String()),
|
||||
string(serverResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Fetch user data
|
||||
|
||||
// Read user data
|
||||
// userDataResp, err := d.bc.client.GetServersServerIdUserDataWithResponse(ctx, data.Id.ValueInt64())
|
||||
// if err != nil {
|
||||
// resp.Diagnostics.AddError(
|
||||
// fmt.Sprintf("Error reading server user data: name=%s", data.Id.String()),
|
||||
// err.Error(),
|
||||
// )
|
||||
// return
|
||||
// }
|
||||
// if userDataResp.StatusCode() != http.StatusOK {
|
||||
// resp.Diagnostics.AddError(
|
||||
// fmt.Sprintf("Unexpected HTTP status code reading server user data: name=%s", data.Id.String()),
|
||||
// fmt.Sprintf("Unexpected HTTP status code reading server user data: %s", serverResp.Body),
|
||||
// )
|
||||
// return
|
||||
// }
|
||||
|
||||
// Set data values
|
||||
data.Id = types.Int64Value(*serverResp.JSON200.Server.Id)
|
||||
data.Name = types.StringValue(*serverResp.JSON200.Server.Name)
|
||||
data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug)
|
||||
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
|
||||
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
|
||||
// data.UserData = types.StringValue(*userDataResp.JSON200.UserData)
|
||||
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
data.PortBlocking = types.BoolValue(serverResp.JSON200.Server.Networks.PortBlocking)
|
||||
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
|
||||
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
|
||||
data.Memory = types.Int32Value(*serverResp.JSON200.Server.Memory)
|
||||
data.Disk = types.Int32Value(*serverResp.JSON200.Server.Disk)
|
||||
publicIpv4Addresses := []string{}
|
||||
privateIpv4Addresses := []string{}
|
||||
|
||||
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
|
||||
if v4address.Type == "public" {
|
||||
publicIpv4Addresses = append(publicIpv4Addresses, v4address.IpAddress)
|
||||
} else {
|
||||
privateIpv4Addresses = append(privateIpv4Addresses, v4address.IpAddress)
|
||||
}
|
||||
}
|
||||
|
||||
tfPublicIpv4Addresses, diag := types.ListValueFrom(ctx, types.StringType, publicIpv4Addresses)
|
||||
diags.Append(diag...)
|
||||
if diag.HasError() {
|
||||
data.PublicIpv4Addresses = types.ListUnknown(data.PublicIpv4Addresses.ElementType(ctx))
|
||||
} else {
|
||||
data.PublicIpv4Addresses = tfPublicIpv4Addresses
|
||||
}
|
||||
|
||||
tfPrivateIpv4Addresses, diag := types.ListValueFrom(ctx, types.StringType, privateIpv4Addresses)
|
||||
diags.Append(diag...)
|
||||
if diag.HasError() {
|
||||
data.PrivateIPv4Addresses = types.ListUnknown(data.PrivateIPv4Addresses.ElementType(ctx))
|
||||
} else {
|
||||
data.PrivateIPv4Addresses = tfPrivateIpv4Addresses
|
||||
}
|
||||
|
||||
// Get user data script
|
||||
userDataResp, err := d.bc.client.GetServersServerIdUserDataWithResponse(ctx, data.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading server user data: id=%s, name=%s", data.Id.String(), data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if userDataResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Unexpected HTTP status %d reading server user data: name=%s, id=%s", userDataResp.StatusCode(), data.Name.ValueString(), data.Id.String()),
|
||||
string(userDataResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
data.UserData = types.StringPointerValue(userDataResp.JSON200.UserData)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSource = &serverFirewallRulesDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &serverFirewallRulesDataSource{}
|
||||
)
|
||||
|
||||
func NewServerFirewallRulesDataSource() datasource.DataSource {
|
||||
return &serverFirewallRulesDataSource{}
|
||||
}
|
||||
|
||||
type serverFirewallRulesDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type serverFirewallRulesDataSourceModel struct {
|
||||
resources.ServerFirewallRulesModel
|
||||
}
|
||||
|
||||
func (d *serverFirewallRulesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_server_firewall_rules"
|
||||
}
|
||||
|
||||
func (d *serverFirewallRulesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(
|
||||
resources.ServerFirewallRulesResourceSchema(ctx),
|
||||
AttributeConfig{
|
||||
RequiredAttributes: &[]string{"server_id"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
|
||||
return
|
||||
}
|
||||
resp.Schema = *ds
|
||||
// resp.Schema.Description = "TODO"
|
||||
}
|
||||
|
||||
func (d *serverFirewallRulesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data serverFirewallRulesDataSourceModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
tflog.Debug(ctx, fmt.Sprintf("Reading server firewall rules: server_id=%d", data.ServerId.ValueInt64()))
|
||||
fwResp, err := d.bc.client.GetServersServerIdAdvancedFirewallRulesWithResponse(ctx, data.ServerId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading server firewall rules: server_id=%d", data.ServerId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if fwResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading server firewall rules",
|
||||
fmt.Sprintf("Received %s reading server firewall rules: server_id=%d. Details: %s", fwResp.Status(), data.ServerId.ValueInt64(), fwResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
firewallRulesValue, diags := GetFirewallRulesState(ctx, &fwResp.JSON200.FirewallRules)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.FirewallRules = firewallRulesValue
|
||||
|
||||
// Save into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (d *serverFirewallRulesDataSource) Configure(
|
||||
_ context.Context,
|
||||
req datasource.ConfigureRequest,
|
||||
resp *datasource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Data Source Configure Type",
|
||||
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData))
|
||||
return
|
||||
}
|
||||
|
||||
d.bc = &bc
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &serverFirewallRulesResource{}
|
||||
_ resource.ResourceWithConfigure = &serverFirewallRulesResource{}
|
||||
_ resource.ResourceWithImportState = &serverFirewallRulesResource{}
|
||||
)
|
||||
|
||||
func NewServerFirewallRulesResource() resource.Resource {
|
||||
return &serverFirewallRulesResource{}
|
||||
}
|
||||
|
||||
type serverFirewallRulesResource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type serverFirewallRulesResourceModel struct {
|
||||
resources.ServerFirewallRulesModel
|
||||
}
|
||||
|
||||
func (d *serverFirewallRulesResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData),
|
||||
)
|
||||
return
|
||||
}
|
||||
d.bc = &bc
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_server_firewall_rules"
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.ServerFirewallRulesResourceSchema(ctx)
|
||||
|
||||
// Overrides
|
||||
serverId := resp.Schema.Attributes["server_id"]
|
||||
resp.Schema.Attributes["server_id"] = &schema.Int64Attribute{
|
||||
Description: serverId.GetDescription(),
|
||||
MarkdownDescription: serverId.GetMarkdownDescription(),
|
||||
Required: true, // Server ID is required to define the firewall rules
|
||||
Validators: []validator.Int64{
|
||||
int64validator.AtLeast(1),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data serverFirewallRulesResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Create API call logic
|
||||
firewallRules := []binarylane.AdvancedFirewallRule{}
|
||||
diags := data.FirewallRules.ElementsAs(ctx, &firewallRules, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if len(firewallRules) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, fmt.Sprintf("Creating server firewall rules: server_id=%s", data.ServerId.String()))
|
||||
serverResp, err := r.bc.client.PostServersServerIdActionsChangeAdvancedFirewallRulesWithResponse(
|
||||
ctx,
|
||||
data.ServerId.ValueInt64(),
|
||||
binarylane.PostServersServerIdActionsChangeAdvancedFirewallRulesJSONRequestBody{
|
||||
Type: "change_advanced_firewall_rules",
|
||||
FirewallRules: firewallRules,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error creating server firewall rules: server_id=%s", data.ServerId.String()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating server firewall rules",
|
||||
fmt.Sprintf("Received %s creating server firewall rules: server_id=%s. Details: %s", serverResp.Status(), data.ServerId.String(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data serverFirewallRulesResourceModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
tflog.Debug(ctx, fmt.Sprintf("Reading server firewall rules: server_id=%d", data.ServerId.ValueInt64()))
|
||||
fwResp, err := r.bc.client.GetServersServerIdAdvancedFirewallRulesWithResponse(ctx, data.ServerId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading server firewall rules: server_id=%d", data.ServerId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if fwResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading server firewall rules",
|
||||
fmt.Sprintf("Received %s reading server firewall rules: server_id=%d. Details: %s", fwResp.Status(), data.ServerId.ValueInt64(), fwResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
firewallRulesValue, diags := GetFirewallRulesState(ctx, &fwResp.JSON200.FirewallRules)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.FirewallRules = firewallRulesValue
|
||||
|
||||
// Save into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data serverFirewallRulesResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
firewallRules := []binarylane.AdvancedFirewallRule{}
|
||||
diags := data.FirewallRules.ElementsAs(ctx, &firewallRules, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if len(firewallRules) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, fmt.Sprintf("Updating server firewall rules: server_id=%s", data.ServerId.String()))
|
||||
serverResp, err := r.bc.client.PostServersServerIdActionsChangeAdvancedFirewallRulesWithResponse(
|
||||
ctx,
|
||||
data.ServerId.ValueInt64(),
|
||||
binarylane.PostServersServerIdActionsChangeAdvancedFirewallRulesJSONRequestBody{
|
||||
Type: "change_advanced_firewall_rules",
|
||||
FirewallRules: firewallRules,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error updating server firewall rules: server_id=%s", data.ServerId.String()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code updating server firewall rules",
|
||||
fmt.Sprintf("Received %s updating server firewall rules: server_id=%s. Details: %s", serverResp.Status(), data.ServerId.String(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data serverFirewallRulesResourceModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete API call logic
|
||||
tflog.Debug(ctx, fmt.Sprintf("Deleting server firewall rules: server_id=%s", data.ServerId.String()))
|
||||
serverResp, err := r.bc.client.PostServersServerIdActionsChangeAdvancedFirewallRulesWithResponse(
|
||||
ctx,
|
||||
data.ServerId.ValueInt64(),
|
||||
binarylane.PostServersServerIdActionsChangeAdvancedFirewallRulesJSONRequestBody{
|
||||
Type: "change_advanced_firewall_rules",
|
||||
FirewallRules: []binarylane.AdvancedFirewallRule{},
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error deleting server firewall rules: server_id=%s", data.ServerId.String()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code deleting server firewall rules",
|
||||
fmt.Sprintf("Received %s deleting server firewall rules: server_id=%s. Details: %s", serverResp.Status(), data.ServerId.String(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func GetFirewallRulesState(ctx context.Context, firewallRules *[]binarylane.AdvancedFirewallRule) (basetypes.ListValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
firewallRulesValue := resources.FirewallRulesValue{}
|
||||
var firewallRulesValues []resources.FirewallRulesValue
|
||||
|
||||
if *firewallRules == nil || len(*firewallRules) == 0 {
|
||||
firewallRulesValues = []resources.FirewallRulesValue{}
|
||||
} else {
|
||||
for _, rule := range *firewallRules {
|
||||
destinationAddresses, diag := types.ListValueFrom(ctx, types.StringType, rule.DestinationAddresses)
|
||||
diags.Append(diag...)
|
||||
|
||||
var destinationPorts basetypes.ListValue
|
||||
if rule.DestinationPorts != nil {
|
||||
destinationPorts, diag = types.ListValueFrom(ctx, types.StringType, rule.DestinationPorts)
|
||||
diags.Append(diag...)
|
||||
}
|
||||
|
||||
sourceAddresses, diag := types.ListValueFrom(ctx, types.StringType, rule.SourceAddresses)
|
||||
diags.Append(diag...)
|
||||
|
||||
if diags.HasError() {
|
||||
return basetypes.NewListUnknown(firewallRulesValue.Type(ctx)), diags
|
||||
}
|
||||
|
||||
ruleValue := resources.NewFirewallRulesValueMust(firewallRulesValue.AttributeTypes(ctx), map[string]attr.Value{
|
||||
"description": types.StringPointerValue(rule.Description),
|
||||
"action": types.StringValue(string(rule.Action)),
|
||||
"protocol": types.StringValue(string(rule.Protocol)),
|
||||
"destination_addresses": destinationAddresses,
|
||||
"destination_ports": destinationPorts,
|
||||
"source_addresses": sourceAddresses,
|
||||
})
|
||||
firewallRulesValues = append(firewallRulesValues, ruleValue)
|
||||
}
|
||||
}
|
||||
|
||||
firewallRulesListValue, diag := types.ListValueFrom(ctx, firewallRulesValue.Type(ctx), firewallRulesValues)
|
||||
diags.Append(diag...)
|
||||
if diags.HasError() {
|
||||
return basetypes.NewListUnknown(firewallRulesValue.Type(ctx)), diags
|
||||
}
|
||||
|
||||
return firewallRulesListValue, diags
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) ImportState(
|
||||
ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
|
||||
) {
|
||||
id, err := strconv.ParseInt(req.ID, 10, 32)
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Error importing server firewall rules",
|
||||
"Could not import server firewall rules, unexpected error (ID should be an integer): "+err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("server_id"), id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
)
|
||||
|
||||
func TestServerFirewallRulesResource(t *testing.T) {
|
||||
// Must assign a password to the server or Binary Lane will send emails
|
||||
pw_bytes := make([]byte, 12)
|
||||
rand.Read(pw_bytes)
|
||||
password := base64.URLEncoding.EncodeToString(pw_bytes)
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
public_ipv4_count = 0
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "test" {
|
||||
server_id = binarylane_server.test.id
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow SSH"
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.test.private_ipv4_addresses.0]
|
||||
destination_ports = ["22"]
|
||||
action = "accept"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
data "binarylane_server_firewall_rules" "test" {
|
||||
server_id = binarylane_server_firewall_rules.test.server_id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "server_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.description", "Allow SSH"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.0", "22"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.action", "accept"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server_firewall_rules.test", "server_id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.description", "Allow SSH"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.0"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.0", "22"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.action", "accept"),
|
||||
),
|
||||
},
|
||||
// ImportState testing
|
||||
// {
|
||||
// ResourceName: "binarylane_server.test",
|
||||
// ImportState: true,
|
||||
// ImportStateVerify: true,
|
||||
// },
|
||||
{
|
||||
ResourceName: "binarylane_server_firewall_rules.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIdentifierAttribute: "server_id",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
resourceState := s.RootModule().Resources["binarylane_server_firewall_rules.test"]
|
||||
return resourceState.Primary.Attributes["server_id"], nil
|
||||
},
|
||||
},
|
||||
// Update and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
public_ipv4_count = 0
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "test" {
|
||||
server_id = binarylane_server.test.id
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow HTTP" # Updated description
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.test.private_ipv4_addresses.0]
|
||||
destination_ports = ["80"] # Updated port
|
||||
action = "accept"
|
||||
},
|
||||
]
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values updated
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "server_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.description", "Allow HTTP"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.0", "80"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.action", "accept"),
|
||||
),
|
||||
},
|
||||
// Delete testing automatically occurs in TestCase
|
||||
},
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,14 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
@@ -10,30 +16,55 @@ import (
|
||||
|
||||
func TestServerResource(t *testing.T) {
|
||||
// Must assign a password to the server or Binary Lane will send emails
|
||||
pw_bytes := make([]byte, 12)
|
||||
rand.Read(pw_bytes)
|
||||
password := base64.URLEncoding.EncodeToString(pw_bytes)
|
||||
pwBytes := make([]byte, 12)
|
||||
rand.Read(pwBytes)
|
||||
password := base64.URLEncoding.EncodeToString(pwBytes)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
pwBytes2 := make([]byte, 12)
|
||||
rand.Read(pwBytes2)
|
||||
password2 := base64.URLEncoding.EncodeToString(pwBytes2)
|
||||
|
||||
sshPublicKeyInitial := GeneratePublicKey(t)
|
||||
sshPublicKeyUpdated := GeneratePublicKey(t)
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-server-resource-test"
|
||||
ip_range = "10.240.0.0/16"
|
||||
name = "tf-test-server-resource"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "initial" {
|
||||
name = "tf-test-server-resource-initial"
|
||||
public_key = "` + sshPublicKeyInitial + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "updated" {
|
||||
name = "tf-test-server-resource-updated"
|
||||
public_key = "` + sshPublicKeyUpdated + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-server-resource-test"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
public_ipv4_count = 0
|
||||
user_data = <<EOT
|
||||
name = "tf-test-server-resource"
|
||||
region = "per"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
memory = 1152
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
public_ipv4_count = 1
|
||||
ssh_keys = [binarylane_ssh_key.initial.id]
|
||||
source_and_destination_check = false
|
||||
backups = true
|
||||
port_blocking = false
|
||||
user_data = <<EOT
|
||||
#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
EOT
|
||||
@@ -42,33 +73,240 @@ EOT
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-server-resource-test"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "region", "per"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-12"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-11"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-min"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "1152"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "20"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "password", password),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "user_data", `#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
`),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_addresses.#", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "private_ipv4_addresses.0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "port_blocking", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "ssh_keys.#", "1"),
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.initial", "id"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "permalink"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "source_and_destination_check", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "backups", "true"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "name", "tf-server-resource-test"),
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "id", "binarylane_server.test", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "name", "tf-test-server-resource"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "region", "per"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "image", "debian-12"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "image", "debian-11"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "size", "std-min"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "permalink", "binarylane_server.test", "permalink"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "user_data", `#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
`),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "memory", "1152"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disk", "20"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "backups", "true"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "port_blocking", "false"),
|
||||
),
|
||||
},
|
||||
// Test import by ID
|
||||
{
|
||||
ResourceName: "binarylane_server.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"password", "ssh_keys", "timeouts"},
|
||||
},
|
||||
// Test import by name
|
||||
{
|
||||
ResourceName: "binarylane_server.test",
|
||||
ImportState: true,
|
||||
ImportStateId: "tf-test-server-resource",
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"password", "ssh_keys", "timeouts"},
|
||||
},
|
||||
// Update and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "initial" {
|
||||
name = "tf-test-server-resource-initial"
|
||||
public_key = "` + sshPublicKeyInitial + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "updated" {
|
||||
name = "tf-test-server-resource-updated"
|
||||
public_key = "` + sshPublicKeyUpdated + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource-2"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-1vcpu"
|
||||
disk = "45"
|
||||
password = "` + password + `"
|
||||
vpc_id = null
|
||||
public_ipv4_count = 0
|
||||
ssh_keys = [binarylane_ssh_key.updated.id]
|
||||
|
||||
# source_and_destination_check = null # defaults to null
|
||||
# backups = false # defaults to false
|
||||
# port_blocking = true # defaults to true
|
||||
|
||||
user_data = <<EOT
|
||||
#cloud-config
|
||||
echo "Hello Whitespace" > /var/tmp/output.txt
|
||||
|
||||
|
||||
EOT
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource-2"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-1vcpu"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "2048"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "45"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_addresses.#", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-12"),
|
||||
resource.TestCheckNoResourceAttr("binarylane_server.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "ssh_keys.#", "1"),
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.updated", "id"),
|
||||
resource.TestCheckNoResourceAttr("binarylane_server.test", "source_and_destination_check"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "backups", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "port_blocking", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "user_data", // test extra whitespace
|
||||
`#cloud-config
|
||||
echo "Hello Whitespace" > /var/tmp/output.txt
|
||||
|
||||
|
||||
`),
|
||||
),
|
||||
},
|
||||
// Change password testing (Cannot run at same time as Rebuild operation, so it has it's own test)
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "initial" {
|
||||
name = "tf-test-server-resource-initial"
|
||||
public_key = "` + sshPublicKeyInitial + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "updated" {
|
||||
name = "tf-test-server-resource-updated"
|
||||
public_key = "` + sshPublicKeyUpdated + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource-2"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-1vcpu"
|
||||
disk = "45"
|
||||
password = "` + password2 + `"
|
||||
vpc_id = null
|
||||
public_ipv4_count = 0
|
||||
ssh_keys = [binarylane_ssh_key.updated.id]
|
||||
|
||||
# source_and_destination_check = null # defaults to null
|
||||
# backups = false # defaults to false
|
||||
# port_blocking = true # defaults to true
|
||||
|
||||
user_data = <<EOT
|
||||
#cloud-config
|
||||
echo "Hello Whitespace" > /var/tmp/output.txt
|
||||
|
||||
|
||||
EOT
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource-2"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "password", password2),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("server", &resource.Sweeper{
|
||||
Name: "server",
|
||||
F: func(_ string) error {
|
||||
client, err := binarylane.NewClientWithDefaultConfig()
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
listResp, err := client.GetServersWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting servers for test sweep: %w", err)
|
||||
}
|
||||
|
||||
if listResp.StatusCode() != http.StatusOK {
|
||||
return fmt.Errorf("Unexpected status code getting servers in test sweep: %s", listResp.Body)
|
||||
}
|
||||
|
||||
servers := *listResp.JSON200.Servers
|
||||
for _, s := range servers {
|
||||
if strings.HasPrefix(*s.Name, "tf-test-") {
|
||||
reason := "Terraform deletion"
|
||||
params := binarylane.DeleteServersServerIdParams{
|
||||
Reason: &reason,
|
||||
}
|
||||
|
||||
deleteResp, err := client.DeleteServersServerIdWithResponse(ctx, *s.Id, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting server %d during test sweep: %w", *s.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting server %d in test sweep: %s", deleteResp.StatusCode(), *s.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted server during test sweep:", *s.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -47,17 +48,42 @@ func (d *sshKeyDataSource) Metadata(ctx context.Context, req datasource.Metadata
|
||||
}
|
||||
|
||||
func (d *sshKeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(resources.SshKeyResourceSchema(ctx))
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(
|
||||
resources.SshKeyResourceSchema(ctx),
|
||||
AttributeConfig{
|
||||
RequiredAttributes: &[]string{"id"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
|
||||
return
|
||||
}
|
||||
resp.Schema = *ds
|
||||
resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
defaultDescription := "If `true`, this SSH key will be included on all new server installations (if the operating " +
|
||||
"system supports SSH key injection)."
|
||||
resp.Schema.Attributes["default"] = schema.BoolAttribute{
|
||||
Description: defaultDescription,
|
||||
MarkdownDescription: defaultDescription,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
// 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)...)
|
||||
@@ -83,11 +109,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)...)
|
||||
|
||||
@@ -4,9 +4,11 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
|
||||
@@ -17,9 +19,9 @@ import (
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ resource.Resource = &sshKeyResource{}
|
||||
_ resource.ResourceWithConfigure = &sshKeyResource{}
|
||||
// _ resource.ResourceWithImportState = &serverResource{}
|
||||
_ resource.Resource = &sshKeyResource{}
|
||||
_ resource.ResourceWithConfigure = &sshKeyResource{}
|
||||
_ resource.ResourceWithImportState = &sshKeyResource{}
|
||||
)
|
||||
|
||||
func NewSshKeyResource() resource.Resource {
|
||||
@@ -30,6 +32,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
|
||||
@@ -38,7 +45,7 @@ func (d *sshKeyResource) Configure(_ context.Context, req resource.ConfigureRequ
|
||||
bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Data Source Configure Type",
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData),
|
||||
)
|
||||
|
||||
@@ -54,15 +61,24 @@ func (r *sshKeyResource) Metadata(ctx context.Context, req resource.MetadataRequ
|
||||
|
||||
func (r *sshKeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.SshKeyResourceSchema(ctx)
|
||||
resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
default_ := resp.Schema.Attributes["default"]
|
||||
idDescription := "The numeric ID of the SSH key."
|
||||
resp.Schema.Attributes["id"] = schema.Int64Attribute{
|
||||
Required: false,
|
||||
Optional: false,
|
||||
Computed: true,
|
||||
Description: idDescription,
|
||||
MarkdownDescription: idDescription,
|
||||
}
|
||||
|
||||
defaultDescription := "If `true`, this SSH key will be included on all new server installations (if the operating " +
|
||||
"system supports SSH key injection). Default is `false`."
|
||||
resp.Schema.Attributes["default"] = schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: default_.GetDescription(),
|
||||
MarkdownDescription: default_.GetMarkdownDescription(),
|
||||
Description: defaultDescription,
|
||||
MarkdownDescription: defaultDescription,
|
||||
Default: booldefault.StaticBool(false), // Add default to backups
|
||||
}
|
||||
|
||||
@@ -73,10 +89,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)...)
|
||||
@@ -85,7 +111,6 @@ func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
return
|
||||
}
|
||||
|
||||
// Create API call logic
|
||||
sshResp, err := r.bc.client.PostAccountKeysWithResponse(ctx, binarylane.SshKeyRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
Default: data.Default.ValueBoolPointer(),
|
||||
@@ -100,14 +125,15 @@ func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
}
|
||||
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 creating SSH key",
|
||||
fmt.Sprintf("Received %s creating SSH key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
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
|
||||
@@ -118,7 +144,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)...)
|
||||
@@ -140,8 +166,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
|
||||
}
|
||||
@@ -150,29 +176,36 @@ 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 plan, state sshKeyModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
sshResp, err := r.bc.client.PutAccountKeysKeyIdWithResponse(ctx, int(data.Id.ValueInt64()), binarylane.UpdateSshKeyRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
Default: data.Default.ValueBoolPointer(),
|
||||
sshResp, err := r.bc.client.PutAccountKeysKeyIdWithResponse(ctx, int(state.Id.ValueInt64()), binarylane.UpdateSshKeyRequest{
|
||||
Name: plan.Name.ValueString(),
|
||||
Default: plan.Default.ValueBoolPointer(),
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error updating SSH Key: name=%s", data.Name.ValueString()),
|
||||
fmt.Sprintf("Error updating SSH Key: name=%s", plan.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
@@ -180,30 +213,29 @@ func (r *sshKeyResource) Update(ctx context.Context, req resource.UpdateRequest,
|
||||
if sshResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code updating SSH Key",
|
||||
fmt.Sprintf("Received %s updating new SSH Key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
fmt.Sprintf("Received %s updating new SSH Key: name=%s. Details: %s", sshResp.Status(), plan.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *sshKeyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data resources.SshKeyModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
var state sshKeyModel
|
||||
|
||||
// Read Terraform prior state into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete API call logic
|
||||
sshResp, err := r.bc.client.DeleteAccountKeysKeyIdWithResponse(ctx, int(data.Id.ValueInt64()))
|
||||
sshResp, err := r.bc.client.DeleteAccountKeysKeyIdWithResponse(ctx, int(state.Id.ValueInt64()))
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error deleting SSH Key: name=%s", data.Name.ValueString()),
|
||||
fmt.Sprintf("Error deleting SSH Key: name=%s", state.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
@@ -211,13 +243,73 @@ func (r *sshKeyResource) Delete(ctx context.Context, req resource.DeleteRequest,
|
||||
if sshResp.StatusCode() != http.StatusNoContent {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code deleting SSH Key",
|
||||
fmt.Sprintf("Received %s deleting SSH Key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
fmt.Sprintf("Received %s deleting SSH Key: name=%s. Details: %s", sshResp.Status(), state.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 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, ¶ms)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Error getting SSH keys 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...)
|
||||
}
|
||||
|
||||
@@ -1,63 +1,81 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"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) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
publicKey := GeneratePublicKey(t)
|
||||
|
||||
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"
|
||||
public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"
|
||||
name = "tf-test-key-resource-test"
|
||||
public_key = "` + publicKey + `"
|
||||
}
|
||||
|
||||
data "binarylane_ssh_key" "test" {
|
||||
depends_on = [binarylane_ssh_key.test]
|
||||
|
||||
id = binarylane_ssh_key.test.id
|
||||
id = binarylane_ssh_key.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "public_key", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"),
|
||||
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", "public_key", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"),
|
||||
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"),
|
||||
// ),
|
||||
// },
|
||||
@@ -65,3 +83,90 @@ data "binarylane_ssh_key" "test" {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func GeneratePublicKey(t *testing.T) string {
|
||||
t.Helper()
|
||||
|
||||
pub, _, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key: %v", err)
|
||||
}
|
||||
|
||||
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 {
|
||||
client, err := binarylane.NewClientWithDefaultConfig()
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
listResp, err := client.GetAccountKeysWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting SSH keys for test sweep: %w", err)
|
||||
}
|
||||
|
||||
if listResp.StatusCode() != http.StatusOK {
|
||||
return fmt.Errorf("Unexpected status code getting SSH keys for test sweep: %s", listResp.Body)
|
||||
}
|
||||
|
||||
keys := listResp.JSON200.SshKeys
|
||||
for _, k := range keys {
|
||||
if strings.HasPrefix(*k.Name, "tf-test-") {
|
||||
|
||||
deleteResp, 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 deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting SSH key %d for test sweep: %s", deleteResp.StatusCode(), *k.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted SSH key for test sweep:", *k.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
resource.TestMain(m)
|
||||
}
|
||||
+194
-91
@@ -1,15 +1,23 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"slices"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
d_schema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
r_schema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
func convertResourceSchemaToDataSourceSchema(rs r_schema.Schema) (*d_schema.Schema, error) {
|
||||
type AttributeConfig struct {
|
||||
RequiredAttributes *[]string
|
||||
OptionalAttributes *[]string
|
||||
ExcludedAttributes *[]string
|
||||
}
|
||||
|
||||
func convertResourceSchemaToDataSourceSchema(rs r_schema.Schema, cfg AttributeConfig) (*d_schema.Schema, error) {
|
||||
ds := d_schema.Schema{
|
||||
Attributes: make(map[string]d_schema.Attribute, len(rs.Attributes)),
|
||||
Blocks: make(map[string]d_schema.Block),
|
||||
@@ -17,97 +25,192 @@ func convertResourceSchemaToDataSourceSchema(rs r_schema.Schema) (*d_schema.Sche
|
||||
MarkdownDescription: rs.GetMarkdownDescription(),
|
||||
DeprecationMessage: rs.GetDeprecationMessage(),
|
||||
}
|
||||
for name, attr := range rs.Attributes {
|
||||
switch attr.GetType() {
|
||||
case types.BoolType:
|
||||
ds.Attributes[name] = d_schema.BoolAttribute{
|
||||
Description: attr.GetDescription(),
|
||||
Optional: attr.IsOptional(),
|
||||
Computed: true,
|
||||
Sensitive: attr.IsSensitive(),
|
||||
MarkdownDescription: attr.GetMarkdownDescription(),
|
||||
DeprecationMessage: attr.GetDeprecationMessage(),
|
||||
}
|
||||
case types.DynamicType:
|
||||
ds.Attributes[name] = d_schema.DynamicAttribute{
|
||||
Description: attr.GetDescription(),
|
||||
Optional: attr.IsOptional(),
|
||||
Computed: true,
|
||||
Sensitive: attr.IsSensitive(),
|
||||
MarkdownDescription: attr.GetMarkdownDescription(),
|
||||
DeprecationMessage: attr.GetDeprecationMessage(),
|
||||
}
|
||||
case types.Float32Type:
|
||||
ds.Attributes[name] = d_schema.Float32Attribute{
|
||||
Description: attr.GetDescription(),
|
||||
Optional: attr.IsOptional(),
|
||||
Computed: true,
|
||||
Sensitive: attr.IsSensitive(),
|
||||
MarkdownDescription: attr.GetMarkdownDescription(),
|
||||
DeprecationMessage: attr.GetDeprecationMessage(),
|
||||
}
|
||||
case types.Float64Type:
|
||||
ds.Attributes[name] = d_schema.Float64Attribute{
|
||||
Description: attr.GetDescription(),
|
||||
Optional: attr.IsOptional(),
|
||||
Computed: true,
|
||||
Sensitive: attr.IsSensitive(),
|
||||
MarkdownDescription: attr.GetMarkdownDescription(),
|
||||
DeprecationMessage: attr.GetDeprecationMessage(),
|
||||
}
|
||||
case types.Int32Type:
|
||||
ds.Attributes[name] = d_schema.Int32Attribute{
|
||||
Description: attr.GetDescription(),
|
||||
Optional: attr.IsOptional(),
|
||||
Computed: true,
|
||||
Sensitive: attr.IsSensitive(),
|
||||
MarkdownDescription: attr.GetMarkdownDescription(),
|
||||
DeprecationMessage: attr.GetDeprecationMessage(),
|
||||
}
|
||||
case types.Int64Type:
|
||||
ds.Attributes[name] = d_schema.Int64Attribute{
|
||||
Description: attr.GetDescription(),
|
||||
Optional: attr.IsOptional(),
|
||||
Computed: true,
|
||||
Sensitive: attr.IsSensitive(),
|
||||
MarkdownDescription: attr.GetMarkdownDescription(),
|
||||
DeprecationMessage: attr.GetDeprecationMessage(),
|
||||
}
|
||||
case types.NumberType:
|
||||
ds.Attributes[name] = d_schema.NumberAttribute{
|
||||
Description: attr.GetDescription(),
|
||||
Optional: attr.IsOptional(),
|
||||
Computed: true,
|
||||
Sensitive: attr.IsSensitive(),
|
||||
MarkdownDescription: attr.GetMarkdownDescription(),
|
||||
DeprecationMessage: attr.GetDeprecationMessage(),
|
||||
}
|
||||
case types.StringType:
|
||||
ds.Attributes[name] = d_schema.StringAttribute{
|
||||
Description: attr.GetDescription(),
|
||||
Optional: attr.IsOptional(),
|
||||
Computed: true,
|
||||
Sensitive: attr.IsSensitive(),
|
||||
MarkdownDescription: attr.GetMarkdownDescription(),
|
||||
DeprecationMessage: attr.GetDeprecationMessage(),
|
||||
}
|
||||
default:
|
||||
// Feel free to to raise a PR and remove this hack
|
||||
if strings.HasPrefix(attr.GetType().String(), "types.ListType") {
|
||||
ds.Attributes[name] = d_schema.ListAttribute{
|
||||
ElementType: attr.GetType().(types.ListType).ElemType,
|
||||
Description: attr.GetDescription(),
|
||||
Optional: attr.IsOptional(),
|
||||
Computed: true,
|
||||
Sensitive: attr.IsSensitive(),
|
||||
MarkdownDescription: attr.GetMarkdownDescription(),
|
||||
DeprecationMessage: attr.GetDeprecationMessage(),
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("failed to convert resource schema attribute to data source schema attribute: name=%s, type=%s", name, attr.GetType())
|
||||
}
|
||||
for name, attribute := range rs.Attributes {
|
||||
if cfg.ExcludedAttributes != nil && slices.Contains(*cfg.ExcludedAttributes, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
required := cfg.RequiredAttributes != nil && slices.Contains(*cfg.RequiredAttributes, name)
|
||||
optional := cfg.OptionalAttributes != nil && slices.Contains(*cfg.OptionalAttributes, name)
|
||||
|
||||
attr, err := convertResourceAttrToDataSourceAttr(name, attribute, required, optional)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert resource schema attribute to data source schema attribute: %w", err)
|
||||
}
|
||||
ds.Attributes[name] = attr
|
||||
}
|
||||
|
||||
return &ds, nil
|
||||
}
|
||||
|
||||
func convertResourceAttrToDataSourceAttr(name string, attribute r_schema.Attribute, required, optional bool) (d_schema.Attribute, error) {
|
||||
switch attribute.GetType() {
|
||||
case types.BoolType:
|
||||
return d_schema.BoolAttribute{
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
case types.DynamicType:
|
||||
return d_schema.DynamicAttribute{
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
case types.Float32Type:
|
||||
return d_schema.Float32Attribute{
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
case types.Float64Type:
|
||||
return d_schema.Float64Attribute{
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
case types.Int32Type:
|
||||
return d_schema.Int32Attribute{
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
case types.Int64Type:
|
||||
return d_schema.Int64Attribute{
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
case types.NumberType:
|
||||
return d_schema.NumberAttribute{
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
case types.StringType:
|
||||
return d_schema.StringAttribute{
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
default:
|
||||
if t, isList := attribute.(r_schema.ListNestedAttribute); isList {
|
||||
nestedObjectAttrs := make(map[string]d_schema.Attribute)
|
||||
for name, attribute := range t.NestedObject.Attributes {
|
||||
nestedAttribute, err := convertResourceAttrToDataSourceAttr(name, attribute, required, optional)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nestedObjectAttrs[name] = nestedAttribute
|
||||
}
|
||||
|
||||
return d_schema.ListNestedAttribute{
|
||||
NestedObject: d_schema.NestedAttributeObject{
|
||||
Attributes: nestedObjectAttrs,
|
||||
CustomType: t.NestedObject.CustomType,
|
||||
Validators: t.NestedObject.Validators,
|
||||
},
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if t, isList := attribute.GetType().(types.ListType); isList {
|
||||
return d_schema.ListAttribute{
|
||||
ElementType: t.ElemType,
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if t, isObject := attribute.(r_schema.SingleNestedAttribute); isObject {
|
||||
attributeTypes := make(map[string]attr.Type, len(t.GetAttributes()))
|
||||
for name, attribute := range t.GetAttributes() {
|
||||
attributeTypes[name] = attribute.GetType()
|
||||
}
|
||||
|
||||
return d_schema.ObjectAttribute{
|
||||
CustomType: t.CustomType,
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
AttributeTypes: attributeTypes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("conversion of attribute type is not implemented: name=%s, type=%s", name, attribute.GetType())
|
||||
}
|
||||
}
|
||||
|
||||
func listContainsUnknown(ctx context.Context, list types.List) bool {
|
||||
// If the whole list is unknown, return true
|
||||
if list.IsUnknown() {
|
||||
return true
|
||||
}
|
||||
|
||||
// Get elements as generic attr.Value to check individual unknown status
|
||||
var elements []attr.Value
|
||||
diags := list.ElementsAs(ctx, &elements, false)
|
||||
if diags.HasError() {
|
||||
return true // Assume unknown in case of errors
|
||||
}
|
||||
|
||||
// Check if any element is unknown
|
||||
for _, elem := range elements {
|
||||
if elem.IsUnknown() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func Pointer[T any](d T) *T {
|
||||
return &d
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
)
|
||||
|
||||
var (
|
||||
_ validator.Int32 = &MultipleOfValidator{}
|
||||
)
|
||||
|
||||
type MultipleOfValidator struct {
|
||||
Multiple, RangeFrom, RangeTo int32
|
||||
}
|
||||
|
||||
func (v MultipleOfValidator) Description(ctx context.Context) string {
|
||||
if v.RangeFrom == 0 {
|
||||
return fmt.Sprintf(`must be a multiple of %d`, v.Multiple)
|
||||
}
|
||||
return fmt.Sprintf("when greater than %d, must be a multiple of %d", v.RangeFrom, v.Multiple)
|
||||
}
|
||||
|
||||
func (v MultipleOfValidator) MarkdownDescription(ctx context.Context) string {
|
||||
if v.RangeFrom == 0 {
|
||||
return fmt.Sprintf(`must be a multiple of %d`, v.Multiple)
|
||||
}
|
||||
return fmt.Sprintf("when greater than %d, must be a multiple of %d", v.RangeFrom, v.Multiple)
|
||||
}
|
||||
|
||||
func (v MultipleOfValidator) ValidateInt32(ctx context.Context, req validator.Int32Request, resp *validator.Int32Response) {
|
||||
if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() {
|
||||
return
|
||||
}
|
||||
memory := req.ConfigValue.ValueInt32()
|
||||
|
||||
if memory%v.Multiple == 0 || v.RangeFrom != 0 && memory < v.RangeFrom || v.RangeTo != 0 && memory >= v.RangeTo {
|
||||
return
|
||||
}
|
||||
|
||||
if v.RangeFrom != 0 {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
req.Path,
|
||||
"Not a Multiple",
|
||||
fmt.Sprintf("if greater than %d, value must be a multiple of %d", v.RangeFrom, v.Multiple),
|
||||
)
|
||||
} else {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
req.Path,
|
||||
"Not a Multiple",
|
||||
fmt.Sprintf("value must be a multiple of %d", v.Multiple),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -50,14 +50,19 @@ func (d *vpcDataSource) Metadata(ctx context.Context, req datasource.MetadataReq
|
||||
}
|
||||
|
||||
func (d *vpcDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(resources.VpcResourceSchema(ctx))
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(
|
||||
resources.VpcResourceSchema(ctx),
|
||||
AttributeConfig{
|
||||
RequiredAttributes: &[]string{"id"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
resp.Schema = *ds
|
||||
resp.Schema.Description = "TODO"
|
||||
// resp.Schema.Description = "TODO"
|
||||
}
|
||||
|
||||
func (d *vpcDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
@@ -88,13 +93,6 @@ func (d *vpcDataSource) Read(ctx context.Context, req datasource.ReadRequest, re
|
||||
data.IpRange = types.StringValue(*vpcResp.JSON200.Vpc.IpRange)
|
||||
data.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// routeEntries, routeEntriesDiags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
// resp.Diagnostics.Append(routeEntriesDiags...)
|
||||
// if resp.Diagnostics.HasError() {
|
||||
// return
|
||||
// }
|
||||
// data.RouteEntries = routeEntries
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
@@ -4,18 +4,22 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &vpcResource{}
|
||||
_ resource.ResourceWithConfigure = &vpcResource{}
|
||||
// _ resource.ResourceWithImportState = &exampleResource{}
|
||||
_ resource.Resource = &vpcResource{}
|
||||
_ resource.ResourceWithConfigure = &vpcResource{}
|
||||
_ resource.ResourceWithImportState = &vpcResource{}
|
||||
)
|
||||
|
||||
func NewVpcResource() resource.Resource {
|
||||
@@ -52,7 +56,18 @@ func (r *vpcResource) Metadata(ctx context.Context, req resource.MetadataRequest
|
||||
|
||||
func (r *vpcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.VpcResourceSchema(ctx)
|
||||
resp.Schema.Description = "TODO"
|
||||
// resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
id := resp.Schema.Attributes["id"]
|
||||
resp.Schema.Attributes["id"] = &schema.Int64Attribute{
|
||||
Description: id.GetDescription(),
|
||||
MarkdownDescription: id.GetMarkdownDescription(),
|
||||
// read only
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *vpcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
@@ -121,6 +136,7 @@ func (r *vpcResource) Read(ctx context.Context, req resource.ReadRequest, resp *
|
||||
fmt.Sprintf("Received %s reading VPC: id=%d. Details: %s", vpcResp.Status(), data.Id.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
data.Id = types.Int64Value(*vpcResp.JSON200.Vpc.Id)
|
||||
data.IpRange = types.StringValue(*vpcResp.JSON200.Vpc.IpRange)
|
||||
data.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
@@ -129,23 +145,31 @@ func (r *vpcResource) Read(ctx context.Context, req resource.ReadRequest, resp *
|
||||
}
|
||||
|
||||
func (r *vpcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data vpcResourceModel
|
||||
var plan, state vpcResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append()
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
tflog.Debug(ctx, fmt.Sprintf("Updating VPC: id=%d", data.Id.ValueInt64()))
|
||||
patchRequest := binarylane.PatchVpcRequest{}
|
||||
|
||||
vpcResp, err := r.bc.client.PutVpcsVpcIdWithResponse(ctx, data.Id.ValueInt64(), binarylane.UpdateVpcRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
})
|
||||
if !plan.Name.Equal(state.Name) {
|
||||
patchRequest.Name = plan.Name.ValueStringPointer()
|
||||
}
|
||||
|
||||
if reflect.ValueOf(plan).NumField() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, fmt.Sprintf("Updating VPC: id=%d", state.Id.ValueInt64()))
|
||||
vpcResp, err := r.bc.client.PatchVpcsVpcIdWithResponse(ctx, state.Id.ValueInt64(), patchRequest)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error updating VPC: id=%d", data.Id.ValueInt64()),
|
||||
fmt.Sprintf("Error updating VPC: id=%d", state.Id.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
@@ -154,15 +178,15 @@ func (r *vpcResource) Update(ctx context.Context, req resource.UpdateRequest, re
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code updating VPC",
|
||||
fmt.Sprintf("Received %s updating VPC: id=%d. Details: %s", vpcResp.Status(), data.Id.ValueInt64(), vpcResp.Body))
|
||||
fmt.Sprintf("Received %s updating VPC: id=%d. Details: %s", vpcResp.Status(), state.Id.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
data.Id = types.Int64Value(*vpcResp.JSON200.Vpc.Id)
|
||||
data.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
state.Id = types.Int64Value(*vpcResp.JSON200.Vpc.Id)
|
||||
state.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (r *vpcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
@@ -192,3 +216,16 @@ func (r *vpcResource) Delete(ctx context.Context, req resource.DeleteRequest, re
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *vpcResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
id, err := strconv.ParseInt(req.ID, 10, 64)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Error importing VPC",
|
||||
fmt.Sprintf("Could not import VPC, unable to parse ID (should be an integer): %s ", err.Error()),
|
||||
)
|
||||
return
|
||||
}
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("id"), id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
|
||||
@@ -1,58 +1,183 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
)
|
||||
|
||||
func TestVpcResource(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
name = "tf-test-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
data "binarylane_vpc" "test" {
|
||||
depends_on = [binarylane_vpc.test]
|
||||
|
||||
id = binarylane_vpc.test.id
|
||||
id = binarylane_vpc.test.id
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "test" {
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "test"
|
||||
destination = "0.0.0.0/0"
|
||||
router = "10.240.0.1"
|
||||
}
|
||||
]
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "test"
|
||||
destination = "0.0.0.0/0"
|
||||
router = "10.240.0.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
data "binarylane_vpc_route_entries" "test" {
|
||||
vpc_id = binarylane_vpc_route_entries.test.vpc_id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
// Verify binarylane_vpc resource values
|
||||
resource.TestCheckResourceAttr("binarylane_vpc.test", "name", "tf-test-vpc"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc.test", "ip_range", "10.240.0.0/16"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_vpc.test", "id"),
|
||||
// Verify data source values
|
||||
// Verify binarylane_vpc data source values
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc.test", "name", "tf-test-vpc"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc.test", "ip_range", "10.240.0.0/16"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_vpc.test", "id"),
|
||||
|
||||
// Verify resource values
|
||||
// Verify binarylane_vpc_route_entries resource values
|
||||
resource.TestCheckResourceAttrSet("binarylane_vpc_route_entries.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.destination", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.router", "10.240.0.1"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.description", "test"),
|
||||
// Verify binarylane_vpc_route_entries data source values
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_vpc_route_entries.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc_route_entries.test", "route_entries.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc_route_entries.test", "route_entries.0.destination", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc_route_entries.test", "route_entries.0.router", "10.240.0.1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc_route_entries.test", "route_entries.0.description", "test"),
|
||||
),
|
||||
},
|
||||
// ImportState testing
|
||||
{
|
||||
ResourceName: "binarylane_vpc.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
{
|
||||
ResourceName: "binarylane_vpc_route_entries.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIdentifierAttribute: "vpc_id",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
resourceState := s.RootModule().Resources["binarylane_vpc_route_entries.test"]
|
||||
return resourceState.Primary.Attributes["vpc_id"], nil
|
||||
},
|
||||
},
|
||||
// Update and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-vpc-renamed"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "test" {
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "test-renamed"
|
||||
destination = "0.0.0.0/0"
|
||||
router = "10.240.0.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify binarylane_vpc resource values updated
|
||||
resource.TestCheckResourceAttr("binarylane_vpc.test", "name", "tf-test-vpc-renamed"), // Updated name
|
||||
resource.TestCheckResourceAttr("binarylane_vpc.test", "ip_range", "10.240.0.0/16"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_vpc.test", "id"),
|
||||
// Verify binarylane_vpc_route_entries resource values updated
|
||||
resource.TestCheckResourceAttrSet("binarylane_vpc_route_entries.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.destination", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.router", "10.240.0.2"), // Updated router
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.description", "test-renamed"), // Updated description
|
||||
),
|
||||
},
|
||||
// Delete testing automatically occurs in TestCase
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("vpc", &resource.Sweeper{
|
||||
Name: "vpc",
|
||||
F: func(_ string) error {
|
||||
client, err := binarylane.NewClientWithDefaultConfig()
|
||||
|
||||
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.GetVpcsParams{
|
||||
Page: &page,
|
||||
PerPage: &perPage,
|
||||
}
|
||||
|
||||
listResp, err := client.GetVpcsWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting VPCs for test sweep: %w", err)
|
||||
}
|
||||
|
||||
if listResp.StatusCode() != http.StatusOK {
|
||||
return fmt.Errorf("Unexpected status code getting VPCs for test sweep: %s", listResp.Body)
|
||||
}
|
||||
|
||||
vpcs := listResp.JSON200.Vpcs
|
||||
for _, vpc := range *vpcs {
|
||||
if strings.HasPrefix(*vpc.Name, "tf-test-") {
|
||||
|
||||
deleteResp, err := client.DeleteVpcsVpcIdWithResponse(ctx, *vpc.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting VPC %d in test sweep: %w", *vpc.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting VPC %d for test sweep: %s", deleteResp.StatusCode(), *vpc.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted VPC for test sweep:", *vpc.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSource = &vpcRouteEntriesDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &vpcRouteEntriesDataSource{}
|
||||
)
|
||||
|
||||
func NewVpcRouteEntriesDataSource() datasource.DataSource {
|
||||
return &vpcRouteEntriesDataSource{}
|
||||
}
|
||||
|
||||
type vpcRouteEntriesDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type vpcRouteEntriesDataSourceModel struct {
|
||||
resources.VpcRouteEntriesModel
|
||||
}
|
||||
|
||||
func (d *vpcRouteEntriesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_vpc_route_entries"
|
||||
}
|
||||
|
||||
func (d *vpcRouteEntriesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(
|
||||
resources.VpcRouteEntriesResourceSchema(ctx),
|
||||
AttributeConfig{
|
||||
RequiredAttributes: &[]string{"vpc_id"},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
|
||||
return
|
||||
}
|
||||
resp.Schema = *ds
|
||||
// resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
vpcId := resp.Schema.Attributes["vpc_id"]
|
||||
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
|
||||
Description: vpcId.GetDescription(),
|
||||
MarkdownDescription: vpcId.GetMarkdownDescription(),
|
||||
Required: true, // vpc_id is required to define the route entries
|
||||
}
|
||||
|
||||
vpcRouteEntries := resp.Schema.Attributes["route_entries"].(schema.ListNestedAttribute)
|
||||
vpcRouteEntriesDescription := "The route entries that control how network traffic is directed through the VPC environment."
|
||||
resp.Schema.Attributes["route_entries"] = &schema.ListNestedAttribute{
|
||||
Description: vpcRouteEntriesDescription,
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
CustomType: vpcRouteEntries.NestedObject.CustomType,
|
||||
Attributes: vpcRouteEntries.NestedObject.Attributes,
|
||||
},
|
||||
CustomType: vpcRouteEntries.CustomType,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *vpcRouteEntriesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data vpcRouteEntriesDataSourceModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
vpcResp, err := d.bc.client.GetVpcsVpcIdWithResponse(ctx, data.VpcId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading VPC: vpc_id=%d", data.VpcId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading VPC",
|
||||
fmt.Sprintf("Received %s reading VPC: vpc_id=%d. Details: %s", vpcResp.Status(), data.VpcId.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
routeEntries, routeEntriesDiags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(routeEntriesDiags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.RouteEntries = routeEntries
|
||||
|
||||
// Example data value setting
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (d *vpcRouteEntriesDataSource) Configure(
|
||||
_ context.Context,
|
||||
req datasource.ConfigureRequest,
|
||||
resp *datasource.ConfigureResponse,
|
||||
) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Data Source Configure Type",
|
||||
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData))
|
||||
return
|
||||
}
|
||||
|
||||
d.bc = &bc
|
||||
}
|
||||
@@ -4,21 +4,25 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &vpcRouteEntriesResource{}
|
||||
_ resource.ResourceWithConfigure = &vpcRouteEntriesResource{}
|
||||
// _ resource.ResourceWithImportState = &vpcRouteEntriesResource{}
|
||||
_ resource.Resource = &vpcRouteEntriesResource{}
|
||||
_ resource.ResourceWithConfigure = &vpcRouteEntriesResource{}
|
||||
_ resource.ResourceWithImportState = &vpcRouteEntriesResource{}
|
||||
)
|
||||
|
||||
func NewVpcRouteEntriesResource() resource.Resource {
|
||||
@@ -39,14 +43,17 @@ func (r *vpcRouteEntriesResource) Metadata(ctx context.Context, req resource.Met
|
||||
|
||||
func (r *vpcRouteEntriesResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.VpcRouteEntriesResourceSchema(ctx)
|
||||
resp.Schema.Description = "TODO"
|
||||
// resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
vpcId := resp.Schema.Attributes["vpc_id"]
|
||||
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
|
||||
Description: vpcId.GetDescription(),
|
||||
MarkdownDescription: vpcId.GetMarkdownDescription(),
|
||||
Required: true, // VPC ID is required to define the route entries
|
||||
Required: true, // vpc_id is required to define the route entries
|
||||
Validators: []validator.Int64{
|
||||
int64validator.AtLeast(1),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +68,7 @@ func (r *vpcRouteEntriesResource) Create(ctx context.Context, req resource.Creat
|
||||
}
|
||||
|
||||
// Create API call logic
|
||||
var routeEntries []binarylane.RouteEntryRequest
|
||||
routeEntries := []binarylane.RouteEntryRequest{}
|
||||
diags := data.VpcRouteEntriesModel.RouteEntries.ElementsAs(ctx, &routeEntries, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
@@ -86,7 +93,7 @@ func (r *vpcRouteEntriesResource) Create(ctx context.Context, req resource.Creat
|
||||
return
|
||||
}
|
||||
|
||||
routeEntriesState, diags := getRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
routeEntriesState, diags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -111,19 +118,19 @@ func (r *vpcRouteEntriesResource) Read(ctx context.Context, req resource.ReadReq
|
||||
vpcResp, err := r.bc.client.GetVpcsVpcIdWithResponse(ctx, data.VpcId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading VPC: vpc_id=%d", data.VpcId.ValueInt64()),
|
||||
fmt.Sprintf("Error reading VPC route entries: vpc_id=%d", data.VpcId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading VPC",
|
||||
fmt.Sprintf("Received %s reading VPC: vpc_id=%d. Details: %s", vpcResp.Status(), data.VpcId.ValueInt64(), vpcResp.Body))
|
||||
"Unexpected HTTP status code reading VPC route entries",
|
||||
fmt.Sprintf("Received %s reading VPC route entries: vpc_id=%d. Details: %s", vpcResp.Status(), data.VpcId.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
routeEntries, routeEntriesDiags := getRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
routeEntries, routeEntriesDiags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(routeEntriesDiags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -135,28 +142,29 @@ func (r *vpcRouteEntriesResource) Read(ctx context.Context, req resource.ReadReq
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data vpcRouteEntriesResourceModel
|
||||
var plan, state vpcRouteEntriesResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
var routeEntries []binarylane.RouteEntryRequest
|
||||
diags := data.VpcRouteEntriesModel.RouteEntries.ElementsAs(ctx, &routeEntries, true)
|
||||
routeEntries := []binarylane.RouteEntryRequest{}
|
||||
diags := plan.VpcRouteEntriesModel.RouteEntries.ElementsAs(ctx, &routeEntries, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
vpcResp, err := r.bc.client.PatchVpcsVpcIdWithResponse(ctx, data.VpcId.ValueInt64(), binarylane.PatchVpcRequest{
|
||||
vpcResp, err := r.bc.client.PatchVpcsVpcIdWithResponse(ctx, state.VpcId.ValueInt64(), binarylane.PatchVpcRequest{
|
||||
RouteEntries: &routeEntries,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error creating VPC route entries: vpc_id=%d", data.VpcId.ValueInt64()),
|
||||
fmt.Sprintf("Error updating VPC route entries: vpc_id=%d", state.VpcId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
@@ -164,20 +172,20 @@ func (r *vpcRouteEntriesResource) Update(ctx context.Context, req resource.Updat
|
||||
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating VPC route entries",
|
||||
fmt.Sprintf("Received %s creating VPC route entries: vpc_id=%d. Details: %s", vpcResp.Status(), data.VpcId.ValueInt64(), vpcResp.Body))
|
||||
"Unexpected HTTP status code updating VPC route entries",
|
||||
fmt.Sprintf("Received %s updating VPC route entries: vpc_id=%d. Details: %s", vpcResp.Status(), state.VpcId.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
routeEntriesState, diags := getRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
routeEntriesState, diags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.RouteEntries = routeEntriesState
|
||||
state.RouteEntries = routeEntriesState
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
@@ -218,7 +226,7 @@ func (d *vpcRouteEntriesResource) Configure(_ context.Context, req resource.Conf
|
||||
bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Data Source Configure Type",
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData),
|
||||
)
|
||||
return
|
||||
@@ -226,14 +234,14 @@ func (d *vpcRouteEntriesResource) Configure(_ context.Context, req resource.Conf
|
||||
d.bc = &bc
|
||||
}
|
||||
|
||||
func getRouteEntriesState(ctx context.Context, routeEntries *[]binarylane.RouteEntry) (basetypes.ListValue, diag.Diagnostics) {
|
||||
func GetRouteEntriesState(ctx context.Context, routeEntries *[]binarylane.RouteEntry) (basetypes.ListValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
routeEntriesValue := resources.RouteEntriesValue{}
|
||||
var routeEntriesValues []resources.RouteEntriesValue
|
||||
|
||||
if *routeEntries == nil || len(*routeEntries) == 0 {
|
||||
routeEntriesValue = resources.RouteEntriesValue{}
|
||||
routeEntriesValues = []resources.RouteEntriesValue{}
|
||||
} else {
|
||||
for _, route := range *routeEntries {
|
||||
r := resources.NewRouteEntriesValueMust(routeEntriesValue.AttributeTypes(ctx), map[string]attr.Value{
|
||||
@@ -253,3 +261,18 @@ func getRouteEntriesState(ctx context.Context, routeEntries *[]binarylane.RouteE
|
||||
|
||||
return routeEntriesListValue, diags
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
id, err := strconv.ParseInt(req.ID, 10, 64)
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Error importing VPC route entries",
|
||||
"Could not import VPC route entries, unexpected error (ID should be an integer): "+err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("vpc_id"), id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,819 @@
|
||||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
)
|
||||
|
||||
func LoadBalancerResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"forwarding_rules": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"entry_protocol": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
|
||||
MarkdownDescription: "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"http",
|
||||
"https",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
CustomType: ForwardingRulesType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: ForwardingRulesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default \"HTTP\" only forwarding rule.",
|
||||
MarkdownDescription: "The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default \"HTTP\" only forwarding rule.",
|
||||
},
|
||||
"health_check": schema.SingleNestedAttribute{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"path": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "Leave null to accept the default '/' path.",
|
||||
MarkdownDescription: "Leave null to accept the default '/' path.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.RegexMatches(regexp.MustCompile("/[A-Za-z0-9/.?=&+%_-]*"), ""),
|
||||
},
|
||||
},
|
||||
"protocol": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "Leave null to accept the default HTTP protocol.",
|
||||
MarkdownDescription: "Leave null to accept the default HTTP protocol.",
|
||||
},
|
||||
},
|
||||
CustomType: HealthCheckType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: HealthCheckValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules.",
|
||||
MarkdownDescription: "The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the load balancer to fetch.",
|
||||
MarkdownDescription: "The ID of the load balancer to fetch.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The hostname of the load balancer.",
|
||||
MarkdownDescription: "The hostname of the load balancer.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "Leave null to create an anycast load balancer.",
|
||||
MarkdownDescription: "Leave null to create an anycast load balancer.",
|
||||
},
|
||||
"server_ids": schema.ListAttribute{
|
||||
ElementType: types.Int64Type,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "A list of server IDs to assign to this load balancer.",
|
||||
MarkdownDescription: "A list of server IDs to assign to this load balancer.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type LoadBalancerModel struct {
|
||||
ForwardingRules types.List `tfsdk:"forwarding_rules"`
|
||||
HealthCheck HealthCheckValue `tfsdk:"health_check"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
ServerIds types.List `tfsdk:"server_ids"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = ForwardingRulesType{}
|
||||
|
||||
type ForwardingRulesType struct {
|
||||
basetypes.ObjectType
|
||||
}
|
||||
|
||||
func (t ForwardingRulesType) Equal(o attr.Type) bool {
|
||||
other, ok := o.(ForwardingRulesType)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.ObjectType.Equal(other.ObjectType)
|
||||
}
|
||||
|
||||
func (t ForwardingRulesType) String() string {
|
||||
return "ForwardingRulesType"
|
||||
}
|
||||
|
||||
func (t ForwardingRulesType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributes := in.Attributes()
|
||||
|
||||
entryProtocolAttribute, ok := attributes["entry_protocol"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`entry_protocol is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
entryProtocolVal, ok := entryProtocolAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`entry_protocol expected to be basetypes.StringValue, was: %T`, entryProtocolAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return ForwardingRulesValue{
|
||||
EntryProtocol: entryProtocolVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewForwardingRulesValueNull() ForwardingRulesValue {
|
||||
return ForwardingRulesValue{
|
||||
state: attr.ValueStateNull,
|
||||
}
|
||||
}
|
||||
|
||||
func NewForwardingRulesValueUnknown() ForwardingRulesValue {
|
||||
return ForwardingRulesValue{
|
||||
state: attr.ValueStateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
func NewForwardingRulesValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (ForwardingRulesValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
|
||||
ctx := context.Background()
|
||||
|
||||
for name, attributeType := range attributeTypes {
|
||||
attribute, ok := attributes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Missing ForwardingRulesValue Attribute Value",
|
||||
"While creating a ForwardingRulesValue value, a missing attribute value was detected. "+
|
||||
"A ForwardingRulesValue must contain values for all attributes, even if null or unknown. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("ForwardingRulesValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
||||
diags.AddError(
|
||||
"Invalid ForwardingRulesValue Attribute Type",
|
||||
"While creating a ForwardingRulesValue value, an invalid attribute value was detected. "+
|
||||
"A ForwardingRulesValue must use a matching attribute type for the value. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("ForwardingRulesValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
||||
fmt.Sprintf("ForwardingRulesValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for name := range attributes {
|
||||
_, ok := attributeTypes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Extra ForwardingRulesValue Attribute Value",
|
||||
"While creating a ForwardingRulesValue value, an extra attribute value was detected. "+
|
||||
"A ForwardingRulesValue must not contain values beyond the expected attribute types. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("Extra ForwardingRulesValue Attribute Name: %s", name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewForwardingRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
entryProtocolAttribute, ok := attributes["entry_protocol"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`entry_protocol is missing from object`)
|
||||
|
||||
return NewForwardingRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
entryProtocolVal, ok := entryProtocolAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`entry_protocol expected to be basetypes.StringValue, was: %T`, entryProtocolAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewForwardingRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
return ForwardingRulesValue{
|
||||
EntryProtocol: entryProtocolVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewForwardingRulesValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) ForwardingRulesValue {
|
||||
object, diags := NewForwardingRulesValue(attributeTypes, attributes)
|
||||
|
||||
if diags.HasError() {
|
||||
// This could potentially be added to the diag package.
|
||||
diagsStrings := make([]string, 0, len(diags))
|
||||
|
||||
for _, diagnostic := range diags {
|
||||
diagsStrings = append(diagsStrings, fmt.Sprintf(
|
||||
"%s | %s | %s",
|
||||
diagnostic.Severity(),
|
||||
diagnostic.Summary(),
|
||||
diagnostic.Detail()))
|
||||
}
|
||||
|
||||
panic("NewForwardingRulesValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (t ForwardingRulesType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
||||
if in.Type() == nil {
|
||||
return NewForwardingRulesValueNull(), nil
|
||||
}
|
||||
|
||||
if !in.Type().Equal(t.TerraformType(ctx)) {
|
||||
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
|
||||
}
|
||||
|
||||
if !in.IsKnown() {
|
||||
return NewForwardingRulesValueUnknown(), nil
|
||||
}
|
||||
|
||||
if in.IsNull() {
|
||||
return NewForwardingRulesValueNull(), nil
|
||||
}
|
||||
|
||||
attributes := map[string]attr.Value{}
|
||||
|
||||
val := map[string]tftypes.Value{}
|
||||
|
||||
err := in.As(&val)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attributes[k] = a
|
||||
}
|
||||
|
||||
return NewForwardingRulesValueMust(ForwardingRulesValue{}.AttributeTypes(ctx), attributes), nil
|
||||
}
|
||||
|
||||
func (t ForwardingRulesType) ValueType(ctx context.Context) attr.Value {
|
||||
return ForwardingRulesValue{}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectValuable = ForwardingRulesValue{}
|
||||
|
||||
type ForwardingRulesValue struct {
|
||||
EntryProtocol basetypes.StringValue `tfsdk:"entry_protocol"`
|
||||
state attr.ValueState
|
||||
}
|
||||
|
||||
func (v ForwardingRulesValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
||||
attrTypes := make(map[string]tftypes.Type, 1)
|
||||
|
||||
var val tftypes.Value
|
||||
var err error
|
||||
|
||||
attrTypes["entry_protocol"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
|
||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
||||
|
||||
switch v.state {
|
||||
case attr.ValueStateKnown:
|
||||
vals := make(map[string]tftypes.Value, 1)
|
||||
|
||||
val, err = v.EntryProtocol.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["entry_protocol"] = val
|
||||
|
||||
if err := tftypes.ValidateValue(objectType, vals); err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
return tftypes.NewValue(objectType, vals), nil
|
||||
case attr.ValueStateNull:
|
||||
return tftypes.NewValue(objectType, nil), nil
|
||||
case attr.ValueStateUnknown:
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
|
||||
}
|
||||
}
|
||||
|
||||
func (v ForwardingRulesValue) IsNull() bool {
|
||||
return v.state == attr.ValueStateNull
|
||||
}
|
||||
|
||||
func (v ForwardingRulesValue) IsUnknown() bool {
|
||||
return v.state == attr.ValueStateUnknown
|
||||
}
|
||||
|
||||
func (v ForwardingRulesValue) String() string {
|
||||
return "ForwardingRulesValue"
|
||||
}
|
||||
|
||||
func (v ForwardingRulesValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"entry_protocol": basetypes.StringType{},
|
||||
}
|
||||
|
||||
if v.IsNull() {
|
||||
return types.ObjectNull(attributeTypes), diags
|
||||
}
|
||||
|
||||
if v.IsUnknown() {
|
||||
return types.ObjectUnknown(attributeTypes), diags
|
||||
}
|
||||
|
||||
objVal, diags := types.ObjectValue(
|
||||
attributeTypes,
|
||||
map[string]attr.Value{
|
||||
"entry_protocol": v.EntryProtocol,
|
||||
})
|
||||
|
||||
return objVal, diags
|
||||
}
|
||||
|
||||
func (v ForwardingRulesValue) Equal(o attr.Value) bool {
|
||||
other, ok := o.(ForwardingRulesValue)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != other.state {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != attr.ValueStateKnown {
|
||||
return true
|
||||
}
|
||||
|
||||
if !v.EntryProtocol.Equal(other.EntryProtocol) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v ForwardingRulesValue) Type(ctx context.Context) attr.Type {
|
||||
return ForwardingRulesType{
|
||||
basetypes.ObjectType{
|
||||
AttrTypes: v.AttributeTypes(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v ForwardingRulesValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
|
||||
return map[string]attr.Type{
|
||||
"entry_protocol": basetypes.StringType{},
|
||||
}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = HealthCheckType{}
|
||||
|
||||
type HealthCheckType struct {
|
||||
basetypes.ObjectType
|
||||
}
|
||||
|
||||
func (t HealthCheckType) Equal(o attr.Type) bool {
|
||||
other, ok := o.(HealthCheckType)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.ObjectType.Equal(other.ObjectType)
|
||||
}
|
||||
|
||||
func (t HealthCheckType) String() string {
|
||||
return "HealthCheckType"
|
||||
}
|
||||
|
||||
func (t HealthCheckType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributes := in.Attributes()
|
||||
|
||||
pathAttribute, ok := attributes["path"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`path is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
pathVal, ok := pathAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`path expected to be basetypes.StringValue, was: %T`, pathAttribute))
|
||||
}
|
||||
|
||||
protocolAttribute, ok := attributes["protocol"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`protocol is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
protocolVal, ok := protocolAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`protocol expected to be basetypes.StringValue, was: %T`, protocolAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return HealthCheckValue{
|
||||
Path: pathVal,
|
||||
Protocol: protocolVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewHealthCheckValueNull() HealthCheckValue {
|
||||
return HealthCheckValue{
|
||||
state: attr.ValueStateNull,
|
||||
}
|
||||
}
|
||||
|
||||
func NewHealthCheckValueUnknown() HealthCheckValue {
|
||||
return HealthCheckValue{
|
||||
state: attr.ValueStateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
func NewHealthCheckValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (HealthCheckValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
|
||||
ctx := context.Background()
|
||||
|
||||
for name, attributeType := range attributeTypes {
|
||||
attribute, ok := attributes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Missing HealthCheckValue Attribute Value",
|
||||
"While creating a HealthCheckValue value, a missing attribute value was detected. "+
|
||||
"A HealthCheckValue must contain values for all attributes, even if null or unknown. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("HealthCheckValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
||||
diags.AddError(
|
||||
"Invalid HealthCheckValue Attribute Type",
|
||||
"While creating a HealthCheckValue value, an invalid attribute value was detected. "+
|
||||
"A HealthCheckValue must use a matching attribute type for the value. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("HealthCheckValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
||||
fmt.Sprintf("HealthCheckValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for name := range attributes {
|
||||
_, ok := attributeTypes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Extra HealthCheckValue Attribute Value",
|
||||
"While creating a HealthCheckValue value, an extra attribute value was detected. "+
|
||||
"A HealthCheckValue must not contain values beyond the expected attribute types. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("Extra HealthCheckValue Attribute Name: %s", name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewHealthCheckValueUnknown(), diags
|
||||
}
|
||||
|
||||
pathAttribute, ok := attributes["path"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`path is missing from object`)
|
||||
|
||||
return NewHealthCheckValueUnknown(), diags
|
||||
}
|
||||
|
||||
pathVal, ok := pathAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`path expected to be basetypes.StringValue, was: %T`, pathAttribute))
|
||||
}
|
||||
|
||||
protocolAttribute, ok := attributes["protocol"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`protocol is missing from object`)
|
||||
|
||||
return NewHealthCheckValueUnknown(), diags
|
||||
}
|
||||
|
||||
protocolVal, ok := protocolAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`protocol expected to be basetypes.StringValue, was: %T`, protocolAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewHealthCheckValueUnknown(), diags
|
||||
}
|
||||
|
||||
return HealthCheckValue{
|
||||
Path: pathVal,
|
||||
Protocol: protocolVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewHealthCheckValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) HealthCheckValue {
|
||||
object, diags := NewHealthCheckValue(attributeTypes, attributes)
|
||||
|
||||
if diags.HasError() {
|
||||
// This could potentially be added to the diag package.
|
||||
diagsStrings := make([]string, 0, len(diags))
|
||||
|
||||
for _, diagnostic := range diags {
|
||||
diagsStrings = append(diagsStrings, fmt.Sprintf(
|
||||
"%s | %s | %s",
|
||||
diagnostic.Severity(),
|
||||
diagnostic.Summary(),
|
||||
diagnostic.Detail()))
|
||||
}
|
||||
|
||||
panic("NewHealthCheckValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (t HealthCheckType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
||||
if in.Type() == nil {
|
||||
return NewHealthCheckValueNull(), nil
|
||||
}
|
||||
|
||||
if !in.Type().Equal(t.TerraformType(ctx)) {
|
||||
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
|
||||
}
|
||||
|
||||
if !in.IsKnown() {
|
||||
return NewHealthCheckValueUnknown(), nil
|
||||
}
|
||||
|
||||
if in.IsNull() {
|
||||
return NewHealthCheckValueNull(), nil
|
||||
}
|
||||
|
||||
attributes := map[string]attr.Value{}
|
||||
|
||||
val := map[string]tftypes.Value{}
|
||||
|
||||
err := in.As(&val)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attributes[k] = a
|
||||
}
|
||||
|
||||
return NewHealthCheckValueMust(HealthCheckValue{}.AttributeTypes(ctx), attributes), nil
|
||||
}
|
||||
|
||||
func (t HealthCheckType) ValueType(ctx context.Context) attr.Value {
|
||||
return HealthCheckValue{}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectValuable = HealthCheckValue{}
|
||||
|
||||
type HealthCheckValue struct {
|
||||
Path basetypes.StringValue `tfsdk:"path"`
|
||||
Protocol basetypes.StringValue `tfsdk:"protocol"`
|
||||
state attr.ValueState
|
||||
}
|
||||
|
||||
func (v HealthCheckValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
||||
attrTypes := make(map[string]tftypes.Type, 2)
|
||||
|
||||
var val tftypes.Value
|
||||
var err error
|
||||
|
||||
attrTypes["path"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["protocol"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
|
||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
||||
|
||||
switch v.state {
|
||||
case attr.ValueStateKnown:
|
||||
vals := make(map[string]tftypes.Value, 2)
|
||||
|
||||
val, err = v.Path.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["path"] = val
|
||||
|
||||
val, err = v.Protocol.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["protocol"] = val
|
||||
|
||||
if err := tftypes.ValidateValue(objectType, vals); err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
return tftypes.NewValue(objectType, vals), nil
|
||||
case attr.ValueStateNull:
|
||||
return tftypes.NewValue(objectType, nil), nil
|
||||
case attr.ValueStateUnknown:
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
|
||||
}
|
||||
}
|
||||
|
||||
func (v HealthCheckValue) IsNull() bool {
|
||||
return v.state == attr.ValueStateNull
|
||||
}
|
||||
|
||||
func (v HealthCheckValue) IsUnknown() bool {
|
||||
return v.state == attr.ValueStateUnknown
|
||||
}
|
||||
|
||||
func (v HealthCheckValue) String() string {
|
||||
return "HealthCheckValue"
|
||||
}
|
||||
|
||||
func (v HealthCheckValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"path": basetypes.StringType{},
|
||||
"protocol": basetypes.StringType{},
|
||||
}
|
||||
|
||||
if v.IsNull() {
|
||||
return types.ObjectNull(attributeTypes), diags
|
||||
}
|
||||
|
||||
if v.IsUnknown() {
|
||||
return types.ObjectUnknown(attributeTypes), diags
|
||||
}
|
||||
|
||||
objVal, diags := types.ObjectValue(
|
||||
attributeTypes,
|
||||
map[string]attr.Value{
|
||||
"path": v.Path,
|
||||
"protocol": v.Protocol,
|
||||
})
|
||||
|
||||
return objVal, diags
|
||||
}
|
||||
|
||||
func (v HealthCheckValue) Equal(o attr.Value) bool {
|
||||
other, ok := o.(HealthCheckValue)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != other.state {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != attr.ValueStateKnown {
|
||||
return true
|
||||
}
|
||||
|
||||
if !v.Path.Equal(other.Path) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Protocol.Equal(other.Protocol) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v HealthCheckValue) Type(ctx context.Context) attr.Type {
|
||||
return HealthCheckType{
|
||||
basetypes.ObjectType{
|
||||
AttrTypes: v.AttributeTypes(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v HealthCheckValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
|
||||
return map[string]attr.Type{
|
||||
"path": basetypes.StringType{},
|
||||
"protocol": basetypes.StringType{},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,813 @@
|
||||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
)
|
||||
|
||||
func ServerFirewallRulesResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"firewall_rules": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"action": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The action to take when there is a match on this rule.",
|
||||
MarkdownDescription: "The action to take when there is a match on this rule.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"drop",
|
||||
"accept",
|
||||
),
|
||||
},
|
||||
},
|
||||
"description": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. \"Block access to RDP\" or \"Allow access from HQ\".",
|
||||
MarkdownDescription: "A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. \"Block access to RDP\" or \"Allow access from HQ\".",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthBetween(0, 250),
|
||||
},
|
||||
},
|
||||
"destination_addresses": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Required: true,
|
||||
Description: "The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
MarkdownDescription: "The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
Validators: []validator.List{
|
||||
listvalidator.SizeAtLeast(1),
|
||||
},
|
||||
},
|
||||
"destination_ports": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The destination ports to match for this rule. Leave null or empty to match on all ports.",
|
||||
MarkdownDescription: "The destination ports to match for this rule. Leave null or empty to match on all ports.",
|
||||
},
|
||||
"protocol": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The protocol to match for this rule.",
|
||||
MarkdownDescription: "The protocol to match for this rule.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"all",
|
||||
"icmp",
|
||||
"tcp",
|
||||
"udp",
|
||||
),
|
||||
},
|
||||
},
|
||||
"source_addresses": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Required: true,
|
||||
Description: "The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
MarkdownDescription: "The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
Validators: []validator.List{
|
||||
listvalidator.SizeAtLeast(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
CustomType: FirewallRulesType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: FirewallRulesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: true,
|
||||
Description: "A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules.",
|
||||
MarkdownDescription: "A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules.",
|
||||
},
|
||||
"server_id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the server for which firewall rules should be listed.",
|
||||
MarkdownDescription: "The ID of the server for which firewall rules should be listed.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ServerFirewallRulesModel struct {
|
||||
FirewallRules types.List `tfsdk:"firewall_rules"`
|
||||
ServerId types.Int64 `tfsdk:"server_id"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = FirewallRulesType{}
|
||||
|
||||
type FirewallRulesType struct {
|
||||
basetypes.ObjectType
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) Equal(o attr.Type) bool {
|
||||
other, ok := o.(FirewallRulesType)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.ObjectType.Equal(other.ObjectType)
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) String() string {
|
||||
return "FirewallRulesType"
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributes := in.Attributes()
|
||||
|
||||
actionAttribute, ok := attributes["action"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`action is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
actionVal, ok := actionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`action expected to be basetypes.StringValue, was: %T`, actionAttribute))
|
||||
}
|
||||
|
||||
descriptionAttribute, ok := attributes["description"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`description is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
||||
}
|
||||
|
||||
destinationAddressesAttribute, ok := attributes["destination_addresses"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination_addresses is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
destinationAddressesVal, ok := destinationAddressesAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination_addresses expected to be basetypes.ListValue, was: %T`, destinationAddressesAttribute))
|
||||
}
|
||||
|
||||
destinationPortsAttribute, ok := attributes["destination_ports"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination_ports is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
destinationPortsVal, ok := destinationPortsAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination_ports expected to be basetypes.ListValue, was: %T`, destinationPortsAttribute))
|
||||
}
|
||||
|
||||
protocolAttribute, ok := attributes["protocol"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`protocol is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
protocolVal, ok := protocolAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`protocol expected to be basetypes.StringValue, was: %T`, protocolAttribute))
|
||||
}
|
||||
|
||||
sourceAddressesAttribute, ok := attributes["source_addresses"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`source_addresses is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
sourceAddressesVal, ok := sourceAddressesAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`source_addresses expected to be basetypes.ListValue, was: %T`, sourceAddressesAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return FirewallRulesValue{
|
||||
Action: actionVal,
|
||||
Description: descriptionVal,
|
||||
DestinationAddresses: destinationAddressesVal,
|
||||
DestinationPorts: destinationPortsVal,
|
||||
Protocol: protocolVal,
|
||||
SourceAddresses: sourceAddressesVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewFirewallRulesValueNull() FirewallRulesValue {
|
||||
return FirewallRulesValue{
|
||||
state: attr.ValueStateNull,
|
||||
}
|
||||
}
|
||||
|
||||
func NewFirewallRulesValueUnknown() FirewallRulesValue {
|
||||
return FirewallRulesValue{
|
||||
state: attr.ValueStateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
func NewFirewallRulesValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (FirewallRulesValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
|
||||
ctx := context.Background()
|
||||
|
||||
for name, attributeType := range attributeTypes {
|
||||
attribute, ok := attributes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Missing FirewallRulesValue Attribute Value",
|
||||
"While creating a FirewallRulesValue value, a missing attribute value was detected. "+
|
||||
"A FirewallRulesValue must contain values for all attributes, even if null or unknown. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("FirewallRulesValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
||||
diags.AddError(
|
||||
"Invalid FirewallRulesValue Attribute Type",
|
||||
"While creating a FirewallRulesValue value, an invalid attribute value was detected. "+
|
||||
"A FirewallRulesValue must use a matching attribute type for the value. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("FirewallRulesValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
||||
fmt.Sprintf("FirewallRulesValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for name := range attributes {
|
||||
_, ok := attributeTypes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Extra FirewallRulesValue Attribute Value",
|
||||
"While creating a FirewallRulesValue value, an extra attribute value was detected. "+
|
||||
"A FirewallRulesValue must not contain values beyond the expected attribute types. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("Extra FirewallRulesValue Attribute Name: %s", name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
actionAttribute, ok := attributes["action"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`action is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
actionVal, ok := actionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`action expected to be basetypes.StringValue, was: %T`, actionAttribute))
|
||||
}
|
||||
|
||||
descriptionAttribute, ok := attributes["description"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`description is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
||||
}
|
||||
|
||||
destinationAddressesAttribute, ok := attributes["destination_addresses"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination_addresses is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
destinationAddressesVal, ok := destinationAddressesAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination_addresses expected to be basetypes.ListValue, was: %T`, destinationAddressesAttribute))
|
||||
}
|
||||
|
||||
destinationPortsAttribute, ok := attributes["destination_ports"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination_ports is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
destinationPortsVal, ok := destinationPortsAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination_ports expected to be basetypes.ListValue, was: %T`, destinationPortsAttribute))
|
||||
}
|
||||
|
||||
protocolAttribute, ok := attributes["protocol"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`protocol is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
protocolVal, ok := protocolAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`protocol expected to be basetypes.StringValue, was: %T`, protocolAttribute))
|
||||
}
|
||||
|
||||
sourceAddressesAttribute, ok := attributes["source_addresses"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`source_addresses is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
sourceAddressesVal, ok := sourceAddressesAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`source_addresses expected to be basetypes.ListValue, was: %T`, sourceAddressesAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
return FirewallRulesValue{
|
||||
Action: actionVal,
|
||||
Description: descriptionVal,
|
||||
DestinationAddresses: destinationAddressesVal,
|
||||
DestinationPorts: destinationPortsVal,
|
||||
Protocol: protocolVal,
|
||||
SourceAddresses: sourceAddressesVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewFirewallRulesValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) FirewallRulesValue {
|
||||
object, diags := NewFirewallRulesValue(attributeTypes, attributes)
|
||||
|
||||
if diags.HasError() {
|
||||
// This could potentially be added to the diag package.
|
||||
diagsStrings := make([]string, 0, len(diags))
|
||||
|
||||
for _, diagnostic := range diags {
|
||||
diagsStrings = append(diagsStrings, fmt.Sprintf(
|
||||
"%s | %s | %s",
|
||||
diagnostic.Severity(),
|
||||
diagnostic.Summary(),
|
||||
diagnostic.Detail()))
|
||||
}
|
||||
|
||||
panic("NewFirewallRulesValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
||||
if in.Type() == nil {
|
||||
return NewFirewallRulesValueNull(), nil
|
||||
}
|
||||
|
||||
if !in.Type().Equal(t.TerraformType(ctx)) {
|
||||
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
|
||||
}
|
||||
|
||||
if !in.IsKnown() {
|
||||
return NewFirewallRulesValueUnknown(), nil
|
||||
}
|
||||
|
||||
if in.IsNull() {
|
||||
return NewFirewallRulesValueNull(), nil
|
||||
}
|
||||
|
||||
attributes := map[string]attr.Value{}
|
||||
|
||||
val := map[string]tftypes.Value{}
|
||||
|
||||
err := in.As(&val)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attributes[k] = a
|
||||
}
|
||||
|
||||
return NewFirewallRulesValueMust(FirewallRulesValue{}.AttributeTypes(ctx), attributes), nil
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) ValueType(ctx context.Context) attr.Value {
|
||||
return FirewallRulesValue{}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectValuable = FirewallRulesValue{}
|
||||
|
||||
type FirewallRulesValue struct {
|
||||
Action basetypes.StringValue `tfsdk:"action"`
|
||||
Description basetypes.StringValue `tfsdk:"description"`
|
||||
DestinationAddresses basetypes.ListValue `tfsdk:"destination_addresses"`
|
||||
DestinationPorts basetypes.ListValue `tfsdk:"destination_ports"`
|
||||
Protocol basetypes.StringValue `tfsdk:"protocol"`
|
||||
SourceAddresses basetypes.ListValue `tfsdk:"source_addresses"`
|
||||
state attr.ValueState
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
||||
attrTypes := make(map[string]tftypes.Type, 6)
|
||||
|
||||
var val tftypes.Value
|
||||
var err error
|
||||
|
||||
attrTypes["action"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["description"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["destination_addresses"] = basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
}.TerraformType(ctx)
|
||||
attrTypes["destination_ports"] = basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
}.TerraformType(ctx)
|
||||
attrTypes["protocol"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["source_addresses"] = basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
}.TerraformType(ctx)
|
||||
|
||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
||||
|
||||
switch v.state {
|
||||
case attr.ValueStateKnown:
|
||||
vals := make(map[string]tftypes.Value, 6)
|
||||
|
||||
val, err = v.Action.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["action"] = val
|
||||
|
||||
val, err = v.Description.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["description"] = val
|
||||
|
||||
val, err = v.DestinationAddresses.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["destination_addresses"] = val
|
||||
|
||||
val, err = v.DestinationPorts.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["destination_ports"] = val
|
||||
|
||||
val, err = v.Protocol.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["protocol"] = val
|
||||
|
||||
val, err = v.SourceAddresses.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["source_addresses"] = val
|
||||
|
||||
if err := tftypes.ValidateValue(objectType, vals); err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
return tftypes.NewValue(objectType, vals), nil
|
||||
case attr.ValueStateNull:
|
||||
return tftypes.NewValue(objectType, nil), nil
|
||||
case attr.ValueStateUnknown:
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
|
||||
}
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) IsNull() bool {
|
||||
return v.state == attr.ValueStateNull
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) IsUnknown() bool {
|
||||
return v.state == attr.ValueStateUnknown
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) String() string {
|
||||
return "FirewallRulesValue"
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
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...)
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return types.ObjectUnknown(map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
}), diags
|
||||
}
|
||||
|
||||
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...)
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return types.ObjectUnknown(map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
}), diags
|
||||
}
|
||||
|
||||
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...)
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return types.ObjectUnknown(map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
}), diags
|
||||
}
|
||||
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
}
|
||||
|
||||
if v.IsNull() {
|
||||
return types.ObjectNull(attributeTypes), diags
|
||||
}
|
||||
|
||||
if v.IsUnknown() {
|
||||
return types.ObjectUnknown(attributeTypes), diags
|
||||
}
|
||||
|
||||
objVal, diags := types.ObjectValue(
|
||||
attributeTypes,
|
||||
map[string]attr.Value{
|
||||
"action": v.Action,
|
||||
"description": v.Description,
|
||||
"destination_addresses": destinationAddressesVal,
|
||||
"destination_ports": destinationPortsVal,
|
||||
"protocol": v.Protocol,
|
||||
"source_addresses": sourceAddressesVal,
|
||||
})
|
||||
|
||||
return objVal, diags
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) Equal(o attr.Value) bool {
|
||||
other, ok := o.(FirewallRulesValue)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != other.state {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != attr.ValueStateKnown {
|
||||
return true
|
||||
}
|
||||
|
||||
if !v.Action.Equal(other.Action) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Description.Equal(other.Description) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.DestinationAddresses.Equal(other.DestinationAddresses) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.DestinationPorts.Equal(other.DestinationPorts) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Protocol.Equal(other.Protocol) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.SourceAddresses.Equal(other.SourceAddresses) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) Type(ctx context.Context) attr.Type {
|
||||
return FirewallRulesType{
|
||||
basetypes.ObjectType{
|
||||
AttrTypes: v.AttributeTypes(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
|
||||
return map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -37,11 +37,11 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Description: "The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.",
|
||||
MarkdownDescription: "The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.",
|
||||
},
|
||||
"password": schema.StringAttribute{
|
||||
"port_blocking": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.\n",
|
||||
MarkdownDescription: "If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.\n",
|
||||
Description: "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.",
|
||||
MarkdownDescription: "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
@@ -59,6 +59,13 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"ssh_keys": schema.ListAttribute{
|
||||
ElementType: types.Int64Type,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.\n",
|
||||
MarkdownDescription: "This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.\n",
|
||||
},
|
||||
"user_data": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
@@ -79,13 +86,14 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
}
|
||||
|
||||
type ServerModel struct {
|
||||
Backups types.Bool `tfsdk:"backups"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Image types.String `tfsdk:"image"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Password types.String `tfsdk:"password"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Size types.String `tfsdk:"size"`
|
||||
UserData types.String `tfsdk:"user_data"`
|
||||
VpcId types.Int64 `tfsdk:"vpc_id"`
|
||||
Backups types.Bool `tfsdk:"backups"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Image types.String `tfsdk:"image"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
PortBlocking types.Bool `tfsdk:"port_blocking"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Size types.String `tfsdk:"size"`
|
||||
SshKeys types.List `tfsdk:"ssh_keys"`
|
||||
UserData types.String `tfsdk:"user_data"`
|
||||
VpcId types.Int64 `tfsdk:"vpc_id"`
|
||||
}
|
||||
|
||||
+267
-3
@@ -3,6 +3,124 @@
|
||||
"name": "binarylane"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "load_balancer",
|
||||
"schema": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "forwarding_rules",
|
||||
"list_nested": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"nested_object": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "entry_protocol",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.OneOf(\n\"http\",\n\"https\",\n)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default \"HTTP\" only forwarding rule."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "health_check",
|
||||
"single_nested": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "path",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "Leave null to accept the default '/' path.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "regexp"
|
||||
},
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.RegexMatches(regexp.MustCompile(\"/[A-Za-z0-9/.?=\u0026+%_-]*\"), \"\")"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "protocol",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "Leave null to accept the default HTTP protocol."
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The hostname of the load balancer.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthAtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "region",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "Leave null to create an anycast load balancer."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "server_ids",
|
||||
"list": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"element_type": {
|
||||
"int64": {}
|
||||
},
|
||||
"description": "A list of server IDs to assign to this load balancer."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "The ID of the load balancer to fetch."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "server",
|
||||
"schema": {
|
||||
@@ -29,10 +147,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"string": {
|
||||
"name": "port_blocking",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.\n"
|
||||
"description": "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts."
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -73,6 +191,16 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ssh_keys",
|
||||
"list": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"element_type": {
|
||||
"int64": {}
|
||||
},
|
||||
"description": "This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "user_data",
|
||||
"string": {
|
||||
@@ -109,6 +237,142 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "server_firewall_rules",
|
||||
"schema": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "firewall_rules",
|
||||
"list_nested": {
|
||||
"computed_optional_required": "required",
|
||||
"nested_object": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "action",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The action to take when there is a match on this rule.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.OneOf(\n\"drop\",\n\"accept\",\n)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. \"Block access to RDP\" or \"Allow access from HQ\".",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthBetween(0, 250)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "destination_addresses",
|
||||
"list": {
|
||||
"computed_optional_required": "required",
|
||||
"element_type": {
|
||||
"string": {}
|
||||
},
|
||||
"description": "The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "listvalidator.SizeAtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "destination_ports",
|
||||
"list": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"element_type": {
|
||||
"string": {}
|
||||
},
|
||||
"description": "The destination ports to match for this rule. Leave null or empty to match on all ports."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "protocol",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The protocol to match for this rule.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.OneOf(\n\"all\",\n\"icmp\",\n\"tcp\",\n\"udp\",\n)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "source_addresses",
|
||||
"list": {
|
||||
"computed_optional_required": "required",
|
||||
"element_type": {
|
||||
"string": {}
|
||||
},
|
||||
"description": "The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "listvalidator.SizeAtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "server_id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "The ID of the server for which firewall rules should be listed."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ssh_key",
|
||||
"schema": {
|
||||
|
||||
+42
-18
@@ -27,38 +27,62 @@ resources:
|
||||
attributes:
|
||||
aliases:
|
||||
server_id: id
|
||||
ipv4_addresses: options.ipv4_addresses
|
||||
overrides:
|
||||
backups:
|
||||
type: bool
|
||||
description: >
|
||||
If true this will enable two daily backups for the server. `options.daily_backups` will override this
|
||||
value if provided. Setting this to false has no effect.
|
||||
optional: true
|
||||
default:
|
||||
static: false
|
||||
computed: false
|
||||
image:
|
||||
type: string
|
||||
description: The slug of the selected operating system.
|
||||
example: 'ubuntu-24.04'
|
||||
password:
|
||||
type: string
|
||||
ssh_keys:
|
||||
description: >
|
||||
If this is provided the specified or default remote user's account password will be set to this value.
|
||||
Only valid if the server supports password change actions (check server.password_change_supported via the
|
||||
servers endpoint). If omitted and the server supports password change actions a random password will be
|
||||
generated and emailed to the account email address.
|
||||
sensitive: true
|
||||
optional_required: optional
|
||||
This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as
|
||||
default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to
|
||||
disable deployment of default keys.
|
||||
ignores:
|
||||
- ipv6
|
||||
- licenses
|
||||
- links
|
||||
- server
|
||||
- ssh_keys
|
||||
- port_blocking
|
||||
- options
|
||||
- password
|
||||
load_balancer:
|
||||
create:
|
||||
path: /load_balancers
|
||||
method: POST
|
||||
read:
|
||||
path: /load_balancers/{load_balancer_id}
|
||||
method: GET
|
||||
update:
|
||||
path: /load_balancers/{load_balancer_id}
|
||||
method: PUT
|
||||
delete:
|
||||
path: /load_balancers/{load_balancer_id}
|
||||
method: DELETE
|
||||
schema:
|
||||
attributes:
|
||||
aliases:
|
||||
load_balancer_id: id
|
||||
ignores:
|
||||
- links
|
||||
- load_balancer
|
||||
server_firewall_rules:
|
||||
create:
|
||||
path: /servers/{server_id}/actions#ChangeAdvancedFirewallRules
|
||||
method: POST
|
||||
read:
|
||||
path: /servers/{server_id}/advanced_firewall_rules
|
||||
method: GET
|
||||
update:
|
||||
path: /servers/{server_id}/actions#ChangeAdvancedFirewallRules
|
||||
method: POST
|
||||
delete:
|
||||
path: /servers/{server_id}/actions#ChangeAdvancedFirewallRules
|
||||
method: POST
|
||||
schema:
|
||||
ignores:
|
||||
- type
|
||||
- action
|
||||
ssh_key:
|
||||
create:
|
||||
path: /account/keys
|
||||
|
||||
Reference in New Issue
Block a user