Skip to content

Commit c1eeb70

Browse files
Add resource for enabling firebase (#3281) (#1885)
* Add resource for enabling firebase * removing whitespace for GA terraform * Change skip_delete to resource level property Signed-off-by: Modular Magician <[email protected]>
1 parent 61737b6 commit c1eeb70

File tree

8 files changed

+435
-2
lines changed

8 files changed

+435
-2
lines changed

.changelog/3281.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:new-resource
2+
firebase: Add the `google_firebase_project` resource which will enable Firebase for a referenced Google project.
3+
```

google-beta/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ type Config struct {
9999
DialogflowBasePath string
100100
DNSBasePath string
101101
FilestoreBasePath string
102+
FirebaseBasePath string
102103
FirestoreBasePath string
103104
GameServicesBasePath string
104105
HealthcareBasePath string
@@ -242,6 +243,7 @@ var DeploymentManagerDefaultBasePath = "https://www.googleapis.com/deploymentman
242243
var DialogflowDefaultBasePath = "https://dialogflow.googleapis.com/v2/"
243244
var DNSDefaultBasePath = "https://www.googleapis.com/dns/v1beta2/"
244245
var FilestoreDefaultBasePath = "https://file.googleapis.com/v1/"
246+
var FirebaseDefaultBasePath = "https://firebase.googleapis.com/v1beta1/"
245247
var FirestoreDefaultBasePath = "https://firestore.googleapis.com/v1/"
246248
var GameServicesDefaultBasePath = "https://gameservices.googleapis.com/v1beta/"
247249
var HealthcareDefaultBasePath = "https://healthcare.googleapis.com/v1beta1/"
@@ -758,6 +760,7 @@ func ConfigureBasePaths(c *Config) {
758760
c.DialogflowBasePath = DialogflowDefaultBasePath
759761
c.DNSBasePath = DNSDefaultBasePath
760762
c.FilestoreBasePath = FilestoreDefaultBasePath
763+
c.FirebaseBasePath = FirebaseDefaultBasePath
761764
c.FirestoreBasePath = FirestoreDefaultBasePath
762765
c.GameServicesBasePath = GameServicesDefaultBasePath
763766
c.HealthcareBasePath = HealthcareDefaultBasePath

google-beta/firebase_operation.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// ----------------------------------------------------------------------------
2+
//
3+
// *** AUTO GENERATED CODE *** AUTO GENERATED CODE ***
4+
//
5+
// ----------------------------------------------------------------------------
6+
//
7+
// This file is automatically generated by Magic Modules and manual
8+
// changes will be clobbered when the file is regenerated.
9+
//
10+
// Please read more about how to change this file in
11+
// .github/CONTRIBUTING.md.
12+
//
13+
// ----------------------------------------------------------------------------
14+
package google
15+
16+
import (
17+
"encoding/json"
18+
"fmt"
19+
)
20+
21+
type FirebaseOperationWaiter struct {
22+
Config *Config
23+
Project string
24+
CommonOperationWaiter
25+
}
26+
27+
func (w *FirebaseOperationWaiter) QueryOp() (interface{}, error) {
28+
if w == nil {
29+
return nil, fmt.Errorf("Cannot query operation, it's unset or nil.")
30+
}
31+
// Returns the proper get.
32+
url := fmt.Sprintf("https://firebase.googleapis.com/v1beta1/%s", w.CommonOperationWaiter.Op.Name)
33+
return sendRequest(w.Config, "GET", w.Project, url, nil)
34+
}
35+
36+
func createFirebaseWaiter(config *Config, op map[string]interface{}, project, activity string) (*FirebaseOperationWaiter, error) {
37+
if val, ok := op["name"]; !ok || val == "" {
38+
// This was a synchronous call - there is no operation to wait for.
39+
return nil, nil
40+
}
41+
w := &FirebaseOperationWaiter{
42+
Config: config,
43+
Project: project,
44+
}
45+
if err := w.CommonOperationWaiter.SetOp(op); err != nil {
46+
return nil, err
47+
}
48+
return w, nil
49+
}
50+
51+
// nolint: deadcode,unused
52+
func firebaseOperationWaitTimeWithResponse(config *Config, op map[string]interface{}, response *map[string]interface{}, project, activity string, timeoutMinutes int) error {
53+
w, err := createFirebaseWaiter(config, op, project, activity)
54+
if err != nil || w == nil {
55+
// If w is nil, the op was synchronous.
56+
return err
57+
}
58+
if err := OperationWait(w, activity, timeoutMinutes, config.PollInterval); err != nil {
59+
return err
60+
}
61+
return json.Unmarshal([]byte(w.CommonOperationWaiter.Op.Response), response)
62+
}
63+
64+
func firebaseOperationWaitTime(config *Config, op map[string]interface{}, project, activity string, timeoutMinutes int) error {
65+
w, err := createFirebaseWaiter(config, op, project, activity)
66+
if err != nil || w == nil {
67+
// If w is nil, the op was synchronous.
68+
return err
69+
}
70+
return OperationWait(w, activity, timeoutMinutes, config.PollInterval)
71+
}

google-beta/provider.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,14 @@ func Provider() terraform.ResourceProvider {
285285
"GOOGLE_FILESTORE_CUSTOM_ENDPOINT",
286286
}, FilestoreDefaultBasePath),
287287
},
288+
"firebase_custom_endpoint": {
289+
Type: schema.TypeString,
290+
Optional: true,
291+
ValidateFunc: validateCustomEndpoint,
292+
DefaultFunc: schema.MultiEnvDefaultFunc([]string{
293+
"GOOGLE_FIREBASE_CUSTOM_ENDPOINT",
294+
}, FirebaseDefaultBasePath),
295+
},
288296
"firestore_custom_endpoint": {
289297
Type: schema.TypeString,
290298
Optional: true,
@@ -578,9 +586,9 @@ func Provider() terraform.ResourceProvider {
578586
return provider
579587
}
580588

581-
// Generated resources: 134
589+
// Generated resources: 135
582590
// Generated IAM resources: 54
583-
// Total generated resources: 188
591+
// Total generated resources: 189
584592
func ResourceMap() map[string]*schema.Resource {
585593
resourceMap, _ := ResourceMapWithErrors()
586594
return resourceMap
@@ -690,6 +698,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
690698
"google_dns_managed_zone": resourceDNSManagedZone(),
691699
"google_dns_policy": resourceDNSPolicy(),
692700
"google_filestore_instance": resourceFilestoreInstance(),
701+
"google_firebase_project": resourceFirebaseProject(),
693702
"google_firestore_index": resourceFirestoreIndex(),
694703
"google_game_services_realm": resourceGameServicesRealm(),
695704
"google_game_services_game_server_cluster": resourceGameServicesGameServerCluster(),
@@ -960,6 +969,7 @@ func providerConfigure(d *schema.ResourceData, p *schema.Provider, terraformVers
960969
config.DialogflowBasePath = d.Get("dialogflow_custom_endpoint").(string)
961970
config.DNSBasePath = d.Get("dns_custom_endpoint").(string)
962971
config.FilestoreBasePath = d.Get("filestore_custom_endpoint").(string)
972+
config.FirebaseBasePath = d.Get("firebase_custom_endpoint").(string)
963973
config.FirestoreBasePath = d.Get("firestore_custom_endpoint").(string)
964974
config.GameServicesBasePath = d.Get("game_services_custom_endpoint").(string)
965975
config.HealthcareBasePath = d.Get("healthcare_custom_endpoint").(string)
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// ----------------------------------------------------------------------------
2+
//
3+
// *** AUTO GENERATED CODE *** AUTO GENERATED CODE ***
4+
//
5+
// ----------------------------------------------------------------------------
6+
//
7+
// This file is automatically generated by Magic Modules and manual
8+
// changes will be clobbered when the file is regenerated.
9+
//
10+
// Please read more about how to change this file in
11+
// .github/CONTRIBUTING.md.
12+
//
13+
// ----------------------------------------------------------------------------
14+
15+
package google
16+
17+
import (
18+
"fmt"
19+
"log"
20+
"time"
21+
22+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
23+
)
24+
25+
func resourceFirebaseProject() *schema.Resource {
26+
return &schema.Resource{
27+
Create: resourceFirebaseProjectCreate,
28+
Read: resourceFirebaseProjectRead,
29+
Delete: resourceFirebaseProjectDelete,
30+
31+
Importer: &schema.ResourceImporter{
32+
State: resourceFirebaseProjectImport,
33+
},
34+
35+
Timeouts: &schema.ResourceTimeout{
36+
Create: schema.DefaultTimeout(10 * time.Minute),
37+
Delete: schema.DefaultTimeout(10 * time.Minute),
38+
},
39+
40+
Schema: map[string]*schema.Schema{
41+
"display_name": {
42+
Type: schema.TypeString,
43+
Computed: true,
44+
Description: `The GCP project display name`,
45+
},
46+
"project_number": {
47+
Type: schema.TypeString,
48+
Computed: true,
49+
Description: `The number of the google project that firebase is enabled on.`,
50+
},
51+
"project": {
52+
Type: schema.TypeString,
53+
Optional: true,
54+
Computed: true,
55+
ForceNew: true,
56+
},
57+
},
58+
}
59+
}
60+
61+
func resourceFirebaseProjectCreate(d *schema.ResourceData, meta interface{}) error {
62+
config := meta.(*Config)
63+
64+
obj := make(map[string]interface{})
65+
66+
url, err := replaceVars(d, config, "{{FirebaseBasePath}}projects/{{project}}:addFirebase")
67+
if err != nil {
68+
return err
69+
}
70+
71+
log.Printf("[DEBUG] Creating new Project: %#v", obj)
72+
project, err := getProject(d, config)
73+
if err != nil {
74+
return err
75+
}
76+
res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutCreate))
77+
if err != nil {
78+
return fmt.Errorf("Error creating Project: %s", err)
79+
}
80+
81+
// Store the ID now
82+
id, err := replaceVars(d, config, "projects/{{project}}/{{name}}")
83+
if err != nil {
84+
return fmt.Errorf("Error constructing id: %s", err)
85+
}
86+
d.SetId(id)
87+
88+
err = firebaseOperationWaitTime(
89+
config, res, project, "Creating Project",
90+
int(d.Timeout(schema.TimeoutCreate).Minutes()))
91+
92+
if err != nil {
93+
// The resource didn't actually create
94+
d.SetId("")
95+
return fmt.Errorf("Error waiting to create Project: %s", err)
96+
}
97+
98+
log.Printf("[DEBUG] Finished creating Project %q: %#v", d.Id(), res)
99+
100+
return resourceFirebaseProjectRead(d, meta)
101+
}
102+
103+
func resourceFirebaseProjectRead(d *schema.ResourceData, meta interface{}) error {
104+
config := meta.(*Config)
105+
106+
url, err := replaceVars(d, config, "{{FirebaseBasePath}}projects/{{project}}/{{name}}")
107+
if err != nil {
108+
return err
109+
}
110+
111+
project, err := getProject(d, config)
112+
if err != nil {
113+
return err
114+
}
115+
res, err := sendRequest(config, "GET", project, url, nil)
116+
if err != nil {
117+
return handleNotFoundError(err, d, fmt.Sprintf("FirebaseProject %q", d.Id()))
118+
}
119+
120+
if err := d.Set("project", project); err != nil {
121+
return fmt.Errorf("Error reading Project: %s", err)
122+
}
123+
124+
if err := d.Set("project_number", flattenFirebaseProjectProjectNumber(res["projectNumber"], d, config)); err != nil {
125+
return fmt.Errorf("Error reading Project: %s", err)
126+
}
127+
if err := d.Set("display_name", flattenFirebaseProjectDisplayName(res["displayName"], d, config)); err != nil {
128+
return fmt.Errorf("Error reading Project: %s", err)
129+
}
130+
131+
return nil
132+
}
133+
134+
func resourceFirebaseProjectDelete(d *schema.ResourceData, meta interface{}) error {
135+
log.Printf("[WARNING] Firebase Project resources"+
136+
" cannot be deleted from GCP. The resource %s will be removed from Terraform"+
137+
" state, but will still be present on the server.", d.Id())
138+
d.SetId("")
139+
140+
return nil
141+
}
142+
143+
func resourceFirebaseProjectImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
144+
config := meta.(*Config)
145+
if err := parseImportId([]string{
146+
"projects/(?P<project>[^/]+)",
147+
"(?P<project>[^/]+)",
148+
}, d, config); err != nil {
149+
return nil, err
150+
}
151+
152+
// Replace import id for the resource id
153+
id, err := replaceVars(d, config, "projects/{{project}}/{{name}}")
154+
if err != nil {
155+
return nil, fmt.Errorf("Error constructing id: %s", err)
156+
}
157+
d.SetId(id)
158+
159+
return []*schema.ResourceData{d}, nil
160+
}
161+
162+
func flattenFirebaseProjectProjectNumber(v interface{}, d *schema.ResourceData, config *Config) interface{} {
163+
return v
164+
}
165+
166+
func flattenFirebaseProjectDisplayName(v interface{}, d *schema.ResourceData, config *Config) interface{} {
167+
return v
168+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// ----------------------------------------------------------------------------
2+
//
3+
// *** AUTO GENERATED CODE *** AUTO GENERATED CODE ***
4+
//
5+
// ----------------------------------------------------------------------------
6+
//
7+
// This file is automatically generated by Magic Modules and manual
8+
// changes will be clobbered when the file is regenerated.
9+
//
10+
// Please read more about how to change this file in
11+
// .github/CONTRIBUTING.md.
12+
//
13+
// ----------------------------------------------------------------------------
14+
15+
package google
16+
17+
import (
18+
"testing"
19+
20+
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
21+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
22+
)
23+
24+
func TestAccFirebaseProject_firebaseProjectBasicExample(t *testing.T) {
25+
t.Parallel()
26+
27+
context := map[string]interface{}{
28+
"org_id": getTestOrgFromEnv(t),
29+
"random_suffix": acctest.RandString(10),
30+
}
31+
32+
resource.Test(t, resource.TestCase{
33+
PreCheck: func() { testAccPreCheck(t) },
34+
Providers: testAccProvidersOiCS,
35+
Steps: []resource.TestStep{
36+
{
37+
Config: testAccFirebaseProject_firebaseProjectBasicExample(context),
38+
},
39+
},
40+
})
41+
}
42+
43+
func testAccFirebaseProject_firebaseProjectBasicExample(context map[string]interface{}) string {
44+
return Nprintf(`
45+
resource "google_project" "default" {
46+
provider = google-beta
47+
48+
project_id = "tf-test%{random_suffix}"
49+
name = "tf-test%{random_suffix}"
50+
org_id = "%{org_id}"
51+
}
52+
53+
resource "google_firebase_project" "default" {
54+
provider = google-beta
55+
project = google_project.default.project_id
56+
}
57+
`, context)
58+
}

0 commit comments

Comments
 (0)