Skip to content

Commit 193df07

Browse files
authored
Add support for oauth and oidc tokens to cloud_scheduler_job
2 parents be9991d + bbaa23f commit 193df07

File tree

3 files changed

+410
-0
lines changed

3 files changed

+410
-0
lines changed

google/resource_cloud_scheduler_job.go

+225
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,55 @@ import (
2626
"github.com/hashicorp/terraform/helper/schema"
2727
)
2828

29+
// Both oidc and oauth headers cannot be set
30+
func validateAuthHeaders(diff *schema.ResourceDiff, v interface{}) error {
31+
httpBlock := diff.Get("http_target.0").(map[string]interface{})
32+
33+
if httpBlock != nil {
34+
oauth := httpBlock["oauth_token"]
35+
oidc := httpBlock["oidc_token"]
36+
37+
if oauth != nil && oidc != nil {
38+
if len(oidc.([]interface{})) > 0 && len(oauth.([]interface{})) > 0 {
39+
return fmt.Errorf("Error in http_target: only one of oauth_token or oidc_token can be specified, but not both.")
40+
}
41+
}
42+
}
43+
44+
return nil
45+
}
46+
47+
func authHeaderDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
48+
// If generating an `oauth_token` and `scope` is not provided in the configuration,
49+
// the default "https://www.googleapis.com/auth/cloud-platform" scope will be used.
50+
// Similarly, if generating an `oidc_token` and `audience` is not provided in the
51+
// configuration, the URI specified in target will be used. Although not in the
52+
// configuration, in both cases the default is returned in the object, but is not in.
53+
// state. We suppress the diff if the values are these defaults but are not stored in state.
54+
55+
b := strings.Split(k, ".")
56+
if b[0] == "http_target" && len(b) > 4 {
57+
block := b[2]
58+
attr := b[4]
59+
60+
if block == "oauth_token" && attr == "scope" {
61+
if old == canonicalizeServiceScope("cloud-platform") && new == "" {
62+
return true
63+
}
64+
}
65+
66+
if block == "oidc_token" && attr == "audience" {
67+
uri := d.Get(strings.Join(b[0:2], ".") + ".uri")
68+
if old == uri && new == "" {
69+
return true
70+
}
71+
}
72+
73+
}
74+
75+
return false
76+
}
77+
2978
func resourceCloudSchedulerJob() *schema.Resource {
3079
return &schema.Resource{
3180
Create: resourceCloudSchedulerJobCreate,
@@ -41,6 +90,8 @@ func resourceCloudSchedulerJob() *schema.Resource {
4190
Delete: schema.DefaultTimeout(4 * time.Minute),
4291
},
4392

93+
CustomizeDiff: validateAuthHeaders,
94+
4495
Schema: map[string]*schema.Schema{
4596
"name": {
4697
Type: schema.TypeString,
@@ -145,6 +196,48 @@ func resourceCloudSchedulerJob() *schema.Resource {
145196
Optional: true,
146197
ForceNew: true,
147198
},
199+
"oauth_token": {
200+
Type: schema.TypeList,
201+
Optional: true,
202+
ForceNew: true,
203+
DiffSuppressFunc: authHeaderDiffSuppress,
204+
MaxItems: 1,
205+
Elem: &schema.Resource{
206+
Schema: map[string]*schema.Schema{
207+
"scope": {
208+
Type: schema.TypeString,
209+
Optional: true,
210+
ForceNew: true,
211+
},
212+
"service_account_email": {
213+
Type: schema.TypeString,
214+
Optional: true,
215+
ForceNew: true,
216+
},
217+
},
218+
},
219+
},
220+
"oidc_token": {
221+
Type: schema.TypeList,
222+
Optional: true,
223+
ForceNew: true,
224+
DiffSuppressFunc: authHeaderDiffSuppress,
225+
MaxItems: 1,
226+
Elem: &schema.Resource{
227+
Schema: map[string]*schema.Schema{
228+
"audience": {
229+
Type: schema.TypeString,
230+
Optional: true,
231+
ForceNew: true,
232+
},
233+
"service_account_email": {
234+
Type: schema.TypeString,
235+
Optional: true,
236+
ForceNew: true,
237+
},
238+
},
239+
},
240+
},
148241
},
149242
},
150243
ConflictsWith: []string{"pubsub_target", "app_engine_http_target"},
@@ -620,6 +713,10 @@ func flattenCloudSchedulerJobHttpTarget(v interface{}, d *schema.ResourceData) i
620713
flattenCloudSchedulerJobHttpTargetBody(original["body"], d)
621714
transformed["headers"] =
622715
flattenCloudSchedulerJobHttpTargetHeaders(original["headers"], d)
716+
transformed["oauth_token"] =
717+
flattenCloudSchedulerJobHttpTargetOauthToken(original["oauthToken"], d)
718+
transformed["oidc_token"] =
719+
flattenCloudSchedulerJobHttpTargetOidcToken(original["oidcToken"], d)
623720
return []interface{}{transformed}
624721
}
625722
func flattenCloudSchedulerJobHttpTargetUri(v interface{}, d *schema.ResourceData) interface{} {
@@ -659,6 +756,52 @@ func flattenCloudSchedulerJobHttpTargetHeaders(v interface{}, d *schema.Resource
659756
return headers
660757
}
661758

759+
func flattenCloudSchedulerJobHttpTargetOauthToken(v interface{}, d *schema.ResourceData) interface{} {
760+
if v == nil {
761+
return nil
762+
}
763+
original := v.(map[string]interface{})
764+
if len(original) == 0 {
765+
return nil
766+
}
767+
transformed := make(map[string]interface{})
768+
transformed["service_account_email"] =
769+
flattenCloudSchedulerJobHttpTargetOauthTokenServiceAccountEmail(original["serviceAccountEmail"], d)
770+
transformed["scope"] =
771+
flattenCloudSchedulerJobHttpTargetOauthTokenScope(original["scope"], d)
772+
return []interface{}{transformed}
773+
}
774+
func flattenCloudSchedulerJobHttpTargetOauthTokenServiceAccountEmail(v interface{}, d *schema.ResourceData) interface{} {
775+
return v
776+
}
777+
778+
func flattenCloudSchedulerJobHttpTargetOauthTokenScope(v interface{}, d *schema.ResourceData) interface{} {
779+
return v
780+
}
781+
782+
func flattenCloudSchedulerJobHttpTargetOidcToken(v interface{}, d *schema.ResourceData) interface{} {
783+
if v == nil {
784+
return nil
785+
}
786+
original := v.(map[string]interface{})
787+
if len(original) == 0 {
788+
return nil
789+
}
790+
transformed := make(map[string]interface{})
791+
transformed["service_account_email"] =
792+
flattenCloudSchedulerJobHttpTargetOidcTokenServiceAccountEmail(original["serviceAccountEmail"], d)
793+
transformed["audience"] =
794+
flattenCloudSchedulerJobHttpTargetOidcTokenAudience(original["audience"], d)
795+
return []interface{}{transformed}
796+
}
797+
func flattenCloudSchedulerJobHttpTargetOidcTokenServiceAccountEmail(v interface{}, d *schema.ResourceData) interface{} {
798+
return v
799+
}
800+
801+
func flattenCloudSchedulerJobHttpTargetOidcTokenAudience(v interface{}, d *schema.ResourceData) interface{} {
802+
return v
803+
}
804+
662805
func expandCloudSchedulerJobName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
663806
var jobName string
664807
project, err := getProject(d, config)
@@ -964,6 +1107,20 @@ func expandCloudSchedulerJobHttpTarget(v interface{}, d TerraformResourceData, c
9641107
transformed["headers"] = transformedHeaders
9651108
}
9661109

1110+
transformedOauthToken, err := expandCloudSchedulerJobHttpTargetOauthToken(original["oauth_token"], d, config)
1111+
if err != nil {
1112+
return nil, err
1113+
} else if val := reflect.ValueOf(transformedOauthToken); val.IsValid() && !isEmptyValue(val) {
1114+
transformed["oauthToken"] = transformedOauthToken
1115+
}
1116+
1117+
transformedOidcToken, err := expandCloudSchedulerJobHttpTargetOidcToken(original["oidc_token"], d, config)
1118+
if err != nil {
1119+
return nil, err
1120+
} else if val := reflect.ValueOf(transformedOidcToken); val.IsValid() && !isEmptyValue(val) {
1121+
transformed["oidcToken"] = transformedOidcToken
1122+
}
1123+
9671124
return transformed, nil
9681125
}
9691126

@@ -989,3 +1146,71 @@ func expandCloudSchedulerJobHttpTargetHeaders(v interface{}, d TerraformResource
9891146
}
9901147
return m, nil
9911148
}
1149+
1150+
func expandCloudSchedulerJobHttpTargetOauthToken(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
1151+
l := v.([]interface{})
1152+
if len(l) == 0 || l[0] == nil {
1153+
return nil, nil
1154+
}
1155+
raw := l[0]
1156+
original := raw.(map[string]interface{})
1157+
transformed := make(map[string]interface{})
1158+
1159+
transformedServiceAccountEmail, err := expandCloudSchedulerJobHttpTargetOauthTokenServiceAccountEmail(original["service_account_email"], d, config)
1160+
if err != nil {
1161+
return nil, err
1162+
} else if val := reflect.ValueOf(transformedServiceAccountEmail); val.IsValid() && !isEmptyValue(val) {
1163+
transformed["serviceAccountEmail"] = transformedServiceAccountEmail
1164+
}
1165+
1166+
transformedScope, err := expandCloudSchedulerJobHttpTargetOauthTokenScope(original["scope"], d, config)
1167+
if err != nil {
1168+
return nil, err
1169+
} else if val := reflect.ValueOf(transformedScope); val.IsValid() && !isEmptyValue(val) {
1170+
transformed["scope"] = transformedScope
1171+
}
1172+
1173+
return transformed, nil
1174+
}
1175+
1176+
func expandCloudSchedulerJobHttpTargetOauthTokenServiceAccountEmail(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
1177+
return v, nil
1178+
}
1179+
1180+
func expandCloudSchedulerJobHttpTargetOauthTokenScope(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
1181+
return v, nil
1182+
}
1183+
1184+
func expandCloudSchedulerJobHttpTargetOidcToken(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
1185+
l := v.([]interface{})
1186+
if len(l) == 0 || l[0] == nil {
1187+
return nil, nil
1188+
}
1189+
raw := l[0]
1190+
original := raw.(map[string]interface{})
1191+
transformed := make(map[string]interface{})
1192+
1193+
transformedServiceAccountEmail, err := expandCloudSchedulerJobHttpTargetOidcTokenServiceAccountEmail(original["service_account_email"], d, config)
1194+
if err != nil {
1195+
return nil, err
1196+
} else if val := reflect.ValueOf(transformedServiceAccountEmail); val.IsValid() && !isEmptyValue(val) {
1197+
transformed["serviceAccountEmail"] = transformedServiceAccountEmail
1198+
}
1199+
1200+
transformedAudience, err := expandCloudSchedulerJobHttpTargetOidcTokenAudience(original["audience"], d, config)
1201+
if err != nil {
1202+
return nil, err
1203+
} else if val := reflect.ValueOf(transformedAudience); val.IsValid() && !isEmptyValue(val) {
1204+
transformed["audience"] = transformedAudience
1205+
}
1206+
1207+
return transformed, nil
1208+
}
1209+
1210+
func expandCloudSchedulerJobHttpTargetOidcTokenServiceAccountEmail(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
1211+
return v, nil
1212+
}
1213+
1214+
func expandCloudSchedulerJobHttpTargetOidcTokenAudience(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
1215+
return v, nil
1216+
}

google/resource_cloud_scheduler_job_generated_test.go

+96
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,102 @@ resource "google_cloud_scheduler_job" "job" {
157157
`, context)
158158
}
159159

160+
func TestAccCloudSchedulerJob_schedulerJobOauthExample(t *testing.T) {
161+
t.Parallel()
162+
163+
context := map[string]interface{}{
164+
"project_name": getTestProjectFromEnv(),
165+
"region": getTestRegionFromEnv(),
166+
"random_suffix": acctest.RandString(10),
167+
}
168+
169+
resource.Test(t, resource.TestCase{
170+
PreCheck: func() { testAccPreCheck(t) },
171+
Providers: testAccProviders,
172+
CheckDestroy: testAccCheckCloudSchedulerJobDestroy,
173+
Steps: []resource.TestStep{
174+
{
175+
Config: testAccCloudSchedulerJob_schedulerJobOauthExample(context),
176+
},
177+
{
178+
ResourceName: "google_cloud_scheduler_job.job",
179+
ImportState: true,
180+
ImportStateVerify: true,
181+
ImportStateVerifyIgnore: []string{"region"},
182+
},
183+
},
184+
})
185+
}
186+
187+
func testAccCloudSchedulerJob_schedulerJobOauthExample(context map[string]interface{}) string {
188+
return Nprintf(`
189+
data "google_compute_default_service_account" "default" { }
190+
191+
resource "google_cloud_scheduler_job" "job" {
192+
name = "test-job%{random_suffix}"
193+
description = "test http job"
194+
schedule = "*/8 * * * *"
195+
time_zone = "America/New_York"
196+
197+
http_target {
198+
http_method = "GET"
199+
uri = "https://cloudscheduler.googleapis.com/v1/projects/%{project_name}/locations/%{region}/jobs"
200+
201+
oauth_token {
202+
service_account_email = "${data.google_compute_default_service_account.default.email}"
203+
}
204+
}
205+
}
206+
`, context)
207+
}
208+
209+
func TestAccCloudSchedulerJob_schedulerJobOidcExample(t *testing.T) {
210+
t.Parallel()
211+
212+
context := map[string]interface{}{
213+
"random_suffix": acctest.RandString(10),
214+
}
215+
216+
resource.Test(t, resource.TestCase{
217+
PreCheck: func() { testAccPreCheck(t) },
218+
Providers: testAccProviders,
219+
CheckDestroy: testAccCheckCloudSchedulerJobDestroy,
220+
Steps: []resource.TestStep{
221+
{
222+
Config: testAccCloudSchedulerJob_schedulerJobOidcExample(context),
223+
},
224+
{
225+
ResourceName: "google_cloud_scheduler_job.job",
226+
ImportState: true,
227+
ImportStateVerify: true,
228+
ImportStateVerifyIgnore: []string{"region"},
229+
},
230+
},
231+
})
232+
}
233+
234+
func testAccCloudSchedulerJob_schedulerJobOidcExample(context map[string]interface{}) string {
235+
return Nprintf(`
236+
data "google_compute_default_service_account" "default" { }
237+
238+
resource "google_cloud_scheduler_job" "job" {
239+
name = "test-job%{random_suffix}"
240+
description = "test http job"
241+
schedule = "*/8 * * * *"
242+
time_zone = "America/New_York"
243+
244+
http_target {
245+
http_method = "GET"
246+
uri = "https://example.com/ping"
247+
248+
oidc_token {
249+
service_account_email = "${data.google_compute_default_service_account.default.email}"
250+
}
251+
}
252+
}
253+
`, context)
254+
}
255+
160256
func testAccCheckCloudSchedulerJobDestroy(s *terraform.State) error {
161257
for name, rs := range s.RootModule().Resources {
162258
if rs.Type != "google_cloud_scheduler_job" {

0 commit comments

Comments
 (0)