-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
network_integration: add network_integration resource
Signed-off-by: Jonatas Ferreira <[email protected]>
- Loading branch information
1 parent
5118735
commit 89a73a2
Showing
4 changed files
with
504 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# incus_network_integration | ||
|
||
Manage integrations between the local Incus deployment and remote networks hosted on Incus or other platforms. Currently available only for [OVN networks](https://linuxcontainers.org/incus/docs/main/reference/network_ovn/#network-ovn). | ||
|
||
## Basic Example | ||
|
||
```hcl | ||
resource "incus_network_integration" "this" { | ||
name = "ovn-region" | ||
config = { | ||
"ovn.northbound_connection" = "tcp:[192.0.2.12]:6645,tcp:[192.0.3.13]:6645,tcp:[192.0.3.14]:6645" | ||
"ovn.southbound_connection" = "tcp:[192.0.2.12]:6646,tcp:[192.0.3.13]:6646,tcp:[192.0.3.14]:6646" | ||
} | ||
} | ||
``` | ||
|
||
## Peer Example | ||
|
||
```hcl | ||
resource "incus_network" "default" { | ||
name = "default" | ||
type = "ovn" | ||
config = { | ||
"ipv4.address" = "192.168.2.0/24" | ||
"ipv4.nat" = "true" | ||
} | ||
} | ||
resource "incus_network_integration" "this" { | ||
name = "ovn-region" | ||
config = { | ||
"ovn.northbound_connection" = "tcp:[192.0.2.12]:6645,tcp:[192.0.3.13]:6645,tcp:[192.0.3.14]:6645" | ||
"ovn.southbound_connection" = "tcp:[192.0.2.12]:6646,tcp:[192.0.3.13]:6646,tcp:[192.0.3.14]:6646" | ||
} | ||
} | ||
resource "incus_network_peer" "this" { | ||
name = "ovn-peer" | ||
network = incus_network.default.name | ||
target_integration = incus_network_integration.this.name | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
* `name` - **Required** - Name of the network integration. | ||
|
||
* `description` *Optional* - Description of the network integration. | ||
|
||
* `type` *Optional* - The type of the network integration. Currently, only supports **ovn** type. If empty, an OVN integration will be created. | ||
|
||
* `project` - *Optional* - Name of the project where the network will be created. | ||
|
||
* `remote` - *Optional* - The remote in which the resource will be created. If | ||
not provided, the provider's default remote will be used. | ||
|
||
* `config` - *Optional* - Map of key/value pairs of [network integration config settings](https://linuxcontainers.org/incus/docs/main/howto/network_integrations/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
package network | ||
|
||
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/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapdefault" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" | ||
"github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
"github.com/hashicorp/terraform-plugin-framework/tfsdk" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
incus "github.com/lxc/incus/v6/client" | ||
"github.com/lxc/incus/v6/shared/api" | ||
"github.com/lxc/terraform-provider-incus/internal/common" | ||
"github.com/lxc/terraform-provider-incus/internal/errors" | ||
provider_config "github.com/lxc/terraform-provider-incus/internal/provider-config" | ||
) | ||
|
||
// NetworkIntegrationModel resource data model that matches the schema. | ||
type NetworkIntegrationModel struct { | ||
Name types.String `tfsdk:"name"` | ||
Type types.String `tfsdk:"type"` | ||
Description types.String `tfsdk:"description"` | ||
Project types.String `tfsdk:"project"` | ||
Remote types.String `tfsdk:"remote"` | ||
Config types.Map `tfsdk:"config"` | ||
} | ||
|
||
// NetworkIntegrationResource represent network integration resource. | ||
type NetworkIntegrationResource struct { | ||
provider *provider_config.IncusProviderConfig | ||
} | ||
|
||
// NewNetworkIntegrationResource returns a new network integration resource. | ||
func NewNetworkIntegrationResource() resource.Resource { | ||
return &NetworkIntegrationResource{} | ||
} | ||
|
||
// Metadata for NetworkIntegrationResource. | ||
func (r NetworkIntegrationResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { | ||
resp.TypeName = fmt.Sprintf("%s_network_integration", req.ProviderTypeName) | ||
} | ||
|
||
// Schema for NetworkIntegrationResource. | ||
func (r NetworkIntegrationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { | ||
resp.Schema = schema.Schema{ | ||
Attributes: map[string]schema.Attribute{ | ||
"name": schema.StringAttribute{ | ||
Required: true, | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
|
||
"description": schema.StringAttribute{ | ||
Optional: true, | ||
Computed: true, | ||
Default: stringdefault.StaticString(""), | ||
}, | ||
|
||
"remote": schema.StringAttribute{ | ||
Optional: true, | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
|
||
"project": schema.StringAttribute{ | ||
Optional: true, | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
Validators: []validator.String{ | ||
stringvalidator.LengthAtLeast(1), | ||
}, | ||
}, | ||
|
||
"type": schema.StringAttribute{ | ||
Optional: true, | ||
Computed: true, | ||
Default: stringdefault.StaticString("ovn"), | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
Validators: []validator.String{ | ||
stringvalidator.OneOf("ovn"), | ||
}, | ||
}, | ||
|
||
"config": schema.MapAttribute{ | ||
Optional: true, | ||
Computed: true, | ||
ElementType: types.StringType, | ||
Default: mapdefault.StaticValue(types.MapValueMust(types.StringType, map[string]attr.Value{})), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func (r *NetworkIntegrationResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { | ||
data := req.ProviderData | ||
if data == nil { | ||
return | ||
} | ||
|
||
provider, ok := data.(*provider_config.IncusProviderConfig) | ||
if !ok { | ||
resp.Diagnostics.Append(errors.NewProviderDataTypeError(req.ProviderData)) | ||
} | ||
|
||
r.provider = provider | ||
} | ||
|
||
func (r NetworkIntegrationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { | ||
var plan NetworkIntegrationModel | ||
|
||
diags := req.Plan.Get(ctx, &plan) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
remote := plan.Remote.ValueString() | ||
project := plan.Project.ValueString() | ||
server, err := r.provider.InstanceServer(remote, project, "") | ||
if err != nil { | ||
resp.Diagnostics.Append(errors.NewInstanceServerError(err)) | ||
return | ||
} | ||
|
||
config, diag := common.ToConfigMap(ctx, plan.Config) | ||
resp.Diagnostics.Append(diag...) | ||
|
||
networkIntegration := api.NetworkIntegrationsPost{ | ||
Name: plan.Name.ValueString(), | ||
Type: plan.Type.ValueString(), | ||
NetworkIntegrationPut: api.NetworkIntegrationPut{ | ||
Description: plan.Description.ValueString(), | ||
Config: config, | ||
}, | ||
} | ||
|
||
err = server.CreateNetworkIntegration(networkIntegration) | ||
if err != nil { | ||
resp.Diagnostics.AddError(fmt.Sprintf("Failed to create network integration %q", networkIntegration.Name), err.Error()) | ||
return | ||
} | ||
|
||
diags = r.SyncState(ctx, &resp.State, server, plan) | ||
resp.Diagnostics.Append(diags...) | ||
} | ||
|
||
func (r NetworkIntegrationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { | ||
var state NetworkIntegrationModel | ||
|
||
diags := req.State.Get(ctx, &state) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
remote := state.Remote.ValueString() | ||
project := state.Project.ValueString() | ||
server, err := r.provider.InstanceServer(remote, project, "") | ||
if err != nil { | ||
resp.Diagnostics.Append(errors.NewInstanceServerError(err)) | ||
} | ||
|
||
diags = r.SyncState(ctx, &resp.State, server, state) | ||
resp.Diagnostics.Append(diags...) | ||
} | ||
|
||
func (r NetworkIntegrationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
var plan NetworkIntegrationModel | ||
|
||
diags := req.Plan.Get(ctx, &plan) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
remote := plan.Remote.ValueString() | ||
project := plan.Project.ValueString() | ||
server, err := r.provider.InstanceServer(remote, project, "") | ||
if err != nil { | ||
resp.Diagnostics.Append(errors.NewInstanceServerError(err)) | ||
return | ||
} | ||
|
||
name := plan.Name.ValueString() | ||
|
||
config, diag := common.ToConfigMap(ctx, plan.Config) | ||
resp.Diagnostics.Append(diag...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
_, etag, err := server.GetNetworkIntegration(name) | ||
if err != nil { | ||
resp.Diagnostics.AddError(fmt.Sprintf("Failed to retrieve existing network integration %q", name), err.Error()) | ||
return | ||
} | ||
|
||
networkIntegrationRequest := api.NetworkIntegrationPut{ | ||
Description: plan.Description.ValueString(), | ||
Config: config, | ||
} | ||
|
||
err = server.UpdateNetworkIntegration(name, networkIntegrationRequest, etag) | ||
if err != nil { | ||
resp.Diagnostics.AddError(fmt.Sprintf("Failed to update network integration %q", name), err.Error()) | ||
} | ||
|
||
diags = r.SyncState(ctx, &resp.State, server, plan) | ||
resp.Diagnostics.Append(diags...) | ||
} | ||
|
||
func (r NetworkIntegrationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { | ||
var state NetworkIntegrationModel | ||
|
||
diags := req.State.Get(ctx, &state) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
remote := state.Remote.ValueString() | ||
project := state.Project.ValueString() | ||
server, err := r.provider.InstanceServer(remote, project, "") | ||
if err != nil { | ||
resp.Diagnostics.Append(errors.NewInstanceServerError(err)) | ||
return | ||
} | ||
|
||
name := state.Name.ValueString() | ||
|
||
err = server.DeleteNetworkIntegration(name) | ||
if err != nil { | ||
resp.Diagnostics.AddError(fmt.Sprintf("Failed to remove network integration %q", name), err.Error()) | ||
} | ||
} | ||
|
||
// SyncState fetches the server's current state for a network integration and updates | ||
// the provided model. It then applies this updated model as the new state | ||
// in Terraform. | ||
func (r NetworkIntegrationResource) SyncState(ctx context.Context, tfState *tfsdk.State, server incus.InstanceServer, m NetworkIntegrationModel) diag.Diagnostics { | ||
var respDiags diag.Diagnostics | ||
|
||
networkIntegrationName := m.Name.ValueString() | ||
networkIntegration, _, err := server.GetNetworkIntegration(networkIntegrationName) | ||
if err != nil { | ||
if errors.IsNotFoundError(err) { | ||
tfState.RemoveResource(ctx) | ||
return nil | ||
} | ||
|
||
respDiags.AddError(fmt.Sprintf("Failed to retrieve network integration %q", networkIntegrationName), err.Error()) | ||
return respDiags | ||
} | ||
|
||
config, diags := common.ToConfigMapType(ctx, common.ToNullableConfig(networkIntegration.Config), m.Config) | ||
respDiags.Append(diags...) | ||
|
||
m.Description = types.StringValue(networkIntegration.Description) | ||
m.Type = types.StringValue(networkIntegration.Type) | ||
m.Config = config | ||
|
||
if respDiags.HasError() { | ||
return respDiags | ||
} | ||
|
||
return tfState.Set(ctx, &m) | ||
} |
Oops, something went wrong.