Skip to content

Commit

Permalink
Terraform Data Source to retrieve SQL instance CA certs
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
chrissng authored and modular-magician committed Jan 6, 2020
1 parent 90324a0 commit fe2740d
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 13 deletions.
87 changes: 87 additions & 0 deletions google/data_source_google_sql_ca_certs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package google

import (
"fmt"
"log"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func dataSourceGoogleSQLCaCerts() *schema.Resource {
return &schema.Resource{
Read: dataSourceGoogleSQLCaCertsRead,

Schema: map[string]*schema.Schema{
"instance": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: compareSelfLinkOrResourceName,
},
"project": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
},
"active_version": {
Type: schema.TypeString,
Computed: true,
},
"certs": {
Type: schema.TypeList,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cert": {
Type: schema.TypeString,
Computed: true,
},
"common_name": {
Type: schema.TypeString,
Computed: true,
},
"create_time": {
Type: schema.TypeString,
Computed: true,
},
"expiration_time": {
Type: schema.TypeString,
Computed: true,
},
"sha1_fingerprint": {
Type: schema.TypeString,
Computed: true,
},
},
},
Computed: true,
},
},
}
}

func dataSourceGoogleSQLCaCertsRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

fv, err := parseProjectFieldValue("instances", d.Get("instance").(string), "project", d, config, false)
if err != nil {
return err
}
project := fv.Project
instance := fv.Name

log.Printf("[DEBUG] Fetching CA certs from instance %s", instance)

response, err := config.clientSqlAdmin.Instances.ListServerCas(project, instance).Do()
if err != nil {
return fmt.Errorf("error retrieving CA certs: %s", err)
}

log.Printf("[DEBUG] Fetched CA certs from instance %s", instance)

d.Set("project", project)
d.Set("certs", flattenServerCaCerts(response.Certs))
d.Set("active_version", response.ActiveVersion)
d.SetId(fmt.Sprintf("projects/%s/instance/%s", project, instance))

return nil
}
92 changes: 92 additions & 0 deletions google/data_source_google_sql_ca_certs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccDataSourceGoogleSQLCaCerts_basic(t *testing.T) {
t.Parallel()

instanceName := fmt.Sprintf("data-ssl-ca-cert-test-%s", acctest.RandString(10))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccDataSourceGoogleSQLCaCertsConfig(instanceName),
Check: resource.ComposeTestCheckFunc(
testAccDataSourceGoogleSQLCaCertsCheck("data.google_sql_ca_certs.ca_certs", "google_sql_database_instance.foo"),
testAccDataSourceGoogleSQLCaCertsCheck("data.google_sql_ca_certs.ca_certs_self_link", "google_sql_database_instance.foo"),
resource.TestCheckResourceAttr("data.google_sql_ca_certs.ca_certs", "certs.#", "1"),
resource.TestCheckResourceAttr("data.google_sql_ca_certs.ca_certs_self_link", "certs.#", "1"),
),
},
},
})
}

func testAccDataSourceGoogleSQLCaCertsCheck(datasourceName string, resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
ds, ok := s.RootModule().Resources[datasourceName]
if !ok {
return fmt.Errorf("root module has no resource called %s", datasourceName)
}

rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("can't find %s in state", resourceName)
}

datasourceAttributes := ds.Primary.Attributes
resourceAttributes := rs.Primary.Attributes

instanceToDatasourceAttrsMapping := map[string]string{
"server_ca_cert.0.cert": "certs.0.cert",
"server_ca_cert.0.common_name": "certs.0.common_name",
"server_ca_cert.0.create_time": "certs.0.create_time",
"server_ca_cert.0.expiration_time": "certs.0.expiration_time",
"server_ca_cert.0.sha1_fingerprint": "certs.0.sha1_fingerprint",
}

for resourceAttr, datasourceAttr := range instanceToDatasourceAttrsMapping {
if resourceAttributes[resourceAttr] != datasourceAttributes[datasourceAttr] {
return fmt.Errorf(
"%s is %s; want %s",
datasourceAttr,
datasourceAttributes[datasourceAttr],
resourceAttributes[resourceAttr],
)
}
}

return nil
}
}

func testAccDataSourceGoogleSQLCaCertsConfig(instanceName string) string {
return fmt.Sprintf(`
resource "google_sql_database_instance" "foo" {
name = "%s"
region = "us-central1"
settings {
tier = "db-f1-micro"
crash_safe_replication = false
}
}
data "google_sql_ca_certs" "ca_certs" {
instance = google_sql_database_instance.foo.name
}
data "google_sql_ca_certs" "ca_certs_self_link" {
instance = google_sql_database_instance.foo.self_link
}
`, instanceName)
}
1 change: 1 addition & 0 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ func Provider() terraform.ResourceProvider {
"google_service_account": dataSourceGoogleServiceAccount(),
"google_service_account_access_token": dataSourceGoogleServiceAccountAccessToken(),
"google_service_account_key": dataSourceGoogleServiceAccountKey(),
"google_sql_ca_certs": dataSourceGoogleSQLCaCerts(),
"google_storage_bucket_object": dataSourceGoogleStorageBucketObject(),
"google_storage_object_signed_url": dataSourceGoogleSignedUrl(),
"google_storage_project_service_account": dataSourceGoogleStorageProjectServiceAccount(),
Expand Down
28 changes: 15 additions & 13 deletions google/resource_sql_database_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) e
d.Set("public_ip_address", publicIpAddress)
d.Set("private_ip_address", privateIpAddress)

if err := d.Set("server_ca_cert", flattenServerCaCert(instance.ServerCaCert)); err != nil {
if err := d.Set("server_ca_cert", flattenServerCaCerts([]*sqladmin.SslCert{instance.ServerCaCert})); err != nil {
log.Printf("[WARN] Failed to set SQL Database CA Certificate")
}

Expand Down Expand Up @@ -1094,22 +1094,24 @@ func flattenIpAddresses(ipAddresses []*sqladmin.IpMapping) []map[string]interfac
return ips
}

func flattenServerCaCert(caCert *sqladmin.SslCert) []map[string]interface{} {
var cert []map[string]interface{}
func flattenServerCaCerts(caCerts []*sqladmin.SslCert) []map[string]interface{} {
var certs []map[string]interface{}

for _, caCert := range caCerts {
if caCert != nil {
data := map[string]interface{}{
"cert": caCert.Cert,
"common_name": caCert.CommonName,
"create_time": caCert.CreateTime,
"expiration_time": caCert.ExpirationTime,
"sha1_fingerprint": caCert.Sha1Fingerprint,
}

if caCert != nil {
data := map[string]interface{}{
"cert": caCert.Cert,
"common_name": caCert.CommonName,
"create_time": caCert.CreateTime,
"expiration_time": caCert.ExpirationTime,
"sha1_fingerprint": caCert.Sha1Fingerprint,
certs = append(certs, data)
}

cert = append(cert, data)
}

return cert
return certs
}

func instanceMutexKey(project, instance_name string) string {
Expand Down
58 changes: 58 additions & 0 deletions website/docs/d/datasource_google_sql_ca_certs.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
subcategory: "Cloud SQL"
layout: "google"
page_title: "Google: google_sql_ca_certs"
sidebar_current: "docs-google-datasource-sql-ca-certs"
description: |-
Get all of the trusted Certificate Authorities (CAs) for the specified SQL database instance.
---

# google\_sql\_ca\_certs

Get all of the trusted Certificate Authorities (CAs) for the specified SQL database instance. For more information see the
[official documentation](https://cloud.google.com/sql/)
and
[API](https://cloud.google.com/sql/docs/mysql/admin-api/rest/v1beta4/instances/listServerCas).


## Example Usage

```hcl
data "google_sql_ca_certs" "ca_certs" {
instance = "primary-database-server"
}
locals {
furthest_expiration_time = reverse(sort([for k, v in data.google_sql_ca_certs.ca_certs.certs : v.expiration_time]))[0]
latest_ca_cert = [for v in data.google_sql_ca_certs.ca_certs.certs : v.cert if v.expiration_time == local.furthest_expiration_time]
}
output "db_latest_ca_cert" {
description = "Latest CA cert used by the primary database server"
value = local.latest_ca_cert
sensitive = true
}
```

## Argument Reference

The following arguments are supported:

* `instance` - (Required) The name or self link of the instance.

---

* `project` - (Optional) The ID of the project in which the resource belongs. If `project` is not provided, the provider project is used.

## Attributes Reference

The following attributes are exported:

* `active_version` - SHA1 fingerprint of the currently active CA certificate.

* `certs` - A list of server CA certificates for the instance. Each contains:
* `cert` - The CA certificate used to connect to the SQL instance via SSL.
* `common_name` - The CN valid for the CA cert.
* `create_time` - Creation time of the CA cert.
* `expiration_time` - Expiration time of the CA cert.
* `sha1_fingerprint` - SHA1 fingerprint of the CA cert.

0 comments on commit fe2740d

Please sign in to comment.