Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Snapshot Replication #141

Merged
merged 2 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
with:
# Allow goreleaser to access older tag information.
fetch-depth: 0
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
with:
go-version-file: "go.mod"
cache: true
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
with:
go-version-file: "go.mod"
cache: true
Expand All @@ -38,10 +38,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
with:
go-version-file: "go.mod"
cache: true
- uses: hashicorp/setup-terraform@v3
- run: go generate ./...
- name: git diff
run: |
Expand Down
4 changes: 2 additions & 2 deletions docs/data-sources/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ data "genesiscloud_images" "preconfigured-images" {
Required:

- `type` (String) Filter by the kind of image.
- The value must be one of: ["base-os" "cloud-image" "preconfigured" "snapshot"].
- The value must be one of: ["cloud-image"].

Optional:

- `region` (String) Filter by the region identifier.
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].


<a id="nestedatt--timeouts"></a>
Expand Down
2 changes: 1 addition & 1 deletion docs/resources/filesystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ resource "genesiscloud_filesystem" "example" {
- `name` (String) The human-readable name for the filesystem.
- `region` (String) The identifier for the region this filesystem exists in.
- If the value of this attribute changes, the resource will be replaced.
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- `size` (Number) The storage size of this filesystem given in GiB.
- The value must be at least 1.
- `type` (String) The storage type of the filesystem.
Expand Down
2 changes: 1 addition & 1 deletion docs/resources/floating_ip.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ resource "genesiscloud_floating_ip" "floating_ip" {
- `name` (String) The human-readable name for the floating IP.
- `region` (String) The region identifier.
- If the value of this attribute changes, the resource will be replaced.
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- `version` (String) The version of the floating IP.
- If the value of this attribute changes, the resource will be replaced.
- The value must be one of: ["ipv4"].
Expand Down
2 changes: 1 addition & 1 deletion docs/resources/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ resource "genesiscloud_instance" "example" {
- `name` (String) The human-readable name for the instance.
- `region` (String) The region identifier.
- If the value of this attribute changes, the resource will be replaced.
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- `type` (String) The instance type identifier. Learn more about instance types [here](https://developers.genesiscloud.com/instances#instance-types).
- If the value of this attribute changes, the resource will be replaced.

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/security_group.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ resource "genesiscloud_security_group" "allow-https" {
- `name` (String) The human-readable name for the security group.
- `region` (String) The region identifier.
- If the value of this attribute changes, the resource will be replaced.
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- `rules` (Attributes List) (see [below for nested schema](#nestedatt--rules))

### Optional
Expand Down
9 changes: 6 additions & 3 deletions docs/resources/snapshot.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,24 @@ resource "genesiscloud_snapshot" "example" {

### Required

- `instance_id` (String) The id of the instance to snapshot.
- If the value of this attribute changes, the resource will be replaced.
- `name` (String) The human-readable name for the snapshot.

### Optional

- `region` (String) The region identifier. Should only be explicity specified when using the 'source_snapshot_id'.
- `replicated_region` (String) Target region for snapshot replication. When specified, also creates a copy of the snapshot in the given region. If omitted, the snapshot exists only in the current region.
- `retain_on_delete` (Boolean) Flag to retain the snapshot when the resource is deleted.
- Sets the default value "false" if the attribute is not set.
- `source_instance_id` (String) The id of the source instance from which this snapshot was derived.
- If the value of this attribute changes, the resource will be replaced.
- `source_snapshot_id` (String) The id of the source snapshot from which this snapsot was derived.
- If the value of this attribute changes, the resource will be replaced.
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))

### Read-Only

- `created_at` (String) The timestamp when this snapshot was created in RFC 3339.
- `id` (String) The unique ID of the snapshot.
- `region` (String) The region identifier.
- `size` (Number) The storage size of this snapshot given in GiB.
- `status` (String) The snapshot status.

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/volume.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ resource "genesiscloud_volume" "example" {
- `name` (String) The human-readable name for the volume.
- `region` (String) The region identifier.
- If the value of this attribute changes, the resource will be replaced.
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
- `size` (Number) The storage size of this volume given in GiB.
- The value must be at least 1.
- `type` (String) The storage type of the volume.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22.0
toolchain go1.23.1

require (
github.com/genesiscloud/genesiscloud-go v1.0.11
github.com/genesiscloud/genesiscloud-go v1.0.14
github.com/hashicorp/go-retryablehttp v0.7.7
github.com/hashicorp/terraform-plugin-docs v0.19.4
github.com/hashicorp/terraform-plugin-framework v1.13.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ 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.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/genesiscloud/genesiscloud-go v1.0.11 h1:SjhBrZ2YXvWspQbfl48lUR+otqXS30QF97fO4TMJXjI=
github.com/genesiscloud/genesiscloud-go v1.0.11/go.mod h1:t2m8sfDGOa8pFio3oitkkjW9YcajyxkiWEu+BHOLyes=
github.com/genesiscloud/genesiscloud-go v1.0.14 h1:ydDXnjBaKj80UmUMz5YCbPNjVTR/pq4hiVHDGDJ0Mh4=
github.com/genesiscloud/genesiscloud-go v1.0.14/go.mod h1:t2m8sfDGOa8pFio3oitkkjW9YcajyxkiWEu+BHOLyes=
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=
Expand Down
2 changes: 1 addition & 1 deletion internal/provider/images_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (d *ImagesDataSource) Read(ctx context.Context, req datasource.ReadRequest,
}

for page := 1; ; page++ {
response, err := d.client.ListImagesWithResponse(ctx, &genesiscloud.ListImagesParams{
response, err := d.client.ListImagesPaginatedWithResponse(ctx, &genesiscloud.ListImagesPaginatedParams{
Page: pointer(page),
PerPage: pointer(100),
Type: filterType,
Expand Down
126 changes: 106 additions & 20 deletions internal/provider/snapshot_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,29 @@ func (r *SnapshotResource) Schema(ctx context.Context, req resource.SchemaReques
MarkdownDescription: "The human-readable name for the snapshot.",
Required: true,
}),
"replicated_region": resourceenhancer.Attribute(ctx, schema.StringAttribute{
MarkdownDescription: "Target region for snapshot replication. When specified, also creates a copy of the snapshot in the given region. If omitted, the snapshot exists only in the current region.",
Required: false,
Optional: true,
}),
"region": resourceenhancer.Attribute(ctx, schema.StringAttribute{
MarkdownDescription: "The region identifier.",
MarkdownDescription: "The region identifier. Should only be explicity specified when using the 'source_snapshot_id'.",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(), // immutable
stringplanmodifier.UseStateForUnknown(),
},
}),
"instance_id": resourceenhancer.Attribute(ctx, schema.StringAttribute{
MarkdownDescription: "The id of the instance to snapshot.",
Required: true,
"source_instance_id": resourceenhancer.Attribute(ctx, schema.StringAttribute{
MarkdownDescription: "The id of the source instance from which this snapshot was derived.",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
}),
"source_snapshot_id": resourceenhancer.Attribute(ctx, schema.StringAttribute{
MarkdownDescription: "The id of the source snapshot from which this snapsot was derived.",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
Expand Down Expand Up @@ -122,27 +135,100 @@ func (r *SnapshotResource) Create(ctx context.Context, req resource.CreateReques
}
defer cancel()

body := genesiscloud.CreateInstanceSnapshotJSONRequestBody{}
body.Name = data.Name.ValueString()

instanceId := data.InstanceId.ValueString()

response, err := r.client.CreateInstanceSnapshotWithResponse(ctx, instanceId, body)
if err != nil {
resp.Diagnostics.AddError("Client Error", generateErrorMessage("create snapshot", err))
if data.SourceInstanceId.IsNull() && data.SourceSnapshotId.IsNull() {
resp.Diagnostics.AddError(
"Invalid Configuration",
"Either 'source_instance_id' or 'source_snapshot_id' must be specified.",
)
return
}

snapshotResponse := response.JSON201
if snapshotResponse == nil {
resp.Diagnostics.AddError("Client Error", generateClientErrorMessage("create snapshot", ErrorResponse{
Body: response.Body,
HTTPResponse: response.HTTPResponse,
Error: response.JSONDefault,
}))
if !data.SourceInstanceId.IsNull() && !data.SourceSnapshotId.IsNull() {
resp.Diagnostics.AddError(
"Invalid Configuration",
"Can only specify either 'source_instance_id' or 'source_snapshot_id', not both at the same time.",
)
return
}

var snapshotResponse *genesiscloud.SingleSnapshotResponse

if !data.SourceInstanceId.IsNull() {
if data.Region.ValueString() != "" {
resp.Diagnostics.AddError(
"Invalid Configuration",
"When specifying a source instance, the 'region' specification won't take effect",
)
return

}
body := genesiscloud.CreateInstanceSnapshotJSONRequestBody{}

body.Name = data.Name.ValueString()

instanceId := data.SourceInstanceId.ValueString()

if !data.ReplicatedRegion.IsNull() {
body.ReplicatedRegion = pointer(genesiscloud.Region(data.ReplicatedRegion.ValueString()))
}

response, err := r.client.CreateInstanceSnapshotWithResponse(ctx, instanceId, body)
if err != nil {
resp.Diagnostics.AddError("Client Error", generateErrorMessage("create snapshot", err))
return
}

snapshotResponse = response.JSON201
if snapshotResponse == nil {
resp.Diagnostics.AddError("Client Error", generateClientErrorMessage("create snapshot", ErrorResponse{
Body: response.Body,
HTTPResponse: response.HTTPResponse,
Error: response.JSONDefault,
}))
return
}
} else if !data.SourceSnapshotId.IsNull() {
if data.Region.IsNull() {
resp.Diagnostics.AddError(
"Invalid Configuration",
"When specifying `source_snapshot_id` as the snapshot source, you must be specified the `region`.",
)
return
}

if !data.ReplicatedRegion.IsNull() {
resp.Diagnostics.AddError(
"Invalid Configuration",
"When specifying `source_snapshot_id` as the snapshot source, you cannot specify the `replicated_region`.",
)
return
}

body := genesiscloud.CloneSnapshotJSONRequestBody{}

body.Name = data.Name.ValueString()

body.Region = genesiscloud.Region(data.Region.ValueString())

snapshotId := data.SourceSnapshotId.ValueString()

response, err := r.client.CloneSnapshotWithResponse(ctx, snapshotId, body)
if err != nil {
resp.Diagnostics.AddError("Client Error", generateErrorMessage("clone snapshot", err))
return
}

snapshotResponse = response.JSON201
if snapshotResponse == nil {
resp.Diagnostics.AddError("Client Error", generateClientErrorMessage("clone snapshot", ErrorResponse{
Body: response.Body,
HTTPResponse: response.HTTPResponse,
Error: response.JSONDefault,
}))
return
}
}

resp.Diagnostics.Append(data.PopulateFromClientResponse(ctx, &snapshotResponse.Snapshot)...)
if resp.Diagnostics.HasError() {
return
Expand Down
18 changes: 15 additions & 3 deletions internal/provider/snapshot_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ type SnapshotResourceModel struct {
// Region The region identifier.
Region types.String `tfsdk:"region"`

// InstanceId The id of the instance that was snapshotted.
InstanceId types.String `tfsdk:"instance_id"`
// SourceInstanceId The id of the source instance from which this snapshot was derived.
SourceInstanceId types.String `tfsdk:"source_instance_id"`

// SourceSnapshotId The id of the source snapshot from which this snapsot was derived.
SourceSnapshotId types.String `tfsdk:"source_snapshot_id"`

// ReplicatedRegion The region identifier when the snapshot should be replicated.
ReplicatedRegion types.String `tfsdk:"replicated_region"`

// Size The storage size of this snapshot given in GiB.
Size types.Int64 `tfsdk:"size"`
Expand All @@ -45,9 +51,15 @@ func (data *SnapshotResourceModel) PopulateFromClientResponse(ctx context.Contex
data.Id = types.StringValue(snapshot.Id)
data.Name = types.StringValue(snapshot.Name)
data.Region = types.StringValue(string(snapshot.Region))
data.InstanceId = types.StringValue(string(snapshot.ResourceId))
data.Size = types.Int64Value(int64(snapshot.Size))
data.Status = types.StringValue(string(snapshot.Status))

if snapshot.SourceInstanceId != nil {
data.SourceInstanceId = types.StringValue(*snapshot.SourceInstanceId)
}
if snapshot.SourceSnapshotId != nil {
data.SourceSnapshotId = types.StringValue(*snapshot.SourceSnapshotId)
}

return
}
Loading