Skip to content

Commit f89de9c

Browse files
committed
integrate snapshot replication
1 parent ba53047 commit f89de9c

14 files changed

+128
-41
lines changed

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
with:
2222
# Allow goreleaser to access older tag information.
2323
fetch-depth: 0
24-
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
24+
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
2525
with:
2626
go-version-file: "go.mod"
2727
cache: true

.github/workflows/test.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
timeout-minutes: 5
2424
steps:
2525
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
26-
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
26+
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
2727
with:
2828
go-version-file: "go.mod"
2929
cache: true
@@ -38,7 +38,7 @@ jobs:
3838
runs-on: ubuntu-latest
3939
steps:
4040
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
41-
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
41+
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
4242
with:
4343
go-version-file: "go.mod"
4444
cache: true

docs/data-sources/images.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ data "genesiscloud_images" "preconfigured-images" {
5555
Required:
5656

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

6060
Optional:
6161

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

6565

6666
<a id="nestedatt--timeouts"></a>

docs/resources/filesystem.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ resource "genesiscloud_filesystem" "example" {
3030
- `name` (String) The human-readable name for the filesystem.
3131
- `region` (String) The identifier for the region this filesystem exists in.
3232
- If the value of this attribute changes, the resource will be replaced.
33-
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
33+
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
3434
- `size` (Number) The storage size of this filesystem given in GiB.
3535
- The value must be at least 1.
3636
- `type` (String) The storage type of the filesystem.

docs/resources/floating_ip.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ resource "genesiscloud_floating_ip" "floating_ip" {
2828
- `name` (String) The human-readable name for the floating IP.
2929
- `region` (String) The region identifier.
3030
- If the value of this attribute changes, the resource will be replaced.
31-
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
31+
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
3232
- `version` (String) The version of the floating IP.
3333
- If the value of this attribute changes, the resource will be replaced.
3434
- The value must be one of: ["ipv4"].

docs/resources/instance.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ resource "genesiscloud_instance" "example" {
3636
- `name` (String) The human-readable name for the instance.
3737
- `region` (String) The region identifier.
3838
- If the value of this attribute changes, the resource will be replaced.
39-
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
39+
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
4040
- `type` (String) The instance type identifier. Learn more about instance types [here](https://developers.genesiscloud.com/instances#instance-types).
4141
- If the value of this attribute changes, the resource will be replaced.
4242

docs/resources/security_group.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ resource "genesiscloud_security_group" "allow-https" {
3535
- `name` (String) The human-readable name for the security group.
3636
- `region` (String) The region identifier.
3737
- If the value of this attribute changes, the resource will be replaced.
38-
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
38+
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
3939
- `rules` (Attributes List) (see [below for nested schema](#nestedatt--rules))
4040

4141
### Optional

docs/resources/snapshot.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,24 @@ resource "genesiscloud_snapshot" "example" {
3030

3131
### Required
3232

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

3735
### Optional
3836

37+
- `region` (String) The region identifier.
38+
- If the value of this attribute changes, the resource will be replaced.
39+
- `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.
3940
- `retain_on_delete` (Boolean) Flag to retain the snapshot when the resource is deleted.
4041
- Sets the default value "false" if the attribute is not set.
42+
- `source_instance_id` (String) The id of the source instance from which this snapshot was derived.
43+
- `source_snapshot_id` (String) The id of the source snapshot from which this snapsot was derived.
44+
- If the value of this attribute changes, the resource will be replaced.
4145
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
4246

4347
### Read-Only
4448

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

docs/resources/volume.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ resource "genesiscloud_volume" "example" {
2929
- `name` (String) The human-readable name for the volume.
3030
- `region` (String) The region identifier.
3131
- If the value of this attribute changes, the resource will be replaced.
32-
- The value must be one of: ["ARC-IS-HAF-1" "EUC-DE-MUC-1" "NORD-NO-KRS-1"].
32+
- The value must be one of: ["EUC-DE-MUC-1" "NORD-NO-KRS-1"].
3333
- `size` (Number) The storage size of this volume given in GiB.
3434
- The value must be at least 1.
3535
- `type` (String) The storage type of the volume.

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.22.0
55
toolchain go1.23.1
66

77
require (
8-
github.com/genesiscloud/genesiscloud-go v1.0.12
8+
github.com/genesiscloud/genesiscloud-go v1.0.13-0.20250219141845-f494e100f9bf
99
github.com/hashicorp/go-retryablehttp v0.7.7
1010
github.com/hashicorp/terraform-plugin-docs v0.19.4
1111
github.com/hashicorp/terraform-plugin-framework v1.13.0

go.sum

+6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ github.com/genesiscloud/genesiscloud-go v1.0.11 h1:SjhBrZ2YXvWspQbfl48lUR+otqXS3
4949
github.com/genesiscloud/genesiscloud-go v1.0.11/go.mod h1:t2m8sfDGOa8pFio3oitkkjW9YcajyxkiWEu+BHOLyes=
5050
github.com/genesiscloud/genesiscloud-go v1.0.12 h1:zQaYpJIqfrHf8ebzLvxseNqOHbdxQ+WcnOCermyXxm0=
5151
github.com/genesiscloud/genesiscloud-go v1.0.12/go.mod h1:t2m8sfDGOa8pFio3oitkkjW9YcajyxkiWEu+BHOLyes=
52+
github.com/genesiscloud/genesiscloud-go v1.0.13-0.20250213143508-d2e03abdee79 h1:SRWVc9yG8GBIA7u+RCtWgRDKZiDOpG4u1Gk7n2um3Ho=
53+
github.com/genesiscloud/genesiscloud-go v1.0.13-0.20250213143508-d2e03abdee79/go.mod h1:t2m8sfDGOa8pFio3oitkkjW9YcajyxkiWEu+BHOLyes=
54+
github.com/genesiscloud/genesiscloud-go v1.0.13-0.20250214135958-c0b20fdab84c h1:JvLv05mOiPMRdU2EbF10YlI+zU7b438QH1X3wHDdsh0=
55+
github.com/genesiscloud/genesiscloud-go v1.0.13-0.20250214135958-c0b20fdab84c/go.mod h1:t2m8sfDGOa8pFio3oitkkjW9YcajyxkiWEu+BHOLyes=
56+
github.com/genesiscloud/genesiscloud-go v1.0.13-0.20250219141845-f494e100f9bf h1:3VG9TTJswWGFy3mvZX2czHs4oyJf/Z88TKVz96VQ/Ho=
57+
github.com/genesiscloud/genesiscloud-go v1.0.13-0.20250219141845-f494e100f9bf/go.mod h1:t2m8sfDGOa8pFio3oitkkjW9YcajyxkiWEu+BHOLyes=
5258
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
5359
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
5460
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=

internal/provider/images_data_source.go

-3
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,6 @@ func (d *ImagesDataSource) Read(ctx context.Context, req datasource.ReadRequest,
122122
}
123123
defer cancel()
124124

125-
filterType := pointer(genesiscloud.ImageType(data.Filter.Type.ValueString()))
126-
127125
var filterRegion *genesiscloud.Region
128126

129127
if !data.Filter.Region.IsNull() && !data.Filter.Region.IsUnknown() {
@@ -134,7 +132,6 @@ func (d *ImagesDataSource) Read(ctx context.Context, req datasource.ReadRequest,
134132
response, err := d.client.ListImagesPaginatedWithResponse(ctx, &genesiscloud.ListImagesPaginatedParams{
135133
Page: pointer(page),
136134
PerPage: pointer(100),
137-
Type: filterType,
138135
})
139136
if err != nil {
140137
resp.Diagnostics.AddError("Client Error", generateErrorMessage("read images", err))

internal/provider/snapshot_resource.go

+90-21
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,29 @@ func (r *SnapshotResource) Schema(ctx context.Context, req resource.SchemaReques
6262
MarkdownDescription: "The human-readable name for the snapshot.",
6363
Required: true,
6464
}),
65+
"replicated_region": resourceenhancer.Attribute(ctx, schema.StringAttribute{
66+
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.",
67+
Required: false,
68+
Optional: true,
69+
}),
6570
"region": resourceenhancer.Attribute(ctx, schema.StringAttribute{
66-
MarkdownDescription: "The region identifier.",
71+
MarkdownDescription: "The region identifier. Should only be explicity specified when using the 'source_snapshot_id'.",
72+
Optional: true,
6773
Computed: true,
6874
PlanModifiers: []planmodifier.String{
69-
stringplanmodifier.UseStateForUnknown(), // immutable
75+
stringplanmodifier.UseStateForUnknown(),
7076
},
7177
}),
72-
"instance_id": resourceenhancer.Attribute(ctx, schema.StringAttribute{
73-
MarkdownDescription: "The id of the instance to snapshot.",
74-
Required: true,
78+
"source_instance_id": resourceenhancer.Attribute(ctx, schema.StringAttribute{
79+
MarkdownDescription: "The id of the source instance from which this snapshot was derived.",
80+
Optional: true,
81+
PlanModifiers: []planmodifier.String{
82+
stringplanmodifier.UseStateForUnknown(),
83+
},
84+
}),
85+
"source_snapshot_id": resourceenhancer.Attribute(ctx, schema.StringAttribute{
86+
MarkdownDescription: "The id of the source snapshot from which this snapsot was derived.",
87+
Optional: true,
7588
PlanModifiers: []planmodifier.String{
7689
stringplanmodifier.RequiresReplace(),
7790
},
@@ -122,25 +135,81 @@ func (r *SnapshotResource) Create(ctx context.Context, req resource.CreateReques
122135
}
123136
defer cancel()
124137

125-
body := genesiscloud.CreateInstanceSnapshotJSONRequestBody{}
126-
body.Name = data.Name.ValueString()
127-
128-
instanceId := data.InstanceId.ValueString()
129-
130-
response, err := r.client.CreateInstanceSnapshotWithResponse(ctx, instanceId, body)
131-
if err != nil {
132-
resp.Diagnostics.AddError("Client Error", generateErrorMessage("create snapshot", err))
138+
if data.SourceInstanceId.IsNull() && data.SourceSnapshotId.IsNull() {
139+
resp.Diagnostics.AddError(
140+
"Invalid Configuration",
141+
"Either 'source_instance_id' or 'source_snapshot_id' must be specified.",
142+
)
133143
return
134144
}
135145

136-
snapshotResponse := response.JSON201
137-
if snapshotResponse == nil {
138-
resp.Diagnostics.AddError("Client Error", generateClientErrorMessage("create snapshot", ErrorResponse{
139-
Body: response.Body,
140-
HTTPResponse: response.HTTPResponse,
141-
Error: response.JSONDefault,
142-
}))
143-
return
146+
var snapshotResponse *genesiscloud.SingleSnapshotResponse
147+
148+
if !data.SourceInstanceId.IsNull() {
149+
if data.Region.ValueString() != "" {
150+
resp.Diagnostics.AddError(
151+
"Invalid Configuration",
152+
"When specifying a source instance, the 'region' specification won't take effect",
153+
)
154+
return
155+
156+
}
157+
body := genesiscloud.CreateInstanceSnapshotJSONRequestBody{}
158+
159+
body.Name = data.Name.ValueString()
160+
161+
instanceId := data.SourceInstanceId.ValueString()
162+
163+
if !data.ReplicatedRegion.IsNull() {
164+
body.ReplicatedRegion = pointer(genesiscloud.Region(data.ReplicatedRegion.ValueString()))
165+
}
166+
167+
response, err := r.client.CreateInstanceSnapshotWithResponse(ctx, instanceId, body)
168+
snapshotResponse = response.JSON201
169+
if err != nil {
170+
resp.Diagnostics.AddError("Client Error", generateErrorMessage("create snapshot", err))
171+
return
172+
}
173+
174+
if snapshotResponse == nil {
175+
resp.Diagnostics.AddError("Client Error", generateClientErrorMessage("create snapshot", ErrorResponse{
176+
Body: response.Body,
177+
HTTPResponse: response.HTTPResponse,
178+
Error: response.JSONDefault,
179+
}))
180+
return
181+
}
182+
} else if !data.SourceSnapshotId.IsNull() {
183+
if data.Region.IsNull() {
184+
resp.Diagnostics.AddError(
185+
"Invalid Configuration",
186+
"When specifying `source_snapshot_id` as the snapshot source, you must be specified the `region`.",
187+
)
188+
return
189+
}
190+
body := genesiscloud.CloneSnapshotJSONRequestBody{}
191+
192+
body.Name = data.Name.ValueString()
193+
194+
body.Region = genesiscloud.Region(data.Region.ValueString())
195+
196+
snapshotId := data.SourceSnapshotId.ValueString()
197+
198+
response, err := r.client.CloneSnapshotWithResponse(ctx, snapshotId, body)
199+
snapshotResponse = response.JSON201
200+
if err != nil {
201+
resp.Diagnostics.AddError("Client Error", generateErrorMessage("clone snapshot", err))
202+
return
203+
}
204+
205+
if snapshotResponse == nil {
206+
resp.Diagnostics.AddError("Client Error", generateClientErrorMessage("clone snapshot", ErrorResponse{
207+
Body: response.Body,
208+
HTTPResponse: response.HTTPResponse,
209+
Error: response.JSONDefault,
210+
}))
211+
return
212+
}
144213
}
145214

146215
resp.Diagnostics.Append(data.PopulateFromClientResponse(ctx, &snapshotResponse.Snapshot)...)

internal/provider/snapshot_types.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,14 @@ type SnapshotResourceModel struct {
2222
// Region The region identifier.
2323
Region types.String `tfsdk:"region"`
2424

25-
// InstanceId The id of the instance that was snapshotted.
26-
InstanceId types.String `tfsdk:"instance_id"`
25+
// SourceInstanceId The id of the source instance from which this snapshot was derived.
26+
SourceInstanceId types.String `tfsdk:"source_instance_id"`
27+
28+
// SourceSnapshotId The id of the source snapshot from which this snapsot was derived.
29+
SourceSnapshotId types.String `tfsdk:"source_snapshot_id"`
30+
31+
// ReplicatedRegion The region identifier when the snapshot should be replicated.
32+
ReplicatedRegion types.String `tfsdk:"replicated_region"`
2733

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

57+
if snapshot.SourceInstanceId != nil {
58+
data.SourceInstanceId = types.StringValue(*snapshot.SourceInstanceId)
59+
}
60+
if snapshot.SourceSnapshotId != nil {
61+
data.SourceSnapshotId = types.StringValue(*snapshot.SourceSnapshotId)
62+
}
63+
5264
return
5365
}

0 commit comments

Comments
 (0)