Skip to content

Commit 42be89a

Browse files
selmanjDmitry Vlasov
authored and
Dmitry Vlasov
committed
Add support for Google RuntimeConfig (hashicorp#315)
* Vendor runtimeconfig * Add support for RuntimeConfig config and variable resources This allows users to create/manage Google RuntimeConfig resources and variables. More information here: https://cloud.google.com/deployment-manager/runtime-configurator/ Closes hashicorp#236 * Remove typo * Use top-level declaration rather than init() * Cleanup testing-related code by using ConflictsWith Also adds better comments around how update works
1 parent 890883e commit 42be89a

12 files changed

+6555
-0
lines changed

google/config.go

+9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"google.golang.org/api/dns/v1"
2626
"google.golang.org/api/iam/v1"
2727
"google.golang.org/api/pubsub/v1"
28+
"google.golang.org/api/runtimeconfig/v1beta1"
2829
"google.golang.org/api/servicemanagement/v1"
2930
"google.golang.org/api/sourcerepo/v1"
3031
"google.golang.org/api/spanner/v1"
@@ -46,6 +47,7 @@ type Config struct {
4647
clientDns *dns.Service
4748
clientPubsub *pubsub.Service
4849
clientResourceManager *cloudresourcemanager.Service
50+
clientRuntimeconfig *runtimeconfig.Service
4951
clientSpanner *spanner.Service
5052
clientSourceRepo *sourcerepo.Service
5153
clientStorage *storage.Service
@@ -177,6 +179,13 @@ func (c *Config) loadAndValidate() error {
177179
}
178180
c.clientResourceManager.UserAgent = userAgent
179181

182+
log.Printf("[INFO] Instantiating Google Cloud Runtimeconfig Client...")
183+
c.clientRuntimeconfig, err = runtimeconfig.New(client)
184+
if err != nil {
185+
return err
186+
}
187+
c.clientRuntimeconfig.UserAgent = userAgent
188+
180189
log.Printf("[INFO] Instantiating Google Cloud IAM Client...")
181190
c.clientIAM, err = iam.New(client)
182191
if err != nil {

google/provider.go

+2
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ func Provider() terraform.ResourceProvider {
123123
"google_project_services": resourceGoogleProjectServices(),
124124
"google_pubsub_topic": resourcePubsubTopic(),
125125
"google_pubsub_subscription": resourcePubsubSubscription(),
126+
"google_runtimeconfig_config": resourceRuntimeconfigConfig(),
127+
"google_runtimeconfig_variable": resourceRuntimeconfigVariable(),
126128
"google_service_account": resourceGoogleServiceAccount(),
127129
"google_storage_bucket": resourceStorageBucket(),
128130
"google_storage_bucket_acl": resourceStorageBucketAcl(),
+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
7+
"github.com/hashicorp/terraform/helper/schema"
8+
"google.golang.org/api/runtimeconfig/v1beta1"
9+
)
10+
11+
var runtimeConfigFullName *regexp.Regexp = regexp.MustCompile("^projects/([^/]+)/configs/(.+)$")
12+
13+
func resourceRuntimeconfigConfig() *schema.Resource {
14+
return &schema.Resource{
15+
Create: resourceRuntimeconfigConfigCreate,
16+
Read: resourceRuntimeconfigConfigRead,
17+
Update: resourceRuntimeconfigConfigUpdate,
18+
Delete: resourceRuntimeconfigConfigDelete,
19+
20+
Schema: map[string]*schema.Schema{
21+
"name": {
22+
Type: schema.TypeString,
23+
Required: true,
24+
ForceNew: true,
25+
ValidateFunc: validateGCPName,
26+
},
27+
28+
"description": {
29+
Type: schema.TypeString,
30+
Optional: true,
31+
},
32+
33+
"project": {
34+
Type: schema.TypeString,
35+
Optional: true,
36+
ForceNew: true,
37+
},
38+
},
39+
}
40+
}
41+
42+
func resourceRuntimeconfigConfigCreate(d *schema.ResourceData, meta interface{}) error {
43+
config := meta.(*Config)
44+
45+
project, err := getProject(d, config)
46+
if err != nil {
47+
return err
48+
}
49+
50+
name := d.Get("name").(string)
51+
fullName := resourceRuntimeconfigFullName(project, name)
52+
runtimeConfig := runtimeconfig.RuntimeConfig{
53+
Name: fullName,
54+
}
55+
56+
if val, ok := d.GetOk("description"); ok {
57+
runtimeConfig.Description = val.(string)
58+
}
59+
60+
_, err = config.clientRuntimeconfig.Projects.Configs.Create("projects/"+project, &runtimeConfig).Do()
61+
62+
if err != nil {
63+
return err
64+
}
65+
d.SetId(fullName)
66+
67+
return nil
68+
}
69+
70+
func resourceRuntimeconfigConfigRead(d *schema.ResourceData, meta interface{}) error {
71+
config := meta.(*Config)
72+
73+
fullName := d.Id()
74+
runConfig, err := config.clientRuntimeconfig.Projects.Configs.Get(fullName).Do()
75+
if err != nil {
76+
return err
77+
}
78+
79+
project, name, err := resourceRuntimeconfigParseFullName(runConfig.Name)
80+
if err != nil {
81+
return err
82+
}
83+
// Check to see if project matches our current defined value - if it doesn't, we'll explicitly set it
84+
curProject, err := getProject(d, config)
85+
if err != nil {
86+
return err
87+
}
88+
if project != curProject {
89+
d.Set("project", project)
90+
}
91+
92+
d.Set("name", name)
93+
d.Set("description", runConfig.Description)
94+
95+
return nil
96+
}
97+
98+
func resourceRuntimeconfigConfigUpdate(d *schema.ResourceData, meta interface{}) error {
99+
config := meta.(*Config)
100+
101+
// Update works more like an 'overwrite' method - we build a new runtimeconfig.RuntimeConfig struct and it becomes
102+
// the new config. This means our Update logic looks an awful lot like Create (and hence, doesn't use
103+
// schema.ResourceData.hasChange()).
104+
fullName := d.Id()
105+
runtimeConfig := runtimeconfig.RuntimeConfig{
106+
Name: fullName,
107+
}
108+
if v, ok := d.GetOk("description"); ok {
109+
runtimeConfig.Description = v.(string)
110+
}
111+
112+
_, err := config.clientRuntimeconfig.Projects.Configs.Update(fullName, &runtimeConfig).Do()
113+
if err != nil {
114+
return err
115+
}
116+
return nil
117+
}
118+
119+
func resourceRuntimeconfigConfigDelete(d *schema.ResourceData, meta interface{}) error {
120+
config := meta.(*Config)
121+
122+
fullName := d.Id()
123+
124+
_, err := config.clientRuntimeconfig.Projects.Configs.Delete(fullName).Do()
125+
if err != nil {
126+
return err
127+
}
128+
d.SetId("")
129+
return nil
130+
}
131+
132+
// resourceRuntimeconfigFullName turns a given project and a 'short name' for a runtime config into a full name
133+
// (e.g. projects/my-project/configs/my-config).
134+
func resourceRuntimeconfigFullName(project, name string) string {
135+
return fmt.Sprintf("projects/%s/configs/%s", project, name)
136+
}
137+
138+
// resourceRuntimeconfigParseFullName parses a full name (e.g. projects/my-project/configs/my-config) by parsing out the
139+
// project and the short name. Returns "", "", nil upon error.
140+
func resourceRuntimeconfigParseFullName(fullName string) (project, name string, err error) {
141+
matches := runtimeConfigFullName.FindStringSubmatch(fullName)
142+
if matches == nil {
143+
return "", "", fmt.Errorf("Given full name doesn't match expected regexp; fullname = '%s'", fullName)
144+
}
145+
return matches[1], matches[2], nil
146+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform/helper/acctest"
8+
"github.com/hashicorp/terraform/helper/resource"
9+
"github.com/hashicorp/terraform/terraform"
10+
"google.golang.org/api/runtimeconfig/v1beta1"
11+
)
12+
13+
func TestAccRuntimeconfigConfig_basic(t *testing.T) {
14+
var runtimeConfig runtimeconfig.RuntimeConfig
15+
configName := fmt.Sprintf("runtimeconfig-test-%s", acctest.RandString(10))
16+
description := "my test description"
17+
18+
resource.Test(t, resource.TestCase{
19+
PreCheck: func() { testAccPreCheck(t) },
20+
Providers: testAccProviders,
21+
CheckDestroy: testAccCheckRuntimeconfigConfigDestroy,
22+
Steps: []resource.TestStep{
23+
{
24+
Config: testAccRuntimeconfigConfig_basicDescription(configName, description),
25+
Check: resource.ComposeTestCheckFunc(
26+
testAccCheckRuntimeConfigExists(
27+
"google_runtimeconfig_config.foobar", &runtimeConfig),
28+
testAccCheckRuntimeConfigDescription(&runtimeConfig, description),
29+
),
30+
},
31+
},
32+
})
33+
}
34+
35+
func TestAccRuntimeconfig_update(t *testing.T) {
36+
var runtimeConfig runtimeconfig.RuntimeConfig
37+
configName := fmt.Sprintf("runtimeconfig-test-%s", acctest.RandString(10))
38+
firstDescription := "my test description"
39+
secondDescription := "my updated test description"
40+
41+
resource.Test(t, resource.TestCase{
42+
PreCheck: func() { testAccPreCheck(t) },
43+
Providers: testAccProviders,
44+
CheckDestroy: testAccCheckRuntimeconfigConfigDestroy,
45+
Steps: []resource.TestStep{
46+
{
47+
Config: testAccRuntimeconfigConfig_basicDescription(configName, firstDescription),
48+
Check: resource.ComposeTestCheckFunc(
49+
testAccCheckRuntimeConfigExists(
50+
"google_runtimeconfig_config.foobar", &runtimeConfig),
51+
testAccCheckRuntimeConfigDescription(&runtimeConfig, firstDescription),
52+
),
53+
}, {
54+
Config: testAccRuntimeconfigConfig_basicDescription(configName, secondDescription),
55+
Check: resource.ComposeTestCheckFunc(
56+
testAccCheckRuntimeConfigExists(
57+
"google_runtimeconfig_config.foobar", &runtimeConfig),
58+
testAccCheckRuntimeConfigDescription(&runtimeConfig, secondDescription),
59+
),
60+
},
61+
},
62+
})
63+
}
64+
65+
func TestAccRuntimeconfig_updateEmptyDescription(t *testing.T) {
66+
var runtimeConfig runtimeconfig.RuntimeConfig
67+
configName := fmt.Sprintf("runtimeconfig-test-%s", acctest.RandString(10))
68+
description := "my test description"
69+
70+
resource.Test(t, resource.TestCase{
71+
PreCheck: func() { testAccPreCheck(t) },
72+
Providers: testAccProviders,
73+
CheckDestroy: testAccCheckRuntimeconfigConfigDestroy,
74+
Steps: []resource.TestStep{
75+
{
76+
Config: testAccRuntimeconfigConfig_basicDescription(configName, description),
77+
Check: resource.ComposeTestCheckFunc(
78+
testAccCheckRuntimeConfigExists(
79+
"google_runtimeconfig_config.foobar", &runtimeConfig),
80+
testAccCheckRuntimeConfigDescription(&runtimeConfig, description),
81+
),
82+
}, {
83+
Config: testAccRuntimeconfigConfig_emptyDescription(configName),
84+
Check: resource.ComposeTestCheckFunc(
85+
testAccCheckRuntimeConfigExists(
86+
"google_runtimeconfig_config.foobar", &runtimeConfig),
87+
testAccCheckRuntimeConfigDescription(&runtimeConfig, ""),
88+
),
89+
},
90+
},
91+
})
92+
}
93+
94+
func testAccCheckRuntimeConfigDescription(runtimeConfig *runtimeconfig.RuntimeConfig, description string) resource.TestCheckFunc {
95+
return func(s *terraform.State) error {
96+
if runtimeConfig.Description != description {
97+
return fmt.Errorf("On runtime config '%s', expected description '%s', but found '%s'",
98+
runtimeConfig.Name, description, runtimeConfig.Description)
99+
}
100+
return nil
101+
}
102+
}
103+
104+
func testAccCheckRuntimeConfigExists(resourceName string, runtimeConfig *runtimeconfig.RuntimeConfig) resource.TestCheckFunc {
105+
return func(s *terraform.State) error {
106+
rs, ok := s.RootModule().Resources[resourceName]
107+
if !ok {
108+
return fmt.Errorf("Not found: %s", resourceName)
109+
}
110+
111+
if rs.Primary.ID == "" {
112+
return fmt.Errorf("No ID is set")
113+
}
114+
115+
config := testAccProvider.Meta().(*Config)
116+
117+
found, err := config.clientRuntimeconfig.Projects.Configs.Get(rs.Primary.ID).Do()
118+
if err != nil {
119+
return err
120+
}
121+
122+
*runtimeConfig = *found
123+
124+
return nil
125+
}
126+
}
127+
128+
func testAccCheckRuntimeconfigConfigDestroy(s *terraform.State) error {
129+
config := testAccProvider.Meta().(*Config)
130+
131+
for _, rs := range s.RootModule().Resources {
132+
if rs.Type != "google_runtimeconfig_config" {
133+
continue
134+
}
135+
136+
_, err := config.clientRuntimeconfig.Projects.Configs.Get(rs.Primary.ID).Do()
137+
138+
if err == nil {
139+
return fmt.Errorf("Runtimeconfig still exists")
140+
}
141+
}
142+
143+
return nil
144+
}
145+
146+
func testAccRuntimeconfigConfig_basicDescription(name, description string) string {
147+
return fmt.Sprintf(`
148+
resource "google_runtimeconfig_config" "foobar" {
149+
name = "%s"
150+
description = "%s"
151+
}`, name, description)
152+
}
153+
154+
func testAccRuntimeconfigConfig_emptyDescription(name string) string {
155+
return fmt.Sprintf(`
156+
resource "google_runtimeconfig_config" "foobar" {
157+
name = "%s"
158+
}`, name)
159+
}

0 commit comments

Comments
 (0)