Implement key pair resource (#588)
* Revert "Revert "Implement key pair resource (#578)" (#581)"
This reverts commit 600847a2ea.
* feat: Update iaas SDK module version; Use beta API in key pair resource
This commit is contained in:
parent
fc27f65925
commit
b1f928f6be
20 changed files with 1282 additions and 138 deletions
36
docs/data-sources/key_pair.md
Normal file
36
docs/data-sources/key_pair.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_key_pair Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Key pair resource schema. Must have a region specified in the provider configuration.
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources.
|
||||
---
|
||||
|
||||
# stackit_key_pair (Data Source)
|
||||
|
||||
Key pair resource schema. Must have a `region` specified in the provider configuration.
|
||||
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "stackit_key_pair" "example" {
|
||||
name = "example-key-pair-name"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) The name of the SSH key pair.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `fingerprint` (String) The fingerprint of the public SSH key.
|
||||
- `id` (String) Terraform's internal resource ID. It takes the value of the key pair "`name`".
|
||||
- `labels` (Map of String) Labels are key-value string pairs which can be attached to a resource container.
|
||||
- `public_key` (String) A string representation of the public SSH key. E.g., `ssh-rsa <key_data>` or `ssh-ed25519 <key-data>`.
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
page_title: "stackit_public_ip Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Volume resource schema. Must have a region specified in the provider configuration.
|
||||
Public IP resource schema. Must have a region specified in the provider configuration.
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources.
|
||||
---
|
||||
|
||||
# stackit_public_ip (Data Source)
|
||||
|
||||
Volume resource schema. Must have a `region` specified in the provider configuration.
|
||||
Public IP resource schema. Must have a `region` specified in the provider configuration.
|
||||
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources.
|
||||
|
||||
|
|
|
|||
74
docs/resources/key_pair.md
Normal file
74
docs/resources/key_pair.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_key_pair Resource - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Key pair resource schema. Must have a region specified in the provider configuration. Allows uploading an SSH public key to be used for server authentication.
|
||||
Usage with server
|
||||
```terraform
|
||||
resource "stackitkeypair" "keypair" {
|
||||
name = "example-key-pair"
|
||||
publickey = chomp(file("path/to/idrsa.pub"))
|
||||
}
|
||||
resource "stackitserver" "example-server" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-server"
|
||||
bootvolume = {
|
||||
size = 64
|
||||
sourcetype = "image"
|
||||
sourceid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
availabilityzone = "eu01-1"
|
||||
machinetype = "g1.1"
|
||||
keypairname = "example-key-pair"
|
||||
}
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources.
|
||||
---
|
||||
|
||||
# stackit_key_pair (Resource)
|
||||
|
||||
Key pair resource schema. Must have a `region` specified in the provider configuration. Allows uploading an SSH public key to be used for server authentication.
|
||||
|
||||
|
||||
|
||||
### Usage with server
|
||||
```terraform
|
||||
resource "stackit_key_pair" "keypair" {
|
||||
name = "example-key-pair"
|
||||
public_key = chomp(file("path/to/id_rsa.pub"))
|
||||
}
|
||||
|
||||
resource "stackit_server" "example-server" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-server"
|
||||
boot_volume = {
|
||||
size = 64
|
||||
source_type = "image"
|
||||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
availability_zone = "eu01-1"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-key-pair"
|
||||
}
|
||||
|
||||
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources.
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) The name of the SSH key pair.
|
||||
- `public_key` (String) A string representation of the public SSH key. E.g., `ssh-rsa <key_data>` or `ssh-ed25519 <key-data>`.
|
||||
|
||||
### Optional
|
||||
|
||||
- `labels` (Map of String) Labels are key-value string pairs which can be attached to a resource container.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `fingerprint` (String) The fingerprint of the public SSH key.
|
||||
- `id` (String) Terraform's internal resource ID. It takes the value of the key pair "`name`".
|
||||
|
|
@ -6,26 +6,44 @@ description: |-
|
|||
Server resource schema. Must have a region specified in the provider configuration.
|
||||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources.
|
||||
Example Usage
|
||||
Boot from volume
|
||||
|
||||
resource "stackit_server" "boot-from-volume" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-server"
|
||||
boot_volume = {
|
||||
size = 64
|
||||
source_type = "image"
|
||||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
availability_zone = "eu01-1"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
With key pair
|
||||
```terraform
|
||||
resource "stackitkeypair" "keypair" {
|
||||
name = "example-key-pair"
|
||||
publickey = chomp(file("path/to/idrsa.pub"))
|
||||
}
|
||||
|
||||
|
||||
resource "stackitserver" "user-data-from-file" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
bootvolume = {
|
||||
size = 64
|
||||
sourcetype = "image"
|
||||
sourceid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
name = "example-server"
|
||||
machinetype = "g1.1"
|
||||
keypairname = stackitkeypair.keypair.name
|
||||
userdata = file("${path.module}/cloud-init.yaml")
|
||||
}
|
||||
```
|
||||
Boot from volume
|
||||
```terraform
|
||||
resource "stackitserver" "boot-from-volume" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-server"
|
||||
bootvolume = {
|
||||
size = 64
|
||||
sourcetype = "image"
|
||||
sourceid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
availabilityzone = "eu01-1"
|
||||
machinetype = "g1.1"
|
||||
keypairname = "example-keypair"
|
||||
}
|
||||
```
|
||||
Boot from existing volume
|
||||
|
||||
resource "stackit_volume" "example-volume" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
```terraform
|
||||
resource "stackitvolume" "example-volume" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
size = 12
|
||||
source = {
|
||||
type = "image"
|
||||
|
|
@ -34,129 +52,117 @@ description: |-
|
|||
name = "example-volume"
|
||||
availability_zone = "eu01-1"
|
||||
}
|
||||
|
||||
resource "stackit_server" "boot-from-volume" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
resource "stackitserver" "boot-from-volume" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-server"
|
||||
boot_volume = {
|
||||
source_type = "volume"
|
||||
source_id = stackit_volume.example-volume.volume_id
|
||||
bootvolume = {
|
||||
sourcetype = "volume"
|
||||
sourceid = stackitvolume.example-volume.volumeid
|
||||
}
|
||||
availability_zone = "eu01-1"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
availabilityzone = "eu01-1"
|
||||
machinetype = "g1.1"
|
||||
keypairname = stackitkeypair.keypair.name
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
Network setup
|
||||
|
||||
resource "stackit_server" "server-with-network" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
```terraform
|
||||
resource "stackitserver" "server-with-network" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-server"
|
||||
boot_volume = {
|
||||
bootvolume = {
|
||||
size = 64
|
||||
source_type = "image"
|
||||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
sourcetype = "image"
|
||||
sourceid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
machinetype = "g1.1"
|
||||
keypairname = stackitkey_pair.keypair.name
|
||||
}
|
||||
|
||||
resource "stackit_network" "network" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
resource "stackitnetwork" "network" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-network"
|
||||
nameservers = ["192.0.2.0", "198.51.100.0", "203.0.113.0"]
|
||||
ipv4_prefix_length = 24
|
||||
ipv4prefixlength = 24
|
||||
}
|
||||
|
||||
resource "stackit_security_group" "sec-group" {
|
||||
resource "stackitsecuritygroup" "sec-group" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-security-group"
|
||||
stateful = true
|
||||
}
|
||||
|
||||
resource "stackit_security_group_rule" "rule" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
security_group_id = stackit_security_group.sec-group.security_group_id
|
||||
resource "stackitsecuritygrouprule" "rule" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
securitygroupid = stackitsecuritygroup.sec-group.securitygroupid
|
||||
direction = "ingress"
|
||||
ether_type = "IPv4"
|
||||
}
|
||||
|
||||
resource "stackit_network_interface" "nic" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
network_id = stackit_network.network.network_id
|
||||
security_group_ids = [stackit_security_group.sec-group.security_group_id]
|
||||
resource "stackitnetworkinterface" "nic" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
networkid = stackitnetwork.network.networkid
|
||||
securitygroupids = [stackitsecuritygroup.sec-group.securitygroupid]
|
||||
}
|
||||
|
||||
resource "stackit_public_ip" "public-ip" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
network_interface_id = stackit_network_interface.nic.network_interface_id
|
||||
resource "stackitpublicip" "public-ip" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
networkinterfaceid = stackitnetworkinterface.nic.networkinterface_id
|
||||
}
|
||||
|
||||
resource "stackit_server_network_interface_attach" "nic-attachment" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
server_id = stackit_server.server-with-network.server_id
|
||||
network_interface_id = stackit_network_interface.nic.network_interface_id
|
||||
resource "stackitservernetworkinterfaceattach" "nic-attachment" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
serverid = stackitserver.server-with-network.serverid
|
||||
networkinterfaceid = stackitnetworkinterface.nic.networkinterfaceid
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
Server with attached volume
|
||||
|
||||
resource "stackit_volume" "example-volume" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
```terraform
|
||||
resource "stackitvolume" "example-volume" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
size = 12
|
||||
performance_class = "storage_premium_perf6"
|
||||
performanceclass = "storagepremiumperf6"
|
||||
name = "example-volume"
|
||||
availability_zone = "eu01-1"
|
||||
availabilityzone = "eu01-1"
|
||||
}
|
||||
|
||||
resource "stackit_server" "server-with-volume" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
resource "stackitserver" "server-with-volume" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-server"
|
||||
boot_volume = {
|
||||
bootvolume = {
|
||||
size = 64
|
||||
source_type = "image"
|
||||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
sourcetype = "image"
|
||||
sourceid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
availability_zone = "eu01-1"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
availabilityzone = "eu01-1"
|
||||
machinetype = "g1.1"
|
||||
keypairname = stackitkeypair.keypair.name
|
||||
}
|
||||
|
||||
resource "stackit_server_volume_attach" "attach_volume" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
server_id = stackit_server.server-with-volume.server_id
|
||||
volume_id = stackit_volume.example-volume.volume_id
|
||||
resource "stackitservervolumeattach" "attachvolume" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
serverid = stackitserver.server-with-volume.serverid
|
||||
volumeid = stackitvolume.example-volume.volume_id
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
Server with user data (cloud-init)
|
||||
|
||||
resource "stackit_server" "user-data" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
boot_volume = {
|
||||
```terraform
|
||||
resource "stackitserver" "user-data" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
bootvolume = {
|
||||
size = 64
|
||||
source_type = "image"
|
||||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
sourcetype = "image"
|
||||
sourceid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
name = "example-server"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
user_data = "#!/bin/bash\n/bin/su"
|
||||
machinetype = "g1.1"
|
||||
keypairname = stackitkeypair.keypair.name
|
||||
userdata = "#!/bin/bash\n/bin/su"
|
||||
}
|
||||
|
||||
resource "stackit_server" "user-data-from-file" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
boot_volume = {
|
||||
resource "stackitserver" "user-data-from-file" {
|
||||
projectid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
bootvolume = {
|
||||
size = 64
|
||||
source_type = "image"
|
||||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
sourcetype = "image"
|
||||
sourceid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
name = "example-server"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
user_data = file("${path.module}/cloud-init.yaml")
|
||||
machinetype = "g1.1"
|
||||
keypairname = stackitkeypair.keypair.name
|
||||
userdata = file("${path.module}/cloud-init.yaml")
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
# stackit_server (Resource)
|
||||
|
|
@ -167,6 +173,28 @@ Server resource schema. Must have a region specified in the provider configurati
|
|||
## Example Usage
|
||||
|
||||
|
||||
### With key pair
|
||||
```terraform
|
||||
resource "stackit_key_pair" "keypair" {
|
||||
name = "example-key-pair"
|
||||
public_key = chomp(file("path/to/id_rsa.pub"))
|
||||
}
|
||||
|
||||
resource "stackit_server" "user-data-from-file" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
boot_volume = {
|
||||
size = 64
|
||||
source_type = "image"
|
||||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
name = "example-server"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
user_data = file("${path.module}/cloud-init.yaml")
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Boot from volume
|
||||
```terraform
|
||||
resource "stackit_server" "boot-from-volume" {
|
||||
|
|
@ -206,7 +234,7 @@ resource "stackit_server" "boot-from-volume" {
|
|||
}
|
||||
availability_zone = "eu01-1"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
}
|
||||
|
||||
```
|
||||
|
|
@ -222,7 +250,7 @@ resource "stackit_server" "server-with-network" {
|
|||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
}
|
||||
|
||||
resource "stackit_network" "network" {
|
||||
|
|
@ -284,7 +312,7 @@ resource "stackit_server" "server-with-volume" {
|
|||
}
|
||||
availability_zone = "eu01-1"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
}
|
||||
|
||||
resource "stackit_server_volume_attach" "attach_volume" {
|
||||
|
|
@ -306,7 +334,7 @@ resource "stackit_server" "user-data" {
|
|||
}
|
||||
name = "example-server"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
user_data = "#!/bin/bash\n/bin/su"
|
||||
}
|
||||
|
||||
|
|
@ -319,7 +347,7 @@ resource "stackit_server" "user-data-from-file" {
|
|||
}
|
||||
name = "example-server"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
user_data = file("${path.module}/cloud-init.yaml")
|
||||
}
|
||||
|
||||
|
|
|
|||
3
examples/data-sources/stackit_key_pair/data-source.tf
Normal file
3
examples/data-sources/stackit_key_pair/data-source.tf
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
data "stackit_key_pair" "example" {
|
||||
name = "example-key-pair-name"
|
||||
}
|
||||
3
go.mod
3
go.mod
|
|
@ -14,7 +14,8 @@ require (
|
|||
github.com/stackitcloud/stackit-sdk-go/core v0.14.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/argus v0.11.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/dns v0.11.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.15.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.16.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.12-alpha
|
||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v0.17.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/logme v0.20.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.20.0
|
||||
|
|
|
|||
6
go.sum
6
go.sum
|
|
@ -155,8 +155,10 @@ github.com/stackitcloud/stackit-sdk-go/services/authorization v0.4.0 h1:WXSIE4Kf
|
|||
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.4.0/go.mod h1:8spVqlPqZrvQQ63Qodbydk3qsZx7lr963ECft+sqFhY=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/dns v0.11.0 h1:+OZ82DwFy4JIJThadVjvll5kUWjHPSLbUIF65njsNBk=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/dns v0.11.0/go.mod h1:mv8U7kuclXo+0VpDHtBCkve/3i9h1yT+RAId/MUi+C8=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.15.0 h1:bPNv+PuSykBcKCYVXHiYOcqNP+KLCA7XMFSY4V6J6ug=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.15.0/go.mod h1:YfuN+eXuqr846xeRyW2Vf1JM2jU0ikeQa76dDI66RsM=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.16.0 h1:geyW780gqNxzSsPvmlxy3kUUJaRA4eiF9V3b2Ibcdjs=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.16.0/go.mod h1:YfuN+eXuqr846xeRyW2Vf1JM2jU0ikeQa76dDI66RsM=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.12-alpha h1:jwpif4t2gthmKmCXsQ84rmtDdcZkw4QQTFiCd7nTW8M=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.12-alpha/go.mod h1:nW/6vvumUHA7o1/JOOqsrEOBNrRHombEKB1U4jmg2wU=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v0.17.0 h1:06CGP64CEk3Zg6i9kZCMRdmCzLLiyMWQqGK1teBr9Oc=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v0.17.0/go.mod h1:JL94zc8K0ebWs+DBGXR28vNCF0EFV54ZLUtrlXOvWgA=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/logme v0.20.0 h1:V0UGP7JEa4Q8SsZFUJsKgLGaoPruLn2KVKnqQtaoWCU=
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
||||
)
|
||||
|
|
@ -96,6 +97,14 @@ var publicIpResource = map[string]string{
|
|||
"network_interface_id": testutil.IaaSNetworkInterfaceId,
|
||||
}
|
||||
|
||||
// Key pair resource data
|
||||
var keyPairResource = map[string]string{
|
||||
"name": "key-pair-name",
|
||||
"public_key": `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIDsPd27M449akqCtdFg2+AmRVJz6eWio0oMP9dVg7XZ`,
|
||||
"label1": "value1",
|
||||
"label1-updated": "value1-updated",
|
||||
}
|
||||
|
||||
func networkResourceConfig(name, nameservers string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "stackit_network" "network" {
|
||||
|
|
@ -323,6 +332,13 @@ func testAccPublicIpConfig(publicIpResourceConfig string) string {
|
|||
)
|
||||
}
|
||||
|
||||
func testAccKeyPairConfig(keyPairResourceConfig string) string {
|
||||
return fmt.Sprintf("%s\n\n%s",
|
||||
testutil.IaaSProviderConfig(),
|
||||
keyPairResourceConfig,
|
||||
)
|
||||
}
|
||||
|
||||
func TestAccNetworkArea(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
|
|
@ -1113,6 +1129,117 @@ func TestAccPublicIp(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccKeyPair(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckIaaSKeyPairDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
|
||||
// Creation
|
||||
{
|
||||
Config: testAccKeyPairConfig(
|
||||
fmt.Sprintf(`
|
||||
resource "stackit_key_pair" "key_pair" {
|
||||
name = "%s"
|
||||
public_key = "%s"
|
||||
labels = {
|
||||
"label1" = "%s"
|
||||
}
|
||||
}
|
||||
`,
|
||||
keyPairResource["name"],
|
||||
keyPairResource["public_key"],
|
||||
keyPairResource["label1"],
|
||||
),
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_key_pair.key_pair", "name", keyPairResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_key_pair.key_pair", "labels.label1", keyPairResource["label1"]),
|
||||
resource.TestCheckResourceAttr("stackit_key_pair.key_pair", "public_key", keyPairResource["public_key"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_key_pair.key_pair", "fingerprint"),
|
||||
),
|
||||
},
|
||||
// Data source
|
||||
{
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "stackit_key_pair" "key_pair" {
|
||||
name = stackit_key_pair.key_pair.name
|
||||
}
|
||||
`,
|
||||
testAccKeyPairConfig(
|
||||
fmt.Sprintf(`
|
||||
resource "stackit_key_pair" "key_pair" {
|
||||
name = "%s"
|
||||
public_key = "%s"
|
||||
labels = {
|
||||
"label1" = "%s"
|
||||
}
|
||||
}
|
||||
`,
|
||||
keyPairResource["name"],
|
||||
keyPairResource["public_key"],
|
||||
keyPairResource["label1"],
|
||||
),
|
||||
),
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Instance
|
||||
resource.TestCheckResourceAttr("data.stackit_key_pair.key_pair", "name", keyPairResource["name"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_key_pair.key_pair", "public_key", keyPairResource["public_key"]),
|
||||
resource.TestCheckResourceAttr("stackit_key_pair.key_pair", "labels.label1", keyPairResource["label1"]),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_key_pair.key_pair", "fingerprint",
|
||||
"data.stackit_key_pair.key_pair", "fingerprint",
|
||||
),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ResourceName: "stackit_key_pair.key_pair",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_key_pair.key_pair"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_key_pair.key_pair")
|
||||
}
|
||||
keyPairName, ok := r.Primary.Attributes["name"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute name")
|
||||
}
|
||||
return keyPairName, nil
|
||||
},
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
// Update
|
||||
{
|
||||
Config: testAccKeyPairConfig(
|
||||
fmt.Sprintf(`
|
||||
resource "stackit_key_pair" "key_pair" {
|
||||
name = "%s"
|
||||
public_key = "%s"
|
||||
labels = {
|
||||
"label1" = "%s"
|
||||
}
|
||||
}
|
||||
`,
|
||||
keyPairResource["name"],
|
||||
keyPairResource["public_key"],
|
||||
keyPairResource["label1-updated"],
|
||||
),
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_key_pair.key_pair", "name", keyPairResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_key_pair.key_pair", "labels.label1", keyPairResource["label1-updated"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_key_pair.key_pair", "fingerprint"),
|
||||
),
|
||||
},
|
||||
// Deletion is done by the framework implicitly
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckNetworkAreaDestroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *iaas.APIClient
|
||||
|
|
@ -1388,3 +1515,49 @@ func testAccCheckIaaSPublicIpDestroy(s *terraform.State) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckIaaSKeyPairDestroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *iaasalpha.APIClient
|
||||
var err error
|
||||
if testutil.IaaSCustomEndpoint == "" {
|
||||
client, err = iaasalpha.NewAPIClient(
|
||||
config.WithRegion("eu01"),
|
||||
)
|
||||
} else {
|
||||
client, err = iaasalpha.NewAPIClient(
|
||||
config.WithEndpoint(testutil.IaaSCustomEndpoint),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating client: %w", err)
|
||||
}
|
||||
|
||||
keyPairsToDestroy := []string{}
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "stackit_key_pair" {
|
||||
continue
|
||||
}
|
||||
// Key pair terraform ID: "[name]"
|
||||
keyPairsToDestroy = append(keyPairsToDestroy, rs.Primary.ID)
|
||||
}
|
||||
|
||||
keyPairsResp, err := client.ListKeyPairsExecute(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting key pairs: %w", err)
|
||||
}
|
||||
|
||||
keyPairs := *keyPairsResp.Items
|
||||
for i := range keyPairs {
|
||||
if keyPairs[i].Name == nil {
|
||||
continue
|
||||
}
|
||||
if utils.Contains(keyPairsToDestroy, *keyPairs[i].Name) {
|
||||
err := client.DeleteKeyPairExecute(ctx, *keyPairs[i].Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("destroying key pair %s during CheckDestroy: %w", *keyPairs[i].Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
25
stackit/internal/services/iaas/keypair/const.go
Normal file
25
stackit/internal/services/iaas/keypair/const.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package keypair
|
||||
|
||||
const exampleUsageWithServer = `
|
||||
|
||||
### Usage with server` + "\n" +
|
||||
|
||||
"```terraform" + `
|
||||
resource "stackit_key_pair" "keypair" {
|
||||
name = "example-key-pair"
|
||||
public_key = chomp(file("path/to/id_rsa.pub"))
|
||||
}
|
||||
|
||||
resource "stackit_server" "example-server" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
name = "example-server"
|
||||
boot_volume = {
|
||||
size = 64
|
||||
source_type = "image"
|
||||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
availability_zone = "eu01-1"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-key-pair"
|
||||
}
|
||||
`
|
||||
155
stackit/internal/services/iaas/keypair/datasource.go
Normal file
155
stackit/internal/services/iaas/keypair/datasource.go
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
package keypair
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
)
|
||||
|
||||
// keyPairDataSourceBetaCheckDone is used to prevent multiple checks for beta resources.
|
||||
// This is a workaround for the lack of a global state in the provider and
|
||||
// needs to exist because the Configure method is called twice.
|
||||
var keyPairDataSourceBetaCheckDone bool
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &keyPairDataSource{}
|
||||
)
|
||||
|
||||
// NewVolumeDataSource is a helper function to simplify the provider implementation.
|
||||
func NewKeyPairDataSource() datasource.DataSource {
|
||||
return &keyPairDataSource{}
|
||||
}
|
||||
|
||||
// keyPairDataSource is the data source implementation.
|
||||
type keyPairDataSource struct {
|
||||
client *iaas.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (d *keyPairDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_key_pair"
|
||||
}
|
||||
|
||||
func (d *keyPairDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var apiClient *iaas.APIClient
|
||||
var err error
|
||||
|
||||
providerData, ok := req.ProviderData.(core.ProviderData)
|
||||
if !ok {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
|
||||
return
|
||||
}
|
||||
|
||||
if !keyPairDataSourceBetaCheckDone {
|
||||
features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_key_pair", "data source")
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
keyPairDataSourceBetaCheckDone = true
|
||||
}
|
||||
|
||||
if providerData.IaaSCustomEndpoint != "" {
|
||||
apiClient, err = iaas.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithEndpoint(providerData.IaaSCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = iaas.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithRegion(providerData.Region),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the data source configuration", err))
|
||||
return
|
||||
}
|
||||
|
||||
d.client = apiClient
|
||||
tflog.Info(ctx, "iaas client configured")
|
||||
}
|
||||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *keyPairDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
description := "Key pair resource schema. Must have a `region` specified in the provider configuration."
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: features.AddBetaDescription(description),
|
||||
Description: description,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: "Terraform's internal resource ID. It takes the value of the key pair \"`name`\".",
|
||||
Computed: true,
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: "The name of the SSH key pair.",
|
||||
Required: true,
|
||||
},
|
||||
"public_key": schema.StringAttribute{
|
||||
Description: "A string representation of the public SSH key. E.g., `ssh-rsa <key_data>` or `ssh-ed25519 <key-data>`.",
|
||||
Computed: true,
|
||||
},
|
||||
"fingerprint": schema.StringAttribute{
|
||||
Description: "The fingerprint of the public SSH key.",
|
||||
Computed: true,
|
||||
},
|
||||
"labels": schema.MapAttribute{
|
||||
Description: "Labels are key-value string pairs which can be attached to a resource container.",
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (r *keyPairDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
name := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
keypairResp, err := r.client.GetKeyPair(ctx, name).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading key pair", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, keypairResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading key pair", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Key pair read")
|
||||
}
|
||||
411
stackit/internal/services/iaas/keypair/resource.go
Normal file
411
stackit/internal/services/iaas/keypair/resource.go
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
package keypair
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"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/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
)
|
||||
|
||||
// resourceBetaCheckDone is used to prevent multiple checks for beta resources.
|
||||
// This is a workaround for the lack of a global state in the provider and
|
||||
// needs to exist because the Configure method is called twice.
|
||||
var resourceBetaCheckDone bool
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ resource.Resource = &keyPairResource{}
|
||||
_ resource.ResourceWithConfigure = &keyPairResource{}
|
||||
_ resource.ResourceWithImportState = &keyPairResource{}
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
Name types.String `tfsdk:"name"`
|
||||
PublicKey types.String `tfsdk:"public_key"`
|
||||
Fingerprint types.String `tfsdk:"fingerprint"`
|
||||
Labels types.Map `tfsdk:"labels"`
|
||||
}
|
||||
|
||||
// NewKeyPairResource is a helper function to simplify the provider implementation.
|
||||
func NewKeyPairResource() resource.Resource {
|
||||
return &keyPairResource{}
|
||||
}
|
||||
|
||||
// keyPairResource is the resource implementation.
|
||||
type keyPairResource struct {
|
||||
client *iaas.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the resource type name.
|
||||
func (r *keyPairResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_key_pair"
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the resource.
|
||||
func (r *keyPairResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
providerData, ok := req.ProviderData.(core.ProviderData)
|
||||
if !ok {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
|
||||
return
|
||||
}
|
||||
|
||||
if !resourceBetaCheckDone {
|
||||
features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_key_pair", "resource")
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
resourceBetaCheckDone = true
|
||||
}
|
||||
|
||||
var apiClient *iaas.APIClient
|
||||
var err error
|
||||
if providerData.IaaSCustomEndpoint != "" {
|
||||
ctx = tflog.SetField(ctx, "iaas_custom_endpoint", providerData.IaaSCustomEndpoint)
|
||||
apiClient, err = iaas.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithEndpoint(providerData.IaaSCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = iaas.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithRegion(providerData.Region),
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
|
||||
return
|
||||
}
|
||||
|
||||
r.client = apiClient
|
||||
tflog.Info(ctx, "iaas client configured")
|
||||
}
|
||||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *keyPairResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
description := "Key pair resource schema. Must have a `region` specified in the provider configuration. Allows uploading an SSH public key to be used for server authentication."
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: features.AddBetaDescription(description + "\n\n" + exampleUsageWithServer),
|
||||
Description: description,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: "Terraform's internal resource ID. It takes the value of the key pair \"`name`\".",
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: "The name of the SSH key pair.",
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"public_key": schema.StringAttribute{
|
||||
Description: "A string representation of the public SSH key. E.g., `ssh-rsa <key_data>` or `ssh-ed25519 <key-data>`.",
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"fingerprint": schema.StringAttribute{
|
||||
Description: "The fingerprint of the public SSH key.",
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"labels": schema.MapAttribute{
|
||||
Description: "Labels are key-value string pairs which can be attached to a resource container.",
|
||||
ElementType: types.StringType,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ModifyPlan will be called in the Plan phase.
|
||||
// It will check if the plan contains a change that requires replacement. If yes, it will show a warning to the user.
|
||||
func (r *keyPairResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// If the state is empty we are creating a new resource
|
||||
// If the plan is empty we are deleting the resource
|
||||
// In both cases we don't need to check for replacement
|
||||
if req.Plan.Raw.IsNull() || req.State.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
|
||||
var planModel Model
|
||||
diags := req.Plan.Get(ctx, &planModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
var stateModel Model
|
||||
diags = req.State.Get(ctx, &stateModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
if planModel.PublicKey.ValueString() != stateModel.PublicKey.ValueString() {
|
||||
core.LogAndAddWarning(ctx, &resp.Diagnostics, "Key pair public key change", "Changing the public key will trigger a replacement of the key pair resource. The new key pair will not be valid to access servers on which the old key was used, as the key is only registered during server creation.")
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates the resource and sets the initial Terraform state.
|
||||
func (r *keyPairResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
name := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toCreatePayload(ctx, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating key pair", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Create new key pair
|
||||
|
||||
keyPair, err := r.client.CreateKeyPair(ctx).CreateKeyPairPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating key pair", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, keyPair, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating key pair", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set state to fully populated data
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Key pair created")
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (r *keyPairResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
name := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
keyPairResp, err := r.client.GetKeyPair(ctx, name).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading key pair", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, keyPairResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading key pair", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Key pair read")
|
||||
}
|
||||
|
||||
// Update updates the resource and sets the updated Terraform state on success.
|
||||
func (r *keyPairResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
name := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
// Retrieve values from state
|
||||
var stateModel Model
|
||||
diags = req.State.Get(ctx, &stateModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toUpdatePayload(ctx, &model, stateModel.Labels)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating key pair", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Update existing key pair
|
||||
updatedKeyPair, err := r.client.UpdateKeyPair(ctx, name).UpdateKeyPairPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating key pair", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFields(ctx, updatedKeyPair, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating key pair", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "key pair updated")
|
||||
}
|
||||
|
||||
// Delete deletes the resource and removes the Terraform state on success.
|
||||
func (r *keyPairResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from state
|
||||
var model Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
name := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
// Delete existing key pair
|
||||
err := r.client.DeleteKeyPair(ctx, name).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting key pair", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "Key pair deleted")
|
||||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: project_id,key_pair_id
|
||||
func (r *keyPairResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 1 || idParts[0] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||
"Error importing key pair",
|
||||
fmt.Sprintf("Expected import identifier with format: [name] Got: %q", req.ID),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
name := idParts[0]
|
||||
ctx = tflog.SetField(ctx, "name", name)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), name)...)
|
||||
tflog.Info(ctx, "Key pair state imported")
|
||||
}
|
||||
|
||||
func mapFields(ctx context.Context, keyPairResp *iaas.Keypair, model *Model) error {
|
||||
if keyPairResp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var name string
|
||||
if model.Name.ValueString() != "" {
|
||||
name = model.Name.ValueString()
|
||||
} else if keyPairResp.Name != nil {
|
||||
name = *keyPairResp.Name
|
||||
} else {
|
||||
return fmt.Errorf("key pair name not present")
|
||||
}
|
||||
|
||||
model.Id = types.StringValue(name)
|
||||
model.PublicKey = types.StringPointerValue(keyPairResp.PublicKey)
|
||||
model.Fingerprint = types.StringPointerValue(keyPairResp.Fingerprint)
|
||||
|
||||
labels, diags := types.MapValueFrom(ctx, types.StringType, map[string]interface{}{})
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("converting labels to StringValue map: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if keyPairResp.Labels != nil && len(*keyPairResp.Labels) != 0 {
|
||||
var diags diag.Diagnostics
|
||||
labels, diags = types.MapValueFrom(ctx, types.StringType, *keyPairResp.Labels)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("converting labels to StringValue map: %w", core.DiagsToError(diags))
|
||||
}
|
||||
} else if model.Labels.IsNull() {
|
||||
labels = types.MapNull(types.StringType)
|
||||
}
|
||||
model.Labels = labels
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateKeyPairPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
labels, err := conversion.ToStringInterfaceMap(ctx, model.Labels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to Go map: %w", err)
|
||||
}
|
||||
|
||||
return &iaas.CreateKeyPairPayload{
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
PublicKey: conversion.StringValueToPointer(model.PublicKey),
|
||||
Labels: &labels,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toUpdatePayload(ctx context.Context, model *Model, currentLabels types.Map) (*iaas.UpdateKeyPairPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
labels, err := conversion.ToJSONMapPartialUpdatePayload(ctx, currentLabels, model.Labels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to Go map: %w", err)
|
||||
}
|
||||
|
||||
return &iaas.UpdateKeyPairPayload{
|
||||
Labels: &labels,
|
||||
}, nil
|
||||
}
|
||||
211
stackit/internal/services/iaas/keypair/resource_test.go
Normal file
211
stackit/internal/services/iaas/keypair/resource_test.go
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
package keypair
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
)
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
state Model
|
||||
input *iaas.Keypair
|
||||
expected Model
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_values",
|
||||
Model{
|
||||
Name: types.StringValue("name"),
|
||||
},
|
||||
&iaas.Keypair{
|
||||
Name: utils.Ptr("name"),
|
||||
},
|
||||
Model{
|
||||
Id: types.StringValue("name"),
|
||||
Name: types.StringValue("name"),
|
||||
PublicKey: types.StringNull(),
|
||||
Fingerprint: types.StringNull(),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"simple_values",
|
||||
Model{
|
||||
Name: types.StringValue("name"),
|
||||
},
|
||||
&iaas.Keypair{
|
||||
Name: utils.Ptr("name"),
|
||||
PublicKey: utils.Ptr("public_key"),
|
||||
Fingerprint: utils.Ptr("fingerprint"),
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
Model{
|
||||
Id: types.StringValue("name"),
|
||||
Name: types.StringValue("name"),
|
||||
PublicKey: types.StringValue("public_key"),
|
||||
Fingerprint: types.StringValue("fingerprint"),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"empty_labels",
|
||||
Model{
|
||||
Name: types.StringValue("name"),
|
||||
},
|
||||
&iaas.Keypair{
|
||||
Name: utils.Ptr("name"),
|
||||
PublicKey: utils.Ptr("public_key"),
|
||||
Fingerprint: utils.Ptr("fingerprint"),
|
||||
Labels: &map[string]interface{}{},
|
||||
},
|
||||
Model{
|
||||
Id: types.StringValue("name"),
|
||||
Name: types.StringValue("name"),
|
||||
PublicKey: types.StringValue("public_key"),
|
||||
Fingerprint: types.StringValue("fingerprint"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"response_nil_fail",
|
||||
Model{},
|
||||
nil,
|
||||
Model{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
Model{},
|
||||
&iaas.Keypair{
|
||||
PublicKey: utils.Ptr("public_key"),
|
||||
Fingerprint: utils.Ptr("fingerprint"),
|
||||
Labels: &map[string]interface{}{},
|
||||
},
|
||||
Model{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
err := mapFields(context.Background(), tt.input, &tt.state)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(tt.state, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCreatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
expected *iaas.CreateKeyPairPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_ok",
|
||||
&Model{
|
||||
Name: types.StringValue("name"),
|
||||
PublicKey: types.StringValue("public_key"),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key1": types.StringValue("value1"),
|
||||
"key2": types.StringValue("value2"),
|
||||
}),
|
||||
},
|
||||
&iaas.CreateKeyPairPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
PublicKey: utils.Ptr("public_key"),
|
||||
Labels: &map[string]interface{}{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
output, err := toCreatePayload(context.Background(), tt.input)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(output, tt.expected, cmp.AllowUnexported(iaas.NullableString{}))
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToUpdatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
expected *iaas.UpdateKeyPairPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_ok",
|
||||
&Model{
|
||||
Name: types.StringValue("name"),
|
||||
PublicKey: types.StringValue("public_key"),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key1": types.StringValue("value1"),
|
||||
"key2": types.StringValue("value2"),
|
||||
}),
|
||||
},
|
||||
&iaas.UpdateKeyPairPayload{
|
||||
Labels: &map[string]interface{}{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
output, err := toUpdatePayload(context.Background(), tt.input, types.MapNull(types.StringType))
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(output, tt.expected, cmp.AllowUnexported(iaas.NullableString{}))
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -181,7 +181,7 @@ func (d *networkInterfaceDataSource) Read(ctx context.Context, req datasource.Re
|
|||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
ctx = tflog.SetField(ctx, "network_interface_id", networkInterfaceId)
|
||||
|
||||
networkInterfaceResp, err := d.client.GetNIC(ctx, projectId, networkId, networkInterfaceId).Execute()
|
||||
networkInterfaceResp, err := d.client.GetNic(ctx, projectId, networkId, networkInterfaceId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ func (r *networkInterfaceResource) Create(ctx context.Context, req resource.Crea
|
|||
}
|
||||
|
||||
// Create new network interface
|
||||
networkInterface, err := r.client.CreateNIC(ctx, projectId, networkId).CreateNICPayload(*payload).Execute()
|
||||
networkInterface, err := r.client.CreateNic(ctx, projectId, networkId).CreateNicPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network interface", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
|
|
@ -311,7 +311,7 @@ func (r *networkInterfaceResource) Read(ctx context.Context, req resource.ReadRe
|
|||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
ctx = tflog.SetField(ctx, "network_interface_id", networkInterfaceId)
|
||||
|
||||
networkInterfaceResp, err := r.client.GetNIC(ctx, projectId, networkId, networkInterfaceId).Execute()
|
||||
networkInterfaceResp, err := r.client.GetNic(ctx, projectId, networkId, networkInterfaceId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
|
|
@ -368,7 +368,7 @@ func (r *networkInterfaceResource) Update(ctx context.Context, req resource.Upda
|
|||
return
|
||||
}
|
||||
// Update existing network
|
||||
nicResp, err := r.client.UpdateNIC(ctx, projectId, networkId, networkInterfaceId).UpdateNICPayload(*payload).Execute()
|
||||
nicResp, err := r.client.UpdateNic(ctx, projectId, networkId, networkInterfaceId).UpdateNicPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network interface", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
|
|
@ -405,7 +405,7 @@ func (r *networkInterfaceResource) Delete(ctx context.Context, req resource.Dele
|
|||
ctx = tflog.SetField(ctx, "network_interface_id", networkInterfaceId)
|
||||
|
||||
// Delete existing network interface
|
||||
err := r.client.DeleteNIC(ctx, projectId, networkId, networkInterfaceId).Execute()
|
||||
err := r.client.DeleteNic(ctx, projectId, networkId, networkInterfaceId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network interface", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
|
|
@ -544,7 +544,7 @@ func mapFields(ctx context.Context, networkInterfaceResp *iaas.NIC, model *Model
|
|||
return nil
|
||||
}
|
||||
|
||||
func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateNICPayload, error) {
|
||||
func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateNicPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
|
@ -586,7 +586,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateNICPayload,
|
|||
labelPayload = &labelMap
|
||||
}
|
||||
|
||||
return &iaas.CreateNICPayload{
|
||||
return &iaas.CreateNicPayload{
|
||||
AllowedAddresses: allowedAddressesPayload,
|
||||
SecurityGroups: &modelSecurityGroups,
|
||||
Labels: labelPayload,
|
||||
|
|
@ -599,7 +599,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateNICPayload,
|
|||
}, nil
|
||||
}
|
||||
|
||||
func toUpdatePayload(ctx context.Context, model *Model, currentLabels types.Map) (*iaas.UpdateNICPayload, error) {
|
||||
func toUpdatePayload(ctx context.Context, model *Model, currentLabels types.Map) (*iaas.UpdateNicPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
|
@ -637,7 +637,7 @@ func toUpdatePayload(ctx context.Context, model *Model, currentLabels types.Map)
|
|||
labelPayload = &labelMap
|
||||
}
|
||||
|
||||
return &iaas.UpdateNICPayload{
|
||||
return &iaas.UpdateNicPayload{
|
||||
AllowedAddresses: &allowedAddressesPayload,
|
||||
SecurityGroups: &modelSecurityGroups,
|
||||
Labels: labelPayload,
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ func TestToCreatePayload(t *testing.T) {
|
|||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
expected *iaas.CreateNICPayload
|
||||
expected *iaas.CreateNicPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
|
|
@ -210,7 +210,7 @@ func TestToCreatePayload(t *testing.T) {
|
|||
}),
|
||||
Security: types.BoolValue(true),
|
||||
},
|
||||
&iaas.CreateNICPayload{
|
||||
&iaas.CreateNicPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
SecurityGroups: &[]string{
|
||||
"sg1",
|
||||
|
|
@ -236,7 +236,7 @@ func TestToCreatePayload(t *testing.T) {
|
|||
|
||||
AllowedAddresses: types.ListNull(types.StringType),
|
||||
},
|
||||
&iaas.CreateNICPayload{
|
||||
&iaas.CreateNicPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
SecurityGroups: &[]string{
|
||||
"sg1",
|
||||
|
|
@ -270,7 +270,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
expected *iaas.UpdateNICPayload
|
||||
expected *iaas.UpdateNicPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
|
|
@ -286,7 +286,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
}),
|
||||
Security: types.BoolValue(true),
|
||||
},
|
||||
&iaas.UpdateNICPayload{
|
||||
&iaas.UpdateNicPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
SecurityGroups: &[]string{
|
||||
"sg1",
|
||||
|
|
@ -312,7 +312,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
|
||||
AllowedAddresses: types.ListNull(types.StringType),
|
||||
},
|
||||
&iaas.UpdateNICPayload{
|
||||
&iaas.UpdateNicPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
SecurityGroups: &[]string{
|
||||
"sg1",
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ func (r *networkInterfaceAttachResource) Create(ctx context.Context, req resourc
|
|||
ctx = tflog.SetField(ctx, "network_interface_id", networkInterfaceId)
|
||||
|
||||
// Create new network interface attachment
|
||||
err := r.client.AddNICToServer(ctx, projectId, serverId, networkInterfaceId).Execute()
|
||||
err := r.client.AddNicToServer(ctx, projectId, serverId, networkInterfaceId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error attaching network interface to server", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
|
|
@ -208,7 +208,7 @@ func (r *networkInterfaceAttachResource) Read(ctx context.Context, req resource.
|
|||
networkInterfaceId := model.NetworkInterfaceId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "network_interface_id", networkInterfaceId)
|
||||
|
||||
nics, err := r.client.ListServerNICs(ctx, projectId, serverId).Execute()
|
||||
nics, err := r.client.ListServerNics(ctx, projectId, serverId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
|
|
@ -267,7 +267,7 @@ func (r *networkInterfaceAttachResource) Delete(ctx context.Context, req resourc
|
|||
ctx = tflog.SetField(ctx, "network_interface_id", network_interfaceId)
|
||||
|
||||
// Remove network_interface from server
|
||||
err := r.client.RemoveNICFromServer(ctx, projectId, serverId, network_interfaceId).Execute()
|
||||
err := r.client.RemoveNicFromServer(ctx, projectId, serverId, network_interfaceId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error removing network interface from server", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ func (d *publicIpDataSource) Configure(ctx context.Context, req datasource.Confi
|
|||
// Schema defines the schema for the resource.
|
||||
func (r *publicIpDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: features.AddBetaDescription("Volume resource schema. Must have a `region` specified in the provider configuration."),
|
||||
Description: "Volume resource schema. Must have a `region` specified in the provider configuration.",
|
||||
MarkdownDescription: features.AddBetaDescription("Public IP resource schema. Must have a `region` specified in the provider configuration."),
|
||||
Description: "Public IP resource schema. Must have a `region` specified in the provider configuration.",
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: "Terraform's internal datasource ID. It is structured as \"`project_id`,`public_ip_id`\".",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,28 @@ Server resource schema. Must have a region specified in the provider configurati
|
|||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources.
|
||||
## Example Usage` + "\n" + `
|
||||
|
||||
### With key pair` + "\n" +
|
||||
|
||||
"```terraform" + `
|
||||
resource "stackit_key_pair" "keypair" {
|
||||
name = "example-key-pair"
|
||||
public_key = chomp(file("path/to/id_rsa.pub"))
|
||||
}
|
||||
|
||||
resource "stackit_server" "user-data-from-file" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
boot_volume = {
|
||||
size = 64
|
||||
source_type = "image"
|
||||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
name = "example-server"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
user_data = file("${path.module}/cloud-init.yaml")
|
||||
}
|
||||
` + "\n```" + `
|
||||
|
||||
### Boot from volume` + "\n" +
|
||||
|
||||
"```terraform" + `
|
||||
|
|
@ -45,7 +67,7 @@ resource "stackit_server" "boot-from-volume" {
|
|||
}
|
||||
availability_zone = "eu01-1"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
}
|
||||
` + "\n```" + `
|
||||
|
||||
|
|
@ -61,7 +83,7 @@ resource "stackit_server" "server-with-network" {
|
|||
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
}
|
||||
|
||||
resource "stackit_network" "network" {
|
||||
|
|
@ -123,7 +145,7 @@ resource "stackit_server" "server-with-volume" {
|
|||
}
|
||||
availability_zone = "eu01-1"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
}
|
||||
|
||||
resource "stackit_server_volume_attach" "attach_volume" {
|
||||
|
|
@ -145,7 +167,7 @@ resource "stackit_server" "user-data" {
|
|||
}
|
||||
name = "example-server"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
user_data = "#!/bin/bash\n/bin/su"
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +180,7 @@ resource "stackit_server" "user-data-from-file" {
|
|||
}
|
||||
name = "example-server"
|
||||
machine_type = "g1.1"
|
||||
keypair_name = "example-keypair"
|
||||
keypair_name = stackit_key_pair.keypair.name
|
||||
user_data = file("${path.module}/cloud-init.yaml")
|
||||
}
|
||||
` + "\n```"
|
||||
|
|
|
|||
|
|
@ -563,7 +563,7 @@ func mapFields(ctx context.Context, serverResp *iaas.Server, model *Model) error
|
|||
} else if serverResp.Id != nil {
|
||||
serverId = *serverResp.Id
|
||||
} else {
|
||||
return fmt.Errorf("Server id not present")
|
||||
return fmt.Errorf("server id not present")
|
||||
}
|
||||
|
||||
idParts := []string{
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
argusScrapeConfig "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/argus/scrapeconfig"
|
||||
dnsRecordSet "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/recordset"
|
||||
dnsZone "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/zone"
|
||||
iaasKeyPair "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/keypair"
|
||||
iaasNetwork "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network"
|
||||
iaasNetworkArea "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/networkarea"
|
||||
iaasNetworkAreaRoute "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/networkarearoute"
|
||||
|
|
@ -412,6 +413,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
|
|||
iaasNetworkInterface.NewNetworkInterfaceDataSource,
|
||||
iaasVolume.NewVolumeDataSource,
|
||||
iaasPublicIp.NewPublicIpDataSource,
|
||||
iaasKeyPair.NewKeyPairDataSource,
|
||||
iaasServer.NewServerDataSource,
|
||||
iaasSecurityGroup.NewSecurityGroupDataSource,
|
||||
iaasSecurityGroupRule.NewSecurityGroupRuleDataSource,
|
||||
|
|
@ -462,6 +464,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
|
|||
iaasNetworkInterface.NewNetworkInterfaceResource,
|
||||
iaasVolume.NewVolumeResource,
|
||||
iaasPublicIp.NewPublicIpResource,
|
||||
iaasKeyPair.NewKeyPairResource,
|
||||
iaasVolumeAttach.NewVolumeAttachResource,
|
||||
iaasNetworkInterfaceAttach.NewNetworkInterfaceAttachResource,
|
||||
iaasServiceAccountAttach.NewServiceAccountAttachResource,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue