Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(integration): add google api integration #1589

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion docs/resources/api_integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,30 @@ description: |-
## Example Usage

```terraform
resource "snowflake_api_integration" "api_integration" {
resource "snowflake_api_integration" "aws" {
name = "aws_integration"
api_provider = "aws_api_gateway"
api_aws_role_arn = "arn:aws:iam::000000000001:/role/test"
api_allowed_prefixes = ["https://123456.execute-api.us-west-2.amazonaws.com/prod/"]
enabled = true
}

resource "snowflake_api_integration" "azure" {
name = "azure_integration"
api_provider = "azure_api_management"
azure_tenant_id = "00000000-0000-0000-0000-000000000000"
azure_ad_application_id = "11111111-1111-1111-1111-111111111111"
api_allowed_prefixes = ["https://apim-hello-world.azure-api.net/"]
enabled = true
}

resource "snowflake_api_integration" "gcp" {
name = "gcp_integration"
api_provider = "google_api_gateway"
google_audience = "api-gateway-id-123456.apigateway.gcp-project.cloud.goog"
api_allowed_prefixes = ["https://gateway-id-123456.uc.gateway.dev/"]
enabled = true
}
```

<!-- schema generated by tfplugindocs -->
Expand All @@ -38,7 +55,9 @@ resource "snowflake_api_integration" "api_integration" {
- `api_key` (String, Sensitive) The API key (also called a “subscription key”).
- `azure_ad_application_id` (String) The 'Application (client) id' of the Azure AD app for your remote service.
- `azure_tenant_id` (String) Specifies the ID for your Office 365 tenant that all Azure API Management instances belong to.
- `comment` (String)
- `enabled` (Boolean) Specifies whether this API integration is enabled or disabled. If the API integration is disabled, any external function that relies on it will not work.
- `google_audience` (String) The audience claim when generating the JWT (JSON Web Token) to authenticate to the Google API Gateway.

### Read-Only

Expand Down
19 changes: 18 additions & 1 deletion examples/resources/snowflake_api_integration/resource.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
resource "snowflake_api_integration" "api_integration" {
resource "snowflake_api_integration" "aws" {
name = "aws_integration"
api_provider = "aws_api_gateway"
api_aws_role_arn = "arn:aws:iam::000000000001:/role/test"
api_allowed_prefixes = ["https://123456.execute-api.us-west-2.amazonaws.com/prod/"]
enabled = true
}

resource "snowflake_api_integration" "azure" {
name = "azure_integration"
api_provider = "azure_api_management"
azure_tenant_id = "00000000-0000-0000-0000-000000000000"
azure_ad_application_id = "11111111-1111-1111-1111-111111111111"
api_allowed_prefixes = ["https://apim-hello-world.azure-api.net/"]
enabled = true
}

resource "snowflake_api_integration" "gcp" {
name = "gcp_integration"
api_provider = "google_api_gateway"
google_audience = "api-gateway-id-123456.apigateway.gcp-project.cloud.goog"
api_allowed_prefixes = ["https://gateway-id-123456.uc.gateway.dev/"]
enabled = true
}
39 changes: 38 additions & 1 deletion pkg/resources/api_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var apiIntegrationSchema = map[string]*schema.Schema{
"api_provider": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"aws_api_gateway", "aws_private_api_gateway", "azure_api_management", "aws_gov_api_gateway", "aws_gov_private_api_gateway"}, false),
ValidateFunc: validation.StringInSlice([]string{"aws_api_gateway", "aws_private_api_gateway", "azure_api_management", "aws_gov_api_gateway", "aws_gov_private_api_gateway", "google_api_gateway"}, false),
Description: "Specifies the HTTPS proxy service type.",
},
"api_aws_role_arn": {
Expand Down Expand Up @@ -64,6 +64,12 @@ var apiIntegrationSchema = map[string]*schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"google_audience": {
Type: schema.TypeString,
Optional: true,
Default: "",
Description: "The audience claim when generating the JWT (JSON Web Token) to authenticate to the Google API Gateway.",
},
"api_allowed_prefixes": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Expand All @@ -89,6 +95,10 @@ var apiIntegrationSchema = map[string]*schema.Schema{
Default: true,
Description: "Specifies whether this API integration is enabled or disabled. If the API integration is disabled, any external function that relies on it will not work.",
},
"comment": {
Type: schema.TypeString,
Optional: true,
},
"created_on": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -132,6 +142,10 @@ func CreateAPIIntegration(d *schema.ResourceData, meta interface{}) error {
stmt.SetString("API_KEY", d.Get("api_key").(string))
}

if _, ok := d.GetOk("comment"); ok {
stmt.SetString("COMMENT", d.Get("comment").(string))
}

// Now, set the API provider
if err := setAPIProviderSettings(d, stmt); err != nil {
return err
Expand Down Expand Up @@ -175,6 +189,10 @@ func ReadAPIIntegration(d *schema.ResourceData, meta interface{}) error {
return err
}

if err := d.Set("comment", s.Comment.String); err != nil {
return err
}

if err := d.Set("created_on", s.CreatedOn.String); err != nil {
return err
}
Expand Down Expand Up @@ -230,6 +248,10 @@ func ReadAPIIntegration(d *schema.ResourceData, meta interface{}) error {
if err := d.Set("azure_multi_tenant_app_name", v.(string)); err != nil {
return err
}
case "GOOGLE_AUDIENCE":
if err := d.Set("google_audience", v.(string)); err != nil {
return err
}
default:
log.Printf("[WARN] unexpected api integration property %v returned from Snowflake", k)
}
Expand Down Expand Up @@ -262,6 +284,11 @@ func UpdateAPIIntegration(d *schema.ResourceData, meta interface{}) error {
stmt.SetString("API_KEY", d.Get("api_key").(string))
}

if d.HasChange("comment") {
runSetStatement = true
stmt.SetString("COMMENT", d.Get("comment").(string))
}

// We need to UNSET this if we remove all api blocked prefixes.
if d.HasChange("api_blocked_prefixes") {
v := d.Get("api_blocked_prefixes").([]interface{})
Expand Down Expand Up @@ -294,6 +321,10 @@ func UpdateAPIIntegration(d *schema.ResourceData, meta interface{}) error {
runSetStatement = true
stmt.SetString("AZURE_AD_APPLICATION_ID", d.Get("azure_ad_application_id").(string))
}
if d.HasChange("google_audience") {
runSetStatement = true
stmt.SetString("GOOGLE_AUDIENCE", d.Get("google_audience").(string))
}
}

if runSetStatement {
Expand Down Expand Up @@ -333,6 +364,12 @@ func setAPIProviderSettings(data *schema.ResourceData, stmt snowflake.SettingBui
return fmt.Errorf("if you use the Azure api provider you must specify an azure_ad_application_id")
}
stmt.SetString(`AZURE_AD_APPLICATION_ID`, v.(string))
case "google_api_gateway":
v, ok := data.GetOk("google_audience")
if !ok {
return fmt.Errorf("if you use GCP api provider you must specify a google_audience")
}
stmt.SetString(`GOOGLE_AUDIENCE`, v.(string))
default:
return fmt.Errorf("unexpected provider %v", apiProvider)
}
Expand Down
40 changes: 34 additions & 6 deletions pkg/resources/api_integration_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,48 @@ func TestAcc_ApiIntegration(t *testing.T) {
t.Skip("Skipping TestAccApiIntegration")
}

apiIntName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
apiIntName2 := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
apiIntNameAWS := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
apiIntNameAzure := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
apiIntNameGCP := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))

resource.Test(t, resource.TestCase{
Providers: providers(),
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: apiIntegrationConfigAWS(apiIntName, []string{"https://123456.execute-api.us-west-2.amazonaws.com/prod/"}),
Config: apiIntegrationConfigAWS(apiIntNameAWS, []string{"https://123456.execute-api.us-west-2.amazonaws.com/prod/"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "name", apiIntName),
resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "name", apiIntNameAWS),
resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_provider", "aws_api_gateway"),
resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "comment", "acceptance test"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "created_on"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "api_aws_iam_user_arn"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "api_aws_external_id"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "api_key"),
),
},
{
Config: apiIntegrationConfigAzure(apiIntName2, []string{"https://apim-hello-world.azure-api.net/"}),
Config: apiIntegrationConfigAzure(apiIntNameAzure, []string{"https://apim-hello-world.azure-api.net/"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "name", apiIntName2),
resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "name", apiIntNameAzure),
resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_provider", "azure_api_management"),
resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "comment", "acceptance test"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "created_on"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "azure_multi_tenant_app_name"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "azure_consent_url"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "api_key"),
),
},
{
Config: apiIntegrationConfigGCP(apiIntNameGCP, []string{"https://gateway-id-123456.uc.gateway.dev/"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "name", apiIntNameGCP),
resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_provider", "google_api_gateway"),
resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "comment", "acceptance test"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_gcp_int", "created_on"),
resource.TestCheckResourceAttrSet("snowflake_api_integration.test_gcp_int", "google_audience"),
),
},
},
})
}
Expand All @@ -56,6 +69,7 @@ func apiIntegrationConfigAWS(name string, prefixes []string) string {
api_aws_role_arn = "arn:aws:iam::000000000001:/role/test"
api_allowed_prefixes = %q
api_key = "12345"
comment = "acceptance test"
enabled = true
}
`, name, prefixes)
Expand All @@ -70,6 +84,20 @@ func apiIntegrationConfigAzure(name string, prefixes []string) string {
azure_ad_application_id = "7890"
api_allowed_prefixes = %q
api_key = "12345"
comment = "acceptance test"
enabled = true
}
`, name, prefixes)
}

func apiIntegrationConfigGCP(name string, prefixes []string) string {
return fmt.Sprintf(`
resource "snowflake_api_integration" "test_gcp_int" {
name = "%s"
api_provider = "google_api_gateway"
google_audience = "api-gateway-id-123456.apigateway.gcp-project.cloud.goog"
api_allowed_prefixes = %q
comment = "acceptance test"
enabled = true
}
`, name, prefixes)
Expand Down
1 change: 1 addition & 0 deletions pkg/snowflake/api_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type APIIntegration struct {
Category sql.NullString `db:"category"`
IntegrationType sql.NullString `db:"type"`
CreatedOn sql.NullString `db:"created_on"`
Comment sql.NullString `db:"comment"`
Enabled sql.NullBool `db:"enabled"`
}

Expand Down