diff --git a/incapsula/client_account.go b/incapsula/client_account.go index 84c55706..0e076984 100644 --- a/incapsula/client_account.go +++ b/incapsula/client_account.go @@ -56,10 +56,12 @@ type AccountStatusResponse struct { Email string `json:"email"` EmailVerified bool `json:"email_verified"` } `json:"logins"` - SupportLevel string `json:"support_level"` - SupportAllTLSVersions bool `json:"supprt_all_tls_versions"` - WildcardSANForNewSites string `json:"wildcard_san_for_new_sites"` - NakedDomainSANForNewWWWSites bool `json:"naked_domain_san_for_new_www_sites"` + SupportLevel string `json:"support_level"` + SupportAllTLSVersions bool `json:"supprt_all_tls_versions"` + WildcardSANForNewSites string `json:"wildcard_san_for_new_sites"` + NakedDomainSANForNewWWWSites bool `json:"naked_domain_san_for_new_www_sites"` + EnableHttp2ForNewSites bool `json:"enable_http2_for_new_sites"` + EnableHttp2ToOriginForNewSites bool `json:"enable_http2_to_origin_for_new_sites"` } `json:"account"` ParentID int `json:"parent_id"` Email string `json:"email"` diff --git a/incapsula/http2_updater.go b/incapsula/http2_updater.go new file mode 100644 index 00000000..5b0a9b25 --- /dev/null +++ b/incapsula/http2_updater.go @@ -0,0 +1,59 @@ +package incapsula + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" +) + +func updateHttp2Properties(client *Client, d *schema.ResourceData) error { + enableHttp2ForNewSiteChanged := d.HasChange("enable_http2_for_new_sites") && d.Get("enable_http2_for_new_sites") != "" + enableHttp2ToOriginForNewSitesChanged := d.HasChange("enable_http2_to_origin_for_new_sites") && d.Get("enable_http2_to_origin_for_new_sites") != "" + + log.Printf("[INFO] adi enable_http2_for_new_sites %v %v %v %v\n", d.HasChange("enable_http2_for_new_sites"), d.Get("enable_http2_for_new_sites"), + d.HasChange("enable_http2_to_origin_for_new_sites"), d.Get("enable_http2_to_origin_for_new_sites")) + + if !enableHttp2ForNewSiteChanged && !enableHttp2ToOriginForNewSitesChanged { + return nil + } + + if d.Get("enable_http2_for_new_sites").(string) == "false" && d.Get("enable_http2_to_origin_for_new_sites").(string) == "true" { + log.Printf("[ERROR] Could not update Incapsula account param enable_http2_for_new_sites with value (%s) and enable_http2_to_origin_for_new_sites with value (%s) for account_id: %s", + d.Get("enable_http2_for_new_sites"), d.Get("enable_http2_to_origin_for_new_sites"), d.Id()) + return fmt.Errorf("[ERROR] invalid values for enable_http2_for_new_sites and enable_http2_to_origin_for_new_sites") + } + + updateParamsList := getParamsToUpdateInOrder(enableHttp2ForNewSiteChanged, enableHttp2ToOriginForNewSitesChanged, d) + + return updateParams(client, d, updateParamsList) +} + +func getParamsToUpdateInOrder(enableHttp2ForNewSiteChanged bool, enableHttp2ToOriginForNewSitesChanged bool, d *schema.ResourceData) []string { + + updateParamsList := make([]string, 0) + if enableHttp2ForNewSiteChanged && !enableHttp2ToOriginForNewSitesChanged { + updateParamsList = append(updateParamsList, "enable_http2_for_new_sites") + } else if !enableHttp2ForNewSiteChanged && enableHttp2ToOriginForNewSitesChanged { + updateParamsList = append(updateParamsList, "enable_http2_to_origin_for_new_sites") + } else if d.Get("enable_http2_to_origin_for_new_sites").(string) == "true" { // if the origin is true, then the client must be set first + updateParamsList = append(updateParamsList, "enable_http2_for_new_sites", "enable_http2_to_origin_for_new_sites") + } else { + updateParamsList = append(updateParamsList, "enable_http2_to_origin_for_new_sites", "enable_http2_for_new_sites") + } + return updateParamsList +} + +func updateParams(client *Client, d *schema.ResourceData, updateParams []string) error { + for i := 0; i < len(updateParams); i++ { + param := updateParams[i] + if d.HasChange(param) && d.Get(param) != "" { + log.Printf("[INFO] Updating Incapsula account param (%s) with value (%s) for account_id: %s\n", param, d.Get(param), d.Id()) + _, err := client.UpdateAccount(d.Id(), param, d.Get(param).(string)) + if err != nil { + log.Printf("[ERROR] Could not update Incapsula account param (%s) with value (%t) for account_id: %s %s\n", param, d.Get(param).(bool), d.Id(), err) + return err + } + } + } + return nil +} diff --git a/incapsula/resource_account.go b/incapsula/resource_account.go index e3c2e4b4..192f10c2 100644 --- a/incapsula/resource_account.go +++ b/incapsula/resource_account.go @@ -110,7 +110,18 @@ func resourceAccount() *schema.Resource { Optional: true, Computed: true, }, - + "enable_http2_for_new_sites": { + Description: "Enable HTTP/2 for traffic between end-users (visitors) and Imperva for newly created SSL sites. Options are `true` and `false`. Defaults to `true`", + Type: schema.TypeString, + Default: "true", + Optional: true, + }, + "enable_http2_to_origin_for_new_sites": { + Description: "Enable HTTP/2 support for traffic between Imperva and your origin server for newly created SSL sites. This option can only be 'true' once 'enable_http2_for_new_sites' is enabled for newly created sites. Options are `true` and `false`. Defaults to `false`", + Type: schema.TypeString, + Default: "false", + Optional: true, + }, // Computed Attributes "support_level": { Description: "Account support level", @@ -201,15 +212,16 @@ func resourceAccountRead(d *schema.ResourceData, m interface{}) error { d.Set("plan_id", accountStatusResponse.Account.PlanID) d.Set("plan_name", accountStatusResponse.Account.PlanName) d.Set("trial_end_date", accountStatusResponse.Account.TrialEndDate) - d.Set("account_id", accountStatusResponse.Account.AccountID) d.Set("ref_id", accountStatusResponse.Account.RefID) d.Set("user_name", accountStatusResponse.Account.UserName) d.Set("account_name", accountStatusResponse.Account.AccountName) d.Set("support_level", accountStatusResponse.Account.SupportLevel) - d.Set("support_all_tls_versions", accountStatusResponse.Account.SupportAllTLSVersions) + d.Set("support_all_tls_versions", strconv.FormatBool(accountStatusResponse.Account.SupportAllTLSVersions)) d.Set("wildcard_san_for_new_sites", accountStatusResponse.Account.WildcardSANForNewSites) d.Set("naked_domain_san_for_new_www_sites", strconv.FormatBool(accountStatusResponse.Account.NakedDomainSANForNewWWWSites)) d.Set("consent_required", accountStatusResponse.ConsentRequired) + d.Set("enable_http2_for_new_sites", strconv.FormatBool(accountStatusResponse.Account.EnableHttp2ForNewSites)) + d.Set("enable_http2_to_origin_for_new_sites", strconv.FormatBool(accountStatusResponse.Account.EnableHttp2ToOriginForNewSites)) // Get the performance settings for the site defaultAccountDataStorageRegion, err := client.GetAccountDataStorageRegion(d.Id()) @@ -307,7 +319,7 @@ func updateAdditionalAccountProperties(client *Client, d *schema.ResourceData) e } } - return nil + return updateHttp2Properties(client, d) } func replaceAccountNameParamName(param string) string { diff --git a/incapsula/resource_account_test.go b/incapsula/resource_account_test.go index f7c5ad9c..c627bdcc 100644 --- a/incapsula/resource_account_test.go +++ b/incapsula/resource_account_test.go @@ -2,39 +2,22 @@ package incapsula import ( "fmt" + "math/rand" "os" "strconv" "strings" "testing" + "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -const testEmail = "example@example.com" +const testEmail = "example@imperva.com" const accountResourceName = "incapsula_account.test-terraform-account" -func GenerateTestEmail(t *testing.T) string { - if v := os.Getenv("INCAPSULA_API_ID"); v == "" { - t.Fatal("INCAPSULA_API_ID must be set for acceptance tests") - } - return "id" + os.Getenv("INCAPSULA_API_ID") + "." + testEmail -} - -func SkipIfAccountTypeIsResellerEndUser(t *testing.T) resource.ErrorCheckFunc { - return func(err error) error { - if err == nil { - return nil - } - if strings.Contains(err.Error(), "Operation not allowed") { - t.Skipf("skipping test since account type is RESELLER_END_USER. Error: %s", err.Error()) - } - - return err - } -} - func TestIncapsulaAccount_Basic(t *testing.T) { + email := GenerateTestEmail(t) resource.Test(t, resource.TestCase{ ErrorCheck: SkipIfAccountTypeIsResellerEndUser(t), PreCheck: func() { testAccPreCheck(t) }, @@ -42,10 +25,10 @@ func TestIncapsulaAccount_Basic(t *testing.T) { CheckDestroy: testCheckIncapsulaAccountDestroy, Steps: []resource.TestStep{ { - Config: testCheckIncapsulaAccountConfigBasic(GenerateTestEmail(t)), + Config: testCheckIncapsulaAccountConfigBasic(email), Check: resource.ComposeTestCheckFunc( testCheckIncapsulaAccountExists(accountResourceName), - resource.TestCheckResourceAttr(accountResourceName, "email", GenerateTestEmail(t)), + resource.TestCheckResourceAttr(accountResourceName, "email", email), ), }, }, @@ -66,6 +49,40 @@ func TestIncapsulaAccount_ImportBasic(t *testing.T) { ResourceName: "incapsula_account.test-terraform-account", ImportState: true, ImportStateVerify: true, + ImportStateIdFunc: testACCStateAccountID, + }, + }, + }) +} + +func TestIncapsulaAccount_Http2Defaults(t *testing.T) { + testIncapsulaAccountHttp2Client(t, true, false) +} + +func TestIncapsulaAccount_Http2ClientAndOriginEnabled(t *testing.T) { + testIncapsulaAccountHttp2Client(t, true, true) +} + +func TestIncapsulaAccount_Http2ClientAndOriginDisabled(t *testing.T) { + testIncapsulaAccountHttp2Client(t, false, false) +} + +func testIncapsulaAccountHttp2Client(t *testing.T, enableHttp2ForNewSites bool, enableHttp2ToOriginForNewSites bool) { + email := GenerateTestEmail(t) + resource.Test(t, resource.TestCase{ + ErrorCheck: SkipIfAccountTypeIsResellerEndUser(t), + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckIncapsulaAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testHttp2AccountConfig(email, enableHttp2ForNewSites, enableHttp2ToOriginForNewSites), + Check: resource.ComposeTestCheckFunc( + testCheckIncapsulaAccountExists(accountResourceName), + resource.TestCheckResourceAttr(accountResourceName, "email", email), + resource.TestCheckResourceAttr(accountResourceName, "enable_http2_for_new_sites", strconv.FormatBool(enableHttp2ForNewSites)), + resource.TestCheckResourceAttr(accountResourceName, "enable_http2_to_origin_for_new_sites", strconv.FormatBool(enableHttp2ToOriginForNewSites)), + ), }, }, }) @@ -127,10 +144,67 @@ func testCheckIncapsulaAccountExists(name string) resource.TestCheckFunc { } func testCheckIncapsulaAccountConfigBasic(email string) string { + return fmt.Sprintf(` + resource "incapsula_account" "test-terraform-account" { + email = "%s" + account_name = "testTerraform" + plan_id = "entTrial" + support_all_tls_versions = "false" + naked_domain_san_for_new_www_sites = "true" + }`, + email, + ) +} + +func testHttp2AccountConfig(email string, enableHttp2ForNewSites bool, enableHttp2ToOriginForNewSites bool) string { return fmt.Sprintf(` resource "incapsula_account" "test-terraform-account" { email = "%s" + enable_http2_for_new_sites = "%t" + enable_http2_to_origin_for_new_sites = "%t" + account_name = "testTerraform" + plan_id = "entTrial" + support_all_tls_versions = "false" }`, email, + enableHttp2ForNewSites, + enableHttp2ToOriginForNewSites, ) } + +func SkipIfAccountTypeIsResellerEndUser(t *testing.T) resource.ErrorCheckFunc { + return func(err error) error { + if err == nil { + return nil + } + if strings.Contains(err.Error(), "Operation not allowed") { + t.Skipf("skipping test since account type is RESELLER_END_USER. Error: %s", err.Error()) + } + + return err + } +} + +func GenerateTestEmail(t *testing.T) string { + if v := os.Getenv("INCAPSULA_API_ID"); v == "" { + t.Fatal("INCAPSULA_API_ID must be set for acceptance tests") + } + + s3 := rand.NewSource(time.Now().UnixNano()) + r3 := rand.New(s3) + generatedDomain = "id" + os.Getenv("INCAPSULA_API_ID") + strconv.Itoa(r3.Intn(1000)) + testEmail + + return generatedDomain +} + +func testACCStateAccountID(s *terraform.State) (string, error) { + for _, rs := range s.RootModule().Resources { + if rs.Type != "incapsula_account" { + continue + } + accountID := rs.Primary.ID + + return accountID, nil + } + return "", fmt.Errorf("Error finding an Account\"") +} diff --git a/incapsula/resource_subaccount.go b/incapsula/resource_subaccount.go index 8fbd7e89..e3f54c56 100644 --- a/incapsula/resource_subaccount.go +++ b/incapsula/resource_subaccount.go @@ -59,6 +59,18 @@ func resourceSubAccount() *schema.Resource { Optional: true, ValidateFunc: validation.StringInSlice([]string{"APAC", "EU", "US", "AU"}, false), }, + "enable_http2_for_new_sites": { + Description: "Enable HTTP/2 for traffic between end-users (visitors) and Imperva for newly created SSL sites. Options are `true` and `false`. Defaults to `true`", + Type: schema.TypeString, + Default: "true", + Optional: true, + }, + "enable_http2_to_origin_for_new_sites": { + Description: "Enable HTTP/2 support for traffic between Imperva and your origin server for newly created SSL sites. This option can only be 'true' once 'enable_http2_for_new_sites' is enabled for newly created sites. Options are `true` and `false`. Defaults to `false`", + Type: schema.TypeString, + Default: "false", + Optional: true, + }, }, } } @@ -96,6 +108,11 @@ func resourceSubAccountCreate(d *schema.ResourceData, m interface{}) error { return err } + err = updateHttp2Properties(client, d) + if err != nil { + return err + } + // There may be a timing/race condition here // Set an arbitrary period to sleep time.Sleep(3 * time.Second) @@ -138,6 +155,8 @@ func resourceSubAccountRead(d *schema.ResourceData, m interface{}) error { return err } d.Set("data_storage_region", defaultAccountDataStorageRegion.Region) + d.Set("enable_http2_for_new_sites", strconv.FormatBool(accountStatusResponse.Account.EnableHttp2ForNewSites)) + d.Set("enable_http2_to_origin_for_new_sites", strconv.FormatBool(accountStatusResponse.Account.EnableHttp2ToOriginForNewSites)) log.Printf("[INFO] Finished reading Incapsula account for account ud: %d\n", accountID) @@ -187,6 +206,11 @@ func resourceSubAccountUpdate(d *schema.ResourceData, m interface{}) error { return err } + err = updateHttp2Properties(client, d) + if err != nil { + return err + } + // Set the rest of the state from the resource read return resourceAccountRead(d, m) } diff --git a/incapsula/resource_subaccount_test.go b/incapsula/resource_subaccount_test.go index c5e76875..cd83b233 100644 --- a/incapsula/resource_subaccount_test.go +++ b/incapsula/resource_subaccount_test.go @@ -41,6 +41,47 @@ func TestAccIncapsulaSubAccount_Basic(t *testing.T) { }) } +func TestAccIncapsulaSubAccount_HTTP2(t *testing.T) { + testIncapsulaSubAccountHttp2Client(t, true, true) +} + +func TestIncapsulaSubAccount_Http2Defaults(t *testing.T) { + testIncapsulaSubAccountHttp2Client(t, true, false) +} + +func TestIncapsulaSubAccount_Http2ClientAndOriginEnabled(t *testing.T) { + testIncapsulaSubAccountHttp2Client(t, false, false) +} + +func testIncapsulaSubAccountHttp2Client(t *testing.T, enableHttp2ForNewSites bool, enableHttp2ToOriginForNewSites bool) { + + log.Printf("========================BEGIN TEST========================") + log.Printf("[DEBUG]Running test resource_txt_settings.go.TestAccIncapsulaSubAccount_Basic") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccIncapsulaSubAccountDestroy, + Steps: []resource.TestStep{ + { + Config: getAccIncapsulaSubAccountWithHttp2Config(enableHttp2ForNewSites, enableHttp2ToOriginForNewSites), + Check: resource.ComposeTestCheckFunc( + testCheckIncapsulaSubAccountExists(), + resource.TestCheckResourceAttr(subAccountResourceType+"."+subAccountResourceName, "sub_account_name", subAccountName), + resource.TestCheckResourceAttr(subAccountResourceType+"."+subAccountResourceName, "enable_http2_for_new_sites", strconv.FormatBool(enableHttp2ForNewSites)), + resource.TestCheckResourceAttr(subAccountResourceType+"."+subAccountResourceName, "enable_http2_to_origin_for_new_sites", strconv.FormatBool(enableHttp2ToOriginForNewSites)), + ), + }, + { + ResourceName: subAccountResourceType + "." + subAccountResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testACCStateSubAccountID, + }, + }, + }) +} + func getAccIncapsulaSubAccountConfigBasic() string { return fmt.Sprintf(` resource "%s" "%s" { @@ -51,6 +92,17 @@ func getAccIncapsulaSubAccountConfigBasic() string { ) } +func getAccIncapsulaSubAccountWithHttp2Config(enableHttp2ForNewSites bool, enableHttp2ToOriginForNewSites bool) string { + return fmt.Sprintf(` + resource "%s" "%s" { + sub_account_name = "%s" + enable_http2_for_new_sites = "%t" + enable_http2_to_origin_for_new_sites = "%t" + }`, + subAccountResourceType, subAccountResourceName, subAccountName, enableHttp2ForNewSites, enableHttp2ToOriginForNewSites, + ) +} + func testAccIncapsulaSubAccountDestroy(state *terraform.State) error { client := testAccProvider.Meta().(*Client) diff --git a/website/docs/r/account.html.markdown b/website/docs/r/account.html.markdown index 7830ea76..051d919b 100644 --- a/website/docs/r/account.html.markdown +++ b/website/docs/r/account.html.markdown @@ -52,6 +52,8 @@ The following arguments are supported: * `wildcard_san_for_new_sites` - (Optional) Add wildcard SAN to Incapsula SSL certificates for new sites. Options are `true`, `false` and `default`. Defaults to `default`. Note: This argument is deprecated. Use use_wild_card_san_instead_of_fqdn in the account_ssl_settings resource instead. * `error_page_template` - (Optional) Base64 encoded template for an error page. +* `enable_http2_for_new_sites` - (Optional) Use this option to enable HTTP/2 support for traffic between end-users (visitors) and Imperva for newly created SSL sites. Options are `true` and `false`. Defaults to `true`. +* `enable_http2_to_origin_for_new_sites` - (Optional) Use this option to enable HTTP/2 support for traffic between Imperva and your origin server for newly created SSL sites. This option can only be 'true' once 'enable_http2_for_new_sites' is enabled for newly created sites. Options are `true` and `false`. Defaults to `false`. ## Attributes Reference diff --git a/website/docs/r/subaccount.html.markdown b/website/docs/r/subaccount.html.markdown index aa9e48ae..427c19e9 100644 --- a/website/docs/r/subaccount.html.markdown +++ b/website/docs/r/subaccount.html.markdown @@ -17,10 +17,12 @@ while non-supported terraform dependent resources won't auto create ```hcl resource "incapsula_subaccount" "example-subaccount" { - sub_account_name = "Example SubAccount" - logs_account_id = "789" - log_level = "full" - data_storage_region = "US" + sub_account_name = "Example SubAccount" + logs_account_id = "789" + log_level = "full" + data_storage_region = "US" + enable_http2_for_new_sites = true + enable_http2_to_origin_for_new_sites = true } ``` @@ -34,6 +36,8 @@ The following arguments are supported: * `logs_account_id` - (Optional) Account where logs should be stored. Available only for Enterprise Plan customers that purchased the Logs Integration SKU. Numeric identifier of the account that purchased the logs integration SKU and which collects the logs. If not specified, operation will be performed on the account identified by the authentication parameters. * `log_level` - (Optional) The log level. Options are `full`, `security`, `none`, `default`. * `data_storage_region` - (Optional) Default data region of the sub-account for newly created sites. Options are `APAC`, `EU`, `US` and `AU`. Defaults to `US`. +* `enable_http2_for_new_sites` - (Optional) Use this option to enable HTTP/2 support for traffic between end-users (visitors) and Imperva for newly created SSL sites. Options are `true` and `false`. Defaults to `true`. +* `enable_http2_to_origin_for_new_sites` - (Optional) Use this option to enable HTTP/2 support for traffic between Imperva and your origin server for newly created SSL sites. This option can only be 'true' once 'enable_http2_for_new_sites' is enabled for newly created sites. Options are `true` and `false`. Defaults to `false`. ## Attributes Reference