Skip to content

Commit b094fb2

Browse files
committed
Add new resource sentry_release_deployment
This resource creates a new deployment for a specific release and environment in sentry.
1 parent 7b8d789 commit b094fb2

File tree

3 files changed

+279
-0
lines changed

3 files changed

+279
-0
lines changed

sentry/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func NewProvider(version string) func() *schema.Provider {
4949
"sentry_project": resourceSentryProject(),
5050
"sentry_rule": resourceSentryRule(),
5151
"sentry_team": resourceSentryTeam(),
52+
"sentry_release_deployment": resourceSentryReleaseDeployment(),
5253
},
5354

5455
DataSourcesMap: map[string]*schema.Resource{
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package sentry
2+
3+
import (
4+
"context"
5+
"sort"
6+
7+
"github.com/hashicorp/go-multierror"
8+
"github.com/hashicorp/terraform-plugin-log/tflog"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/jianyuan/go-sentry/v2/sentry"
12+
)
13+
14+
func resourceSentryReleaseDeployment() *schema.Resource {
15+
return &schema.Resource{
16+
CreateContext: resourceSentryReleaseDeploymentCreate,
17+
ReadContext: resourceSentryReleaseDeploymentRead,
18+
UpdateContext: resourceSentryReleaseDeploymentUpdate,
19+
DeleteContext: resourceSentryReleaseDeploymentDelete,
20+
21+
Importer: &schema.ResourceImporter{
22+
StateContext: schema.ImportStatePassthroughContext,
23+
},
24+
25+
Schema: map[string]*schema.Schema{
26+
"organization": {
27+
Description: "The slug of the organization the deploy belongs to.",
28+
Type: schema.TypeString,
29+
Required: true,
30+
},
31+
"version": {
32+
Description: "The version identifier of the release.",
33+
Type: schema.TypeString,
34+
Required: true,
35+
},
36+
"environment": {
37+
Type: schema.TypeString,
38+
Required: true,
39+
Description: "The environment this deployment is for.",
40+
},
41+
"url": {
42+
Description: "The optional URL that points to the deploy.",
43+
Type: schema.TypeString,
44+
Optional: true,
45+
},
46+
"name": {
47+
Description: "The optional name of the deploy.",
48+
Type: schema.TypeString,
49+
Optional: true,
50+
},
51+
"projects": {
52+
Description: "The optional list of projects to deploy.",
53+
Type: schema.TypeList,
54+
Elem: &schema.Schema{
55+
Type: schema.TypeString,
56+
},
57+
Optional: true,
58+
},
59+
},
60+
}
61+
}
62+
63+
func resourceSentryReleaseDeploymentCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
64+
client := meta.(*sentry.Client)
65+
66+
org := d.Get("organization").(string)
67+
version := d.Get("version").(string)
68+
params := releaseDeploymentCreateParams(d)
69+
70+
deploy, _, err := client.ReleaseDeployments.Create(ctx, org, version, params)
71+
if err != nil {
72+
return diag.FromErr(err)
73+
}
74+
75+
d.SetId(buildThreePartID(org, version, deploy.ID))
76+
return resourceSentryReleaseDeploymentRead(ctx, d, meta)
77+
}
78+
79+
func resourceSentryReleaseDeploymentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
80+
client := meta.(*sentry.Client)
81+
82+
org, release, deployID, err := splitSentryReleaseDeploymentID(d.Id())
83+
84+
tflog.Debug(ctx, "Reading release deployment", map[string]interface{}{
85+
"org": org,
86+
"release": release,
87+
"deployID": deployID,
88+
})
89+
deploy, resp, err := client.ReleaseDeployments.Get(ctx, org, release, deployID)
90+
if found, err := checkClientGet(resp, err, d); !found {
91+
tflog.Info(ctx, "Removed deployment from state because it no longer exists in Sentry", map[string]interface{}{
92+
"org": org,
93+
"release": release,
94+
"deployID": deployID,
95+
})
96+
return diag.FromErr(err)
97+
}
98+
99+
sort.Strings(deploy.Projects)
100+
101+
d.SetId(buildThreePartID(org, release, deploy.ID))
102+
retErr := multierror.Append(
103+
d.Set("organization", org),
104+
d.Set("version", release),
105+
d.Set("environment", deploy.Environment),
106+
d.Set("url", deploy.URL),
107+
d.Set("name", deploy.Name),
108+
d.Set("projects", deploy.Projects),
109+
)
110+
return diag.FromErr(retErr.ErrorOrNil())
111+
}
112+
113+
func resourceSentryReleaseDeploymentUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
114+
client := meta.(*sentry.Client)
115+
116+
// Since we cannot update or delete deployments we create a new deployment
117+
// when an update is needed
118+
org := d.Get("organization").(string)
119+
version := d.Get("version").(string)
120+
params := releaseDeploymentCreateParams(d)
121+
122+
deploy, _, err := client.ReleaseDeployments.Create(ctx, org, version, params)
123+
if err != nil {
124+
return diag.FromErr(err)
125+
}
126+
127+
d.SetId(buildThreePartID(org, version, deploy.ID))
128+
return resourceSentryReleaseDeploymentRead(ctx, d, meta)
129+
}
130+
131+
func resourceSentryReleaseDeploymentDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
132+
org, release, deployID, err := splitSentryReleaseDeploymentID(d.Id())
133+
if err != nil {
134+
return diag.FromErr(err)
135+
}
136+
137+
tflog.Debug(ctx, "Deleting deployment", map[string]interface{}{
138+
"org": org,
139+
"release": release,
140+
"deployID": deployID,
141+
})
142+
143+
// Sentry has no option to delete a deployment. So we skip this and just
144+
// remove it from the terraform state
145+
return nil
146+
}
147+
148+
func splitSentryReleaseDeploymentID(id string) (org, release, deployID string, err error) {
149+
org, release, deployID, err = splitThreePartID(id, "organization-id", "release", "deploy-id")
150+
return
151+
}
152+
153+
func releaseDeploymentCreateParams(d *schema.ResourceData) *sentry.ReleaseDeployment {
154+
params := &sentry.ReleaseDeployment{
155+
Environment: d.Get("environment").(string),
156+
}
157+
if v := d.Get("name").(string); v != "" {
158+
params.Name = sentry.String(v)
159+
}
160+
if v := d.Get("url").(string); v != "" {
161+
params.URL = sentry.String(v)
162+
}
163+
if v, ok := d.GetOk("projects"); ok {
164+
projects := expandStringList(v.([]interface{}))
165+
if len(projects) > 0 {
166+
params.Projects = projects
167+
}
168+
}
169+
return params
170+
171+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package sentry
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
11+
"github.com/jianyuan/go-sentry/v2/sentry"
12+
)
13+
14+
func TestAccSentryReleaseDeployment_basic(t *testing.T) {
15+
rn := "sentry_organization_member.john_doe"
16+
environment := "test"
17+
18+
check := func(role string) resource.TestCheckFunc {
19+
var deploy sentry.ReleaseDeployment
20+
return resource.ComposeTestCheckFunc(
21+
testAccCheckSentryReleaseDeploymentExists(rn, &deploy),
22+
resource.TestCheckResourceAttr(rn, "organization", testOrganization),
23+
resource.TestCheckResourceAttr(rn, "version", "0.1.0"),
24+
)
25+
}
26+
27+
resource.Test(t, resource.TestCase{
28+
PreCheck: func() { testAccPreCheck(t) },
29+
ProviderFactories: testAccProviderFactories,
30+
CheckDestroy: testAccCheckSentryReleaseDeploymentDestroy,
31+
Steps: []resource.TestStep{
32+
{
33+
Config: testAccSentryReleaseDeploymentConfig("0.1.0", environment),
34+
Check: check("member"),
35+
},
36+
{
37+
Config: testAccSentryReleaseDeploymentConfig("0.2.0", environment),
38+
Check: check("manager"),
39+
},
40+
},
41+
})
42+
}
43+
44+
func testAccCheckSentryReleaseDeploymentDestroy(s *terraform.State) error {
45+
client := testAccProvider.Meta().(*sentry.Client)
46+
47+
for _, rs := range s.RootModule().Resources {
48+
if rs.Type != "sentry_release_deployment" {
49+
continue
50+
}
51+
52+
ctx := context.Background()
53+
deploy, resp, err := client.ReleaseDeployments.Get(
54+
ctx,
55+
rs.Primary.Attributes["organization"],
56+
rs.Primary.Attributes["version"],
57+
rs.Primary.ID,
58+
)
59+
if err == nil {
60+
if deploy != nil {
61+
return errors.New("release deployment still exists")
62+
}
63+
}
64+
if resp.StatusCode != 404 {
65+
return err
66+
}
67+
return nil
68+
}
69+
return nil
70+
}
71+
72+
func testAccCheckSentryReleaseDeploymentExists(n string, deploy *sentry.ReleaseDeployment) resource.TestCheckFunc {
73+
return func(s *terraform.State) error {
74+
rs, ok := s.RootModule().Resources[n]
75+
if !ok {
76+
return fmt.Errorf("not found: %s", n)
77+
}
78+
79+
if rs.Primary.ID == "" {
80+
return errors.New("no deploy ID is set")
81+
}
82+
83+
org, version, id, err := splitSentryReleaseDeploymentID(rs.Primary.ID)
84+
if err != nil {
85+
return err
86+
}
87+
client := testAccProvider.Meta().(*sentry.Client)
88+
ctx := context.Background()
89+
gotDeployment, _, err := client.ReleaseDeployments.Get(ctx, org, version, id)
90+
91+
if err != nil {
92+
return err
93+
}
94+
*deploy = *gotDeployment
95+
return nil
96+
}
97+
}
98+
99+
func testAccSentryReleaseDeploymentConfig(version, environment string) string {
100+
return testAccSentryOrganizationDataSourceConfig + fmt.Sprintf(`
101+
resource "sentry_release_deployment" "my-release" {
102+
organization = data.sentry_organization.test.id
103+
version = "%[1]s"
104+
environment = "%[2]s"
105+
}
106+
`, version, environment)
107+
}

0 commit comments

Comments
 (0)