Skip to content

Commit ae1cd8b

Browse files
authored
fix renaming a relation identifier (#7)
* fix renaming a relation identifier * bump ver * docs and lint
1 parent fc19404 commit ae1cd8b

File tree

5 files changed

+198
-9
lines changed

5 files changed

+198
-9
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ HOSTNAME=github.com
22
NAMESPACE=port-labs
33
NAME=port-labs
44
BINARY=terraform-provider-${NAME}
5-
VERSION=0.2.0
5+
VERSION=0.3.1
66
OS=$(shell go env GOOS)
77
ARCH=$(shell go env GOARCH)
88
OS_ARCH=${OS}_${ARCH}

docs/resources/blueprint.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ Required:
6262
Optional:
6363

6464
- `identifier` (String) The identifier of the relation
65-
- `many` (Boolean) Whether or not the relation is many
65+
- `many` (Boolean) Unsupported ATM.
66+
Whether or not the relation is many
6667
- `required` (Boolean) Whether or not the relation is required
6768

6869

port/cli/relation.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func (c *PortClient) CreateRelation(ctx context.Context, bpID string, r *Relatio
1717
if err != nil {
1818
return "", err
1919
}
20-
if !result["ok"].(bool) {
20+
if resp.StatusCode() > 299 || resp.StatusCode() < 200 || !result["ok"].(bool) {
2121
return "", fmt.Errorf("failed to create relation, got: %s", resp.Body())
2222
}
2323
return result["identifier"].(string), nil
@@ -53,3 +53,21 @@ func (c *PortClient) ReadRelations(ctx context.Context, blueprintID string) ([]*
5353
}
5454
return bpRelations, nil
5555
}
56+
57+
func (c *PortClient) DeleteRelation(ctx context.Context, blueprintID, relationID string) error {
58+
url := "v1/blueprints/{blueprint_identifier}/relations/{relation_identifier}"
59+
result := map[string]interface{}{}
60+
resp, err := c.Client.R().
61+
SetContext(ctx).
62+
SetResult(&result).
63+
SetPathParam("blueprint_identifier", blueprintID).
64+
SetPathParam("relation_identifier", relationID).
65+
Delete(url)
66+
if err != nil {
67+
return err
68+
}
69+
if !result["ok"].(bool) {
70+
return fmt.Errorf("failed to delete relation, got: %s", resp.Body())
71+
}
72+
return nil
73+
}

port/resource_port_blueprint.go

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package port
33
import (
44
"context"
55

6+
"github.com/hashicorp/go-cty/cty"
67
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
78
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
89
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -15,7 +16,7 @@ func newBlueprintResource() *schema.Resource {
1516
return &schema.Resource{
1617
Description: "Port blueprint",
1718
CreateContext: createBlueprint,
18-
UpdateContext: createBlueprint,
19+
UpdateContext: updateBlueprint,
1920
ReadContext: readBlueprint,
2021
DeleteContext: deleteBlueprint,
2122
Schema: map[string]*schema.Schema{
@@ -68,9 +69,15 @@ func newBlueprintResource() *schema.Resource {
6869
Description: "Whether or not the relation is required",
6970
},
7071
"many": {
71-
Type: schema.TypeBool,
72-
Optional: true,
73-
Description: "Whether or not the relation is many",
72+
Type: schema.TypeBool,
73+
Optional: true,
74+
ValidateDiagFunc: func(i interface{}, path cty.Path) diag.Diagnostics {
75+
if i.(bool) {
76+
return diag.Errorf("Many relations are not supported")
77+
}
78+
return nil
79+
},
80+
Description: "Unsupported ATM.\nWhether or not the relation is many",
7481
},
7582
},
7683
},
@@ -244,8 +251,7 @@ func deleteBlueprint(ctx context.Context, d *schema.ResourceData, m interface{})
244251
return diags
245252
}
246253

247-
func createRelations(ctx context.Context, d *schema.ResourceData, m interface{}) error {
248-
c := m.(*cli.PortClient)
254+
func getRelations(d *schema.ResourceData) (rel []*cli.Relation) {
249255
relations, ok := d.GetOk("relations")
250256
if !ok {
251257
return nil
@@ -265,6 +271,18 @@ func createRelations(ctx context.Context, d *schema.ResourceData, m interface{})
265271
if req, ok := relation["required"]; ok {
266272
r.Required = req.(bool)
267273
}
274+
if m, ok := relation["many"]; ok {
275+
r.Many = m.(bool)
276+
}
277+
rel = append(rel, r)
278+
}
279+
return
280+
}
281+
282+
func createRelations(ctx context.Context, d *schema.ResourceData, m interface{}) error {
283+
c := m.(*cli.PortClient)
284+
rels := getRelations(d)
285+
for _, r := range rels {
268286
_, err := c.CreateRelation(ctx, d.Id(), r)
269287
if err != nil {
270288
return err
@@ -273,6 +291,44 @@ func createRelations(ctx context.Context, d *schema.ResourceData, m interface{})
273291
return nil
274292
}
275293

294+
func contains(s []string, e string) bool {
295+
for _, a := range s {
296+
if a == e {
297+
return true
298+
}
299+
}
300+
return false
301+
}
302+
303+
// patchDeleteDeprecatedRelations deletes relations that are no longer present in the resource.
304+
// This is necessary because we bundled relations inside the blueprint resource.
305+
// In the future, the API of blueprints should support getting the relations and then we can delete this patch.
306+
func patchDeleteDeprecatedRelations(ctx context.Context, d *schema.ResourceData, m interface{}) error {
307+
c := m.(*cli.PortClient)
308+
rels := getRelations(d)
309+
ids := make([]string, len(rels))
310+
for i, r := range rels {
311+
ids[i] = r.Identifier
312+
}
313+
remoteRelations, err := c.ReadRelations(ctx, d.Id())
314+
if err != nil {
315+
return err
316+
}
317+
toDel := make([]*cli.Relation, 0)
318+
for _, r := range remoteRelations {
319+
if !contains(ids, r.Identifier) {
320+
toDel = append(toDel, r)
321+
}
322+
}
323+
for _, r := range toDel {
324+
err := c.DeleteRelation(ctx, d.Id(), r.Identifier)
325+
if err != nil {
326+
return err
327+
}
328+
}
329+
return nil
330+
}
331+
276332
func createBlueprint(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
277333
var diags diag.Diagnostics
278334
c := m.(*cli.PortClient)
@@ -297,6 +353,34 @@ func createBlueprint(ctx context.Context, d *schema.ResourceData, m interface{})
297353
return diags
298354
}
299355

356+
func updateBlueprint(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
357+
var diags diag.Diagnostics
358+
c := m.(*cli.PortClient)
359+
b, err := blueprintResourceToBody(d)
360+
if err != nil {
361+
return diag.FromErr(err)
362+
}
363+
var bp *cli.Blueprint
364+
if d.Id() != "" {
365+
bp, err = c.UpdateBlueprint(ctx, b, d.Id())
366+
} else {
367+
bp, err = c.CreateBlueprint(ctx, b)
368+
}
369+
if err != nil {
370+
return diag.FromErr(err)
371+
}
372+
writeBlueprintComputedFieldsToResource(d, bp)
373+
err = patchDeleteDeprecatedRelations(ctx, d, m)
374+
if err != nil {
375+
return diag.FromErr(err)
376+
}
377+
err = createRelations(ctx, d, m)
378+
if err != nil {
379+
return diag.FromErr(err)
380+
}
381+
return diags
382+
}
383+
300384
func writeBlueprintComputedFieldsToResource(d *schema.ResourceData, b *cli.Blueprint) {
301385
d.SetId(b.Identifier)
302386
d.Set("created_at", b.CreatedAt.String())

port/resource_port_blueprint_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,89 @@ func TestAccPortBlueprintUpdate(t *testing.T) {
181181
},
182182
})
183183
}
184+
185+
func TestAccPortBlueprintUpdateRelation(t *testing.T) {
186+
envID := genID()
187+
vmID := genID()
188+
var testAccActionConfigCreate = fmt.Sprintf(`
189+
provider "port-labs" {}
190+
resource "port-labs_blueprint" "Environment" {
191+
title = "Environment"
192+
icon = "Environment"
193+
identifier = "%s"
194+
properties {
195+
identifier = "env_name"
196+
type = "string"
197+
title = "Name"
198+
}
199+
}
200+
resource "port-labs_blueprint" "vm" {
201+
title = "Virtual Machine"
202+
icon = "Azure"
203+
identifier = "%s"
204+
properties {
205+
identifier = "image"
206+
type = "string"
207+
title = "Image"
208+
}
209+
relations {
210+
identifier = "vm-to-environment"
211+
title = "Related Environment"
212+
target = port-labs_blueprint.Environment.identifier
213+
}
214+
}
215+
`, envID, vmID)
216+
var testAccActionConfigUpdate = fmt.Sprintf(`
217+
provider "port-labs" {}
218+
resource "port-labs_blueprint" "Environment" {
219+
title = "Environment"
220+
icon = "Environment"
221+
identifier = "%s"
222+
properties {
223+
identifier = "env_name"
224+
type = "string"
225+
title = "Name"
226+
}
227+
}
228+
resource "port-labs_blueprint" "vm" {
229+
title = "Virtual Machine"
230+
icon = "Azure"
231+
identifier = "%s"
232+
properties {
233+
identifier = "image"
234+
type = "string"
235+
title = "Image"
236+
}
237+
relations {
238+
identifier = "environment"
239+
title = "Related Environment"
240+
target = port-labs_blueprint.Environment.identifier
241+
}
242+
}
243+
`, envID, vmID)
244+
resource.Test(t, resource.TestCase{
245+
Providers: map[string]*schema.Provider{
246+
"port-labs": Provider(),
247+
},
248+
Steps: []resource.TestStep{
249+
{
250+
Config: testAccActionConfigCreate,
251+
Check: resource.ComposeTestCheckFunc(
252+
resource.TestCheckResourceAttr("port-labs_blueprint.vm", "relations.#", "1"),
253+
resource.TestCheckResourceAttr("port-labs_blueprint.vm", "relations.0.title", "Related Environment"),
254+
resource.TestCheckResourceAttr("port-labs_blueprint.vm", "relations.0.target", envID),
255+
resource.TestCheckResourceAttr("port-labs_blueprint.vm", "relations.0.identifier", "vm-to-environment"),
256+
),
257+
},
258+
{
259+
Config: testAccActionConfigUpdate,
260+
Check: resource.ComposeTestCheckFunc(
261+
resource.TestCheckResourceAttr("port-labs_blueprint.vm", "relations.#", "1"),
262+
resource.TestCheckResourceAttr("port-labs_blueprint.vm", "relations.0.title", "Related Environment"),
263+
resource.TestCheckResourceAttr("port-labs_blueprint.vm", "relations.0.target", envID),
264+
resource.TestCheckResourceAttr("port-labs_blueprint.vm", "relations.0.identifier", "environment"),
265+
),
266+
},
267+
},
268+
})
269+
}

0 commit comments

Comments
 (0)