diff --git a/citrixadc/provider.go b/citrixadc/provider.go index aee677c0d..b92ea8f63 100644 --- a/citrixadc/provider.go +++ b/citrixadc/provider.go @@ -139,6 +139,7 @@ func providerResources() map[string]*schema.Resource { "citrixadc_servicegroup_lbmonitor_binding": resourceCitrixAdcServicegroup_lbmonitor_binding(), "citrixadc_nsparam": resourceCitrixAdcNsparam(), "citrixadc_sslvserver_sslpolicy_binding": resourceCitrixAdcSslvserver_sslpolicy_binding(), + "citrixadc_sslprofile_sslcipher_binding": resourceCitrixAdcSslprofile_sslcipher_binding(), } } diff --git a/citrixadc/resource_citrixadc_sslprofile.go b/citrixadc/resource_citrixadc_sslprofile.go index 205667175..ab0a7c81b 100644 --- a/citrixadc/resource_citrixadc_sslprofile.go +++ b/citrixadc/resource_citrixadc_sslprofile.go @@ -1226,6 +1226,11 @@ func updateSslprofileCipherBindings(d *schema.ResourceData, meta interface{}) er func readSslprofileCipherbindings(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] citrixadc-provider: In readSslprofileCipherbindings") client := meta.(*NetScalerNitroClient).client + + // Ignore bindings unless there is an explicit configuration for it + if _, ok := d.GetOk("cipherbindings"); !ok { + return nil + } sslprofileName := d.Get("name").(string) bindings, _ := client.FindResourceArray(netscaler.Sslprofile_sslcipher_binding.Type(), sslprofileName) log.Printf("bindings %v\n", bindings) diff --git a/citrixadc/resource_citrixadc_sslprofile_sslcipher_binding.go b/citrixadc/resource_citrixadc_sslprofile_sslcipher_binding.go new file mode 100644 index 000000000..f72bb71d4 --- /dev/null +++ b/citrixadc/resource_citrixadc_sslprofile_sslcipher_binding.go @@ -0,0 +1,166 @@ +package citrixadc + +import ( + "github.com/chiradeep/go-nitro/config/ssl" + + "github.com/chiradeep/go-nitro/netscaler" + "github.com/hashicorp/terraform/helper/schema" + + "fmt" + "log" + "strings" +) + +func resourceCitrixAdcSslprofile_sslcipher_binding() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + Create: createSslprofile_sslcipher_bindingFunc, + Read: readSslprofile_sslcipher_bindingFunc, + Delete: deleteSslprofile_sslcipher_bindingFunc, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "ciphername": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "cipherpriority": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func createSslprofile_sslcipher_bindingFunc(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] citrixadc-provider: In createSslprofile_sslcipher_bindingFunc") + client := meta.(*NetScalerNitroClient).client + profileName := d.Get("name").(string) + cipherName := d.Get("ciphername").(string) + + // Use `,` as the separator since it is invalid character for adc entity strings + bindingId := fmt.Sprintf("%s,%s", profileName, cipherName) + + sslprofile_sslcipher_binding := ssl.Sslprofilesslcipherbinding{ + Ciphername: d.Get("ciphername").(string), + Cipherpriority: d.Get("cipherpriority").(int), + Name: d.Get("name").(string), + } + + err := client.UpdateUnnamedResource(netscaler.Sslprofile_sslcipher_binding.Type(), &sslprofile_sslcipher_binding) + if err != nil { + return err + } + + d.SetId(bindingId) + + err = readSslprofile_sslcipher_bindingFunc(d, meta) + if err != nil { + log.Printf("[ERROR] netscaler-provider: ?? we just created this sslprofile_sslcipher_binding but we can't read it ?? %s", bindingId) + return nil + } + return nil +} + +func readSslprofile_sslcipher_bindingFunc(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] citrixadc-provider: In readSslprofile_sslcipher_bindingFunc") + client := meta.(*NetScalerNitroClient).client + + bindingId := d.Id() + idSlice := strings.Split(bindingId, ",") + + if len(idSlice) < 2 { + return fmt.Errorf("Cannot deduce ciphername from id string") + } + + if len(idSlice) > 2 { + return fmt.Errorf("Too many separators \",\" in id string") + } + + profileName := idSlice[0] + cipherName := idSlice[1] + + findParams := netscaler.FindParams{ + ResourceType: "sslprofile_sslcipher_binding", + ResourceName: profileName, + ResourceMissingErrorCode: 3248, + } + + dataArr, err := client.FindResourceArrayWithParams(findParams) + + if err != nil { + if strings.Contains(err.Error(), "\"errorcode\": 3248") { + return nil + } else { + // Unexpected error + log.Printf("[DEBUG] citrixadc-provider: Error during FindResourceArrayWithParams %s", err.Error()) + return err + } + } + + // Resource is missing + if len(dataArr) == 0 { + log.Printf("[DEBUG] citrixadc-provider: FindResourceArrayWithParams returned empty array") + log.Printf("[WARN] citrixadc-provider: Clearing sslprofile_sslcipher_binding state %s", bindingId) + d.SetId("") + return nil + } + + // Iterate through results to find the one with the right policy name + foundIndex := -1 + for i, v := range dataArr { + if v["cipheraliasname"].(string) == cipherName { + foundIndex = i + break + } + } + + // Resource is missing + if foundIndex == -1 { + log.Printf("[DEBUG] citrixadc-provider: FindResourceArrayWithParams cipher name not found in array") + log.Printf("[WARN] citrixadc-provider: Clearing sslprofile_sslcipher_binding state %s", bindingId) + d.SetId("") + return nil + } + // Fallthrough + + data := dataArr[foundIndex] + + d.Set("name", data["name"]) + d.Set("ciphername", data["cipheraliasname"]) + d.Set("cipherpriority", data["cipherpriority"]) + + return nil + +} + +func deleteSslprofile_sslcipher_bindingFunc(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] citrixadc-provider: In deleteSslprofile_sslcipher_bindingFunc") + client := meta.(*NetScalerNitroClient).client + + bindingId := d.Id() + idSlice := strings.Split(bindingId, ",") + + profileName := idSlice[0] + cipherName := idSlice[1] + + args := make([]string, 0) + args = append(args, fmt.Sprintf("ciphername:%v", cipherName)) + + err := client.DeleteResourceWithArgs(netscaler.Sslprofile_sslcipher_binding.Type(), profileName, args) + if err != nil { + return err + } + + d.SetId("") + + return nil +} diff --git a/citrixadc/resource_citrixadc_sslprofile_sslcipher_binding_test.go b/citrixadc/resource_citrixadc_sslprofile_sslcipher_binding_test.go new file mode 100644 index 000000000..b5c5b75b8 --- /dev/null +++ b/citrixadc/resource_citrixadc_sslprofile_sslcipher_binding_test.go @@ -0,0 +1,223 @@ +/* +Copyright 2016 Citrix Systems, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package citrixadc + +import ( + "fmt" + "github.com/chiradeep/go-nitro/netscaler" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "strings" + "testing" +) + +func TestAccSslprofile_sslcipher_binding_basic(t *testing.T) { + + if isCpxRun { + t.Skip("Operation not permitted under CPX") + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSslprofile_sslcipher_bindingDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSslprofile_sslcipher_binding_basic_step1, + Check: resource.ComposeTestCheckFunc( + testAccCheckSslprofile_sslcipher_bindingExist("citrixadc_sslprofile_sslcipher_binding.tf_binding", nil), + testAccCheckSslprofile_sslcipher_bindingExist("citrixadc_sslprofile_sslcipher_binding.tf_binding2", nil), + ), + }, + resource.TestStep{ + Config: testAccSslprofile_sslcipher_binding_basic_step2, + Check: resource.ComposeTestCheckFunc( + testAccCheckSslprofile_sslcipher_bindingExist("citrixadc_sslprofile_sslcipher_binding.tf_binding", nil), + testAccCheckSslprofile_sslcipher_bindingExist("citrixadc_sslprofile_sslcipher_binding.tf_binding2", nil), + ), + }, + resource.TestStep{ + Config: testAccSslprofile_sslcipher_binding_basic_step3, + Check: resource.ComposeTestCheckFunc( + testAccCheckSslprofile_sslcipher_bindingExist("citrixadc_sslprofile_sslcipher_binding.tf_binding", nil), + ), + }, + }, + }) +} + +func testAccCheckSslprofile_sslcipher_bindingExist(n string, id *string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No lb vserver name is set") + } + + if id != nil { + if *id != "" && *id != rs.Primary.ID { + return fmt.Errorf("Resource ID has changed!") + } + + *id = rs.Primary.ID + } + + nsClient := testAccProvider.Meta().(*NetScalerNitroClient).client + bindingId := rs.Primary.ID + idSlice := strings.Split(bindingId, ",") + + profileName := idSlice[0] + cipherName := idSlice[1] + + findParams := netscaler.FindParams{ + ResourceType: "sslprofile_sslcipher_binding", + ResourceName: profileName, + ResourceMissingErrorCode: 3248, + } + + dataArr, err := nsClient.FindResourceArrayWithParams(findParams) + + if err != nil { + return err + } + + if len(dataArr) == 0 { + return fmt.Errorf("[DEBUG] citrixadc-provider: FindResourceArrayWithParams returned empty array") + } + + foundIndex := -1 + for i, v := range dataArr { + if v["cipheraliasname"].(string) == cipherName { + foundIndex = i + break + } + } + + if foundIndex == -1 { + return fmt.Errorf("[DEBUG] citrixadc-provider: FindResourceArrayWithParams cipher name not found in array") + } + + return nil + } +} + +func testAccCheckSslprofile_sslcipher_bindingDestroy(s *terraform.State) error { + nsClient := testAccProvider.Meta().(*NetScalerNitroClient).client + + for _, rs := range s.RootModule().Resources { + if rs.Type != "citrixadc_sslprofile_sslcipher_binding" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No name is set") + } + + idSlice := strings.Split(rs.Primary.ID, ",") + + profileName := idSlice[0] + + findParams := netscaler.FindParams{ + ResourceType: "sslprofile_sslcipher_binding", + ResourceName: profileName, + ResourceMissingErrorCode: 3248, + } + + dataArr, err := nsClient.FindResourceArrayWithParams(findParams) + + if err != nil { + if strings.Contains(err.Error(), "\"errorcode\": 3248") { + // Expected since ssl profile is already deleted + return nil + } else { + // Unexpected error + return err + } + } + + if len(dataArr) > 0 { + return fmt.Errorf("sslcipher binding %s still exists", rs.Primary.ID) + } + + } + + return nil +} + +const testAccSslprofile_sslcipher_binding_basic_step1 = ` + +resource "citrixadc_sslprofile" "tf_sslprofile" { + name = "tf_sslprofile" + + ecccurvebindings = [] + +} + +resource "citrixadc_sslprofile_sslcipher_binding" "tf_binding" { + name = citrixadc_sslprofile.tf_sslprofile.name + ciphername = "HIGH" + cipherpriority = 10 +} + +resource "citrixadc_sslprofile_sslcipher_binding" "tf_binding2" { + name = citrixadc_sslprofile.tf_sslprofile.name + ciphername = "LOW" + cipherpriority = 20 +} + +` + +const testAccSslprofile_sslcipher_binding_basic_step2 = ` + +resource "citrixadc_sslprofile" "tf_sslprofile" { + name = "tf_sslprofile" + + ecccurvebindings = [] + +} + +resource "citrixadc_sslprofile_sslcipher_binding" "tf_binding" { + name = citrixadc_sslprofile.tf_sslprofile.name + ciphername = "HIGH" + cipherpriority = 10 +} + +resource "citrixadc_sslprofile_sslcipher_binding" "tf_binding2" { + name = citrixadc_sslprofile.tf_sslprofile.name + ciphername = "LOW" + cipherpriority = 30 +} + +` + +const testAccSslprofile_sslcipher_binding_basic_step3 = ` + +resource "citrixadc_sslprofile" "tf_sslprofile" { + name = "tf_sslprofile" + + ecccurvebindings = [] + +} + +resource "citrixadc_sslprofile_sslcipher_binding" "tf_binding" { + name = citrixadc_sslprofile.tf_sslprofile.name + ciphername = "HIGH" + cipherpriority = 10 +} +` diff --git a/examples/sslprofile_sslcipher_binding/provider.tf b/examples/sslprofile_sslcipher_binding/provider.tf new file mode 100644 index 000000000..3d4508593 --- /dev/null +++ b/examples/sslprofile_sslcipher_binding/provider.tf @@ -0,0 +1,3 @@ +provider "citrixadc" { + endpoint = "http://localhost:8080" +} diff --git a/examples/sslprofile_sslcipher_binding/resources.tf b/examples/sslprofile_sslcipher_binding/resources.tf new file mode 100644 index 000000000..4672ea195 --- /dev/null +++ b/examples/sslprofile_sslcipher_binding/resources.tf @@ -0,0 +1,20 @@ +resource "citrixadc_sslprofile" "tf_sslprofile" { + name = "tf_sslprofile" + + ecccurvebindings = [] + // Don't define any bindings here + // since we are using the explicit bindings resource + +} + +resource "citrixadc_sslprofile_sslcipher_binding" "tf_binding" { + name = citrixadc_sslprofile.tf_sslprofile.name + ciphername = "HIGH" + cipherpriority = 10 +} + +resource "citrixadc_sslprofile_sslcipher_binding" "tf_binding2" { + name = citrixadc_sslprofile.tf_sslprofile.name + ciphername = "LOW" + cipherpriority = 20 +}