Skip to content

Commit

Permalink
Merge pull request #111 from citrix/devel
Browse files Browse the repository at this point in the history
Devel
  • Loading branch information
George Nikolopoulos authored Aug 11, 2020
2 parents 492844e + 0fb8361 commit 64f91cf
Show file tree
Hide file tree
Showing 14 changed files with 516 additions and 28 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ ADC](https://www.citrix.com/products/netscaler-adc/)

## Description

This project is a terraform custom provider for Citrix ADC. It uses the [Nitro API](https://developer-docs.citrix.com/projects/netscaler-nitro-api/en/12.0/) to create/configure LB configurations.
This project is a terraform custom provider for Citrix ADC. Configure your ADCs using Terraform for different use-cases such as Load Balancing, SSL, Content Switching, GSLB, NAT etc. These ADCs could be deployed anywhere - Public Cloud , On-Prem etc.

All the ADC modules available for Terraform automation can be found in citrixadc folder. It uses the [Nitro API](https://developer-docs.citrix.com/projects/citrix-adc-nitro-api-reference/en/latest/) to create/configure LB configurations. To get you started quickly we also have configuration examples in the example folder. You can modify them for your configurations or create your own.

To deploy Citrix ADC in public clouds, check out cloud scripts in github repo [terraform-cloud-scripts](https://github.com/citrix/terraform-cloud-scripts) .

**Important note: The provider will not commit the config changes to Citrix ADC's persistent
store.**
Expand Down
1 change: 1 addition & 0 deletions citrixadc/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
}

Expand Down
57 changes: 43 additions & 14 deletions citrixadc/resource_citrixadc_sslcipher.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ func resourceCitrixAdcSslcipher() *schema.Resource {
SchemaVersion: 1,
Create: createSslcipherFunc,
Read: readSslcipherFunc,
// Update: updateSslcipherFunc, // All fields are ForceNew or Computed w/out Optional, Update is superfluous
Delete: deleteSslcipherFunc,
Update: updateSslcipherFunc,
Delete: deleteSslcipherFunc,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Expand All @@ -35,7 +35,6 @@ func resourceCitrixAdcSslcipher() *schema.Resource {
"ciphersuitebinding": {
Type: schema.TypeSet,
Required: true,
ForceNew: true,
Set: sslcipherCipherSuitebindingMappingHash,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -84,6 +83,24 @@ func createSslcipherFunc(d *schema.ResourceData, meta interface{}) error {
return nil
}

func updateSslcipherFunc(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] citrixadc-provider: In updateSslcipherFunc")
sslcipherGroupName := d.Get("ciphergroupname").(string)
hasChange := false
if d.HasChange("ciphersuitebinding") {
log.Printf("[DEBUG] citrixadc-provider: Ciphersuitebinding has changed for sslcipherGroupName %s, starting update", sslcipherGroupName)
hasChange = true
}
if hasChange {
err := updateSslCipherCipherSuiteBindings(d, meta)
if err != nil {
return err
}
}

return nil
}

func readSslcipherFunc(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] citrixadc-provider: In readSslcipherFunc")
client := meta.(*NetScalerNitroClient).client
Expand Down Expand Up @@ -121,8 +138,6 @@ func deleteSslcipherFunc(d *schema.ResourceData, meta interface{}) error {
return nil
}

// sslcipher_sslciphersuite_binding

type cipherPriority struct {
cipherName string
cipherPriority int
Expand Down Expand Up @@ -197,18 +212,32 @@ func addSingleSslCipherCipherSuiteBinding(d *schema.ResourceData, meta interface

func updateSslCipherCipherSuiteBindings(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] citrixadc-provider: In updateSslCipherCipherSuiteBindings")
oldSet, newSet := d.GetChange("ciphersuitebinding")
log.Printf("[DEBUG] citrixadc-provider: oldSet %v\n", oldSet)
log.Printf("[DEBUG] citrixadc-provider: newSet %v\n", newSet)
remove := oldSet.(*schema.Set).Difference(newSet.(*schema.Set))
add := newSet.(*schema.Set).Difference(oldSet.(*schema.Set))

for _, binding := range remove.List() {
if err := deleteSingleSslCipherCipherSuiteBinding(d, meta, binding.(map[string]interface{})); err != nil {
client := meta.(*NetScalerNitroClient).client

findParams := netscaler.FindParams{
ResourceType: "sslcipher_sslciphersuite_binding",
ResourceName: d.Get("ciphergroupname").(string),
}

dataArr, err := client.FindResourceArrayWithParams(findParams)
if err != nil {
return err
}

// We need to do this since adding a ciphersuite with lower priority than an existing one
// will bump the existing priority by one.
// Delete all existing bindings
for _, data := range dataArr {
binding := make(map[string]interface{})
binding["ciphername"] = data["ciphername"]
binding["cipherpriority"] = data["cipherpriority"]

if err := deleteSingleSslCipherCipherSuiteBinding(d, meta, binding); err != nil {
return err
}
}

// Add all configured bindings
add := d.Get("ciphersuitebinding").(*schema.Set)
for _, binding := range getSortedCipherBindigs(add) {
if err := addSingleSslCipherCipherSuiteBinding(d, meta, binding); err != nil {
return err
Expand Down
35 changes: 33 additions & 2 deletions citrixadc/resource_citrixadc_sslcipher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
const testAccSslcipher_add = `
resource "citrixadc_sslcipher" "foo" {
ciphergroupname = "tfAccsslcipher"
# ciphersuitebinding is MANDATORY attribute
# Any change in the ciphersuitebinding will result in re-creation of the whole sslcipher resource.
ciphersuitebinding {
Expand All @@ -46,11 +46,32 @@ const testAccSslcipher_add = `
}
`

const testAccSslcipher_transpose = `
resource "citrixadc_sslcipher" "foo" {
ciphergroupname = "tfAccsslcipher"
# ciphersuitebinding is MANDATORY attribute
# Any change in the ciphersuitebinding will result in re-creation of the whole sslcipher resource.
ciphersuitebinding {
ciphername = "TLS1.2-ECDHE-RSA-AES128-GCM-SHA256"
cipherpriority = 3
}
ciphersuitebinding {
ciphername = "TLS1.2-ECDHE-RSA-AES256-GCM-SHA384"
cipherpriority = 2
}
ciphersuitebinding {
ciphername = "TLS1.2-ECDHE-RSA-AES-128-SHA256"
cipherpriority = 1
}
}
`

// Update re-creates the while ciphergroup
const testAccSslcipher_update = `
resource "citrixadc_sslcipher" "foo" {
ciphergroupname = "tfAccsslcipher"
# ciphersuitebinding is MANDATORY attribute
# Any change in the ciphersuitebinding will result in re-creation of the whole sslcipher resource.
ciphersuitebinding {
Expand All @@ -76,6 +97,16 @@ func TestAccSslcipher_basic(t *testing.T) {
testAccCheckSslcipherCiphersuiteBinding("tfAccsslcipher", "TLS1.2-ECDHE-RSA-AES-128-SHA256", 3),
),
},
resource.TestStep{
Config: testAccSslcipher_transpose,
Check: resource.ComposeTestCheckFunc(
testAccCheckSslcipherExist("citrixadc_sslcipher.foo", nil),
resource.TestCheckResourceAttr("citrixadc_sslcipher.foo", "ciphergroupname", "tfAccsslcipher"),
testAccCheckSslcipherCiphersuiteBinding("tfAccsslcipher", "TLS1.2-ECDHE-RSA-AES128-GCM-SHA256", 3),
testAccCheckSslcipherCiphersuiteBinding("tfAccsslcipher", "TLS1.2-ECDHE-RSA-AES256-GCM-SHA384", 2),
testAccCheckSslcipherCiphersuiteBinding("tfAccsslcipher", "TLS1.2-ECDHE-RSA-AES-128-SHA256", 1),
),
},
resource.TestStep{
Config: testAccSslcipher_update,
Check: resource.ComposeTestCheckFunc(
Expand Down
5 changes: 5 additions & 0 deletions citrixadc/resource_citrixadc_sslprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
166 changes: 166 additions & 0 deletions citrixadc/resource_citrixadc_sslprofile_sslcipher_binding.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 64f91cf

Please sign in to comment.