Skip to content

Commit 2300a83

Browse files
authored
chore: support update instance in create operation (#33)
* chore: migrate to new instance api * chore: update example * chore: support update instance in create operation * fix: lint * fix: tidy
1 parent a5a7093 commit 2300a83

File tree

6 files changed

+197
-29
lines changed

6 files changed

+197
-29
lines changed

api/client.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ type Client interface {
2828
// CreateInstance creates the instance.
2929
CreateInstance(ctx context.Context, environmentID, instanceID string, instance *InstanceMessage) (*InstanceMessage, error)
3030
// UpdateInstance updates the instance.
31-
UpdateInstance(ctx context.Context, environmentID, instanceID string, instance *InstanceMessage) (*InstanceMessage, error)
31+
UpdateInstance(ctx context.Context, environmentID, instanceID string, patch *InstancePatchMessage) (*InstanceMessage, error)
3232
// DeleteInstance deletes the instance.
3333
DeleteInstance(ctx context.Context, environmentID, instanceID string) error
34+
// UndeleteInstance undeletes the instance.
35+
UndeleteInstance(ctx context.Context, environmentID, instanceID string) (*InstanceMessage, error)
3436

3537
// Role
3638
// CreateRole creates the role in the instance.

api/instance.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ type InstanceMessage struct {
1111
DataSources []*DataSourceMessage `json:"dataSources"`
1212
}
1313

14+
// InstancePatchMessage is the API message to patch the instance.
15+
type InstancePatchMessage struct {
16+
Title *string `json:"title,omitempty"`
17+
ExternalLink *string `json:"externalLink,omitempty"`
18+
DataSources []*DataSourceMessage `json:"dataSources,omitempty"`
19+
}
20+
1421
// InstanceFindMessage is the API message for finding instance.
1522
type InstanceFindMessage struct {
1623
EnvironmentID string

client/instance.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,24 @@ func (c *client) CreateInstance(ctx context.Context, environmentID, instanceID s
8080
}
8181

8282
// UpdateInstance updates the instance.
83-
func (c *client) UpdateInstance(ctx context.Context, environmentID, instanceID string, instance *api.InstanceMessage) (*api.InstanceMessage, error) {
84-
payload, err := json.Marshal(instance)
83+
func (c *client) UpdateInstance(ctx context.Context, environmentID, instanceID string, patch *api.InstancePatchMessage) (*api.InstanceMessage, error) {
84+
payload, err := json.Marshal(patch)
8585
if err != nil {
8686
return nil, err
8787
}
8888

89-
req, err := http.NewRequestWithContext(ctx, "PATCH", fmt.Sprintf("%s/environments/%s/instances/%s", c.HostURL, environmentID, instanceID), strings.NewReader(string(payload)))
89+
paths := []string{}
90+
if patch.Title != nil {
91+
paths = append(paths, "instance.title")
92+
}
93+
if patch.ExternalLink != nil {
94+
paths = append(paths, "instance.external_link")
95+
}
96+
if patch.DataSources != nil {
97+
paths = append(paths, "instance.data_sources")
98+
}
99+
100+
req, err := http.NewRequestWithContext(ctx, "PATCH", fmt.Sprintf("%s/environments/%s/instances/%s?update_mask=%s", c.HostURL, environmentID, instanceID, strings.Join(paths, ",")), strings.NewReader(string(payload)))
90101

91102
if err != nil {
92103
return nil, err
@@ -118,3 +129,24 @@ func (c *client) DeleteInstance(ctx context.Context, environmentID, instanceID s
118129
}
119130
return nil
120131
}
132+
133+
// UndeleteInstance undeletes the instance.
134+
func (c *client) UndeleteInstance(ctx context.Context, environmentID, instanceID string) (*api.InstanceMessage, error) {
135+
req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/environments/%s/instances/%s:undelete", c.HostURL, environmentID, instanceID), nil)
136+
if err != nil {
137+
return nil, err
138+
}
139+
140+
body, err := c.doRequest(req)
141+
if err != nil {
142+
return nil, err
143+
}
144+
145+
var res api.InstanceMessage
146+
err = json.Unmarshal(body, &res)
147+
if err != nil {
148+
return nil, err
149+
}
150+
151+
return &res, nil
152+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.19
55
require (
66
github.com/google/go-querystring v1.1.0
77
github.com/hashicorp/terraform-plugin-docs v0.13.0
8+
github.com/hashicorp/terraform-plugin-log v0.7.0
89
github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0
910
github.com/pkg/errors v0.9.1
1011
)
@@ -37,7 +38,6 @@ require (
3738
github.com/hashicorp/terraform-exec v0.17.3 // indirect
3839
github.com/hashicorp/terraform-json v0.14.0 // indirect
3940
github.com/hashicorp/terraform-plugin-go v0.14.0 // indirect
40-
github.com/hashicorp/terraform-plugin-log v0.7.0 // indirect
4141
github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c // indirect
4242
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect
4343
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect

provider/internal/mock_client.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func (c *mockClient) CreateInstance(_ context.Context, environmentID, instanceID
177177
}
178178

179179
// UpdateInstance updates the instance.
180-
func (c *mockClient) UpdateInstance(ctx context.Context, environmentID, instanceID string, instance *api.InstanceMessage) (*api.InstanceMessage, error) {
180+
func (c *mockClient) UpdateInstance(ctx context.Context, environmentID, instanceID string, patch *api.InstancePatchMessage) (*api.InstanceMessage, error) {
181181
ins, err := c.GetInstance(ctx, &api.InstanceFindMessage{
182182
InstanceID: instanceID,
183183
EnvironmentID: environmentID,
@@ -186,14 +186,14 @@ func (c *mockClient) UpdateInstance(ctx context.Context, environmentID, instance
186186
return nil, err
187187
}
188188

189-
if instance.Title != "" {
190-
ins.Title = instance.Title
189+
if v := patch.Title; v != nil {
190+
ins.Title = *v
191191
}
192-
if instance.ExternalLink != "" {
193-
ins.ExternalLink = instance.ExternalLink
192+
if v := patch.ExternalLink; v != nil {
193+
ins.ExternalLink = *v
194194
}
195-
if instance.DataSources != nil {
196-
ins.DataSources = instance.DataSources
195+
if v := patch.DataSources; v != nil {
196+
ins.DataSources = v
197197
}
198198

199199
c.instanceMap[ins.Name] = ins
@@ -216,6 +216,22 @@ func (c *mockClient) DeleteInstance(ctx context.Context, environmentID, instance
216216
return nil
217217
}
218218

219+
// UndeleteInstance undeletes the instance.
220+
func (c *mockClient) UndeleteInstance(ctx context.Context, environmentID, instanceID string) (*api.InstanceMessage, error) {
221+
ins, err := c.GetInstance(ctx, &api.InstanceFindMessage{
222+
EnvironmentID: environmentID,
223+
InstanceID: instanceID,
224+
})
225+
if err != nil {
226+
return nil, err
227+
}
228+
229+
ins.State = api.Active
230+
c.instanceMap[ins.Name] = ins
231+
232+
return ins, nil
233+
}
234+
219235
// CreateRole creates the role in the instance.
220236
func (c *mockClient) CreateRole(_ context.Context, instanceID int, create *api.RoleUpsert) (*api.Role, error) {
221237
id := getRoleMapID(instanceID, create.Name)

provider/resource_instance.go

Lines changed: 128 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package provider
22

33
import (
44
"context"
5+
"fmt"
56

7+
"github.com/hashicorp/terraform-plugin-log/tflog"
68
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
79
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
810
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -146,20 +148,90 @@ func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, m inter
146148
return diag.FromErr(err)
147149
}
148150

149-
instance, err := c.CreateInstance(ctx, d.Get("environment").(string), d.Get("resource_id").(string), &api.InstanceMessage{
150-
Title: d.Get("title").(string),
151-
Engine: d.Get("engine").(string),
152-
ExternalLink: d.Get("external_link").(string),
153-
State: api.Active,
154-
DataSources: dataSourceList,
151+
environmentID := d.Get("environment").(string)
152+
instanceID := d.Get("resource_id").(string)
153+
instanceName := fmt.Sprintf("environments/%s/instances/%s", environmentID, instanceID)
154+
155+
existedInstance, err := c.GetInstance(ctx, &api.InstanceFindMessage{
156+
EnvironmentID: environmentID,
157+
InstanceID: instanceID,
158+
ShowDeleted: true,
155159
})
156160
if err != nil {
157-
return diag.FromErr(err)
161+
tflog.Debug(ctx, fmt.Sprintf("get instance %s failed with error: %v", instanceName, err))
158162
}
159163

160-
d.SetId(instance.Name)
164+
var diags diag.Diagnostics
165+
if existedInstance != nil && err == nil {
166+
diags = append(diags, diag.Diagnostic{
167+
Severity: diag.Warning,
168+
Summary: "Instance already exists",
169+
Detail: fmt.Sprintf("Instance %s already exists, try to exec the update operation", instanceName),
170+
})
171+
172+
engine := d.Get("engine").(string)
173+
if existedInstance.Engine != engine {
174+
diags = append(diags, diag.Diagnostic{
175+
Severity: diag.Error,
176+
Summary: "Invalid argument",
177+
Detail: fmt.Sprintf("cannot update instance %s engine to %s", instanceName, engine),
178+
})
179+
return diags
180+
}
181+
182+
if existedInstance.State == api.Deleted {
183+
diags = append(diags, diag.Diagnostic{
184+
Severity: diag.Warning,
185+
Summary: "Instance is deleted",
186+
Detail: fmt.Sprintf("Instance %s already deleted, try to undelete the instance", instanceName),
187+
})
188+
if _, err := c.UndeleteInstance(ctx, environmentID, instanceID); err != nil {
189+
diags = append(diags, diag.Diagnostic{
190+
Severity: diag.Error,
191+
Summary: "Failed to undelete instance",
192+
Detail: fmt.Sprintf("Undelete instance %s failed, error: %v", instanceName, err),
193+
})
194+
return diags
195+
}
196+
}
161197

162-
return resourceInstanceRead(ctx, d, m)
198+
title := d.Get("title").(string)
199+
externalLink := d.Get("external_link").(string)
200+
instance, err := c.UpdateInstance(ctx, environmentID, instanceID, &api.InstancePatchMessage{
201+
Title: &title,
202+
ExternalLink: &externalLink,
203+
DataSources: dataSourceList,
204+
})
205+
if err != nil {
206+
diags = append(diags, diag.Diagnostic{
207+
Severity: diag.Error,
208+
Summary: "Failed to update instance",
209+
Detail: fmt.Sprintf("Update instance %s failed, error: %v", instanceName, err),
210+
})
211+
return diags
212+
}
213+
214+
d.SetId(instance.Name)
215+
} else {
216+
instance, err := c.CreateInstance(ctx, environmentID, instanceID, &api.InstanceMessage{
217+
Title: d.Get("title").(string),
218+
Engine: d.Get("engine").(string),
219+
ExternalLink: d.Get("external_link").(string),
220+
State: api.Active,
221+
DataSources: dataSourceList,
222+
})
223+
if err != nil {
224+
return diag.FromErr(err)
225+
}
226+
d.SetId(instance.Name)
227+
}
228+
229+
diag := resourceInstanceRead(ctx, d, m)
230+
if diag != nil {
231+
diags = append(diags, diag...)
232+
}
233+
234+
return diags
163235
}
164236

165237
func resourceInstanceRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
@@ -189,16 +261,50 @@ func resourceInstanceUpdate(ctx context.Context, d *schema.ResourceData, m inter
189261
return diag.FromErr(err)
190262
}
191263

192-
patch := &api.InstanceMessage{}
193-
if d.HasChange("title") {
194-
if title, ok := d.GetOk("title"); ok {
195-
patch.Title = title.(string)
264+
instanceName := fmt.Sprintf("environments/%s/instances/%s", envID, instanceID)
265+
266+
existedInstance, err := c.GetInstance(ctx, &api.InstanceFindMessage{
267+
EnvironmentID: envID,
268+
InstanceID: instanceID,
269+
ShowDeleted: true,
270+
})
271+
if err != nil {
272+
return diag.FromErr(err)
273+
}
274+
275+
var diags diag.Diagnostics
276+
if existedInstance.Engine != d.Get("engine").(string) {
277+
diags = append(diags, diag.Diagnostic{
278+
Severity: diag.Error,
279+
Summary: "Invalid argument",
280+
Detail: fmt.Sprintf("cannot update instance %s engine to %s", instanceName, d.Get("engine").(string)),
281+
})
282+
return diags
283+
}
284+
if existedInstance.State == api.Deleted {
285+
diags = append(diags, diag.Diagnostic{
286+
Severity: diag.Warning,
287+
Summary: "Instance is deleted",
288+
Detail: fmt.Sprintf("Instance %s already deleted, try to undelete the instance", instanceName),
289+
})
290+
if _, err := c.UndeleteInstance(ctx, envID, instanceID); err != nil {
291+
diags = append(diags, diag.Diagnostic{
292+
Severity: diag.Error,
293+
Summary: "Failed to undelete instance",
294+
Detail: fmt.Sprintf("Undelete instance %s failed, error: %v", instanceName, err),
295+
})
296+
return diags
196297
}
197298
}
299+
300+
patch := &api.InstancePatchMessage{}
301+
if d.HasChange("title") {
302+
v := d.Get("title").(string)
303+
patch.Title = &v
304+
}
198305
if d.HasChange("external_link") {
199-
if link, ok := d.GetOk("external_link"); ok {
200-
patch.ExternalLink = link.(string)
201-
}
306+
v := d.Get("external_link").(string)
307+
patch.ExternalLink = &v
202308
}
203309
if d.HasChange("data_sources") {
204310
dataSourceList, err := convertDataSourceCreateList(d)
@@ -212,7 +318,12 @@ func resourceInstanceUpdate(ctx context.Context, d *schema.ResourceData, m inter
212318
return diag.FromErr(err)
213319
}
214320

215-
return resourceInstanceRead(ctx, d, m)
321+
diag := resourceInstanceRead(ctx, d, m)
322+
if diag != nil {
323+
diags = append(diags, diag...)
324+
}
325+
326+
return diags
216327
}
217328

218329
func resourceInstanceDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {

0 commit comments

Comments
 (0)