diff --git a/citrixadc/data_source_citrixadc_hanode_test.go b/citrixadc/data_source_citrixadc_hanode_test.go index 0c7d6daca..5e03cabdb 100644 --- a/citrixadc/data_source_citrixadc_hanode_test.go +++ b/citrixadc/data_source_citrixadc_hanode_test.go @@ -25,7 +25,7 @@ import ( const testAccDataSourceHanode = ` data "citrixadc_hanode" "hanode" { hanode_id = 0 - } +} ` func TestAccDataSourceHanode_basic(t *testing.T) { diff --git a/citrixadc/helpers_test.go b/citrixadc/helpers_test.go index 5ec13bf8f..cd6a543aa 100644 --- a/citrixadc/helpers_test.go +++ b/citrixadc/helpers_test.go @@ -17,6 +17,34 @@ import ( "github.com/citrix/adc-nitro-go/service" ) +func doSslcertkeyPreChecks(t *testing.T) { + testAccPreCheck(t) + + uploads := []string{ + "ca.crt", + "intermediate.crt", + "certificate1.crt", + "certificate2.crt", + "certificate3.crt", + "key1.pem", + "key2.pem", + "key3.pem", + } + + c, err := testHelperInstantiateClient("", "", "", false) + if err != nil { + t.Fatalf("Failed to instantiate client. %v\n", err) + } + + //c := testAccProvider.Meta().(*NetScalerNitroClient) + for _, filename := range uploads { + err := uploadTestdataFile(c, t, filename, "/var/tmp") + if err != nil { + t.Errorf("%v", err) + } + } +} + func uploadTestdataFile(c *NetScalerNitroClient, t *testing.T, filename, targetDir string) error { client := c.client diff --git a/citrixadc/provider.go b/citrixadc/provider.go index 00e0a143a..de1ad3674 100644 --- a/citrixadc/provider.go +++ b/citrixadc/provider.go @@ -112,35 +112,35 @@ func providerDataSources() map[string]*schema.Resource { func providerResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "citrixadc_lbmetrictable": resourceCitrixAdcLbmetrictable(), - "citrixadc_sslservice_sslcertkey_binding": resourceCitrixAdcSslservice_sslcertkey_binding(), - "citrixadc_sslservice_sslciphersuite_binding": resourceCitrixAdcSslservice_sslciphersuite_binding(), - "citrixadc_sslservice_ecccurve_binding": resourceCitrixAdcSslservice_ecccurve_binding(), - "citrixadc_sslservice": resourceCitrixAdcSslservice(), - "citrixadc_sslfipskey": resourceCitrixAdcSslfipskey(), - "citrixadc_sslhsmkey": resourceCitrixAdcSslhsmkey(), - "citrixadc_sslcacertgroup_sslcertkey_binding": resourceCitrixAdcSslcacertgroup_sslcertkey_binding(), - "citrixadc_lbroute6": resourceCitrixAdcLbroute6(), - "citrixadc_sslpolicylabel": resourceCitrixAdcSslpolicylabel(), - "citrixadc_ssllogprofile": resourceCitrixAdcSsllogprofile(), - "citrixadc_sslprofile_sslcertkey_binding": resourceCitrixAdcSslprofile_sslcertkey_binding(), - "citrixadc_sslpolicylabel_sslpolicy_binding": resourceCitrixAdcSslpolicylabel_sslpolicy_binding(), - "citrixadc_lbvserver_botpolicy_binding": resourceCitrixAdcLbvserver_botpolicy_binding(), - "citrixadc_lbvserver_auditsyslogpolicy_binding": resourceCitrixAdcLbvserver_auditsyslogpolicy_binding(), - "citrixadc_sslcacertgroup": resourceCitrixAdcSslcacertgroup(), - "citrixadc_botsettings": resourceCitrixAdcBotsettings(), - "citrixadc_botpolicy": resourceCitrixAdcBotpolicy(), - "citrixadc_lbvserver_analyticsprofile_binding": resourceCitrixAdcLbvserver_analyticsprofile_binding(), - "citrixadc_lbvserver_appqoepolicy_binding": resourceCitrixAdcLbvserver_appqoepolicy_binding(), - "citrixadc_lbmonitor_metric_binding": resourceCitrixAdcLbmonitor_metric_binding(), - "citrixadc_lbvserver": resourceCitrixAdcLbvserver(), - "citrixadc_service": resourceCitrixAdcService(), - "citrixadc_csvserver": resourceCitrixAdcCsvserver(), - "citrixadc_cspolicy": resourceCitrixAdcCspolicy(), - "citrixadc_csaction": resourceCitrixAdcCsaction(), - "citrixadc_sslaction": resourceCitrixAdcSslaction(), - "citrixadc_sslpolicy": resourceCitrixAdcSslpolicy(), - "citrixadc_sslcertkey": resourceCitrixAdcSslcertkey(), + "citrixadc_lbmetrictable": resourceCitrixAdcLbmetrictable(), + "citrixadc_sslservice_sslcertkey_binding": resourceCitrixAdcSslservice_sslcertkey_binding(), + "citrixadc_sslservice_sslciphersuite_binding": resourceCitrixAdcSslservice_sslciphersuite_binding(), + "citrixadc_sslservice_ecccurve_binding": resourceCitrixAdcSslservice_ecccurve_binding(), + "citrixadc_sslservice": resourceCitrixAdcSslservice(), + "citrixadc_sslfipskey": resourceCitrixAdcSslfipskey(), + "citrixadc_sslhsmkey": resourceCitrixAdcSslhsmkey(), + "citrixadc_sslcacertgroup_sslcertkey_binding": resourceCitrixAdcSslcacertgroup_sslcertkey_binding(), + "citrixadc_lbroute6": resourceCitrixAdcLbroute6(), + "citrixadc_sslpolicylabel": resourceCitrixAdcSslpolicylabel(), + "citrixadc_ssllogprofile": resourceCitrixAdcSsllogprofile(), + "citrixadc_sslprofile_sslcertkey_binding": resourceCitrixAdcSslprofile_sslcertkey_binding(), + "citrixadc_sslpolicylabel_sslpolicy_binding": resourceCitrixAdcSslpolicylabel_sslpolicy_binding(), + "citrixadc_lbvserver_botpolicy_binding": resourceCitrixAdcLbvserver_botpolicy_binding(), + "citrixadc_lbvserver_auditsyslogpolicy_binding": resourceCitrixAdcLbvserver_auditsyslogpolicy_binding(), + "citrixadc_sslcacertgroup": resourceCitrixAdcSslcacertgroup(), + "citrixadc_botsettings": resourceCitrixAdcBotsettings(), + "citrixadc_botpolicy": resourceCitrixAdcBotpolicy(), + "citrixadc_lbvserver_analyticsprofile_binding": resourceCitrixAdcLbvserver_analyticsprofile_binding(), + "citrixadc_lbvserver_appqoepolicy_binding": resourceCitrixAdcLbvserver_appqoepolicy_binding(), + "citrixadc_lbmonitor_metric_binding": resourceCitrixAdcLbmonitor_metric_binding(), + "citrixadc_lbvserver": resourceCitrixAdcLbvserver(), + "citrixadc_service": resourceCitrixAdcService(), + "citrixadc_csvserver": resourceCitrixAdcCsvserver(), + "citrixadc_cspolicy": resourceCitrixAdcCspolicy(), + "citrixadc_csaction": resourceCitrixAdcCsaction(), + "citrixadc_sslaction": resourceCitrixAdcSslaction(), + "citrixadc_sslpolicy": resourceCitrixAdcSslpolicy(), + // "citrixadc_sslcertkey": resourceCitrixAdcSslcertkey(), "citrixadc_sslprofile": resourceCitrixAdcSslprofile(), "citrixadc_sslparameter": resourceCitrixAdcSslparameter(), "citrixadc_ssldhparam": resourceCitrixAdcSsldhparam(), diff --git a/citrixadc_framework/acctests/helpers_test.go b/citrixadc_framework/acctests/helpers_test.go new file mode 100644 index 000000000..6fd7678b5 --- /dev/null +++ b/citrixadc_framework/acctests/helpers_test.go @@ -0,0 +1,196 @@ +package acctests + +import ( + "encoding/base64" + "errors" + "fmt" + "io/ioutil" + "log" + "os" + "path" + "regexp" + "runtime" + "strings" + "sync" + "testing" + + "github.com/citrix/adc-nitro-go/resource/config/system" + "github.com/citrix/adc-nitro-go/service" +) + +// NetScalerNitroClient is a wrapper around the service.NitroClient +type NetScalerNitroClient struct { + Username string + Password string + Endpoint string + client *service.NitroClient + lock sync.Mutex +} + +func doSslcertkeyPreChecks(t *testing.T) { + testAccPreCheck(t) + + uploads := []string{ + "ca.crt", + "intermediate.crt", + "certificate1.crt", + "certificate2.crt", + "certificate3.crt", + "key1.pem", + "key2.pem", + "key3.pem", + "servercert1.cert", + "servercert1.key", + "intermediate1.cert", + "rootcert1.cert", + "servercert2.cert", + "servercert2.key", + "servercert3.cert", + "servercert3.key", + } + + c, err := testHelperInstantiateClient("", "", "", false) + if err != nil { + t.Fatalf("Failed to instantiate client. %v\n", err) + } + + //c := testAccProvider.Meta().(*NetScalerNitroClient) + for _, filename := range uploads { + err := uploadTestdataFile(c, t, filename, "/nsconfig/ssl") + if err != nil { + t.Errorf("%v", err) + } + } +} + +func uploadTestdataFile(c *NetScalerNitroClient, t *testing.T, filename, targetDir string) error { + client := c.client + + // Get here path + _, here_filename, _, _ := runtime.Caller(1) + b, err := ioutil.ReadFile(path.Join(path.Dir(here_filename), "testdata", filename)) + + if err != nil { + return err + } + + sf := system.Systemfile{ + Filename: filename, + Filecontent: base64.StdEncoding.EncodeToString(b), + Filelocation: targetDir, + } + _, err = client.AddResource(service.Systemfile.Type(), filename, &sf) + if err != nil && strings.Contains(err.Error(), "File already exists") { + url_args := map[string]string{"filelocation": strings.Replace(targetDir, "/", "%2F", -1)} + err := client.DeleteResourceWithArgsMap(service.Systemfile.Type(), filename, url_args) + if err != nil { + return err + } + _, err = client.AddResource(service.Systemfile.Type(), filename, &sf) + if err != nil { + return err + } + } + return nil +} + +var helperClient *NetScalerNitroClient + +func testHelperInstantiateClient(nsUrl, username, password string, sslVerify bool) (*NetScalerNitroClient, error) { + if helperClient != nil { + log.Printf("Returning existing helper client\n") + return helperClient, nil + } + + if nsUrl == "" { + if nsUrl = os.Getenv("NS_URL"); nsUrl == "" { + return nil, errors.New("No nsUrl defined") + } + } + + if username == "" { + if username = os.Getenv("NS_LOGIN"); username == "" { + username = "nsroot" + } + } + + if password == "" { + if password = os.Getenv("NS_PASSWORD"); password == "" { + password = "nsroot" + } + } + + c := NetScalerNitroClient{ + Username: username, + Password: password, + Endpoint: nsUrl, + } + + params := service.NitroParams{ + Url: nsUrl, + Username: username, + Password: password, + //ProxiedNs: d.Get("proxied_ns").(string), + SslVerify: sslVerify, + } + client, err := service.NewNitroClientFromParams(params) + if err != nil { + return nil, err + } + + c.client = client + helperClient = &c + log.Printf("Helper client instantiated\n") + + return helperClient, nil +} + +func testHelperEnsureResourceDeletion(c *NetScalerNitroClient, t *testing.T, resourceType, resourceName string, deleteArgsMap map[string]string) { + if _, err := c.client.FindResource(resourceType, resourceName); err != nil { + targetSubstring := fmt.Sprintf("No resource %s of type %s found", resourceName, resourceType) + actualError := err.Error() + t.Logf("targetSubstring \"%s\"", targetSubstring) + t.Logf("actualError \"%s\"", actualError) + if strings.Contains(err.Error(), targetSubstring) { + t.Logf("Ensure delete found no remaining resource %s", resourceName) + return + } else { + t.Fatalf("Unexpected error while ensuring delete of resource %v. %v", resourceName, err) + return + } + } + + // Fallthrough + if deleteArgsMap == nil { + if err := c.client.DeleteResource(resourceType, resourceName); err != nil { + t.Logf("Ensuring delete failed for resource %s.", resourceName) + t.Fatal(err) + return + } else { + t.Logf("Ensuring deletion of %s successful", resourceName) + } + } else { + if err := c.client.DeleteResourceWithArgsMap(resourceType, resourceName, deleteArgsMap); err != nil { + t.Logf("Ensuring delete failed for resource %s with argsMap %v", resourceName, deleteArgsMap) + t.Fatal(err) + return + } else { + t.Logf("Ensuring deletion of %s successful", resourceName) + } + } + +} +func testHelperVerifyImmutabilityFunc(c *NetScalerNitroClient, t *testing.T, resourceType, resourceName string, resourceInstance interface{}, attribute string) { + if _, err := c.client.UpdateResource(resourceType, resourceName, resourceInstance); err != nil { + r := regexp.MustCompile(fmt.Sprintf("errorcode.*278.*Invalid argument \\[%s\\]", attribute)) + + if r.Match([]byte(err.Error())) { + t.Logf("Succesfully verified immutability of attribute \"%s\"", attribute) + } else { + t.Errorf("Error while assesing immutability of attribute \"%s\"", attribute) + t.Fatal(err) + } + } else { + t.Fatalf("Error (no error) while assesing immutability of attribute \"%s\"", attribute) + } +} diff --git a/citrixadc_framework/provider_test.go b/citrixadc_framework/acctests/provider_test.go similarity index 57% rename from citrixadc_framework/provider_test.go rename to citrixadc_framework/acctests/provider_test.go index eaae0caef..7f306bf16 100644 --- a/citrixadc_framework/provider_test.go +++ b/citrixadc_framework/acctests/provider_test.go @@ -1,4 +1,4 @@ -package citrixadc_framework +package acctests import ( "fmt" @@ -6,6 +6,7 @@ import ( "testing" "github.com/citrix/adc-nitro-go/service" + "github.com/citrix/terraform-provider-citrixadc/citrixadc_framework/provider" "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" ) @@ -15,7 +16,7 @@ import ( // CLI command executed to create a provider server to which the CLI can // reattach. var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ - "citrixadc": providerserver.NewProtocol6WithError(New("test")()), + "citrixadc": providerserver.NewProtocol6WithError(provider.New("test")()), } func testAccPreCheck(t *testing.T) { @@ -55,33 +56,33 @@ func testAccGetFrameworkClient() (*service.NitroClient, error) { return client, nil } -func TestProviderNew(t *testing.T) { - provider := New("test")() +// func TestProviderNew(t *testing.T) { +// provider := provider.New("test")() - if provider == nil { - t.Fatal("Provider should not be nil") - } +// if provider == nil { +// t.Fatal("Provider should not be nil") +// } - // Test that the provider implements the required interface - _, ok := provider.(*CitrixAdcFrameworkProvider) - if !ok { - t.Fatal("Provider should be of type *CitrixAdcFrameworkProvider") - } -} +// // Test that the provider implements the required interface +// _, ok := provider.(*(provider.CitrixAdcFrameworkProvider)) +// if !ok { +// t.Fatal("Provider should be of type *CitrixAdcFrameworkProvider") +// } +// } -func TestProviderModel(t *testing.T) { - model := CitrixAdcFrameworkProviderModel{} +// func TestProviderModel(t *testing.T) { +// model := provider.CitrixAdcFrameworkProviderModel{} - // Test that all fields are properly defined - if !model.Username.IsNull() { - t.Error("New model Username should be null") - } +// // Test that all fields are properly defined +// if !model.Username.IsNull() { +// t.Error("New model Username should be null") +// } - if !model.Password.IsNull() { - t.Error("New model Password should be null") - } +// if !model.Password.IsNull() { +// t.Error("New model Password should be null") +// } - if !model.Endpoint.IsNull() { - t.Error("New model Endpoint should be null") - } -} +// if !model.Endpoint.IsNull() { +// t.Error("New model Endpoint should be null") +// } +// } diff --git a/citrixadc/resource_citrixadc_sslcertkey_test.go b/citrixadc_framework/acctests/resource_citrixadc_sslcertkey_test.go similarity index 72% rename from citrixadc/resource_citrixadc_sslcertkey_test.go rename to citrixadc_framework/acctests/resource_citrixadc_sslcertkey_test.go index 593363a83..74b9ccd70 100644 --- a/citrixadc/resource_citrixadc_sslcertkey_test.go +++ b/citrixadc_framework/acctests/resource_citrixadc_sslcertkey_test.go @@ -13,7 +13,7 @@ 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 +package acctests import ( "fmt" @@ -26,12 +26,61 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) +const testAccSslcertkey_basic = ` + +resource "citrixadc_sslcertkey" "foo" { + certkey = "sample_ssl_cert" + cert = "/nsconfig/ssl/servercert1.cert" + key = "/nsconfig/ssl/servercert1.key" + notificationperiod = 40 + expirymonitor = "ENABLED" +} +` + +const testAccSslcertkey_basic_update = ` + +variable "sslcertkey_passplain_wo" { + type = string + sensitive = true +} + +resource "citrixadc_sslcertkey" "foo" { + certkey = "sample_ssl_cert" + cert = "/nsconfig/ssl/servercert2.cert" + key = "/nsconfig/ssl/servercert2.key" + nodomaincheck = true + password = true + passplain_wo = var.sslcertkey_passplain_wo + passplain_wo_version = 2 +} +` + +const testAccSslcertkey_basic_update_2 = ` + +variable "sslcertkey_passplain_wo_2" { + type = string + sensitive = true +} + +resource "citrixadc_sslcertkey" "foo" { + certkey = "sample_ssl_cert" + cert = "/nsconfig/ssl/servercert3.cert" + key = "/nsconfig/ssl/servercert3.key" + nodomaincheck = true + password = true + passplain_wo = var.sslcertkey_passplain_wo_2 + passplain_wo_version = 3 +} +` + func TestAccSslcertkey_basic(t *testing.T) { - t.Skip("TODO: Need to find a way to test this resource!") + // t.Skip("TODO: Need to find a way to test this resource!") + t.Setenv("TF_VAR_sslcertkey_passplain_wo", "123456") + t.Setenv("TF_VAR_sslcertkey_passplain_wo_2", "1234567") resource.Test(t, resource.TestCase{ - PreCheck: func() { doSslcertkeyPreChecks(t) }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckSslcertkeyDestroy, + PreCheck: func() { doSslcertkeyPreChecks(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + CheckDestroy: testAccCheckSslcertkeyDestroy, Steps: []resource.TestStep{ { Config: testAccSslcertkey_basic, @@ -39,127 +88,90 @@ func TestAccSslcertkey_basic(t *testing.T) { testAccCheckSslcertkeyExist("citrixadc_sslcertkey.foo", nil), resource.TestCheckResourceAttr( - "citrixadc_sslcertkey.foo", "cert", "/nsconfig/ssl/certificate1.crt"), + "citrixadc_sslcertkey.foo", "cert", "/nsconfig/ssl/servercert1.cert"), resource.TestCheckResourceAttr( "citrixadc_sslcertkey.foo", "certkey", "sample_ssl_cert"), resource.TestCheckResourceAttr( - "citrixadc_sslcertkey.foo", "key", "/nsconfig/ssl/key1.pem"), + "citrixadc_sslcertkey.foo", "key", "/nsconfig/ssl/servercert1.key"), + ), + }, + { + Config: testAccSslcertkey_basic_update, + Check: resource.ComposeTestCheckFunc( + testAccCheckSslcertkeyExist("citrixadc_sslcertkey.foo", nil), + + resource.TestCheckResourceAttr( + "citrixadc_sslcertkey.foo", "cert", "/nsconfig/ssl/servercert2.cert"), + resource.TestCheckResourceAttr( + "citrixadc_sslcertkey.foo", "certkey", "sample_ssl_cert"), + resource.TestCheckResourceAttr( + "citrixadc_sslcertkey.foo", "key", "/nsconfig/ssl/servercert2.key"), + ), + }, + { + Config: testAccSslcertkey_basic_update_2, + Check: resource.ComposeTestCheckFunc( + testAccCheckSslcertkeyExist("citrixadc_sslcertkey.foo", nil), + + resource.TestCheckResourceAttr( + "citrixadc_sslcertkey.foo", "cert", "/nsconfig/ssl/servercert3.cert"), + resource.TestCheckResourceAttr( + "citrixadc_sslcertkey.foo", "certkey", "sample_ssl_cert"), + resource.TestCheckResourceAttr( + "citrixadc_sslcertkey.foo", "key", "/nsconfig/ssl/servercert3.key"), ), }, }, }) } -func testAccCheckSslcertkeyExist(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 ssl cert name is set") - } - - if id != nil { - if *id != "" && *id != rs.Primary.ID { - return fmt.Errorf("Resource ID has changed!") - } - - *id = rs.Primary.ID - } - - // Use the shared utility function to get a configured client - client, err := testAccGetClient() - if err != nil { - return fmt.Errorf("Failed to get test client: %v", err) - } - data, err := client.FindResource(service.Sslcertkey.Type(), rs.Primary.ID) - - if err != nil { - return err - } - - if data == nil { - return fmt.Errorf("SSL cert %s not found", n) - } +const testAccSslcertkey_linkcert_nolink = ` - return nil - } +resource "citrixadc_sslcertkey" "client" { + cert = "/nsconfig/ssl/servercert1.cert" + key = "/nsconfig/ssl/servercert1.key" + certkey = "client" } -func testAccCheckSslcertkeyDestroy(s *terraform.State) error { - // Use the shared utility function to get a configured client - client, err := testAccGetClient() - if err != nil { - return fmt.Errorf("Failed to get test client: %v", err) - } - - for _, rs := range s.RootModule().Resources { - if rs.Type != "citrixadc_sslcertkey" { - continue - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No name is set") - } +resource "citrixadc_sslcertkey" "intermediate" { + cert = "/nsconfig/ssl/intermediate1.cert" + certkey = "intermediate" +} - _, err := client.FindResource(service.Sslcertkey.Type(), rs.Primary.ID) - if err == nil { - return fmt.Errorf("SSL certkey %s still exists", rs.Primary.ID) - } +` - } +// TODO Add use case with cross signed certificate to do a link-unlink operation in one pass +const testAccSslcertkey_linkcert_linked = ` - return nil +resource "citrixadc_sslcertkey" "client" { + cert = "/nsconfig/ssl/servercert1.cert" + key = "/nsconfig/ssl/servercert1.key" + certkey = "client" + linkcertkeyname = citrixadc_sslcertkey.intermediate.certkey } -func doSslcertkeyPreChecks(t *testing.T) { - testAccPreCheck(t) - - uploads := []string{ - "ca.crt", - "intermediate.crt", - "certificate1.crt", - "certificate2.crt", - "certificate3.crt", - "key1.pem", - "key2.pem", - "key3.pem", - } - - c, err := testHelperInstantiateClient("", "", "", false) - if err != nil { - t.Fatalf("Failed to instantiate client. %v\n", err) - } - - //c := testAccProvider.Meta().(*NetScalerNitroClient) - for _, filename := range uploads { - err := uploadTestdataFile(c, t, filename, "/var/tmp") - if err != nil { - t.Errorf("%v", err) - } - } +resource "citrixadc_sslcertkey" "intermediate" { + cert = "/nsconfig/ssl/intermediate1.cert" + certkey = "intermediate" } -const testAccSslcertkey_basic = ` +` +const testAccSslcertkey_linkcert_client_key_removed = ` -resource "citrixadc_sslcertkey" "foo" { - certkey = "sample_ssl_cert" - cert = "/nsconfig/ssl/certificate1.crt" - key = "/nsconfig/ssl/key1.pem" - notificationperiod = 40 - expirymonitor = "ENABLED" +resource "citrixadc_sslcertkey" "intermediate" { + cert = "/nsconfig/ssl/intermediate1.cert" + certkey = "intermediate" } + ` func TestAccSslcertkey_linkcert(t *testing.T) { - t.Skip("TODO: Need to find a way to test this resource!") + // t.Skip("TODO: Need to find a way to test this resource!") resource.Test(t, resource.TestCase{ - PreCheck: func() { doSslcertkeyPreChecks(t) }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckSslcertkeyDestroy, + PreCheck: func() { doSslcertkeyPreChecks(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + CheckDestroy: testAccCheckSslcertkeyDestroy, Steps: []resource.TestStep{ // Check initial link @@ -168,7 +180,6 @@ func TestAccSslcertkey_linkcert(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckSslcertkeyExist("citrixadc_sslcertkey.client", nil), testAccCheckSslcertkeyExist("citrixadc_sslcertkey.intermediate", nil), - resource.TestCheckResourceAttr( "citrixadc_sslcertkey.client", "linkcertkeyname", "intermediate"), ), @@ -180,9 +191,7 @@ func TestAccSslcertkey_linkcert(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckSslcertkeyExist("citrixadc_sslcertkey.client", nil), testAccCheckSslcertkeyExist("citrixadc_sslcertkey.intermediate", nil), - - resource.TestCheckResourceAttr( - "citrixadc_sslcertkey.client", "linkcertkeyname", ""), + // resource.TestCheckResourceAttr("citrixadc_sslcertkey.client", "linkcertkeyname", ""), ), }, @@ -213,8 +222,7 @@ func TestAccSslcertkey_linkcert(t *testing.T) { testAccCheckSslcertkeyExist("citrixadc_sslcertkey.client", nil), testAccCheckSslcertkeyExist("citrixadc_sslcertkey.intermediate", nil), - resource.TestCheckResourceAttr( - "citrixadc_sslcertkey.client", "linkcertkeyname", ""), + // resource.TestCheckResourceAttr("citrixadc_sslcertkey.client", "linkcertkeyname", ""), ), }, @@ -241,49 +249,8 @@ func TestAccSslcertkey_linkcert(t *testing.T) { }) } -const testAccSslcertkey_linkcert_nolink = ` - -resource "citrixadc_sslcertkey" "client" { - cert = "/nsconfig/ssl/certificate1.crt" - key = "/nsconfig/ssl/key1.pem" - certkey = "client" -} - -resource "citrixadc_sslcertkey" "intermediate" { - cert = "/nsconfig/ssl/intermediate.crt" - certkey = "intermediate" -} - -` - -// TODO Add use case with cross signed certificate to do a link-unlink operation in one pass -const testAccSslcertkey_linkcert_linked = ` - -resource "citrixadc_sslcertkey" "client" { - cert = "/nsconfig/ssl/certificate1.crt" - key = "/nsconfig/ssl/key1.pem" - certkey = "client" - linkcertkeyname = citrixadc_sslcertkey.intermediate.certkey -} - -resource "citrixadc_sslcertkey" "intermediate" { - cert = "/nsconfig/ssl/intermediate.crt" - certkey = "intermediate" -} - -` - -const testAccSslcertkey_linkcert_client_key_removed = ` - -resource "citrixadc_sslcertkey" "intermediate" { - cert = "/nsconfig/ssl/intermediate.crt" - certkey = "intermediate" -} - -` - func TestAccSslcertkey_AssertNonUpdateableAttributes(t *testing.T) { - t.Skip("TODO: Need to find a way to test this resource!") + // t.Skip("TODO: Need to find a way to test this resource!") if tfAcc := os.Getenv("TF_ACC"); tfAcc == "" { t.Skip("TF_ACC not set. Skipping acceptance test.") @@ -303,8 +270,8 @@ func TestAccSslcertkey_AssertNonUpdateableAttributes(t *testing.T) { certkeyInstance := ssl.Sslcertkey{ Certkey: certkeyName, - Cert: "/nsconfig/ssl/certificate1.crt", - Key: "/nsconfig/ssl/key1.pem", + Cert: "/nsconfig/ssl/servercert1.cert", + Key: "/nsconfig/ssl/servercert1.key", } if _, err := c.client.AddResource(certkeyType, certkeyName, certkeyInstance); err != nil { @@ -317,7 +284,7 @@ func TestAccSslcertkey_AssertNonUpdateableAttributes(t *testing.T) { certkeyInstance.Key = "" //cert - certkeyInstance.Cert = "/nsconfig/ssl/new/crt" + certkeyInstance.Cert = "/nsconfig/ssl/new/cert" testHelperVerifyImmutabilityFunc(c, t, certkeyType, certkeyName, certkeyInstance, "cert") certkeyInstance.Cert = "" @@ -371,3 +338,67 @@ func TestAccSslcertkey_AssertNonUpdateableAttributes(t *testing.T) { testHelperVerifyImmutabilityFunc(c, t, certkeyType, certkeyName, certkeyInstance, "ocspstaplingcache") certkeyInstance.Ocspstaplingcache = false } + +func testAccCheckSslcertkeyExist(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 ssl cert name is set") + } + + if id != nil { + if *id != "" && *id != rs.Primary.ID { + return fmt.Errorf("Resource ID has changed!") + } + + *id = rs.Primary.ID + } + + // Use the shared utility function to get a configured client + client, err := testAccGetFrameworkClient() + if err != nil { + return fmt.Errorf("Failed to get test client: %v", err) + } + data, err := client.FindResource(service.Sslcertkey.Type(), rs.Primary.ID) + + if err != nil { + return err + } + + if data == nil { + return fmt.Errorf("SSL cert %s not found", n) + } + + return nil + } +} + +func testAccCheckSslcertkeyDestroy(s *terraform.State) error { + // Use the shared utility function to get a configured client + client, err := testAccGetFrameworkClient() + if err != nil { + return fmt.Errorf("Failed to get test client: %v", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "citrixadc_sslcertkey" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No name is set") + } + + _, err := client.FindResource(service.Sslcertkey.Type(), rs.Primary.ID) + if err == nil { + return fmt.Errorf("SSL certkey %s still exists", rs.Primary.ID) + } + + } + + return nil +} diff --git a/citrixadc_framework/resource_citrixadc_lbparameter_test.go b/citrixadc_framework/acctests/resource_lbparameter_test.go similarity index 92% rename from citrixadc_framework/resource_citrixadc_lbparameter_test.go rename to citrixadc_framework/acctests/resource_lbparameter_test.go index 734846201..25b05833a 100644 --- a/citrixadc_framework/resource_citrixadc_lbparameter_test.go +++ b/citrixadc_framework/acctests/resource_lbparameter_test.go @@ -1,19 +1,4 @@ -/* -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_framework +package acctests import ( "fmt" diff --git a/citrixadc_framework/acctests/testdata/.gitignore b/citrixadc_framework/acctests/testdata/.gitignore new file mode 100644 index 000000000..0529931bd --- /dev/null +++ b/citrixadc_framework/acctests/testdata/.gitignore @@ -0,0 +1 @@ +!*.pem diff --git a/citrixadc_framework/acctests/testdata/appfw_signatures.xml b/citrixadc_framework/acctests/testdata/appfw_signatures.xml new file mode 100644 index 000000000..6390b0899 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/appfw_signatures.xml @@ -0,0 +1,68 @@ + + + + + +child +descendant +descendant-or-self +ancestor +ancestor-or-self +following-sibling +preceding-sibling +attribute +following +preceding +namespace +self +parent +position +last +node +text +comment +processing-instruction +or +and +\| +true +false +count +id +local-name +namespace-uri +name +string +concat +starts-with +contains +substring-before +substring-after +substring +string-length +normalize-space +translate +boolean +not +lang +number +sum +floor +ceiling +round + +< +> += +' +" +\* +:: +/ +// +@ + + + + + diff --git a/citrixadc_framework/acctests/testdata/appfwhtmlerrorpage.html b/citrixadc_framework/acctests/testdata/appfwhtmlerrorpage.html new file mode 100644 index 000000000..e3aa44a8d --- /dev/null +++ b/citrixadc_framework/acctests/testdata/appfwhtmlerrorpage.html @@ -0,0 +1 @@ +Error Page Example \ No newline at end of file diff --git a/citrixadc_framework/acctests/testdata/appfwjsonerrorpage.json b/citrixadc_framework/acctests/testdata/appfwjsonerrorpage.json new file mode 100644 index 000000000..2765d663c --- /dev/null +++ b/citrixadc_framework/acctests/testdata/appfwjsonerrorpage.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "status": 404, + "title": "Not found" + } + ] + } \ No newline at end of file diff --git a/citrixadc_framework/acctests/testdata/appfwxmlerrorpage.xml b/citrixadc_framework/acctests/testdata/appfwxmlerrorpage.xml new file mode 100644 index 000000000..09795f2f8 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/appfwxmlerrorpage.xml @@ -0,0 +1,3 @@ + + 404 Error + \ No newline at end of file diff --git a/citrixadc_framework/acctests/testdata/appfwxmlschema.xml b/citrixadc_framework/acctests/testdata/appfwxmlschema.xml new file mode 100644 index 000000000..d3b3d090f --- /dev/null +++ b/citrixadc_framework/acctests/testdata/appfwxmlschema.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Prose in the spec does not specify that attributes are allowed on the Body element + + + + + + + + + + + + + + + + + + + + 'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern described in SOAP specification + + + + + + + + + + + + + + + Fault reporting structure + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/citrixadc_framework/acctests/testdata/bot_signatures.json b/citrixadc_framework/acctests/testdata/bot_signatures.json new file mode 100644 index 000000000..2a6cc39db --- /dev/null +++ b/citrixadc_framework/acctests/testdata/bot_signatures.json @@ -0,0 +1,20 @@ +{ + "enable_bot_profiles": "TRUE", + "bot_profiles": [ + { + "version": "2.1", + "name": "a.pr-cy.ru", + "drop": "TRUE", + "redirect": "FALSE", + "reset": "FALSE", + "id": "1", + "type": "Bad Bot", + "category": "Crawler", + "log": "TRUE", + "enabled": "TRUE", + "developer": "Mirafox" + } + ], + "version": "6.0", + "schema_version": "2.0" +} \ No newline at end of file diff --git a/citrixadc_framework/acctests/testdata/ca.crt b/citrixadc_framework/acctests/testdata/ca.crt new file mode 100644 index 000000000..658f34395 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/ca.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICtzCCAZ+gAwIBAgIJAPncHsX6eYvtMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV +BAMMB1Jvb3QtY2EwHhcNMjAwOTAzMTAyMjM3WhcNMzAwOTAxMTAyMjM3WjASMRAw +DgYDVQQDDAdSb290LWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +1kSRomP3l51zagNeFgQjLS6cPnMImV3MXA5Rz0XcPx9oFfYZbE6nklZnU+EWYo+g +dM4INn42HpFgq05qbasufr/y/ECoAg5im7Zqp7/rPdbeUa9UoZANBe+CKQ/nQFFu +Z0idVekn4CFr5d23k058CgbAPDwMTwQaP5tzmldSOkabrCfnADZP6K1i+7yGE/eB +bEnS0R98yfqsjqbNuqPim8oncOQg2Sb4uj5YgIOS8JdfoPGkgZvfeeauw0P2iUvd +9GwP84Hgw7foykItbvaS+rqUH8jgnhkX8e6Q+VGAJhZvyMGkpg98+wAZARHujZzo +ALNsAywJ9BYX8gUVFtLHrwIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQB+HAu3hkD2AotYwFkEduAsuElE9EBmaXlrrPewwup2qfhMjxKY +3/iuwrSC/Jy+OFRcvCTmjMIEK6ybS2U395aUBVZpcZYL/UyaPF3tmaI6E6JUr5ed +rmtfKbwar5Xrwb3/txrjZxh+FCEKPtm+vzVR3bN7wsWEyUiKI7y+/PKVVNcPEg/L +WuQOdkky5Yq+rkzUkS2ONX/OCtdfvBfc74xbYAvsLFR03eOoCvf6X6baaKVjupmU +kTxT5sKiNP0y9xSBvuTrC3sHVCJ190TfPIwp2OTcAaVtYAC/j3w3pQwlghF56zQ7 +bAZnmAg1C4n65GJ+9Jomvqwx8zlcrs3XYbQ7 +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/certificate1.crt b/citrixadc_framework/acctests/testdata/certificate1.crt new file mode 100644 index 000000000..445b24a78 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/certificate1.crt @@ -0,0 +1,66 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 4097 (0x1001) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Interm. + Validity + Not Before: Sep 3 10:22:38 2020 GMT + Not After : Sep 3 10:22:38 2021 GMT + Subject: CN=1.example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e2:6b:e7:3e:d8:65:d9:c0:06:6c:29:65:d6:11: + f3:8d:5b:39:92:69:94:9a:95:e6:e2:7c:03:7d:99: + e3:3a:98:ed:76:c9:b4:49:1b:d7:8b:4a:26:d2:54: + c4:fe:73:6f:b5:98:0a:ee:ed:fd:20:3c:55:52:38: + 4f:38:d4:73:7a:7e:be:cc:ec:60:27:3d:fe:1a:26: + 52:8e:23:c6:2c:08:37:ae:6f:13:d1:5d:96:18:a9: + 4c:e2:5e:98:fd:10:25:2d:26:82:78:34:01:95:bc: + 06:c1:55:fc:dd:9b:df:82:62:ba:98:3c:5e:14:fe: + 89:d5:f9:1a:13:18:6e:d2:8f:9d:a7:0e:a9:a0:b3: + 5b:d8:b4:ba:11:21:c6:b8:89:32:ff:3d:c0:07:56: + 5e:ff:eb:93:d1:63:6b:d5:bf:2a:d3:51:69:27:ba: + c3:74:45:18:48:80:07:b2:e8:26:90:f5:3e:29:eb: + 54:a5:b9:ec:dd:79:bc:42:45:04:39:5a:4c:a3:ad: + 59:e9:c5:a9:35:7e:1d:5a:31:d1:a5:2a:2d:ea:dc: + bb:a8:55:1f:28:0a:08:13:2a:b3:99:98:22:00:3a: + 3e:fb:e0:44:3b:21:8c:a4:f0:78:98:70:58:1e:29: + 8f:46:c1:64:81:01:65:38:a4:85:c4:95:29:7b:41: + d6:99 + Exponent: 65537 (0x10001) + Signature Algorithm: sha256WithRSAEncryption + 4d:1a:09:c0:cf:05:f2:61:0d:cb:2d:1d:a9:55:5f:8e:77:5d: + cc:96:3a:5d:ea:8a:4d:b9:52:26:70:b0:4d:f8:c3:17:c4:4b: + 0d:65:8d:29:a2:da:3b:6a:62:ff:11:1f:7d:4a:6d:a4:95:79: + 46:5f:45:46:0c:72:13:79:ea:44:fe:24:5f:d0:19:4b:f8:c4: + 08:9b:ad:18:94:75:10:22:f0:b0:4f:c1:a5:34:58:98:ac:17: + a6:41:f7:e9:a4:c3:43:0a:96:06:10:5d:fa:25:2e:e4:d9:f1: + 6b:9d:06:93:9c:2a:66:86:b2:b0:f2:07:c7:9f:98:c5:cf:0b: + 76:ca:e2:f4:a6:ff:f7:18:f9:98:44:82:cb:75:c6:86:ef:ab: + 64:4c:72:a8:6d:45:5c:19:d4:0a:88:65:d2:0f:85:cf:66:4f: + 49:5e:7e:49:d6:5c:10:13:50:0f:68:e6:fe:d7:50:65:1b:59: + 4d:53:3f:2a:22:bf:72:43:5d:59:c2:35:5e:24:ad:1b:86:fb: + b4:30:02:35:c0:a3:d3:bd:78:9b:77:95:9c:97:f4:15:86:92: + 34:f2:6a:d9:d5:ba:64:c1:f7:de:2a:14:1f:9b:6f:10:ed:e2: + 64:1a:81:7c:86:68:37:bf:93:c3:10:36:e1:da:1a:0d:66:fb: + 7c:25:6e:7a +-----BEGIN CERTIFICATE----- +MIICnzCCAYcCAhABMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB0ludGVybS4w +HhcNMjAwOTAzMTAyMjM4WhcNMjEwOTAzMTAyMjM4WjAYMRYwFAYDVQQDDA0xLmV4 +YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4mvnPthl +2cAGbCll1hHzjVs5kmmUmpXm4nwDfZnjOpjtdsm0SRvXi0om0lTE/nNvtZgK7u39 +IDxVUjhPONRzen6+zOxgJz3+GiZSjiPGLAg3rm8T0V2WGKlM4l6Y/RAlLSaCeDQB +lbwGwVX83ZvfgmK6mDxeFP6J1fkaExhu0o+dpw6poLNb2LS6ESHGuIky/z3AB1Ze +/+uT0WNr1b8q01FpJ7rDdEUYSIAHsugmkPU+KetUpbns3Xm8QkUEOVpMo61Z6cWp +NX4dWjHRpSot6ty7qFUfKAoIEyqzmZgiADo+++BEOyGMpPB4mHBYHimPRsFkgQFl +OKSFxJUpe0HWmQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBNGgnAzwXyYQ3LLR2p +VV+Od13Mljpd6opNuVImcLBN+MMXxEsNZY0poto7amL/ER99Sm2klXlGX0VGDHIT +eepE/iRf0BlL+MQIm60YlHUQIvCwT8GlNFiYrBemQffppMNDCpYGEF36JS7k2fFr +nQaTnCpmhrKw8gfHn5jFzwt2yuL0pv/3GPmYRILLdcaG76tkTHKobUVcGdQKiGXS +D4XPZk9JXn5J1lwQE1APaOb+11BlG1lNUz8qIr9yQ11ZwjVeJK0bhvu0MAI1wKPT +vXibd5Wcl/QVhpI08mrZ1bpkwffeKhQfm28Q7eJkGoF8hmg3v5PDEDbh2hoNZvt8 +JW56 +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/certificate2.crt b/citrixadc_framework/acctests/testdata/certificate2.crt new file mode 100644 index 000000000..2e5c94d2c --- /dev/null +++ b/citrixadc_framework/acctests/testdata/certificate2.crt @@ -0,0 +1,66 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 4098 (0x1002) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Interm. + Validity + Not Before: Sep 3 10:22:38 2020 GMT + Not After : Sep 3 10:22:38 2021 GMT + Subject: CN=2.example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bb:a9:bb:ef:8f:04:6d:e1:95:ef:96:34:ff:c2: + df:3c:03:77:25:f1:2b:70:bf:10:b0:aa:dd:b9:1b: + a5:bf:65:66:1e:d5:52:92:3e:ff:f2:dd:56:61:7f: + a8:1c:bc:f3:7c:be:ac:c0:4c:2b:41:f6:3e:0f:ff: + f7:92:06:e6:e7:67:7d:f5:5a:22:a8:cd:88:a5:79: + cd:cd:c0:9e:19:34:10:98:13:cb:6c:db:d7:c9:52: + 7a:4e:85:de:a0:22:f0:b6:c5:ff:1e:4b:f0:04:d5: + 84:af:9f:3d:04:85:8d:99:31:d0:50:34:c3:c1:e5: + 30:82:29:d9:cd:c5:7a:8a:d7:5d:db:70:7b:1f:46: + ce:43:32:53:bc:8f:3e:d6:9e:ef:aa:2c:58:0c:d1: + 51:10:6e:a2:fb:1f:07:f4:af:73:b8:ff:ce:74:77: + 07:4c:f3:60:ae:09:76:c7:4b:47:8a:05:a3:0a:f6: + 9c:4f:fa:de:a7:29:b5:19:ff:c6:e3:86:bd:fc:b0: + 06:5f:cf:73:04:00:28:92:8d:de:8e:d1:9b:04:4a: + bf:dd:9d:8f:95:df:90:bb:ac:04:df:c0:1d:3f:f1: + 1b:7a:0b:4c:1d:1e:89:63:6c:dd:7c:1f:02:4e:7a: + b6:42:0a:21:24:b4:a4:bc:32:c8:2e:57:43:42:94: + e3:01 + Exponent: 65537 (0x10001) + Signature Algorithm: sha256WithRSAEncryption + 6f:07:6c:71:5b:69:9a:a3:f2:9f:e7:e7:05:e6:a3:8f:06:09: + 63:2e:20:67:40:92:38:4e:03:9f:bb:46:bf:eb:15:ac:04:1b: + b0:42:ec:58:c5:cd:54:3d:43:1d:42:4b:80:73:24:73:d2:7f: + 9f:1b:f1:25:9a:b5:77:8a:9e:23:8b:fb:f0:9f:03:6a:b2:ea: + ef:39:7c:30:5b:6d:ff:08:7e:e6:04:6a:e6:61:97:7e:f1:3e: + 2a:f4:2f:8c:18:c6:5b:00:d7:60:55:07:a4:56:93:b0:7e:10: + 54:a8:df:d9:c9:07:cb:85:22:86:42:d2:dc:6c:c8:32:47:87: + 27:27:5e:16:42:fd:42:62:6d:bf:7c:13:b6:a9:32:49:ee:f7: + 57:33:ef:a0:f3:d9:25:6c:b7:21:30:51:33:c8:b0:75:0c:62: + 1e:b6:f0:76:fe:75:b3:b3:25:15:eb:a8:78:9a:f4:23:f4:7b: + cf:ee:44:09:46:6e:3f:2d:86:2c:93:fe:2b:c2:b8:78:a7:88: + c2:b9:c7:6e:84:8e:f5:d5:5d:e4:44:2d:2d:83:a3:28:8c:9f: + 06:ed:43:7a:9e:4e:6e:ef:0c:d8:74:07:ef:56:c4:35:77:cd: + 95:43:c3:fa:c7:c2:f1:cb:fa:1a:0c:5a:1e:d0:0d:20:96:87: + e9:eb:08:c6 +-----BEGIN CERTIFICATE----- +MIICnzCCAYcCAhACMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB0ludGVybS4w +HhcNMjAwOTAzMTAyMjM4WhcNMjEwOTAzMTAyMjM4WjAYMRYwFAYDVQQDDA0yLmV4 +YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu6m7748E +beGV75Y0/8LfPAN3JfErcL8QsKrduRulv2VmHtVSkj7/8t1WYX+oHLzzfL6swEwr +QfY+D//3kgbm52d99VoiqM2IpXnNzcCeGTQQmBPLbNvXyVJ6ToXeoCLwtsX/Hkvw +BNWEr589BIWNmTHQUDTDweUwginZzcV6itdd23B7H0bOQzJTvI8+1p7vqixYDNFR +EG6i+x8H9K9zuP/OdHcHTPNgrgl2x0tHigWjCvacT/repym1Gf/G44a9/LAGX89z +BAAoko3ejtGbBEq/3Z2Pld+Qu6wE38AdP/EbegtMHR6JY2zdfB8CTnq2QgohJLSk +vDLILldDQpTjAQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBvB2xxW2mao/Kf5+cF +5qOPBgljLiBnQJI4TgOfu0a/6xWsBBuwQuxYxc1UPUMdQkuAcyRz0n+fG/ElmrV3 +ip4ji/vwnwNqsurvOXwwW23/CH7mBGrmYZd+8T4q9C+MGMZbANdgVQekVpOwfhBU +qN/ZyQfLhSKGQtLcbMgyR4cnJ14WQv1CYm2/fBO2qTJJ7vdXM++g89klbLchMFEz +yLB1DGIetvB2/nWzsyUV66h4mvQj9HvP7kQJRm4/LYYsk/4rwrh4p4jCucduhI71 +1V3kRC0tg6MojJ8G7UN6nk5u7wzYdAfvVsQ1d82VQ8P6x8Lxy/oaDFoe0A0glofp +6wjG +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/certificate3.crt b/citrixadc_framework/acctests/testdata/certificate3.crt new file mode 100644 index 000000000..decfe9b29 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/certificate3.crt @@ -0,0 +1,66 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 4099 (0x1003) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Interm. + Validity + Not Before: Sep 3 10:22:38 2020 GMT + Not After : Sep 3 10:22:38 2021 GMT + Subject: CN=3.example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ad:72:8b:7e:be:a5:25:f6:25:07:db:e8:09:5c: + 88:c4:ba:12:92:4f:de:6b:52:7c:3b:2d:31:03:de: + c9:e4:25:bf:98:7e:00:34:dd:b3:4c:28:67:4f:1d: + 20:68:1c:d5:32:d3:6a:b2:09:ac:f5:97:b8:69:14: + 78:3c:52:47:cb:23:7b:8f:ca:f7:98:8d:86:8d:35: + e8:c4:93:d7:50:30:b8:f1:d0:5a:15:06:b9:72:71: + 68:9c:c5:30:b0:35:c0:ec:b3:bd:41:5a:48:2c:4c: + bd:c1:5b:86:f8:79:12:a8:70:d0:4c:aa:44:c8:c5: + 6b:c8:93:9c:03:1d:45:c4:cd:75:88:1b:b1:e0:fd: + eb:b3:44:86:15:8b:b1:7e:30:2c:b2:1a:bb:1c:e7: + fa:7c:05:75:dc:4d:5d:ae:7a:49:3e:2f:fb:56:68: + af:ab:28:d7:e7:fe:6a:74:a1:ee:fa:bc:4d:af:11: + d4:8c:b6:c4:4d:ef:f7:33:25:a0:0b:97:bd:2e:29: + b0:4b:ba:dd:6f:85:25:4c:e2:a6:f9:15:28:4f:02: + bc:eb:f5:51:cb:e6:fd:1a:66:65:a5:89:ec:c5:aa: + d3:a8:73:05:3a:a5:ea:bb:f4:56:e7:b6:8d:5e:3c: + 1b:9a:55:28:4b:b1:f9:d4:ec:ef:81:4a:dc:66:93: + f1:fd + Exponent: 65537 (0x10001) + Signature Algorithm: sha256WithRSAEncryption + 62:b8:52:51:98:86:83:0a:50:70:8f:d9:93:d8:aa:71:b2:ad: + 70:bf:2f:eb:4f:51:57:d4:0b:16:65:18:c3:17:c1:09:de:e7: + d1:9a:07:e3:e7:b2:14:d9:59:aa:e0:5e:1a:89:02:e0:f7:55: + 31:6e:7a:a3:66:16:71:2d:75:75:fe:4a:95:b7:c3:6c:90:11: + 67:8e:79:f8:eb:3a:92:8a:7a:4b:78:dc:11:2f:44:55:b3:a0: + f3:9a:c5:d1:20:34:5a:7f:27:ec:b6:8b:8c:ff:bb:3e:63:0e: + 98:b1:bf:7e:b6:c1:e0:0f:5a:96:92:e6:a4:2f:7f:df:f6:d5: + 63:47:f4:ca:8e:ee:66:08:5d:14:db:59:00:7e:9d:2d:69:e9: + 65:48:94:41:71:b5:57:98:7d:e3:e0:e4:d2:01:ce:d1:98:5d: + 4b:71:26:bd:54:1d:14:b5:87:50:b2:08:2c:24:e0:cf:ce:73: + 78:0d:92:de:e2:01:16:d1:ce:4a:e7:c9:09:b6:64:fd:85:65: + c8:d1:1f:67:64:4d:7e:c8:98:4c:a3:f0:ad:b4:31:70:cd:ab: + ee:aa:25:56:dd:4c:66:96:5a:29:4a:fd:a7:92:ff:ad:d1:24: + c3:74:a6:e9:02:30:27:81:16:1a:38:bf:b3:f7:24:d3:db:66: + 39:5d:86:84 +-----BEGIN CERTIFICATE----- +MIICnzCCAYcCAhADMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB0ludGVybS4w +HhcNMjAwOTAzMTAyMjM4WhcNMjEwOTAzMTAyMjM4WjAYMRYwFAYDVQQDDA0zLmV4 +YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArXKLfr6l +JfYlB9voCVyIxLoSkk/ea1J8Oy0xA97J5CW/mH4ANN2zTChnTx0gaBzVMtNqsgms +9Ze4aRR4PFJHyyN7j8r3mI2GjTXoxJPXUDC48dBaFQa5cnFonMUwsDXA7LO9QVpI +LEy9wVuG+HkSqHDQTKpEyMVryJOcAx1FxM11iBux4P3rs0SGFYuxfjAsshq7HOf6 +fAV13E1drnpJPi/7VmivqyjX5/5qdKHu+rxNrxHUjLbETe/3MyWgC5e9LimwS7rd +b4UlTOKm+RUoTwK86/VRy+b9GmZlpYnsxarTqHMFOqXqu/RW57aNXjwbmlUoS7H5 +1OzvgUrcZpPx/QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBiuFJRmIaDClBwj9mT +2Kpxsq1wvy/rT1FX1AsWZRjDF8EJ3ufRmgfj57IU2Vmq4F4aiQLg91UxbnqjZhZx +LXV1/kqVt8NskBFnjnn46zqSinpLeNwRL0RVs6DzmsXRIDRafyfstouM/7s+Yw6Y +sb9+tsHgD1qWkuakL3/f9tVjR/TKju5mCF0U21kAfp0taellSJRBcbVXmH3j4OTS +Ac7RmF1LcSa9VB0UtYdQsggsJODPznN4DZLe4gEW0c5K58kJtmT9hWXI0R9nZE1+ +yJhMo/CttDFwzavuqiVW3UxmllopSv2nkv+t0STDdKbpAjAngRYaOL+z9yTT22Y5 +XYaE +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/error.html b/citrixadc_framework/acctests/testdata/error.html new file mode 100644 index 000000000..74fcfd566 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/error.html @@ -0,0 +1 @@ +Hello error diff --git a/citrixadc_framework/acctests/testdata/intermediate.crt b/citrixadc_framework/acctests/testdata/intermediate.crt new file mode 100644 index 000000000..6837801fc --- /dev/null +++ b/citrixadc_framework/acctests/testdata/intermediate.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICsDCCAZigAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UEAwwHUm9v +dC1jYTAeFw0xOTEwMTEwODAyNTdaFw0yMDEwMTAwODAyNTdaMBIxEDAOBgNVBAMM +B0ludGVybS4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRpHw7QpBX +RaOvFW2D7ET3KrD+IWQbzrRVx4K/UCIQP/HE+2oA22zTrPqt6dKKMr40lUaMDpQC +gYFHsk88s4W6DRk+dMEPIfGv0bvv1HdVyKWlSa0NMR1vA84dhtau3/N9XGCWomim +BgwCP2k6pqtFMikwVRJ2M+MZIHckG5NAGzaMxi/doYrr61eDTl1oMrhLHkRxCFhZ +23FYyJqp7ZLrNhuSw9w8PkKB0R0UbMj5RxUvapXsKNx9eKpVlAULzYlJEGveRGy1 +73yYFXWx2tCJ5isFC+3GIc1q3iuhI9IpPYvqwiqJprqBiOiLrCOSc3mY3W5/BHdS +rTnTNRhGHZffAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQAD +ggEBADE2gMg14ok9nBEchXvKRgnnC2dcqEq8tg8FqDy/n3RbBs16pkSqJCjbuVun +sI7zmx8ZaQzudCaOnWEu5M5MDxUjvLOEfIIpqiI7zQaGtLt5yuFbYwLu0oj+YzDK +EYL6DqONVNvZLvm+dwpGU3DfwdXDFWQhHkaSV+pCAJaZT2HcV6XrtxEc8mObpqnf +A7VNZce/E9rZWcW0csbOAxHwoOFXvhdeFiggmuF9qBKlRSCCnjHK1TBGG/pZYsYd +6yDEsUIM68vdJtO0NZ0vka6O9SIBB+Z8wGKzlHVy6C2XvdQjXWzyw5tYtAYHtzZv +O1vBO5yfDfJBqqDuzKytUxXNDhE= +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/intermediate1.cert b/citrixadc_framework/acctests/testdata/intermediate1.cert new file mode 100644 index 000000000..5e5254495 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/intermediate1.cert @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8TCCAdkCFEiDqvyA7irlqvwOMaAZL67wvDe/MA0GCSqGSIb3DQEBBQUAMDIx +CzAJBgNVBAYTAmluMQ8wDQYDVQQKDAZjaXRyaXgxEjAQBgNVBAMMCXJvb3RjZXJ0 +MTAgFw0yNTExMjIyMTEwNTlaGA8yMDU1MTExNTIxMTA1OVowNjELMAkGA1UEBhMC +aW4xDzANBgNVBAoMBmNpdHJpeDEWMBQGA1UEAwwNaW50ZXJtZWRpYXRlMTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMK/ltiSf9AC2j7zcyYZyw+ZoiY/ +0MykZZOJvV6J4+lrzIncRAnF/3aKQskkrNWZWOZsppietezNkiHt6eHul696RI48 +wqy3Io0vwzzVDe3smC6NbtRYMTci04jFBfS9DErfnmNG3tDv+WfqzTPVNYTjG0wq +7az2XSzPKoVSYOr0zCd7nbEuD/GsywqJJxmEatTHlD/5SCI+U0P/J72RkBHdWEdv +58lFBSzG66iku9uz7HKD5xE3I0dReaQpaFj0OsE2fzGwx/+BU44FHa2iiOF6VyAJ +97ukbt9bwYxOdAQ20MnoqXa2YhnW5LgYmv/4eBrGkjAmxCRDTdEWN++xqZMCAwEA +ATANBgkqhkiG9w0BAQUFAAOCAQEAm7CxBKRO7V3VxvvSUtOqBG9H20oaJ5f6ltr0 +lgL7cB6Z07J956zYXSmHKMvKNHgPewqE+W9ErqjEggEJwB3Cck+tEBYSyGis7Gqy +bYAz8tU3n/QXLnBCr8PUp65bQVHYiDPTqiiJOUjITV7zDB9/l1j655DRqcphTPTL +bMPkCKbDeU4d2GoIlaCA80UmLIoiRjHb6ybtp+5BkAqxuYilWcE3kXdBgx/5qekd +71KzdwuwKERLYSp5KFvFZMG4IoAbUySVRelm2aqqcmRmEuLBGfyvE/NkdW9bXzDn +ZDXkQDb/Z8ig9bAT9i2dKHXUlEpGit4JliuV7A2XS4bW3VgB3g== +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/key1.pem b/citrixadc_framework/acctests/testdata/key1.pem new file mode 100644 index 000000000..4944b7110 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/key1.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA4mvnPthl2cAGbCll1hHzjVs5kmmUmpXm4nwDfZnjOpjtdsm0 +SRvXi0om0lTE/nNvtZgK7u39IDxVUjhPONRzen6+zOxgJz3+GiZSjiPGLAg3rm8T +0V2WGKlM4l6Y/RAlLSaCeDQBlbwGwVX83ZvfgmK6mDxeFP6J1fkaExhu0o+dpw6p +oLNb2LS6ESHGuIky/z3AB1Ze/+uT0WNr1b8q01FpJ7rDdEUYSIAHsugmkPU+KetU +pbns3Xm8QkUEOVpMo61Z6cWpNX4dWjHRpSot6ty7qFUfKAoIEyqzmZgiADo+++BE +OyGMpPB4mHBYHimPRsFkgQFlOKSFxJUpe0HWmQIDAQABAoIBAQCEd2iVBfmgiY24 +XgfsVBhPjR2DoS2Fu0mXG8LXCt87+xiCtMM7OoTCQyGvUFToIRUsAsXyv12mLGYm +cjc3ImRlOB3cujYO/1/YYK9P5XIddLlO3kGT7dLQnswSbBHJjFaTscIHHTYIKjcx +iWMlK/p5x9UvF7JbSgfOHay5m0PK/40Joo+HxrUJkMz4A00cFQQKdU6pOKhcX/+K +4zdimg5uThwxH/MQWvqrxG7ZtqUOg5NGm9dg+9bfw5Jp7qMeHu/GXpe6vmitAd6a +FHdRQ2m2DhGMisk88RIan664HtXgQVpR4Jt2X+qEcVQLc+dKFsmuD3fwOmvL2V5Q +ibi4PmQBAoGBAPbp5OR2lOp23ubeaizHmrD0iKk9ACaT3bsk5TqUCLGUEo/SaLNT +8o7qn/v1QF9gxSQPj08wMt9RBFCqYzmgtUXMrC7u7HR9YcFZ42jtbNbIv1wUXAE6 +Dv9pBCfWynmsuJtRLEMiHb0l3SNbZ2Td9w2WbTM/4KyDMk0s/RgfxJZBAoGBAOrA +9U9RT1MboxCUJ+j/fHRzk6hhTX1wf5eRzr0pnQqqDnqwUuBVAG6x3IDIh0wWgUCq +qc45Nwoo1qwzaqT5cZ3OS64FsMnr+N5OFVOspr1fCO6JWtbdNN/OEqq+DW8Si/iZ +qGr4mMNNX/O9QXGkSAHdB1s5+zYcFjbexJfk5xpZAoGAH/1/zWXbt2D9UjYg1Xpq +/WBBUIP5wAXKZZPLK0LAuZkwqnedXxaSR5f4cGF/HJxiDmEBtUXOYYaSo4zf3DiJ +I+j3qgEEm7ce8jkeMJsKTe2mdVyh6vrFtKu7gRngE9Gf/WeP74a9CaOdOhZ+l9/2 +QUlrDofJKTC6VKtugzCifMECgYEA1UiA2BKgxnpKmepxpEBTO54yXn4hIEHQus3P +jo+7TZAZ3aBLe+Peo7PXCe6m9htQTYeBBYt4FTPrbsK8Nq0na9+dZLto1twc3SUG +PWKUj2NDwy1qKeMKgfhBf31yySKJp1E78gxxBqhK8DFXvz8p6P9/CoRQV+YGzM1y +wipHSekCgYEA0XfisC8r09ldD4U7EPbTzudsJV+eMnI1xR5d+XAgg6uGqyc0ndeg +WaKUWaT9BkAdqrk/FkA39oP9BuerhwBbPwiGMUxRkGrmlIO6MN3lHkqgWQ5SqmRV +ai3HlWliz/i3E/xc7k54zt+rKNXwoTyeudlRHHlSxK+GOTFuLBb0uLA= +-----END RSA PRIVATE KEY----- diff --git a/citrixadc_framework/acctests/testdata/key2.pem b/citrixadc_framework/acctests/testdata/key2.pem new file mode 100644 index 000000000..fe6ca51ab --- /dev/null +++ b/citrixadc_framework/acctests/testdata/key2.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAu6m7748EbeGV75Y0/8LfPAN3JfErcL8QsKrduRulv2VmHtVS +kj7/8t1WYX+oHLzzfL6swEwrQfY+D//3kgbm52d99VoiqM2IpXnNzcCeGTQQmBPL +bNvXyVJ6ToXeoCLwtsX/HkvwBNWEr589BIWNmTHQUDTDweUwginZzcV6itdd23B7 +H0bOQzJTvI8+1p7vqixYDNFREG6i+x8H9K9zuP/OdHcHTPNgrgl2x0tHigWjCvac +T/repym1Gf/G44a9/LAGX89zBAAoko3ejtGbBEq/3Z2Pld+Qu6wE38AdP/EbegtM +HR6JY2zdfB8CTnq2QgohJLSkvDLILldDQpTjAQIDAQABAoIBAEVvqzsbZt8lXe31 +XQzZVPIGsjpWvuULvSgxQLLyOOTVpvB3rAKyzs9U+FZA/roLa+hXQOIyDLtuWk5f +PoJIjX1HvMJgpHxi+FGJ/Q1JXPYkXpN2l4li2rgCDYEqZVJJO1nVbu0N2fRf8KVu +vQwEQn1Rgit7Kod+vvEafR13GT1vimwO94Nt1fye4HaQJkWW7UUS+7p6f22wQj+e +5zb73W3Mk72jh6LG1x48vL4iY4kgIKrtOVG1F9dtwVBkNr80I6LeH8kgbOvTnrNw +omyKGg/fMFssM08CN5WcOsIEHOiyyyL408O7vUuNk06Vcp8qmKtpbcSqmnXdGsUe +IdoNX8ECgYEA4CORF1AMmFln7oEG8GSZBTZYDTIBRzAVEd3fi33qIkcT955SuJTE ++iyVVDtjvZXPMfxO1kLln8ih36m9S66pjzi3Hvi7V2Xo5uTsnjWtjSx5vsyu38B2 +icQvz2R4h6N9Xk+pXrxrILrIZstg0auzRiSOR5OKSCup27p70dgUsMkCgYEA1lbO +YwjdnAx45Ln4A2MNDc7sk24f4hhZJS3qhirKS/gqbUCWKc74FcZBi5zLYvCdwYXu +WHKYl3hEk7KbEJM0KiQz/vFCOJADMULiFYw4pQrpClTcljr5lG8m/OHVQF0/6mp5 +rEwxi5FwcIX6guP8+ZRd3Z63h7+4I5cTq7CAtHkCgYBBZd9FwBOMuDl8+6S8q32C +adLTNs3sqXjcV7KMDtcr3TVUQJu+Q5odrLh9dT6q6HUmDooqNiatsmqYyfvzgyjy +Iwg7PzPaUl/cTttDZkIXOOzk4O/9VTjBBb81cglA+lDwHao7fBp92EH0zE6ZntGW +G8Bv3fqxCBxtgkHyfmu7EQKBgDQmxuzd2V4AwuGURj48uY5kjLeKkgNnPTmIpImk +m7hEV75heqgNjdtuc8BOlEYsmZXeypGGwI4KW1U8nfI4fvbJ/ETJ2vz8PWqdBXmM +trOhpfY3k7yR+Owe53OcV1Dj34tgAN7lYyC8cIlQcBWs936alQQ5fBpxkZJHTif2 +ODqxAoGBAIVKy9l9KRnGrR0WAJcN1+ZyHBz3hCG7tbMD3emHDhFObEuZv2+70viz +VbkRgSxygb2iQPFJWIXVRiByKX5z+QEMU3En2BkQPP5ZDZ0uIiH/H57hPxVwzE0j +k3w/KRcsawsLf/45yfSF7HTgkwRDHhynTN1V25YEZ9W6N8NBTSjk +-----END RSA PRIVATE KEY----- diff --git a/citrixadc_framework/acctests/testdata/key3.pem b/citrixadc_framework/acctests/testdata/key3.pem new file mode 100644 index 000000000..2048214df --- /dev/null +++ b/citrixadc_framework/acctests/testdata/key3.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArXKLfr6lJfYlB9voCVyIxLoSkk/ea1J8Oy0xA97J5CW/mH4A +NN2zTChnTx0gaBzVMtNqsgms9Ze4aRR4PFJHyyN7j8r3mI2GjTXoxJPXUDC48dBa +FQa5cnFonMUwsDXA7LO9QVpILEy9wVuG+HkSqHDQTKpEyMVryJOcAx1FxM11iBux +4P3rs0SGFYuxfjAsshq7HOf6fAV13E1drnpJPi/7VmivqyjX5/5qdKHu+rxNrxHU +jLbETe/3MyWgC5e9LimwS7rdb4UlTOKm+RUoTwK86/VRy+b9GmZlpYnsxarTqHMF +OqXqu/RW57aNXjwbmlUoS7H51OzvgUrcZpPx/QIDAQABAoIBAHGgls5DqwGBjaTm +Zc6sNlVa6Qu9EyMP+J7z7iZw76ZtLGWENjJsAygjV/q2RCKwsOMJKd7VA+AoswD0 +DgjGho4IAsyi1S21ma4s4P7XM7kvEhooursHmrnknfoGHO1zaZ4n6hZERP/wjmd7 +xKJG/vgmX++5pDI7U28ldJF9vdU9CGh6wmprzZgGa1NoZlLWeW0uIXBAjs/WKlko +woqyygsGFo3SuVVYZRKd9+OOoLRexWiCk4cMLt730Z1SnhIB39/EaDWZqDDkTqNx +qnIEkWVZGxpHAJ6/T3Bery6ncOqr72msim4bSV3iU3Bqc07Ql8hWDerAA8zWr63I +9/I3QCkCgYEA0u1U9SJUa4l0KhzKKfQbJWvzsBCd6weS9rPpCSR2xy9v2Gf9q3+M +wxSQLlcV8vjh8GUfQ57VadmlgPC0kp4kDB+2s1wSg/iRjhAfe7JTd5k4/An4dIvt +RUuItMwsiEL5V2gA4KULXL7rvBji+lH+NDEBdP8wjeP+374OBzfaZF8CgYEA0oLo +KJP68DPOpVlXKFUeAa7iCi/l8Pi0aBkrLJSqvmaRSNeiFJEz6VorC3aN1M2KN0Z9 +QcV9t9WSvNOYQSTNrjVcFGeywvDD6pKcVD3as3ouu+nhtH2kOXxIXXix3Kg0hRQq +5pGul533lFcJpTDIDgUbnUGSCs38Z5Pd1mpqZyMCgYEAiuyQYWh8C9wbq8UMjnde +Dda9SUCYkn2JmX3DxibDKMwgsXtEw9kdwDth/3OSXFb6kVg5MFOEItScQoHHnS4V +dfrJXfcNpuhoDhamddVtTj+YHcD/aNvkqhhm8RXtWs4p5hz1PwDVq/9/yoLltJOe +h4ejewi9VSdO7tUB7lUmPacCgYEAq9gXIoDieVEhYNNUleUd8KvNdBlzsMmlo9Df +8K2P0Iw0D22PrxB1ewmTV/E4iL4dFVBikd1g6j/bYG+uu4cKrCp8919Li1014Xg7 +S32O2bJlEhszl7504ER3Ym5Ta1iYPwaemsfT6YsXfy2p/wKaXO+Igk/zowRSBk+r +6QvHvlcCgYAaH2dzg0OMWO4CQlMlfPL8CQulcjM+OmQ6FIurPhT+4oHpbZ72WsDg +T/OQoh/+URME351w2DH0lCqKLjEhC4HDr/gMFtvlEzvjwREmByZisHc8fVT4O7YE +5O4Ju1080QdeMQXECDFdPV0AbPka8JvRqjU1xWfy0/OK7bnLkyBy6Q== +-----END RSA PRIVATE KEY----- diff --git a/citrixadc_framework/acctests/testdata/other_error.html b/citrixadc_framework/acctests/testdata/other_error.html new file mode 100644 index 000000000..3b08922fb --- /dev/null +++ b/citrixadc_framework/acctests/testdata/other_error.html @@ -0,0 +1 @@ +Hello other error diff --git a/citrixadc_framework/acctests/testdata/rootcert1.cert b/citrixadc_framework/acctests/testdata/rootcert1.cert new file mode 100644 index 000000000..ac758f863 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/rootcert1.cert @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC7TCCAdUCFHiVFXos6wufZid3TDdeTw0CnB2rMA0GCSqGSIb3DQEBBQUAMDIx +CzAJBgNVBAYTAmluMQ8wDQYDVQQKDAZjaXRyaXgxEjAQBgNVBAMMCXJvb3RjZXJ0 +MTAgFw0yNTExMjIyMTEwNTlaGA8yMDU1MTExNTIxMTA1OVowMjELMAkGA1UEBhMC +aW4xDzANBgNVBAoMBmNpdHJpeDESMBAGA1UEAwwJcm9vdGNlcnQxMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApBHacLFruY2CUyk7sQ/JilIX5Z4lZjJS +V1x54+cEXghUCMpGMoX2AHNX8SP5Cd2loYY8vuDe57ODYJrbSlJHGvOVvtVnjxeQ +EdNN0b0Ot0bwpnqTZBgEiSHJXcEXyH2rzY0LnR3ke/eXlGMIGhyavWrPXDDs5csL +0jnHFxiCjkpIWseuB8/KBZxoJFjnmbV9+p4rAZsZrnALpd6OLQ/kUTGy6BGP79oq +gy9Vae0II0C6PRTH7VlcMrKswJKyE0tUIgqomHDLYvnHqGl+cIubDib0mXh1Vtly ++I/oJKs8ytC2x8ndc/w9+Nd4BtNctYRSGBFWCDenD/O8r1JzcwRGXwIDAQABMA0G +CSqGSIb3DQEBBQUAA4IBAQASTUo9b1oOQr8LPMRaBdM5YfzSg00Wg5TaHPeSZMMv +9mF/E15xiKqM8tuUEJ9PBrgLO0nW+mIAInpVpEvHD+NkmxyhH4WfBGRbVFaSzdgJ +ewPf9nVQL/zRb4CtOUtqg/ojG0BkUsReaW456mH4zmf3FOfDOKkKw97j1uUymMW4 +pUwa5Iib0OIZUhpqr0b+UTiFEOOLnCL5T2N1gaA998Yl8CYgfxCHojmySgihMu6t +KBAqtPCOC0Z7Q3maIMET3/+7tjsk/9vZMY1KyT2tDQ8rMPMF/HmztMKnQj5zKgbj +rBTeR1Nb6mKQxfG7Dp63amtf+BncSC19aD7Q3kVOFkcp +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/sample.wsdl b/citrixadc_framework/acctests/testdata/sample.wsdl new file mode 100644 index 000000000..49875b13b --- /dev/null +++ b/citrixadc_framework/acctests/testdata/sample.wsdl @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WSDL File for HelloService + + + + + diff --git a/citrixadc_framework/acctests/testdata/scripts/cachain/generate_all.sh b/citrixadc_framework/acctests/testdata/scripts/cachain/generate_all.sh new file mode 100644 index 000000000..0e2754f3f --- /dev/null +++ b/citrixadc_framework/acctests/testdata/scripts/cachain/generate_all.sh @@ -0,0 +1,91 @@ +#!/bin/bash -x + +# Create a full CA chain with self signed CA root +# Intermediate CA +# and 3 client certificates +set -e + +for C in `echo root-ca intermediate out`; do + + if [[ -d $C ]]; then + echo "Deleting directory $C" + rm -rf $C + fi +done + + +for C in `echo root-ca intermediate`; do + + mkdir $C + cd $C + mkdir certs crl newcerts private + cd .. + + echo 1000 > $C/serial + touch $C/index.txt $C/index.txt.attr + + echo ' +[ ca ] +default_ca = CA_default +[ CA_default ] +dir = '$C' # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +new_certs_dir = $dir/newcerts # default place for new certs. +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/ca.key.pem # The private key +RANDFILE = $dir/.rnd # private random number file +nameopt = default_ca +certopt = default_ca +policy = policy_match +default_days = 365 +default_md = sha256 + +[ policy_match ] +countryName = optional +stateOrProvinceName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[req] +req_extensions = v3_req +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[v3_req] +basicConstraints = CA:TRUE +' > $C/openssl.conf +done + +openssl genrsa -out root-ca/private/ca.key 2048 +openssl req -config root-ca/openssl.conf -new -x509 -days 3650 -key root-ca/private/ca.key -sha256 -extensions v3_req -out root-ca/certs/ca.crt -subj '/CN=Root-ca' + +# Copy root ca certificate to top level testdata directory +cp root-ca/certs/ca.crt ../../ + +openssl genrsa -out intermediate/private/intermediate.key 2048 +openssl req -config intermediate/openssl.conf -sha256 -new -key intermediate/private/intermediate.key -out intermediate/certs/intermediate.csr -subj '/CN=Interm.' +openssl ca -batch -config root-ca/openssl.conf -keyfile root-ca/private/ca.key -cert root-ca/certs/ca.crt -extensions v3_req -notext -md sha256 -in intermediate/certs/intermediate.csr -out intermediate/certs/intermediate.crt + +# Copy intermediate certificate to top level testdata directory +cp intermediate/certs/intermediate.crt ../../ + +mkdir out + +for I in `seq 1 3` ; do + openssl genrsa -out out/key${I}.pem 2048 + openssl req -new -out out/$I.request -days 365 -key out/key${I}.pem -nodes -subj "/CN=$I.example.com" + openssl ca -batch -config root-ca/openssl.conf -keyfile intermediate/private/intermediate.key -cert intermediate/certs/intermediate.crt -out out/certificate${I}.crt -infiles out/$I.request + + # Copy to root of testdata + cp out/key${I}.pem ../../ + cp out/certificate${I}.crt ../../ +done + +echo All Good! diff --git a/citrixadc_framework/acctests/testdata/servercert1.cert b/citrixadc_framework/acctests/testdata/servercert1.cert new file mode 100644 index 000000000..eeb2b48b3 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/servercert1.cert @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8zCCAdsCFHcoCAiHk5Mxj6ShrrQZ1492naXiMA0GCSqGSIb3DQEBBQUAMDYx +CzAJBgNVBAYTAmluMQ8wDQYDVQQKDAZjaXRyaXgxFjAUBgNVBAMMDWludGVybWVk +aWF0ZTEwIBcNMjUxMTIyMjExMTAwWhgPMjA1NTExMTUyMTExMDBaMDQxCzAJBgNV +BAYTAmluMQ8wDQYDVQQKDAZjaXRyaXgxFDASBgNVBAMMC3NlcnZlcmNlcnQxMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnHx1rgBn68nvwECTp8y3zpKN +satFnh7gKKyKMx2sE+HbD2zVSlqJaMr52e0JbimqWRjBbJEW5f6N5hZjjKyy397i +53gwWmZYLtvCJAs5OZizWiPja5HkEjwJTRqblsBLTj6rlH1XOjQsWJhq8areFV1B +LEPfaR2yg8ITsqSvRx8GhpL+RuPJ8ta2yRVMlG2V5TQFlCvHXdZHgL2Xe3VgDavc +cngDtSyPYwu1roKQ06qsVJpH+Z362XQg1JKQd8VpNnUO3ZOdMaX3gnHLX1UD8Qh5 +oZGd0t55RVBkhvtOwPXImb9+qRw7kxJxoiw1hN0HUgL4qcmbnGIhE1lCn/Sm7wID +AQABMA0GCSqGSIb3DQEBBQUAA4IBAQA2Z/7FnDUowdgAA8Uv0lKrao+JooBZ4ujF ++42d068qlSvyoveC6gyoHTGmw0XK5ctO69kapscfl5y1dcSk8YDvmCfF/+FLOzkW +KxFwZ4iYsSWKwhVcr3IJTqrjR/l+hNFVJtsypYcMJfANvFyf942GzLqAmgquqiKO +WqTrsB1Lj4ci9t3kc5gL2nxLZyXC8c7iqL3LgMaRl5ICsCBOrE30pX8Xm4vDNezf +eY3NrqKDBPFVTOsHVQ6v20/QNGgKidK9p76M1CDVUubqyi7KcJA/ZiMzHIkMu4Y9 +TEUAnVTBVeSnmL1rqqvPotJtNW8rkuMnpB+/ov2eZPlaczq6i119 +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/servercert1.key b/citrixadc_framework/acctests/testdata/servercert1.key new file mode 100644 index 000000000..43bef3324 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/servercert1.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCcfHWuAGfrye/A +QJOnzLfOko2xq0WeHuAorIozHawT4dsPbNVKWoloyvnZ7QluKapZGMFskRbl/o3m +FmOMrLLf3uLneDBaZlgu28IkCzk5mLNaI+NrkeQSPAlNGpuWwEtOPquUfVc6NCxY +mGrxqt4VXUEsQ99pHbKDwhOypK9HHwaGkv5G48ny1rbJFUyUbZXlNAWUK8dd1keA +vZd7dWANq9xyeAO1LI9jC7WugpDTqqxUmkf5nfrZdCDUkpB3xWk2dQ7dk50xpfeC +cctfVQPxCHmhkZ3S3nlFUGSG+07A9ciZv36pHDuTEnGiLDWE3QdSAvipyZucYiET +WUKf9KbvAgMBAAECggEAQi2CSO1EBZvXR/snVpHFc9dXk/kGDoopKxpYsahNQvVD +goD4rKKgNNCOHELZt5xoPZiyCBtNz0M5OEJkpDPOy81DMTeLX1ej2GE4d6zFbR2u +T6MYxL+kb6+zEUf836J/1l27iyb8shEpZehDZeNoWymea71iaF5WRdFYKjbqJ02G +iRcF4D0FRhxKg0rlqJTFxmy3E8jL8R0zr3hyarI3SgraYPp22rhuDZIhfdKN/P6X +w9k79Vw9N3KYhxVDJrIuPj4L/slrolxrmlpKPbksT4E7mM+a8lriF1IOzmEExWOy +YAAOOw8VViqXg1NjyHPLkP2zeWuSEU1i9DwjTj354QKBgQDUin4Zy8hMpSazILmj +2fP1w5Gp10vb8fNisXUGO6OyzkAvkm7wuL8gBK4nUFTE93t3MaJgV8ZfnlUMgm5M +t0S71EFn/bV3RpAUm1lzEPzUzQJgtcyiG3srv0xyR5A1t3QkLtTLvx1FFz5CZQLH +iKmlYIJDKQaQjCcnVTkza+FEzwKBgQC8e8PETWoSFlAZYuAwFoUAeRnvz7y5xTQ2 +BGqZ4APCISoZmhxCpn7puQDqfZoMNQlCwbSyGGl59Dew2V53tXYy5EZoz1lD0vyF +zawLLeymatT/qayg2JKFeRUoYBvShmMTVubHx1nMLpB/C3p9b1z/l8Zgj6MfCye8 +3MbCLftD4QKBgHxW2Ra2ROOx6kX46yGULEimtNyre1Gc5knijxeqZEYCq0IpZIWn +TwioNEoDkNP6BFziyJ+cOg1OT2sWEvkGbkuEDQ+NOVAiE8A6ccYDNiY4GSACu0hK +02/wZgSlIRSL0oIoc40OrUzyIBYvicS5iqWZJBuuMIz3sSAtl10hy5O5AoGAGdCZ +r3kq3e9QaSmxquRqsvXjJ4U8Q/VOgVd5gjm8SgpgycDhvf8vwrMj/PnW73UUH6CI +LxxI1fss5XvgBGVGGxJI0nITt8Zd77WLqrxPfTuEkL+cdSs0ZjN/Qlhndx4Q08VD +NnmHQv/dqojX7fYitp8C/JavsMDGYIeccVv63gECgYAyUUjeRtiOEyFsT87YNJio +lHJHvbWkpHysg1kmH0nllUbNOFvpaxgQFgrkAOIkm/yr1NlfdO3vvm07q5YSkdG4 +37TxjV3/oG+gCmq3OO+G3j1MoKmVbDIcGDLDQxFQpY8Ysv2W8Zv9RJdsRlgKl9Cf +xyecK6zOH2G9paxAsMVy/w== +-----END PRIVATE KEY----- diff --git a/citrixadc_framework/acctests/testdata/servercert2.cert b/citrixadc_framework/acctests/testdata/servercert2.cert new file mode 100644 index 000000000..7d19599a7 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/servercert2.cert @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8TCCAdkCFE9/930cXTbXGmmYYI2w2hrnH0x2MA0GCSqGSIb3DQEBBQUAMDQx +CzAJBgNVBAYTAmluMQ8wDQYDVQQKDAZjaXRyaXgxFDASBgNVBAMMC3NlcnZlcmNl +cnQyMCAXDTI1MTEyMzE4MTg0OFoYDzIwNTUxMTE2MTgxODQ4WjA0MQswCQYDVQQG +EwJpbjEPMA0GA1UECgwGY2l0cml4MRQwEgYDVQQDDAtzZXJ2ZXJjZXJ0MjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALovNXgxect5cycg1SJvz1JWl9yb +N8O1u4mCCivV2OUX58VD4O50SfH5OqnzHDs73igx/nOyNlhVlDiSdUCyCeyHqY1s +yQ4tkg81tZ5MgKkMvCteDVFxr6CbvnzMv4IAFM+blAlhatMlP8QrOoZPHwFoDRhG +6N796280elDtAiEPBWm2yol2ppizIs8sOY8V3YcV4kfvt9/uWmoQatT8oK6vkOWD +fGsQtHoFhDNM36aw9OHmOBYuD0aJsvpSbUpLWoXQCmm37sSiJIAOLT+TQ9o4QtXq +dXRqgkBVH5uFYRiffT03oPBs7mhcUmFszHdoypcDPjcopeF73R8vtb0rLw8CAwEA +ATANBgkqhkiG9w0BAQUFAAOCAQEAT9CP3hK2SjHRV4Y9qzn+w5y58cI46lyoPm4M +AzMMwUtH26NfRF88hY165Rcx+GZE8m6vIhS/iye/SYXejUFV73T3V6C8T4lTWNYT +gyEwFOw5OcaxmUuG7nJ0iC0MuEFPoUECgve0dWzo2oCBx7ph5bFh8crpw4XY/uFW +bb1kfPWulZc9RTFwL4JmOloBcN/Hogg5BSWxySVHowqtnD/DHLWtwCLlFC0Icwes +Zno8kYhG+Ldmty6WRz/cwnZloanCbu5Pg3Tr1Hkz/n8VoZaY0JVLUlHDxtKW1rRT +/t+QqcRTrxG39TIN3r1l34FQj56feZLLEQxqLxKeSScduxD47g== +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/servercert2.key b/citrixadc_framework/acctests/testdata/servercert2.key new file mode 100644 index 000000000..6c6737be9 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/servercert2.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIuCRrPSbI2HICAggA +MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECOB3ebE4qYugBIIEyOSrC2YvMdGU +/0owhm4N4JweVzEO9ybGANZloTn8l+k3b8A1rt7C56LbVy3U8g1XCQIi4xSLOIqY +09L3HH0Ar64qGTcdwRpykHQV6cUF01BfvLKp7IPFogzpF+YIqn3WE2iNye7DeC5J +lFNvhPbqg27Xbluv7pLyCPurASqCDitcXQm+iR1Neupb8HBk56nrLBE74Yr5O7Hv +mPq3XC1ivMAYzm1B5O0OOiM6yH5cdGuUfLNMnBZs9g+ZqnzdpQmvdr0Lfq1WmPie +FZoIbccyQ8sq+O0AmjlOuOqLUuzxd0+SWVGc59TMYQi+fuPqVnvlwGUiUGPG4h7p +mdpRgquMiNr4ag2VpZX2Sc8W8M2/+y3HYsmCoPdEtVEuom/YDidnD72w1SJfdls1 +W0jIppYaOF++9Ik7kWLOiPkiI4ag2cn/r4nIz+k1Cjv8obK1tjQmXMzRvH5x2FIv +UY6DllzdR9e2OmryllLzkqWKn1jzSVRAMc9cbrrFxMpX7Xxli6jipZSOkWjshd1j +4gb/tckdLHgSVoGSCvWeaNB7fyVtMGkgEUKy01WTn5iPsHeMouP0V8eVdRXHs6kN +Z9h/N0W+vBBJ2sAtVYLU2rbiBHvE7ulLps/xvGYTBAnVF7QXt3jW1tB3j1rqlK/W +D9MgXSXV/pfbcVgIQ5+Y/tk1HstiDSYQzdXcRlLZrN9rr2xGrmJ9ONcfIEXYY1bC +Od7tLYjDkTOhHquW+E15nVCd4u+C/1wzhK5GEnO7FzFXm74cx8qjIfFseArVJkEi +ISWhSdgOsKYpedx55cjG44JH/mmsRzmLgpi39vqKzQSYgGpGF+fK5aiGrd3GKSe9 +K/qzxQ0RjpgdiCk8OoYAGk1AFwEkRgqNOWQEOCisVh8WSBtCe2znG+W82YZvOzo5 +eOZvvGkUAp0+LSnueR777rzS9QPxY/Wfy+wTBrvEjorD4vTDGX1LSadTjIM3rGZs ++fVILuLwAx5+FtKm/68h+9e7l659576DfEb67LFDEVuMPrTwSfHP6Ava5njoLUaZ +TBgpx62nTLLcwYVClYzxtvizT01hdDKkR/sPguPxnjXvct3IZZXq4spHR4oUJM56 +mg1Aw4qINJp8Ty4Mt3KAXc/QPx8aRRK35J/4n73uiIYwEuHdW9FGlM9+zFYaIlG8 +jqVDdhFsO8Q++ues9Vp5kWQHe79sn2/lEkHyw+kHSJV+UhhTQHRM+98R6sMg2A8S +8T5FSzwBUgPeslRpA08bemluFttsECP021vwBp6P2np1d7Q2iRfhf+gtaaGJjzjM +WAnc0/1ew54ElEwslCuyEvGtbaKDCZMZuukORnlFeUEWEYcEhoUvLecIOQ7Pqvub +ClK24KZ/d4LPGoKp7tvVVAdtET1vFNVGuk/QH+ATxhyo3IK0fp9Y3WiSX4BdvEN/ +qMOGmJ6ZE1PXMyhAXTcGoUbcrIT/98mreO1PV/x6DfzaNBdaUO8oGAS2xfTcaJ7+ +bgZWCNORt1BUjntGwOhX4rzRg6/wx/vHeYZyCGq9kCQpUFcnCV1dJbc4joiXfgdL +Vy+Qo4phNPTKAcv9/izLRcwJdYrSmrmZwTmKFdvurCN5la8Oua3abCsHW8BBdcpt +Tdk69FAVX8LFDnmUIdluPA== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/citrixadc_framework/acctests/testdata/servercert3.cert b/citrixadc_framework/acctests/testdata/servercert3.cert new file mode 100644 index 000000000..c23b90b0f --- /dev/null +++ b/citrixadc_framework/acctests/testdata/servercert3.cert @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8TCCAdkCFGEcxOmTxr9+1WkbiVdloefWdAEgMA0GCSqGSIb3DQEBBQUAMDQx +CzAJBgNVBAYTAmluMQ8wDQYDVQQKDAZjaXRyaXgxFDASBgNVBAMMC3NlcnZlcmNl +cnQzMCAXDTI1MTEyMzE4MzkwOVoYDzIwNTUxMTE2MTgzOTA5WjA0MQswCQYDVQQG +EwJpbjEPMA0GA1UECgwGY2l0cml4MRQwEgYDVQQDDAtzZXJ2ZXJjZXJ0MzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/JEhuTkHevuRirWKYzVH4L+PbJ +n2h9DLkeVZqQKpakTSHjlO94eZSXDsp3939XkxiVX2jJ/dhRhVyHt5r48Hxsf4FP +mu6YAKvRc7BMOinqm8jE8i/PcVCYdnU9wr8txZ3iBvjn/NYDglgKqOduarntGmsK +WgqBuby2Mq3Clcicd4DgNBONLEFKmL5cgkqoo6p9yc3AgVs0UEPtUX/Li+/ikola +p6WBSGWldJ2KxmQrw8+E4H0aBb6QEc97I1229yeg22ZTwudH5pmL5KRwncoiFzLr +61lgpIt3LUvyUhFDducomJbPCvWXfccYYtOaxT2zLihaW3S8C3dhJ0HBgLkCAwEA +ATANBgkqhkiG9w0BAQUFAAOCAQEAI2PNl+KdL0sZiKTpkCDlHmWMqFxo71B5Sjk7 +8IR88E0+I5Pgl+nRbD1sVApwqwLsUlk//nwQx7sIzrhZQ/YgSojJ+DSHipyfPery +GnjOZzRtXDPcTe2Ki4UN8Bb81K5NuEIk+bh8bNMoRd5gcIxLYt+y94cKqYfTDHd2 +jhhQhfBBSGItrWt4gcNkVUgKd31bQNXpy5iJF8L7Bc4NqzM4BMjUOh/LDZKqvUQ4 +MAkaZDqsTgtXuc5dlFSQtitM51f+Nl44NEUvL5BoImSxn1MBPDdnSWX7Pn2NweIY +JFH6LlLsuYQWjjI9lK5vWIA/xxPiknYAtCuB0Z/cdOQE8ppz0A== +-----END CERTIFICATE----- diff --git a/citrixadc_framework/acctests/testdata/servercert3.key b/citrixadc_framework/acctests/testdata/servercert3.key new file mode 100644 index 000000000..e5585a76c --- /dev/null +++ b/citrixadc_framework/acctests/testdata/servercert3.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIn+Tht6MNwdUCAggA +MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECBbfGZHYWKRiBIIEyBwKHPLu/TOE +p0B3X0qHe2zWy5vlscJFRaNocXbCYdOB+WOLP5o+NGvjcKKSlD91uQ/YEHedEUUU +NyyV8MY1x0etzYhlkYwXKt8voXCBdnlbw9t+BQWB5nDk4h71fjI/OzEXa5xAZb9U +4ZCz8Pnwxy+9/5riRx2yTJtOab+nY3FxcWRPbG3ney49cnJGb//ZWSdhEpOWvX35 +wLb2DRHSB1DWyPVLUN3AtDKEIkHR5P0Gmp1GPwwTpRmiprosYyWyZc3P3roL1bVQ +isYbVwXO7BQjqF793XN4ce2Q9saKrNdtaufWPQhs2b6H+084jnRcowfdtq9j7nNE +ilyyJQd1ZX700vRTZ3DfSy2oIIimjXm0syXsFRf4DeMSbvb2/TXdWaOEPrAX8MnP +RUbp44dYi61NIP5nL/wu6/wpmG6W+AHRly/AO3DSDSs4vi9AlTZvyOL4ItEFFU1f +NgFKiQdZRGRkjC6AaYp5JSMubG2s4plewibHS+//33a13UW8n0Z5MQe8F09Jy3Nw +vgCez8uSTC3s98iQVgbDuj0hd15NXhO9AHM8ppLs8nUYyon2yHBhuPAonjK1s6rf +iMLvp2KIOVrdeDDICHNO3ToDT/TweCJTbS4tsOoemQUBcVn6vpnk3rj/xdxkS1AL +APxlowRAv2eAENPCcGxoYvwjrIcSf799I7E32lJfZkKa2EBrOvThYb97QhugmiYz +hhsXxLV/tBBuFhUYRi0g/KVKOPWLeT0ACZeMtDNx9T1RpMbvXQFuLDWXbWGzulta +74qFUdz57Ni2Fv0pOOk2fVpyh/UztuvH6Sl9z45OKFSUrMA00S8bsol6T8ZD4nMw +wtfykFMhygenNrvjArCKG/rHS8gAcQYjNK07WN2SRcAubZd9nC48/aCIycLrWKGo +OFHu6auWCBvbBTotucLnnlICuea9T31aRoWU6jNgZfT0fmOgyik12HW3QTvdodzv +oB+bBUwqMQbJR9b4dS6K4yQrmiz+W6hdhQbLljlVluMpbNMRhDR32JfgTrwtXhVx +jWle37C68DvBpp0k3BEwq5xy9AmQZlTTI3rAPXvrGgtpvoR7NbA7Pah76CQjhqwe +v3VePwzDqBDPYK1XPyC1FqOGT4NK5duu+AsHPXmbZFBdKoWSHgdiEyXfsjNiwWqQ +MHq8piT3DCL+rfct38Mcfp3QA/vGcEMaCiYE0c84K3w9AgbsWxQcMsnthx3S+Oj1 +NwoJloJ2II1Xm2zzc101eiRx5L7ubNhYpivFDlEqvCnDPb0Q+sxMh0HfLbThfZKz +c4ufEdkr5RjmmkJG5I2SKWX9epWTsUkQrOubEoyE3t6wLIkxsIMyIxBulTTrhvz7 +N7/PKSvfR9a/HSspJWoy2nMVbQysA8PHIG7aeo2Tj63pc7Rj1uGsHMr45czSK+zP +5bjDJAx/EpsiKORPiGqtD0EyXho9JPX/RyF4dMdub2nIOQGZ3W+kEBV7uO6lMnzT +D2pkXJNKUfiv7clhIz77wmi4U6uVS4cmIxUdpEAFCOaQCiRXKnJgVXPqtvo/ay2m +arUo5S1biCkQ78vsblb9dyuNRPy3CMsnvg30ltMDagQi98kKCkJjZYUFUXu61Nkt +9GIFLKkKDb0P/fM5//DAcg== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/citrixadc_framework/acctests/testdata/workflows.yaml b/citrixadc_framework/acctests/testdata/workflows.yaml new file mode 100644 index 000000000..f3650d535 --- /dev/null +++ b/citrixadc_framework/acctests/testdata/workflows.yaml @@ -0,0 +1,654 @@ +workflow: + + server: + lifecycle: object + endpoint: server + primary_id_attribute: name + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: + - domain + - state + - ipv6address + - td + - querytype + - delay + - graceful + - Internal + - newname + + service: + lifecycle: object + endpoint: service + primary_id_attribute: name + resource_missing_errorcode: 344 + allow_recreate: true + non_updateable_attributes: + - ip + - servername + - servicetype + - port + - cleartextport + - cachetype + - state + - td + - delay + - graceful + - all + - Internal + - newname + + servicegroup: + lifecycle: object + endpoint: servicegroup + primary_id_attribute: servicegroupname + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: + - servicetype + - cachetype + - td + - state + - memberport + - delay + - graceful + - includemembers + - newname + + service_lbmonitor_binding: + lifecycle: binding + endpoint: service_lbmonitor_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: monitor_name + delete_id_attributes: + - monitor_name + + servicegroup_lbmonitor_binding: + lifecycle: binding + endpoint: servicegroup_lbmonitor_binding + bound_resource_missing_errorcode: 351 + primary_id_attribute: servicegroupname + secondary_id_attribute: monitor_name + delete_id_attributes: + - monitor_name + - port + + lbgroup: + lifecycle: object + endpoint: lbgroup + primary_id_attribute: name + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: + - newname + + lbgroup_lbvserver_binding: + lifecycle: binding + endpoint: lbgroup_lbvserver_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: vservername + delete_id_attributes: + - vservername + + lbvserver: + lifecycle: object + endpoint: lbvserver + primary_id_attribute: name + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: + - servicetype + - port + - range + - state + - td + - redirurlflags + - newname + + lbvserver_analyticsprofile_binding: + lifecycle: binding + endpoint: lbvserver_analyticsprofile_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: analyticsprofile + delete_id_attributes: + - analyticsprofile + + lbvserver_appflowpolicy_binding: + lifecycle: binding + endpoint: lbvserver_appflowpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_appfwpolicy_binding: + lifecycle: binding + endpoint: lbvserver_appfwpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_appqoepolicy_binding: + lifecycle: binding + endpoint: lbvserver_appqoepolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_auditnslogpolicy_binding: + lifecycle: binding + endpoint: lbvserver_auditnslogpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_auditsyslogpolicy_binding: + lifecycle: binding + endpoint: lbvserver_auditsyslogpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_authorizationpolicy_binding: + lifecycle: binding + endpoint: lbvserver_authorizationpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_cachepolicy_binding: + lifecycle: binding + endpoint: lbvserver_cachepolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_cmppolicy_binding: + lifecycle: binding + endpoint: lbvserver_cmppolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_csvserver_binding: + lifecycle: binding + endpoint: lbvserver_csvserver_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + delete_id_attributes: [] + + lbvserver_dnspolicy64_binding: + lifecycle: binding + endpoint: lbvserver_dnspolicy64_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_feopolicy_binding: + lifecycle: binding + endpoint: lbvserver_feopolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_filterpolicy_binding: + lifecycle: binding + endpoint: lbvserver_filterpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_pqpolicy_binding: + lifecycle: binding + endpoint: lbvserver_pqpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_responderpolicy_binding: + lifecycle: binding + endpoint: lbvserver_responderpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_rewritepolicy_binding: + lifecycle: binding + endpoint: lbvserver_rewritepolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_scpolicy_binding: + lifecycle: binding + endpoint: lbvserver_scpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_servicegroupmember_binding: + lifecycle: binding + endpoint: lbvserver_servicegroupmember_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + delete_id_attributes: [] + + lbvserver_servicegroup_binding: + lifecycle: binding + endpoint: lbvserver_servicegroup_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: servicegroupname + delete_id_attributes: + - servicegroupname + - servicename + + lbvserver_service_binding: + lifecycle: binding + endpoint: lbvserver_service_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: servicename + delete_id_attributes: + - servicename + - servicegroupname + + lbvserver_spilloverpolicy_binding: + lifecycle: binding + endpoint: lbvserver_spilloverpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - bindpoint + - priority + + lbvserver_transformpolicy_binding: + lifecycle: binding + endpoint: lbvserver_transformpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_contentinspectionpolicy_binding: + lifecycle: binding + endpoint: lbvserver_contentinspectionpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_videooptimizationdetectionpolicy_binding: + lifecycle: binding + endpoint: lbvserver_videooptimizationdetectionpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbvserver_videooptimizationpacingpolicy_binding: + lifecycle: binding + endpoint: lbvserver_videooptimizationpacingpolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + lbmetrictable: + lifecycle: object + endpoint: lbmetrictable + primary_id_attribute: metrictable + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: [] + + lbmetrictable_metric_binding: + lifecycle: binding + endpoint: lbmetrictable_metric_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: metrictable + secondary_id_attribute: metric + delete_id_attributes: + - metric + + lbmonitor: + lifecycle: object + endpoint: lbmonitor + primary_id_attribute: monitorname + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: + - servicename + - servicegroupname + delete_id_attributes: + - type + - respcode + + lbmonitor_metric_binding: + lifecycle: binding + endpoint: lbmonitor_metric_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: monitorname + secondary_id_attribute: metric + delete_id_attributes: + - metric + + lbmonitor_sslcertkey_binding: + lifecycle: binding + endpoint: lbmonitor_sslcertkey_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: monitorname + secondary_id_attribute: certkeyname + delete_id_attributes: + - certkeyname + - ca + + lbprofile: + lifecycle: object + endpoint: lbprofile + primary_id_attribute: lbprofilename + resource_missing_errorcode: 3574 + allow_recreate: true + non_updateable_attributes: [] + + lbroute: + lifecycle: non_updateable_object + endpoint: lbroute + primary_id_attribute: network + resource_missing_errorcode: 258 + delete_id_attributes: + - netmask + - td + + lbroute6: + lifecycle: non_updateable_object + endpoint: lbroute6 + primary_id_attribute: network + resource_missing_errorcode: 258 + delete_id_attributes: + - td + + csvserver_rewritepolicy_binding: + lifecycle: binding + endpoint: csvserver_rewritepolicy_binding + bound_resource_missing_errorcode: 258 + primary_id_attribute: name + secondary_id_attribute: policyname + delete_id_attributes: + - policyname + - priority + - bindpoint + + spilloverpolicy: + lifecycle: object + endpoint: spilloverpolicy + primary_id_attribute: name + resource_missing_errorcode: 2054 + allow_recreate: true + non_updateable_attributes: + - newname + + rewriteaction: + lifecycle: object + endpoint: rewriteaction + primary_id_attribute: name + resource_missing_errorcode: 538 + allow_recreate: true + non_updateable_attributes: + - type + - newname + + rewritepolicy: + lifecycle: object + endpoint: rewritepolicy + primary_id_attribute: name + resource_missing_errorcode: 2054 + allow_recreate: true + non_updateable_attributes: + - newname + + sslvserver_sslcertkey_binding: + lifecycle: binding + endpoint: sslvserver_sslcertkey_binding + bound_resource_missing_errorcode: 461 + primary_id_attribute: vservername + secondary_id_attribute: certkeyname + delete_id_attributes: + - certkeyname + - crlcheck + - ocspcheck + - ca + - snicert + + sslvserver_sslcipher_binding: + lifecycle: binding + endpoint: sslvserver_sslcipher_binding + bound_resource_missing_errorcode: 461 + primary_id_attribute: vservername + secondary_id_attribute: ciphername + delete_id_attributes: + - ciphername + + sslvserver: + lifecycle: object + endpoint: sslvserver + primary_id_attribute: vservername + resource_missing_errorcode: 461 + allow_recreate: true + non_updateable_attributes: [] + + sslprofile: + lifecycle: object + endpoint: sslprofile + primary_id_attribute: name + resource_missing_errorcode: 3248 + allow_recreate: true + non_updateable_attributes: + - sslprofiletype + + sslprofile_sslcipher_binding: + lifecycle: binding + endpoint: sslprofile_sslcipher_binding + bound_resource_missing_errorcode: 3248 + primary_id_attribute: name + secondary_id_attribute: ciphername + delete_id_attributes: + - ciphername + + sslcipher: + lifecycle: object + endpoint: sslcipher + primary_id_attribute: ciphergroupname + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: + - ciphgrpalias + - sslprofile + + sslparameter: + lifecycle: parameter_object + endpoint: sslparameter + + policypatset: + lifecycle: non_updateable_object + endpoint: policypatset + primary_id_attribute: name + resource_missing_errorcode: 2823 + delete_id_attributes: [] + + policypatset_pattern_binding: + lifecycle: binding + endpoint: policypatset_pattern_binding + bound_resource_missing_errorcode: 2823 + primary_id_attribute: name + secondary_id_attribute: String + delete_id_attributes: + - String + + transformprofile: + lifecycle: object + endpoint: transformprofile + primary_id_attribute: name + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: [] + + transformaction: + lifecycle: object + endpoint: transformaction + primary_id_attribute: name + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: + - profilename + + transformpolicy: + lifecycle: object + endpoint: transformpolicy + primary_id_attribute: name + resource_missing_errorcode: 2054 + allow_recreate: true + non_updateable_attributes: + - newname + + dnssoarec: + lifecycle: object + endpoint: dnssoarec + primary_id_attribute: domain + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: + - ecssubnet + - type + - nodeid + + ntpserver: + lifecycle: object_by_args + endpoint: ntpserver + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: [] + delete_id_attributes: + - servername + - serverip + + ntpparam: + lifecycle: parameter_object + endpoint: ntpparam + + snmpmanager: + lifecycle: object_by_args + endpoint: snmpmanager + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: [] + delete_id_attributes: + - ipaddress + - netmask + + snmptrap: + lifecycle: object_by_args + endpoint: snmptrap + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: [] + delete_id_attributes: + - trapdestination + - trapclass + - version + + snmpcommunity: + lifecycle: object + endpoint: snmpcommunity + primary_id_attribute: communityname + resource_missing_errorcode: 258 + allow_recreate: true + non_updateable_attributes: + - communityname + - permissions + + systemuser: + lifecycle: object + endpoint: systemuser + primary_id_attribute: username + resource_missing_errorcode: 2626 + allow_recreate: true + skip_attributes: + - password + non_updateable_attributes: [] diff --git a/citrixadc_framework/lbparameter/datasource_lbparameter.go b/citrixadc_framework/lbparameter/datasource_lbparameter.go new file mode 100644 index 000000000..b84805ef7 --- /dev/null +++ b/citrixadc_framework/lbparameter/datasource_lbparameter.go @@ -0,0 +1,59 @@ +package lbparameter + +import ( + "context" + "fmt" + + "github.com/citrix/adc-nitro-go/service" + + "github.com/hashicorp/terraform-plugin-framework/datasource" +) + +var _ datasource.DataSource = (*LbParameterDataSource)(nil) + +func LBParameterDataSource() datasource.DataSource { + return &LbParameterDataSource{} +} + +type LbParameterDataSource struct { + client *service.NitroClient +} + +func (d *LbParameterDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_lbparameter" +} + +func (d *LbParameterDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + d.client = *req.ProviderData.(**service.NitroClient) +} + +func (d *LbParameterDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = LBParameterDataSourceSchema() +} + +func (d *LbParameterDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data LbParameterResourceModel + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + var getResponseData map[string]interface{} + var err error + + getResponseData, err = d.client.FindResource(service.Lbparameter.Type(), "") + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read lbparameter, got error: %s", err)) + return + } + + lbparameterSetAttrFromGet(ctx, &data, getResponseData) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/citrixadc_framework/lbparameter/datasource_schema.go b/citrixadc_framework/lbparameter/datasource_schema.go new file mode 100644 index 000000000..e8bd77f83 --- /dev/null +++ b/citrixadc_framework/lbparameter/datasource_schema.go @@ -0,0 +1,111 @@ +package lbparameter + +import ( + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func LBParameterDataSourceSchema() schema.Schema { + return schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "allowboundsvcremoval": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "computedadccookieattribute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "consolidatedlconn": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "cookiepassphrase": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "dbsttl": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "dropmqttjumbomessage": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "httponlycookieflag": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "literaladccookieattribute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "maxpipelinenat": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "monitorconnectionclose": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "monitorskipmaxclient": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "preferdirectroute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "proximityfromself": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "retainservicestate": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "startuprrfactor": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "storemqttclientidandusername": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "sessionsthreshold": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "undefaction": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "useencryptedpersistencecookie": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "useportforhashlb": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "usesecuredpersistencecookie": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "vserverspecificmac": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "lbhashalgorithm": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "lbhashfingers": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + }, + } +} diff --git a/citrixadc_framework/lbparameter/resource_lbparameter.go b/citrixadc_framework/lbparameter/resource_lbparameter.go new file mode 100644 index 000000000..0f85d8835 --- /dev/null +++ b/citrixadc_framework/lbparameter/resource_lbparameter.go @@ -0,0 +1,155 @@ +package lbparameter + +import ( + "context" + "fmt" + + "github.com/citrix/adc-nitro-go/service" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &LbParameterResource{} +var _ resource.ResourceWithConfigure = (*LbParameterResource)(nil) +var _ resource.ResourceWithImportState = (*LbParameterResource)(nil) + +func NewLbParameterResource() resource.Resource { + return &LbParameterResource{} +} + +// LbParameterResource defines the resource implementation. +type LbParameterResource struct { + client *service.NitroClient +} + +func (r *LbParameterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *LbParameterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_lbparameter" +} + +func (r *LbParameterResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + // Set the client for the resource. + r.client = *req.ProviderData.(**service.NitroClient) +} + +func (r *LbParameterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data LbParameterResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Creating lbparameter resource") + + lbparameter := lbparameterGetThePayloadFromtheConfig(ctx, &data) + + // Make API call + err := r.client.UpdateUnnamedResource(service.Lbparameter.Type(), &lbparameter) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create lbparameter, got error: %s", err)) + return + } + + // Generate unique ID for this configuration resource + data.Id = types.StringValue("lbparameter-config") + + tflog.Trace(ctx, "Created lbparameter resource") + + // Read the updated state back + r.readLbParameterFromApi(ctx, &data, &resp.Diagnostics) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *LbParameterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data LbParameterResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Reading lbparameter resource") + + r.readLbParameterFromApi(ctx, &data, &resp.Diagnostics) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *LbParameterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data LbParameterResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Updating lbparameter resource") + + // Create API request body from the model + lbparameter := lbparameterGetThePayloadFromtheConfig(ctx, &data) + + // Make API call + err := r.client.UpdateUnnamedResource(service.Lbparameter.Type(), &lbparameter) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update lbparameter, got error: %s", err)) + return + } + + tflog.Trace(ctx, "Updated lbparameter resource") + + // Read the updated state back + r.readLbParameterFromApi(ctx, &data, &resp.Diagnostics) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *LbParameterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data LbParameterResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Deleting lbparameter resource") + + // For lbparameter, we don't actually delete the resource as it's a global configuration + // We just remove it from state + tflog.Trace(ctx, "Deleted lbparameter resource from state") +} + +// Helper function to read lbparameter data from API +func (r *LbParameterResource) readLbParameterFromApi(ctx context.Context, data *LbParameterResourceModel, diags *diag.Diagnostics) { + getResponseData, err := r.client.FindResource(service.Lbparameter.Type(), "") + if err != nil { + diags.AddError("Client Error", fmt.Sprintf("Unable to read lbparameter, got error: %s", err)) + return + } + + lbparameterSetAttrFromGet(ctx, data, getResponseData) + +} diff --git a/citrixadc_framework/lbparameter/resource_schema.go b/citrixadc_framework/lbparameter/resource_schema.go new file mode 100644 index 000000000..e239c2689 --- /dev/null +++ b/citrixadc_framework/lbparameter/resource_schema.go @@ -0,0 +1,370 @@ +package lbparameter + +import ( + "context" + + "github.com/citrix/adc-nitro-go/resource/config/lb" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/citrix/terraform-provider-citrixadc/citrixadc_framework/utils" +) + +// LbParameterResourceModel describes the resource data model. +type LbParameterResourceModel struct { + Id types.String `tfsdk:"id"` + AllowBoundSvcRemoval types.String `tfsdk:"allowboundsvcremoval"` + ComputedAdcCookieAttribute types.String `tfsdk:"computedadccookieattribute"` + ConsolidatedLconn types.String `tfsdk:"consolidatedlconn"` + CookiePassphrase types.String `tfsdk:"cookiepassphrase"` + DbsTtl types.Int64 `tfsdk:"dbsttl"` + DropMqttJumboMessage types.String `tfsdk:"dropmqttjumbomessage"` + HttpOnlyCookieFlag types.String `tfsdk:"httponlycookieflag"` + LiteralAdcCookieAttribute types.String `tfsdk:"literaladccookieattribute"` + MaxPipelineNat types.Int64 `tfsdk:"maxpipelinenat"` + MonitorConnectionClose types.String `tfsdk:"monitorconnectionclose"` + MonitorSkipMaxClient types.String `tfsdk:"monitorskipmaxclient"` + PreferDirectRoute types.String `tfsdk:"preferdirectroute"` + ProximityFromSelf types.String `tfsdk:"proximityfromself"` + RetainServiceState types.String `tfsdk:"retainservicestate"` + StartupRrFactor types.Int64 `tfsdk:"startuprrfactor"` + StoreMqttClientIdAndUsername types.String `tfsdk:"storemqttclientidandusername"` + SessionsThreshold types.Int64 `tfsdk:"sessionsthreshold"` + UndefAction types.String `tfsdk:"undefaction"` + UseEncryptedPersistenceCookie types.String `tfsdk:"useencryptedpersistencecookie"` + UsePortForHashLb types.String `tfsdk:"useportforhashlb"` + UseSecuredPersistenceCookie types.String `tfsdk:"usesecuredpersistencecookie"` + VserverSpecificMac types.String `tfsdk:"vserverspecificmac"` + LbHashAlgorithm types.String `tfsdk:"lbhashalgorithm"` + LbHashFingers types.Int64 `tfsdk:"lbhashfingers"` +} + +func (r *LbParameterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Version: 1, + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "allowboundsvcremoval": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "computedadccookieattribute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "consolidatedlconn": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "cookiepassphrase": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "dbsttl": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "dropmqttjumbomessage": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "httponlycookieflag": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "literaladccookieattribute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "maxpipelinenat": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "monitorconnectionclose": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "monitorskipmaxclient": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "preferdirectroute": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "proximityfromself": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "retainservicestate": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "startuprrfactor": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "storemqttclientidandusername": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "sessionsthreshold": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + "undefaction": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "useencryptedpersistencecookie": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "useportforhashlb": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "usesecuredpersistencecookie": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "vserverspecificmac": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "lbhashalgorithm": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "lbhashfingers": schema.Int64Attribute{ + Optional: true, + Computed: true, + }, + }, + } +} + +func lbparameterGetThePayloadFromtheConfig(ctx context.Context, data *LbParameterResourceModel) lb.Lbparameter { + tflog.Debug(ctx, "In lbparameterGetThePayloadFromtheConfig Function") + + // Create API request body from the model + lbparameter := lb.Lbparameter{} + + if !data.AllowBoundSvcRemoval.IsNull() { + lbparameter.Allowboundsvcremoval = data.AllowBoundSvcRemoval.ValueString() + } + if !data.ComputedAdcCookieAttribute.IsNull() { + lbparameter.Computedadccookieattribute = data.ComputedAdcCookieAttribute.ValueString() + } + if !data.ConsolidatedLconn.IsNull() { + lbparameter.Consolidatedlconn = data.ConsolidatedLconn.ValueString() + } + if !data.CookiePassphrase.IsNull() { + lbparameter.Cookiepassphrase = data.CookiePassphrase.ValueString() + } + if !data.DbsTtl.IsNull() { + lbparameter.Dbsttl = utils.IntPtr(int(data.DbsTtl.ValueInt64())) + } + if !data.DropMqttJumboMessage.IsNull() { + lbparameter.Dropmqttjumbomessage = data.DropMqttJumboMessage.ValueString() + } + if !data.HttpOnlyCookieFlag.IsNull() { + lbparameter.Httponlycookieflag = data.HttpOnlyCookieFlag.ValueString() + } + if !data.LiteralAdcCookieAttribute.IsNull() { + lbparameter.Literaladccookieattribute = data.LiteralAdcCookieAttribute.ValueString() + } + if !data.MaxPipelineNat.IsNull() { + lbparameter.Maxpipelinenat = utils.IntPtr(int(data.MaxPipelineNat.ValueInt64())) + } + if !data.MonitorConnectionClose.IsNull() { + lbparameter.Monitorconnectionclose = data.MonitorConnectionClose.ValueString() + } + if !data.MonitorSkipMaxClient.IsNull() { + lbparameter.Monitorskipmaxclient = data.MonitorSkipMaxClient.ValueString() + } + if !data.PreferDirectRoute.IsNull() { + lbparameter.Preferdirectroute = data.PreferDirectRoute.ValueString() + } + if !data.ProximityFromSelf.IsNull() { + lbparameter.Proximityfromself = data.ProximityFromSelf.ValueString() + } + if !data.RetainServiceState.IsNull() { + lbparameter.Retainservicestate = data.RetainServiceState.ValueString() + } + if !data.StartupRrFactor.IsNull() { + lbparameter.Startuprrfactor = utils.IntPtr(int(data.StartupRrFactor.ValueInt64())) + } + if !data.StoreMqttClientIdAndUsername.IsNull() { + lbparameter.Storemqttclientidandusername = data.StoreMqttClientIdAndUsername.ValueString() + } + if !data.UndefAction.IsNull() { + lbparameter.Undefaction = data.UndefAction.ValueString() + } + if !data.UseEncryptedPersistenceCookie.IsNull() { + lbparameter.Useencryptedpersistencecookie = data.UseEncryptedPersistenceCookie.ValueString() + } + if !data.UsePortForHashLb.IsNull() { + lbparameter.Useportforhashlb = data.UsePortForHashLb.ValueString() + } + if !data.UseSecuredPersistenceCookie.IsNull() { + lbparameter.Usesecuredpersistencecookie = data.UseSecuredPersistenceCookie.ValueString() + } + if !data.VserverSpecificMac.IsNull() { + lbparameter.Vserverspecificmac = data.VserverSpecificMac.ValueString() + } + if !data.LbHashAlgorithm.IsNull() { + lbparameter.Lbhashalgorithm = data.LbHashAlgorithm.ValueString() + } + if !data.LbHashFingers.IsNull() { + lbparameter.Lbhashfingers = utils.IntPtr(int(data.LbHashFingers.ValueInt64())) + } + + return lbparameter +} + +func lbparameterSetAttrFromGet(ctx context.Context, data *LbParameterResourceModel, getResponseData map[string]interface{}) *LbParameterResourceModel { + tflog.Debug(ctx, "In lbparameterSetAttrFromGet Function") + + // Set ID for the resource + data.Id = types.StringValue("lbparameter-config") + + // Convert API response to model + if val, ok := getResponseData["allowboundsvcremoval"]; ok && val != nil { + data.AllowBoundSvcRemoval = types.StringValue(val.(string)) + } else { + data.AllowBoundSvcRemoval = types.StringNull() + } + if val, ok := getResponseData["computedadccookieattribute"]; ok && val != nil { + data.ComputedAdcCookieAttribute = types.StringValue(val.(string)) + } else { + data.ComputedAdcCookieAttribute = types.StringNull() + } + if val, ok := getResponseData["consolidatedlconn"]; ok && val != nil { + data.ConsolidatedLconn = types.StringValue(val.(string)) + } else { + data.ConsolidatedLconn = types.StringNull() + } + if val, ok := getResponseData["cookiepassphrase"]; ok && val != nil { + data.CookiePassphrase = types.StringValue(val.(string)) + } else { + data.CookiePassphrase = types.StringNull() + } + if val, ok := getResponseData["dbsttl"]; ok && val != nil { + if intVal, err := utils.ConvertToInt64(val); err == nil { + data.DbsTtl = types.Int64Value(intVal) + } + } else { + data.DbsTtl = types.Int64Null() + } + if val, ok := getResponseData["dropmqttjumbomessage"]; ok && val != nil { + data.DropMqttJumboMessage = types.StringValue(val.(string)) + } else { + data.DropMqttJumboMessage = types.StringNull() + } + if val, ok := getResponseData["httponlycookieflag"]; ok && val != nil { + data.HttpOnlyCookieFlag = types.StringValue(val.(string)) + } else { + data.HttpOnlyCookieFlag = types.StringNull() + } + if val, ok := getResponseData["literaladccookieattribute"]; ok && val != nil { + data.LiteralAdcCookieAttribute = types.StringValue(val.(string)) + } else { + data.LiteralAdcCookieAttribute = types.StringNull() + } + if val, ok := getResponseData["maxpipelinenat"]; ok && val != nil { + if intVal, err := utils.ConvertToInt64(val); err == nil { + data.MaxPipelineNat = types.Int64Value(intVal) + } + } else { + data.MaxPipelineNat = types.Int64Null() + } + if val, ok := getResponseData["monitorconnectionclose"]; ok && val != nil { + data.MonitorConnectionClose = types.StringValue(val.(string)) + } else { + data.MonitorConnectionClose = types.StringNull() + } + if val, ok := getResponseData["monitorskipmaxclient"]; ok && val != nil { + data.MonitorSkipMaxClient = types.StringValue(val.(string)) + } else { + data.MonitorSkipMaxClient = types.StringNull() + } + if val, ok := getResponseData["preferdirectroute"]; ok && val != nil { + data.PreferDirectRoute = types.StringValue(val.(string)) + } else { + data.PreferDirectRoute = types.StringNull() + } + if val, ok := getResponseData["proximityfromself"]; ok && val != nil { + data.ProximityFromSelf = types.StringValue(val.(string)) + } else { + data.ProximityFromSelf = types.StringNull() + } + if val, ok := getResponseData["retainservicestate"]; ok && val != nil { + data.RetainServiceState = types.StringValue(val.(string)) + } else { + data.RetainServiceState = types.StringNull() + } + if val, ok := getResponseData["startuprrfactor"]; ok && val != nil { + if intVal, err := utils.ConvertToInt64(val); err == nil { + data.StartupRrFactor = types.Int64Value(intVal) + } + } else { + data.StartupRrFactor = types.Int64Null() + } + if val, ok := getResponseData["storemqttclientidandusername"]; ok && val != nil { + data.StoreMqttClientIdAndUsername = types.StringValue(val.(string)) + } else { + data.StoreMqttClientIdAndUsername = types.StringNull() + } + if val, ok := getResponseData["sessionsthreshold"]; ok && val != nil { + if intVal, err := utils.ConvertToInt64(val); err == nil { + data.SessionsThreshold = types.Int64Value(intVal) + } + } else { + data.SessionsThreshold = types.Int64Null() + } + if val, ok := getResponseData["undefaction"]; ok && val != nil { + data.UndefAction = types.StringValue(val.(string)) + } else { + data.UndefAction = types.StringNull() + } + if val, ok := getResponseData["useencryptedpersistencecookie"]; ok && val != nil { + data.UseEncryptedPersistenceCookie = types.StringValue(val.(string)) + } else { + data.UseEncryptedPersistenceCookie = types.StringNull() + } + if val, ok := getResponseData["useportforhashlb"]; ok && val != nil { + data.UsePortForHashLb = types.StringValue(val.(string)) + } else { + data.UsePortForHashLb = types.StringNull() + } + if val, ok := getResponseData["usesecuredpersistencecookie"]; ok && val != nil { + data.UseSecuredPersistenceCookie = types.StringValue(val.(string)) + } else { + data.UseSecuredPersistenceCookie = types.StringNull() + } + if val, ok := getResponseData["vserverspecificmac"]; ok && val != nil { + data.VserverSpecificMac = types.StringValue(val.(string)) + } else { + data.VserverSpecificMac = types.StringNull() + } + if val, ok := getResponseData["lbhashalgorithm"]; ok && val != nil { + data.LbHashAlgorithm = types.StringValue(val.(string)) + } else { + data.LbHashAlgorithm = types.StringNull() + } + if val, ok := getResponseData["lbhashfingers"]; ok && val != nil { + if intVal, err := utils.ConvertToInt64(val); err == nil { + data.LbHashFingers = types.Int64Value(intVal) + } + } else { + data.LbHashFingers = types.Int64Null() + } + + return data +} diff --git a/citrixadc_framework/provider.go b/citrixadc_framework/provider/provider.go similarity index 92% rename from citrixadc_framework/provider.go rename to citrixadc_framework/provider/provider.go index 4b626bfda..62d5c3425 100644 --- a/citrixadc_framework/provider.go +++ b/citrixadc_framework/provider/provider.go @@ -14,12 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -package citrixadc_framework +package provider import ( "context" "os" + "github.com/citrix/adc-nitro-go/service" + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" @@ -27,7 +29,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/citrix/adc-nitro-go/service" + "github.com/citrix/terraform-provider-citrixadc/citrixadc_framework/lbparameter" + "github.com/citrix/terraform-provider-citrixadc/citrixadc_framework/sslcertkey" ) // Ensure CitrixAdcFrameworkProvider satisfies various provider interfaces. @@ -212,27 +215,31 @@ func (p *CitrixAdcFrameworkProvider) Configure(ctx context.Context, req provider } } - providerData := &ProviderData{ - Client: client, - Username: username, - Password: password, - Endpoint: endpoint, - } + // providerData := &ProviderData{ + // Client: client, + // Username: username, + // Password: password, + // Endpoint: endpoint, + // } - resp.DataSourceData = providerData - resp.ResourceData = providerData + resp.DataSourceData = &client + resp.ResourceData = &client tflog.Info(ctx, "Configured CitrixADC Framework Provider", map[string]any{"success": true}) } func (p *CitrixAdcFrameworkProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ - NewLbParameterResource, + lbparameter.NewLbParameterResource, + sslcertkey.NewSslCertKeyResource, } } func (p *CitrixAdcFrameworkProvider) DataSources(ctx context.Context) []func() datasource.DataSource { - return []func() datasource.DataSource{} + return []func() datasource.DataSource{ + lbparameter.LBParameterDataSource, + sslcertkey.SslCertKeyDataSource, + } } func New(version string) func() provider.Provider { diff --git a/citrixadc_framework/resource_citrixadc_lbparameter.go b/citrixadc_framework/resource_citrixadc_lbparameter.go deleted file mode 100644 index bfb46bc24..000000000 --- a/citrixadc_framework/resource_citrixadc_lbparameter.go +++ /dev/null @@ -1,620 +0,0 @@ -package citrixadc_framework - -import ( - "context" - "fmt" - "strconv" - - "github.com/citrix/adc-nitro-go/service" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-log/tflog" -) - -// Ensure provider defined types fully satisfy framework interfaces. -var _ resource.Resource = &LbParameterResource{} - -func NewLbParameterResource() resource.Resource { - return &LbParameterResource{} -} - -// LbParameterResource defines the resource implementation. -type LbParameterResource struct { - providerData *ProviderData -} - -// LbParameterResourceModel describes the resource data model. -type LbParameterResourceModel struct { - Id types.String `tfsdk:"id"` - AllowBoundSvcRemoval types.String `tfsdk:"allowboundsvcremoval"` - ComputedAdcCookieAttribute types.String `tfsdk:"computedadccookieattribute"` - ConsolidatedLconn types.String `tfsdk:"consolidatedlconn"` - CookiePassphrase types.String `tfsdk:"cookiepassphrase"` - DbsTtl types.Int64 `tfsdk:"dbsttl"` - DropMqttJumboMessage types.String `tfsdk:"dropmqttjumbomessage"` - HttpOnlyCookieFlag types.String `tfsdk:"httponlycookieflag"` - LiteralAdcCookieAttribute types.String `tfsdk:"literaladccookieattribute"` - MaxPipelineNat types.Int64 `tfsdk:"maxpipelinenat"` - MonitorConnectionClose types.String `tfsdk:"monitorconnectionclose"` - MonitorSkipMaxClient types.String `tfsdk:"monitorskipmaxclient"` - PreferDirectRoute types.String `tfsdk:"preferdirectroute"` - ProximityFromSelf types.String `tfsdk:"proximityfromself"` - RetainServiceState types.String `tfsdk:"retainservicestate"` - StartupRrFactor types.Int64 `tfsdk:"startuprrfactor"` - StoreMqttClientIdAndUsername types.String `tfsdk:"storemqttclientidandusername"` - SessionsThreshold types.Int64 `tfsdk:"sessionsthreshold"` - UndefAction types.String `tfsdk:"undefaction"` - UseEncryptedPersistenceCookie types.String `tfsdk:"useencryptedpersistencecookie"` - UsePortForHashLb types.String `tfsdk:"useportforhashlb"` - UseSecuredPersistenceCookie types.String `tfsdk:"usesecuredpersistencecookie"` - VserverSpecificMac types.String `tfsdk:"vserverspecificmac"` - LbHashAlgorithm types.String `tfsdk:"lbhashalgorithm"` - LbHashFingers types.Int64 `tfsdk:"lbhashfingers"` -} - -type Lbparameter struct { - Adccookieattributewarningmsg string `json:"adccookieattributewarningmsg,omitempty"` - Allowboundsvcremoval string `json:"allowboundsvcremoval,omitempty"` - Builtin interface{} `json:"builtin,omitempty"` - Computedadccookieattribute string `json:"computedadccookieattribute,omitempty"` - Consolidatedlconn string `json:"consolidatedlconn,omitempty"` - Cookiepassphrase string `json:"cookiepassphrase,omitempty"` - Dbsttl *int `json:"dbsttl,omitempty"` - Dropmqttjumbomessage string `json:"dropmqttjumbomessage,omitempty"` - Feature string `json:"feature,omitempty"` - Httponlycookieflag string `json:"httponlycookieflag,omitempty"` - Literaladccookieattribute string `json:"literaladccookieattribute,omitempty"` - Maxpipelinenat *int `json:"maxpipelinenat,omitempty"` - Monitorconnectionclose string `json:"monitorconnectionclose,omitempty"` - Monitorskipmaxclient string `json:"monitorskipmaxclient,omitempty"` - Preferdirectroute string `json:"preferdirectroute,omitempty"` - Proximityfromself string `json:"proximityfromself,omitempty"` - Retainservicestate string `json:"retainservicestate,omitempty"` - Sessionsthreshold *int `json:"sessionsthreshold,omitempty"` - Startuprrfactor *int `json:"startuprrfactor,omitempty"` - Storemqttclientidandusername string `json:"storemqttclientidandusername,omitempty"` - Undefaction string `json:"undefaction,omitempty"` - Useencryptedpersistencecookie string `json:"useencryptedpersistencecookie,omitempty"` - Useportforhashlb string `json:"useportforhashlb,omitempty"` - Usesecuredpersistencecookie string `json:"usesecuredpersistencecookie,omitempty"` - Vserverspecificmac string `json:"vserverspecificmac,omitempty"` - Lbhashalgorithm string `json:"lbhashalgorithm,omitempty"` - Lbhashfingers *int `json:"lbhashfingers,omitempty"` -} - -func (r *LbParameterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_lbparameter" -} - -func (r *LbParameterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - Version: 1, - Attributes: map[string]schema.Attribute{ - "id": schema.StringAttribute{ - Computed: true, - }, - "allowboundsvcremoval": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "computedadccookieattribute": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "consolidatedlconn": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "cookiepassphrase": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "dbsttl": schema.Int64Attribute{ - Optional: true, - Computed: true, - }, - "dropmqttjumbomessage": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "httponlycookieflag": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "literaladccookieattribute": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "maxpipelinenat": schema.Int64Attribute{ - Optional: true, - Computed: true, - }, - "monitorconnectionclose": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "monitorskipmaxclient": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "preferdirectroute": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "proximityfromself": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "retainservicestate": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "startuprrfactor": schema.Int64Attribute{ - Optional: true, - Computed: true, - }, - "storemqttclientidandusername": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "sessionsthreshold": schema.Int64Attribute{ - Optional: true, - Computed: true, - }, - "undefaction": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "useencryptedpersistencecookie": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "useportforhashlb": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "usesecuredpersistencecookie": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "vserverspecificmac": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "lbhashalgorithm": schema.StringAttribute{ - Optional: true, - Computed: true, - }, - "lbhashfingers": schema.Int64Attribute{ - Optional: true, - Computed: true, - }, - }, - } -} - -func (r *LbParameterResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - // Prevent panic if the provider has not been configured. - if req.ProviderData == nil { - return - } - - providerData, ok := req.ProviderData.(*ProviderData) - - if !ok { - resp.Diagnostics.AddError( - "Unexpected Resource Configure Type", - fmt.Sprintf("Expected *ProviderData, got: %T. Please report this issue to the provider developers.", req.ProviderData), - ) - - return - } - - r.providerData = providerData -} - -func (r *LbParameterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data LbParameterResourceModel - - // Read Terraform plan data into the model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - tflog.Debug(ctx, "Creating lbparameter resource") - - // Create API request body from the model - lbparameter := Lbparameter{} - - if !data.AllowBoundSvcRemoval.IsNull() { - lbparameter.Allowboundsvcremoval = data.AllowBoundSvcRemoval.ValueString() - } - if !data.ComputedAdcCookieAttribute.IsNull() { - lbparameter.Computedadccookieattribute = data.ComputedAdcCookieAttribute.ValueString() - } - if !data.ConsolidatedLconn.IsNull() { - lbparameter.Consolidatedlconn = data.ConsolidatedLconn.ValueString() - } - if !data.CookiePassphrase.IsNull() { - lbparameter.Cookiepassphrase = data.CookiePassphrase.ValueString() - } - if !data.DbsTtl.IsNull() { - lbparameter.Dbsttl = intPtr(int(data.DbsTtl.ValueInt64())) - } - if !data.DropMqttJumboMessage.IsNull() { - lbparameter.Dropmqttjumbomessage = data.DropMqttJumboMessage.ValueString() - } - if !data.HttpOnlyCookieFlag.IsNull() { - lbparameter.Httponlycookieflag = data.HttpOnlyCookieFlag.ValueString() - } - if !data.LiteralAdcCookieAttribute.IsNull() { - lbparameter.Literaladccookieattribute = data.LiteralAdcCookieAttribute.ValueString() - } - if !data.MaxPipelineNat.IsNull() { - lbparameter.Maxpipelinenat = intPtr(int(data.MaxPipelineNat.ValueInt64())) - } - if !data.MonitorConnectionClose.IsNull() { - lbparameter.Monitorconnectionclose = data.MonitorConnectionClose.ValueString() - } - if !data.MonitorSkipMaxClient.IsNull() { - lbparameter.Monitorskipmaxclient = data.MonitorSkipMaxClient.ValueString() - } - if !data.PreferDirectRoute.IsNull() { - lbparameter.Preferdirectroute = data.PreferDirectRoute.ValueString() - } - if !data.ProximityFromSelf.IsNull() { - lbparameter.Proximityfromself = data.ProximityFromSelf.ValueString() - } - if !data.RetainServiceState.IsNull() { - lbparameter.Retainservicestate = data.RetainServiceState.ValueString() - } - if !data.StartupRrFactor.IsNull() { - lbparameter.Startuprrfactor = intPtr(int(data.StartupRrFactor.ValueInt64())) - } - if !data.StoreMqttClientIdAndUsername.IsNull() { - lbparameter.Storemqttclientidandusername = data.StoreMqttClientIdAndUsername.ValueString() - } - if !data.SessionsThreshold.IsNull() { - lbparameter.Sessionsthreshold = intPtr(int(data.SessionsThreshold.ValueInt64())) - } - if !data.UndefAction.IsNull() { - lbparameter.Undefaction = data.UndefAction.ValueString() - } - if !data.UseEncryptedPersistenceCookie.IsNull() { - lbparameter.Useencryptedpersistencecookie = data.UseEncryptedPersistenceCookie.ValueString() - } - if !data.UsePortForHashLb.IsNull() { - lbparameter.Useportforhashlb = data.UsePortForHashLb.ValueString() - } - if !data.UseSecuredPersistenceCookie.IsNull() { - lbparameter.Usesecuredpersistencecookie = data.UseSecuredPersistenceCookie.ValueString() - } - if !data.VserverSpecificMac.IsNull() { - lbparameter.Vserverspecificmac = data.VserverSpecificMac.ValueString() - } - if !data.LbHashAlgorithm.IsNull() { - lbparameter.Lbhashalgorithm = data.LbHashAlgorithm.ValueString() - } - if !data.LbHashFingers.IsNull() { - lbparameter.Lbhashfingers = intPtr(int(data.LbHashFingers.ValueInt64())) - } - - // Make API call - err := r.providerData.Client.UpdateUnnamedResource(service.Lbparameter.Type(), &lbparameter) - if err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create lbparameter, got error: %s", err)) - return - } - - // Generate unique ID for this configuration resource - data.Id = types.StringValue("lbparameter-config") - - tflog.Trace(ctx, "Created lbparameter resource") - - // Read the updated state back - r.readLbParameterFromApi(ctx, &data, &resp.Diagnostics) - - // Save data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func (r *LbParameterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var data LbParameterResourceModel - - // Read Terraform prior state data into the model - resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - tflog.Debug(ctx, "Reading lbparameter resource") - - r.readLbParameterFromApi(ctx, &data, &resp.Diagnostics) - - // Save updated data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func (r *LbParameterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var data LbParameterResourceModel - - // Read Terraform plan data into the model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - tflog.Debug(ctx, "Updating lbparameter resource") - - // Create API request body from the model - lbparameter := Lbparameter{} - - if !data.AllowBoundSvcRemoval.IsNull() { - lbparameter.Allowboundsvcremoval = data.AllowBoundSvcRemoval.ValueString() - } - if !data.ComputedAdcCookieAttribute.IsNull() { - lbparameter.Computedadccookieattribute = data.ComputedAdcCookieAttribute.ValueString() - } - if !data.ConsolidatedLconn.IsNull() { - lbparameter.Consolidatedlconn = data.ConsolidatedLconn.ValueString() - } - if !data.CookiePassphrase.IsNull() { - lbparameter.Cookiepassphrase = data.CookiePassphrase.ValueString() - } - if !data.DbsTtl.IsNull() { - lbparameter.Dbsttl = intPtr(int(data.DbsTtl.ValueInt64())) - } - if !data.DropMqttJumboMessage.IsNull() { - lbparameter.Dropmqttjumbomessage = data.DropMqttJumboMessage.ValueString() - } - if !data.HttpOnlyCookieFlag.IsNull() { - lbparameter.Httponlycookieflag = data.HttpOnlyCookieFlag.ValueString() - } - if !data.LiteralAdcCookieAttribute.IsNull() { - lbparameter.Literaladccookieattribute = data.LiteralAdcCookieAttribute.ValueString() - } - if !data.MaxPipelineNat.IsNull() { - lbparameter.Maxpipelinenat = intPtr(int(data.MaxPipelineNat.ValueInt64())) - } - if !data.MonitorConnectionClose.IsNull() { - lbparameter.Monitorconnectionclose = data.MonitorConnectionClose.ValueString() - } - if !data.MonitorSkipMaxClient.IsNull() { - lbparameter.Monitorskipmaxclient = data.MonitorSkipMaxClient.ValueString() - } - if !data.PreferDirectRoute.IsNull() { - lbparameter.Preferdirectroute = data.PreferDirectRoute.ValueString() - } - if !data.ProximityFromSelf.IsNull() { - lbparameter.Proximityfromself = data.ProximityFromSelf.ValueString() - } - if !data.RetainServiceState.IsNull() { - lbparameter.Retainservicestate = data.RetainServiceState.ValueString() - } - if !data.StartupRrFactor.IsNull() { - lbparameter.Startuprrfactor = intPtr(int(data.StartupRrFactor.ValueInt64())) - } - if !data.StoreMqttClientIdAndUsername.IsNull() { - lbparameter.Storemqttclientidandusername = data.StoreMqttClientIdAndUsername.ValueString() - } - if !data.SessionsThreshold.IsNull() { - lbparameter.Sessionsthreshold = intPtr(int(data.SessionsThreshold.ValueInt64())) - } - if !data.UndefAction.IsNull() { - lbparameter.Undefaction = data.UndefAction.ValueString() - } - if !data.UseEncryptedPersistenceCookie.IsNull() { - lbparameter.Useencryptedpersistencecookie = data.UseEncryptedPersistenceCookie.ValueString() - } - if !data.UsePortForHashLb.IsNull() { - lbparameter.Useportforhashlb = data.UsePortForHashLb.ValueString() - } - if !data.UseSecuredPersistenceCookie.IsNull() { - lbparameter.Usesecuredpersistencecookie = data.UseSecuredPersistenceCookie.ValueString() - } - if !data.VserverSpecificMac.IsNull() { - lbparameter.Vserverspecificmac = data.VserverSpecificMac.ValueString() - } - if !data.LbHashAlgorithm.IsNull() { - lbparameter.Lbhashalgorithm = data.LbHashAlgorithm.ValueString() - } - if !data.LbHashFingers.IsNull() { - lbparameter.Lbhashfingers = intPtr(int(data.LbHashFingers.ValueInt64())) - } - - // Make API call - err := r.providerData.Client.UpdateUnnamedResource(service.Lbparameter.Type(), &lbparameter) - if err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update lbparameter, got error: %s", err)) - return - } - - tflog.Trace(ctx, "Updated lbparameter resource") - - // Read the updated state back - r.readLbParameterFromApi(ctx, &data, &resp.Diagnostics) - - // Save updated data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func (r *LbParameterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var data LbParameterResourceModel - - // Read Terraform prior state data into the model - resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - tflog.Debug(ctx, "Deleting lbparameter resource") - - // For lbparameter, we don't actually delete the resource as it's a global configuration - // We just remove it from state - tflog.Trace(ctx, "Deleted lbparameter resource from state") -} - -// Helper function to read lbparameter data from API -func (r *LbParameterResource) readLbParameterFromApi(ctx context.Context, data *LbParameterResourceModel, diags *diag.Diagnostics) { - result, err := r.providerData.Client.FindResource(service.Lbparameter.Type(), "") - if err != nil { - diags.AddError("Client Error", fmt.Sprintf("Unable to read lbparameter, got error: %s", err)) - return - } - - // Set ID for the resource - data.Id = types.StringValue("lbparameter-config") - - // Convert API response to model - if val, ok := result["allowboundsvcremoval"]; ok && val != nil { - data.AllowBoundSvcRemoval = types.StringValue(val.(string)) - } else { - data.AllowBoundSvcRemoval = types.StringNull() - } - if val, ok := result["computedadccookieattribute"]; ok && val != nil { - data.ComputedAdcCookieAttribute = types.StringValue(val.(string)) - } else { - data.ComputedAdcCookieAttribute = types.StringNull() - } - if val, ok := result["consolidatedlconn"]; ok && val != nil { - data.ConsolidatedLconn = types.StringValue(val.(string)) - } else { - data.ConsolidatedLconn = types.StringNull() - } - if val, ok := result["cookiepassphrase"]; ok && val != nil { - data.CookiePassphrase = types.StringValue(val.(string)) - } else { - data.CookiePassphrase = types.StringNull() - } - if val, ok := result["dbsttl"]; ok && val != nil { - if intVal, err := convertToInt64(val); err == nil { - data.DbsTtl = types.Int64Value(intVal) - } - } else { - data.DbsTtl = types.Int64Null() - } - if val, ok := result["dropmqttjumbomessage"]; ok && val != nil { - data.DropMqttJumboMessage = types.StringValue(val.(string)) - } else { - data.DropMqttJumboMessage = types.StringNull() - } - if val, ok := result["httponlycookieflag"]; ok && val != nil { - data.HttpOnlyCookieFlag = types.StringValue(val.(string)) - } else { - data.HttpOnlyCookieFlag = types.StringNull() - } - if val, ok := result["literaladccookieattribute"]; ok && val != nil { - data.LiteralAdcCookieAttribute = types.StringValue(val.(string)) - } else { - data.LiteralAdcCookieAttribute = types.StringNull() - } - if val, ok := result["maxpipelinenat"]; ok && val != nil { - if intVal, err := convertToInt64(val); err == nil { - data.MaxPipelineNat = types.Int64Value(intVal) - } - } else { - data.MaxPipelineNat = types.Int64Null() - } - if val, ok := result["monitorconnectionclose"]; ok && val != nil { - data.MonitorConnectionClose = types.StringValue(val.(string)) - } else { - data.MonitorConnectionClose = types.StringNull() - } - if val, ok := result["monitorskipmaxclient"]; ok && val != nil { - data.MonitorSkipMaxClient = types.StringValue(val.(string)) - } else { - data.MonitorSkipMaxClient = types.StringNull() - } - if val, ok := result["preferdirectroute"]; ok && val != nil { - data.PreferDirectRoute = types.StringValue(val.(string)) - } else { - data.PreferDirectRoute = types.StringNull() - } - if val, ok := result["proximityfromself"]; ok && val != nil { - data.ProximityFromSelf = types.StringValue(val.(string)) - } else { - data.ProximityFromSelf = types.StringNull() - } - if val, ok := result["retainservicestate"]; ok && val != nil { - data.RetainServiceState = types.StringValue(val.(string)) - } else { - data.RetainServiceState = types.StringNull() - } - if val, ok := result["startuprrfactor"]; ok && val != nil { - if intVal, err := convertToInt64(val); err == nil { - data.StartupRrFactor = types.Int64Value(intVal) - } - } else { - data.StartupRrFactor = types.Int64Null() - } - if val, ok := result["storemqttclientidandusername"]; ok && val != nil { - data.StoreMqttClientIdAndUsername = types.StringValue(val.(string)) - } else { - data.StoreMqttClientIdAndUsername = types.StringNull() - } - if val, ok := result["sessionsthreshold"]; ok && val != nil { - if intVal, err := convertToInt64(val); err == nil { - data.SessionsThreshold = types.Int64Value(intVal) - } - } else { - data.SessionsThreshold = types.Int64Null() - } - if val, ok := result["undefaction"]; ok && val != nil { - data.UndefAction = types.StringValue(val.(string)) - } else { - data.UndefAction = types.StringNull() - } - if val, ok := result["useencryptedpersistencecookie"]; ok && val != nil { - data.UseEncryptedPersistenceCookie = types.StringValue(val.(string)) - } else { - data.UseEncryptedPersistenceCookie = types.StringNull() - } - if val, ok := result["useportforhashlb"]; ok && val != nil { - data.UsePortForHashLb = types.StringValue(val.(string)) - } else { - data.UsePortForHashLb = types.StringNull() - } - if val, ok := result["usesecuredpersistencecookie"]; ok && val != nil { - data.UseSecuredPersistenceCookie = types.StringValue(val.(string)) - } else { - data.UseSecuredPersistenceCookie = types.StringNull() - } - if val, ok := result["vserverspecificmac"]; ok && val != nil { - data.VserverSpecificMac = types.StringValue(val.(string)) - } else { - data.VserverSpecificMac = types.StringNull() - } - if val, ok := result["lbhashalgorithm"]; ok && val != nil { - data.LbHashAlgorithm = types.StringValue(val.(string)) - } else { - data.LbHashAlgorithm = types.StringNull() - } - if val, ok := result["lbhashfingers"]; ok && val != nil { - if intVal, err := convertToInt64(val); err == nil { - data.LbHashFingers = types.Int64Value(intVal) - } - } else { - data.LbHashFingers = types.Int64Null() - } -} - -// Helper function to convert interface{} to int64 -func convertToInt64(value interface{}) (int64, error) { - switch v := value.(type) { - case int: - return int64(v), nil - case int64: - return v, nil - case string: - return strconv.ParseInt(v, 10, 64) - default: - return 0, fmt.Errorf("cannot convert %T to int64", value) - } -} diff --git a/citrixadc_framework/sslcertkey/datasource_schema.go b/citrixadc_framework/sslcertkey/datasource_schema.go new file mode 100644 index 000000000..c747515bf --- /dev/null +++ b/citrixadc_framework/sslcertkey/datasource_schema.go @@ -0,0 +1,89 @@ +package sslcertkey + +import ( + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func SslCertKeyDataSourceSchema() schema.Schema { + return schema.Schema{ + Description: "Data source to read SSL certificate key pair configuration.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of the SSL certificate key pair.", + }, + "certkey": schema.StringAttribute{ + Required: true, + Description: "Name of the certificate and private-key pair to read.", + }, + "cert": schema.StringAttribute{ + Computed: true, + Description: "Name of and path to the X509 certificate file.", + }, + "key": schema.StringAttribute{ + Computed: true, + Description: "Name of and path to the private-key file.", + }, + "password": schema.BoolAttribute{ + Computed: true, + Description: "Passphrase that was used to encrypt the private-key.", + }, + "fipskey": schema.StringAttribute{ + Computed: true, + Description: "Name of the FIPS key in the Hardware Security Module (HSM).", + }, + "hsmkey": schema.StringAttribute{ + Computed: true, + Description: "Name of the HSM key in the External Hardware Security Module (HSM).", + }, + "inform": schema.StringAttribute{ + Computed: true, + Description: "Input format of the certificate and the private-key files (PEM, DER, or PFX).", + }, + "expirymonitor": schema.StringAttribute{ + Computed: true, + Description: "Issue an alert when the certificate is about to expire.", + }, + "notificationperiod": schema.Int64Attribute{ + Computed: true, + Description: "Time, in days, before certificate expiration at which to generate an alert.", + }, + "bundle": schema.StringAttribute{ + Computed: true, + Description: "Parse the certificate chain as a single file.", + }, + "linkcertkeyname": schema.StringAttribute{ + Computed: true, + Description: "Name of the Certificate Authority certificate-key pair linked to this certificate.", + }, + "nodomaincheck": schema.BoolAttribute{ + Computed: true, + Description: "Override the check for matching domain names during certificate update.", + }, + "ocspstaplingcache": schema.BoolAttribute{ + Computed: true, + Description: "Clear cached ocspStapling response.", + }, + "deletefromdevice": schema.BoolAttribute{ + Computed: true, + Description: "Delete cert/key file from file system.", + }, + "deletecertkeyfilesonremoval": schema.StringAttribute{ + Computed: true, + Description: "Delete certificate and key files when the certificate is removed.", + }, + "passplain": schema.StringAttribute{ + Computed: true, + Description: "Pass phrase used to encrypt the private-key. Required when adding an encrypted private-key in PEM format.", + }, + "passplain_wo": schema.StringAttribute{ + Computed: true, + Description: "Pass phrase used to encrypt the private-key. Required when adding an encrypted private-key in PEM format.", + }, + "passplain_wo_version": schema.Int64Attribute{ + Description: "Increment this version to signal a passplain_wo update.", + Computed: true, + }, + }, + } +} diff --git a/citrixadc_framework/sslcertkey/datasource_sslcertkey.go b/citrixadc_framework/sslcertkey/datasource_sslcertkey.go new file mode 100644 index 000000000..d05cc34ef --- /dev/null +++ b/citrixadc_framework/sslcertkey/datasource_sslcertkey.go @@ -0,0 +1,62 @@ +package sslcertkey + +import ( + "context" + "fmt" + + "github.com/citrix/adc-nitro-go/service" + + "github.com/hashicorp/terraform-plugin-framework/datasource" +) + +var _ datasource.DataSource = (*SslcertkeyDataSource)(nil) + +func SslCertKeyDataSource() datasource.DataSource { + return &SslcertkeyDataSource{} +} + +type SslcertkeyDataSource struct { + client *service.NitroClient +} + +func (d *SslcertkeyDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_sslcertkey" +} + +func (d *SslcertkeyDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + d.client = *req.ProviderData.(**service.NitroClient) +} + +func (d *SslcertkeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = SslCertKeyDataSourceSchema() +} + +func (d *SslcertkeyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data SslCertKeyResourceModel + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Get the certkey name from config + sslcertkeyName := data.Certkey.ValueString() + + var getResponseData map[string]interface{} + var err error + + getResponseData, err = d.client.FindResource(service.Sslcertkey.Type(), sslcertkeyName) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read sslcertkey %s, got error: %s", sslcertkeyName, err)) + return + } + + sslcertkeySetAttrFromGet(ctx, &data, getResponseData) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/citrixadc_framework/sslcertkey/resource_schema.go b/citrixadc_framework/sslcertkey/resource_schema.go new file mode 100644 index 000000000..da64dd6a7 --- /dev/null +++ b/citrixadc_framework/sslcertkey/resource_schema.go @@ -0,0 +1,278 @@ +package sslcertkey + +import ( + "context" + + "github.com/citrix/adc-nitro-go/resource/config/ssl" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/citrix/terraform-provider-citrixadc/citrixadc_framework/utils" +) + +// SslCertKeyResourceModel describes the resource data model. +type SslCertKeyResourceModel struct { + Id types.String `tfsdk:"id"` + Certkey types.String `tfsdk:"certkey"` + Cert types.String `tfsdk:"cert"` + Key types.String `tfsdk:"key"` + Password types.Bool `tfsdk:"password"` + Fipskey types.String `tfsdk:"fipskey"` + Hsmkey types.String `tfsdk:"hsmkey"` + Inform types.String `tfsdk:"inform"` + Expirymonitor types.String `tfsdk:"expirymonitor"` + NotificationPeriod types.Int64 `tfsdk:"notificationperiod"` + Bundle types.String `tfsdk:"bundle"` + LinkCertKeyName types.String `tfsdk:"linkcertkeyname"` + NoDomainCheck types.Bool `tfsdk:"nodomaincheck"` + OcspStaplingCache types.Bool `tfsdk:"ocspstaplingcache"` + DeleteFromDevice types.Bool `tfsdk:"deletefromdevice"` + DeleteCertKeyFilesOnRemoval types.String `tfsdk:"deletecertkeyfilesonremoval"` + Passplain types.String `tfsdk:"passplain"` + PassplainWo types.String `tfsdk:"passplain_wo"` + PassplainWoVersion types.Int64 `tfsdk:"passplain_wo_version"` +} + +func (r *SslCertKeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Version: 1, + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of the SSL certificate key pair. This is the same as the certkey attribute.", + }, + "certkey": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Description: "Name for the certificate and private-key pair. Must begin with an ASCII alphanumeric or underscore (_) character, and must contain only ASCII alphanumeric, underscore, hash (#), period (.), space, colon (:), at (@), equals (=), and hyphen (-) characters. The following fields cannot be changed after creation: certkey, bundle, hsmkey.", + }, + "cert": schema.StringAttribute{ + Required: true, + Description: "Name of and, optionally, path to the X509 certificate file that is used to form the certificate-key pair. The certificate file should be present on the appliance's hard-disk drive or solid-state drive. Storing a certificate in any location other than the default might cause inconsistency in a high availability setup. /nsconfig/ssl/ is the default path.", + }, + "key": schema.StringAttribute{ + Optional: true, + Description: "Name of and, optionally, path to the private-key file that is used to form the certificate-key pair. The certificate file should be present on the appliance's hard-disk drive or solid-state drive. Storing a certificate in any location other than the default might cause inconsistency in a high availability setup. /nsconfig/ssl/ is the default path.", + }, + "password": schema.BoolAttribute{ + Optional: true, + Description: "Passphrase that was used to encrypt the private-key. Use this option to load encrypted private-keys in PEM format.", + }, + "fipskey": schema.StringAttribute{ + Optional: true, + Description: "Name of the FIPS key that was created inside the Hardware Security Module (HSM) of a FIPS appliance, or a key that was imported into the HSM.", + }, + "hsmkey": schema.StringAttribute{ + Optional: true, + Description: "Name of the HSM key that was created in the External Hardware Security Module (HSM) of a FIPS appliance. The following fields cannot be changed after creation: certkey, bundle, hsmkey.", + }, + "inform": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "Input format of the certificate and the private-key files. The three formats supported by the appliance are: PEM - Privacy Enhanced Mail, DER - Distinguished Encoding Rule, PFX - Personal Information Exchange. Default: PEM", + }, + "expirymonitor": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "Issue an alert when the certificate is about to expire. Possible values: ENABLED, DISABLED", + }, + "notificationperiod": schema.Int64Attribute{ + Optional: true, + Computed: true, + Description: "Time, in number of days, before certificate expiration, at which to generate an alert that the certificate is about to expire. Minimum value: 10, Maximum value: 100", + }, + "bundle": schema.StringAttribute{ + Optional: true, + Description: "Parse the certificate chain as a single file after linking the server certificate to its issuer's certificate within the file. Possible values: YES, NO. The following fields cannot be changed after creation: certkey, bundle, hsmkey.", + }, + "linkcertkeyname": schema.StringAttribute{ + Optional: true, + Description: "Name of the Certificate Authority certificate-key pair to which to link a certificate-key pair.", + }, + "nodomaincheck": schema.BoolAttribute{ + Optional: true, + Description: "Override the check for matching domain names during a certificate update operation.", + }, + "ocspstaplingcache": schema.BoolAttribute{ + Optional: true, + Description: "Clear cached ocspStapling response in case of an update operation.", + }, + "deletefromdevice": schema.BoolAttribute{ + Optional: true, + Description: "Delete cert/key file from file system. Possible values: true, false", + }, + "deletecertkeyfilesonremoval": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "Delete certificate and key files when the certificate is removed. Possible values: YES, NO", + }, + "passplain": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "Pass phrase used to encrypt the private-key. Required when adding an encrypted private-key in PEM format.", + }, + "passplain_wo": schema.StringAttribute{ + Optional: true, + Sensitive: true, + WriteOnly: true, + Description: "Pass phrase used to encrypt the private-key. Required when adding an encrypted private-key in PEM format.", + }, + "passplain_wo_version": schema.Int64Attribute{ + Description: "Increment this version to signal a passplain_wo update.", + Optional: true, + Computed: true, + Default: int64default.StaticInt64(1), + }, + }, + } +} + +func sslcertkeyGetThePayloadFromtheConfig(ctx context.Context, data *SslCertKeyResourceModel) ssl.Sslcertkey { + tflog.Debug(ctx, "In sslcertkeyGetThePayloadFromtheConfig Function") + + // Create API request body from the model + sslcertkey := ssl.Sslcertkey{} + + if !data.Certkey.IsNull() { + sslcertkey.Certkey = data.Certkey.ValueString() + } + if !data.Cert.IsNull() { + sslcertkey.Cert = data.Cert.ValueString() + } + if !data.Key.IsNull() { + sslcertkey.Key = data.Key.ValueString() + } + if !data.Password.IsNull() { + sslcertkey.Password = data.Password.ValueBool() + } + if !data.Fipskey.IsNull() { + sslcertkey.Fipskey = data.Fipskey.ValueString() + } + if !data.Hsmkey.IsNull() { + sslcertkey.Hsmkey = data.Hsmkey.ValueString() + } + if !data.Inform.IsNull() { + sslcertkey.Inform = data.Inform.ValueString() + } + if !data.Passplain.IsNull() { + sslcertkey.Passplain = data.Passplain.ValueString() + } + if !data.Expirymonitor.IsNull() { + sslcertkey.Expirymonitor = data.Expirymonitor.ValueString() + } + if !data.NotificationPeriod.IsNull() { + sslcertkey.Notificationperiod = utils.IntPtr(int(data.NotificationPeriod.ValueInt64())) + } + if !data.Bundle.IsNull() { + sslcertkey.Bundle = data.Bundle.ValueString() + } + if !data.NoDomainCheck.IsNull() { + sslcertkey.Nodomaincheck = data.NoDomainCheck.ValueBool() + } + if !data.OcspStaplingCache.IsNull() { + sslcertkey.Ocspstaplingcache = data.OcspStaplingCache.ValueBool() + } + if !data.DeleteCertKeyFilesOnRemoval.IsNull() { + sslcertkey.Deletecertkeyfilesonremoval = data.DeleteCertKeyFilesOnRemoval.ValueString() + } + if !data.PassplainWo.IsNull() { + passplainWo := data.PassplainWo.ValueString() + if passplainWo != "" { + sslcertkey.Passplain = passplainWo + } + } + + return sslcertkey +} + +func sslcertkeySetAttrFromGet(ctx context.Context, data *SslCertKeyResourceModel, getResponseData map[string]interface{}) *SslCertKeyResourceModel { + tflog.Debug(ctx, "In sslcertkeySetAttrFromGet Function") + + // Set ID for the resource - use certkey as the identifier + if val, ok := getResponseData["certkey"]; ok && val != nil { + data.Id = types.StringValue(val.(string)) + data.Certkey = types.StringValue(val.(string)) + } + + // Convert API response to model + if val, ok := getResponseData["cert"]; ok && val != nil { + data.Cert = types.StringValue(val.(string)) + } else { + data.Cert = types.StringNull() + } + if val, ok := getResponseData["key"]; ok && val != nil { + data.Key = types.StringValue(val.(string)) + } else { + data.Key = types.StringNull() + } + // Password and passplain are not returned by NITRO API - keep existing state + // The API returns hashed values which would cause drift, so we skip updating these fields + // They will retain their configured values from the plan + if val, ok := getResponseData["fipskey"]; ok && val != nil { + data.Fipskey = types.StringValue(val.(string)) + } else { + data.Fipskey = types.StringNull() + } + if val, ok := getResponseData["hsmkey"]; ok && val != nil { + data.Hsmkey = types.StringValue(val.(string)) + } else { + data.Hsmkey = types.StringNull() + } + if val, ok := getResponseData["inform"]; ok && val != nil { + data.Inform = types.StringValue(val.(string)) + } else { + data.Inform = types.StringNull() + } + if val, ok := getResponseData["expirymonitor"]; ok && val != nil { + data.Expirymonitor = types.StringValue(val.(string)) + } else { + data.Expirymonitor = types.StringNull() + } + if val, ok := getResponseData["notificationperiod"]; ok && val != nil { + if intVal, err := utils.ConvertToInt64(val); err == nil { + data.NotificationPeriod = types.Int64Value(intVal) + } + } else { + data.NotificationPeriod = types.Int64Null() + } + if val, ok := getResponseData["bundle"]; ok && val != nil { + data.Bundle = types.StringValue(val.(string)) + } else { + data.Bundle = types.StringNull() + } + if val, ok := getResponseData["linkcertkeyname"]; ok && val != nil { + data.LinkCertKeyName = types.StringValue(val.(string)) + } else { + data.LinkCertKeyName = types.StringNull() + } + // if val, ok := getResponseData["nodomaincheck"]; ok && val != nil { + // data.NoDomainCheck = types.BoolValue(val.(bool)) + // } else { + // data.NoDomainCheck = types.BoolNull() + // } + if val, ok := getResponseData["ocspstaplingcache"]; ok && val != nil { + data.OcspStaplingCache = types.BoolValue(val.(bool)) + } else { + data.OcspStaplingCache = types.BoolNull() + } + if val, ok := getResponseData["deletecertkeyfilesonremoval"]; ok && val != nil { + data.DeleteCertKeyFilesOnRemoval = types.StringValue(val.(string)) + } else { + data.DeleteCertKeyFilesOnRemoval = types.StringNull() + } + if val, ok := getResponseData["deletefromdevice"]; ok && val != nil { + data.DeleteFromDevice = types.BoolValue(val.(bool)) + } else { + data.DeleteFromDevice = types.BoolNull() + } + + return data +} diff --git a/citrixadc_framework/sslcertkey/resource_sslcertkey.go b/citrixadc_framework/sslcertkey/resource_sslcertkey.go new file mode 100644 index 000000000..42d36751d --- /dev/null +++ b/citrixadc_framework/sslcertkey/resource_sslcertkey.go @@ -0,0 +1,430 @@ +package sslcertkey + +import ( + "context" + "fmt" + + "github.com/citrix/adc-nitro-go/resource/config/ssl" + "github.com/citrix/adc-nitro-go/service" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/citrix/terraform-provider-citrixadc/citrixadc_framework/utils" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &SslCertKeyResource{} +var _ resource.ResourceWithConfigure = (*SslCertKeyResource)(nil) +var _ resource.ResourceWithImportState = (*SslCertKeyResource)(nil) + +func NewSslCertKeyResource() resource.Resource { + return &SslCertKeyResource{} +} + +// SslCertKeyResource defines the resource implementation. +type SslCertKeyResource struct { + client *service.NitroClient +} + +func (r *SslCertKeyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *SslCertKeyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_sslcertkey" +} + +func (r *SslCertKeyResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + // Set the client for the resource. + r.client = *req.ProviderData.(**service.NitroClient) +} + +func (r *SslCertKeyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SslCertKeyResourceModel + var config SslCertKeyResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + // Read config to access write-only attributes (like passplain) + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Creating sslcertkey resource") + + // Get certkey name - if not provided, generate one + var sslcertkeyName string + if data.Certkey.IsNull() || data.Certkey.ValueString() == "" { + sslcertkeyName = "tf-sslcertkey-" + fmt.Sprintf("%d", len(data.Cert.ValueString())) + data.Certkey = types.StringValue(sslcertkeyName) + } else { + sslcertkeyName = data.Certkey.ValueString() + } + + // Use config (not plan) to get write-only attributes like passplain + sslcertkey := sslcertkeyGetThePayloadFromtheConfig(ctx, &config) + + // Nodomaincheck is always set to false on creation + sslcertkey.Nodomaincheck = false + + // Make API call + _, err := r.client.AddResource(service.Sslcertkey.Type(), sslcertkeyName, &sslcertkey) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create sslcertkey, got error: %s", err)) + return + } + + data.Id = types.StringValue(sslcertkeyName) + + tflog.Trace(ctx, "Created sslcertkey resource") + + // Handle linked certificate if configured + if !data.LinkCertKeyName.IsNull() && data.LinkCertKeyName.ValueString() != "" { + if err := r.handleLinkedCertificate(ctx, &data, &resp.Diagnostics); err != nil { + tflog.Error(ctx, "Error linking certificate during creation") + // If linking fails, delete the created certificate + delErr := r.client.DeleteResource(service.Sslcertkey.Type(), sslcertkeyName) + if delErr != nil { + resp.Diagnostics.AddError("Cleanup Error", + fmt.Sprintf("Failed to delete certificate after link error. Link error: %s, Delete error: %s", err, delErr)) + } else { + resp.Diagnostics.AddError("Client Error", + fmt.Sprintf("Failed to link certificate: %s. Certificate has been deleted.", err)) + } + return + } + } + + // Read the updated state back + r.readSslCertKeyFromApi(ctx, &data, &resp.Diagnostics) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SslCertKeyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data SslCertKeyResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Reading sslcertkey resource") + + r.readSslCertKeyFromApi(ctx, &data, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SslCertKeyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan, state, config SslCertKeyResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Updating sslcertkey resource") + + sslcertkeyName := plan.Certkey.ValueString() + + // Determine which type of update is needed + needsUpdate := false + needsChange := false + needsClear := false + + sslcertkeyUpdate := ssl.Sslcertkey{ + Certkey: sslcertkeyName, + } + sslcertkeyChange := ssl.Sslcertkey{ + Certkey: sslcertkeyName, + } + sslcertkeyClear := ssl.Sslcertkey{ + Certkey: sslcertkeyName, + } + + // Check for changes that require Update API + if !plan.Expirymonitor.Equal(state.Expirymonitor) { + tflog.Debug(ctx, "Expirymonitor has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + sslcertkeyUpdate.Expirymonitor = plan.Expirymonitor.ValueString() + needsUpdate = true + } + if !plan.NotificationPeriod.Equal(state.NotificationPeriod) { + tflog.Debug(ctx, "Notificationperiod has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + if !plan.NotificationPeriod.IsNull() { + sslcertkeyUpdate.Notificationperiod = utils.IntPtr(int(plan.NotificationPeriod.ValueInt64())) + } + needsUpdate = true + } + if !plan.DeleteCertKeyFilesOnRemoval.Equal(state.DeleteCertKeyFilesOnRemoval) { + tflog.Debug(ctx, "DeleteCertKeyFilesOnRemoval has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + sslcertkeyUpdate.Deletecertkeyfilesonremoval = plan.DeleteCertKeyFilesOnRemoval.ValueString() + needsUpdate = true + } + + // Check for changes that require Change API + if !plan.Cert.Equal(state.Cert) { + tflog.Debug(ctx, "Cert has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + sslcertkeyChange.Cert = plan.Cert.ValueString() + needsChange = true + } + if !plan.Key.Equal(state.Key) { + tflog.Debug(ctx, "Key has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + sslcertkeyChange.Key = plan.Key.ValueString() + needsChange = true + } + if !plan.Password.Equal(state.Password) { + tflog.Debug(ctx, "Password has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + sslcertkeyChange.Password = plan.Password.ValueBool() + needsChange = true + } + if !plan.Fipskey.Equal(state.Fipskey) { + tflog.Debug(ctx, "Fipskey has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + sslcertkeyChange.Fipskey = plan.Fipskey.ValueString() + needsChange = true + } + if !plan.Inform.Equal(state.Inform) { + tflog.Debug(ctx, "Inform has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + sslcertkeyChange.Inform = plan.Inform.ValueString() + needsChange = true + } + if !plan.Passplain.Equal(state.Passplain) || !plan.PassplainWoVersion.Equal(state.PassplainWoVersion) { + tflog.Debug(ctx, "PassplainWoVersion has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + + sslcertkeyChange.Passplain = config.Passplain.ValueString() + // Use config value since passplain is WriteOnly + if !config.PassplainWo.IsNull() { + passplainWo := config.PassplainWo.ValueString() + if passplainWo != "" { + sslcertkeyChange.Passplain = config.PassplainWo.ValueString() + } + } + needsChange = true + } + + // Check for changes that require Clear API + if !config.OcspStaplingCache.IsNull() && !plan.OcspStaplingCache.Equal(state.OcspStaplingCache) { + tflog.Debug(ctx, "OcspStaplingCache has changed for sslcertkey", map[string]interface{}{"certkey": sslcertkeyName}) + sslcertkeyClear.Ocspstaplingcache = plan.OcspStaplingCache.ValueBool() + needsClear = true + } + + // Execute Update API if needed + if needsUpdate { + // Expirymonitor is always expected by NITRO API + if !plan.Expirymonitor.IsNull() { + sslcertkeyUpdate.Expirymonitor = plan.Expirymonitor.ValueString() + } + _, err := r.client.UpdateResource(service.Sslcertkey.Type(), sslcertkeyName, &sslcertkeyUpdate) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update sslcertkey %s, got error: %s", sslcertkeyName, err)) + return + } + } + + // Execute Change API if needed + if needsChange { + // Nodomaincheck is a flag for the change operation + if !plan.NoDomainCheck.IsNull() { + sslcertkeyChange.Nodomaincheck = plan.NoDomainCheck.ValueBool() + } + _, err := r.client.ChangeResource(service.Sslcertkey.Type(), sslcertkeyName, &sslcertkeyChange) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to change sslcertkey %s, got error: %s", sslcertkeyName, err)) + return + } + } + + // Execute Clear API if needed + if needsClear { + err := r.client.ActOnResource(service.Sslcertkey.Type(), &sslcertkeyClear, "clear") + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to clear sslcertkey %s, got error: %s", sslcertkeyName, err)) + return + } + } + + // Handle linked certificate changes + if err := r.handleLinkedCertificate(ctx, &plan, &resp.Diagnostics); err != nil { + tflog.Error(ctx, "Error linking certificate during update") + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to handle linked certificate for %s, got error: %s", sslcertkeyName, err)) + return + } + + tflog.Trace(ctx, "Updated sslcertkey resource") + + // Ensure plan has the correct ID before reading from API + plan.Id = state.Id + + // Read the updated state back - update plan with fresh API data + r.readSslCertKeyFromApi(ctx, &plan, &resp.Diagnostics) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *SslCertKeyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data SslCertKeyResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Deleting sslcertkey resource") + + sslcertkeyName := data.Id.ValueString() + + // Unlink certificate before deletion + if err := r.unlinkCertificate(ctx, &data, &resp.Diagnostics); err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to unlink certificate %s, got error: %s", sslcertkeyName, err)) + return + } + + // Build delete arguments + args := make([]string, 0) + if !data.DeleteFromDevice.IsNull() { + args = append(args, fmt.Sprintf("deletefromdevice:%t", data.DeleteFromDevice.ValueBool())) + } + if !data.DeleteCertKeyFilesOnRemoval.IsNull() { + args = append(args, fmt.Sprintf("deletecertkeyfilesonremoval:%s", data.DeleteCertKeyFilesOnRemoval.ValueString())) + } + + // Make API call with arguments + err := r.client.DeleteResourceWithArgs(service.Sslcertkey.Type(), sslcertkeyName, args) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete sslcertkey %s, got error: %s", sslcertkeyName, err)) + return + } + + tflog.Trace(ctx, "Deleted sslcertkey resource") +} + +// Helper function to read sslcertkey data from API +func (r *SslCertKeyResource) readSslCertKeyFromApi(ctx context.Context, data *SslCertKeyResourceModel, diags *diag.Diagnostics) { + sslcertkeyName := data.Id.ValueString() + + getResponseData, err := r.client.FindResource(service.Sslcertkey.Type(), sslcertkeyName) + if err != nil { + tflog.Warn(ctx, fmt.Sprintf("Clearing sslcertkey state %s", sslcertkeyName)) + data.Id = types.StringNull() + return + } + + sslcertkeySetAttrFromGet(ctx, data, getResponseData) +} + +// Helper function to handle linked certificate +func (r *SslCertKeyResource) handleLinkedCertificate(ctx context.Context, data *SslCertKeyResourceModel, diags *diag.Diagnostics) error { + tflog.Debug(ctx, "In handleLinkedCertificate") + + sslcertkeyName := data.Certkey.ValueString() + + // Get current state from API + getResponseData, err := r.client.FindResource(service.Sslcertkey.Type(), sslcertkeyName) + if err != nil { + tflog.Error(ctx, fmt.Sprintf("Error finding sslcertkey %s", sslcertkeyName)) + data.Id = types.StringNull() + return err + } + + // Get actual and configured linked certificate names + var actualLinkedCertKeyname interface{} = nil + if val, ok := getResponseData["linkcertkeyname"]; ok { + actualLinkedCertKeyname = val + } + + configuredLinkedCertKeyname := "" + if !data.LinkCertKeyName.IsNull() { + configuredLinkedCertKeyname = data.LinkCertKeyName.ValueString() + } + + // Check for noop conditions + if actualLinkedCertKeyname != nil && actualLinkedCertKeyname.(string) == configuredLinkedCertKeyname { + tflog.Debug(ctx, fmt.Sprintf("actual and configured linked certificates identical: %s", actualLinkedCertKeyname)) + return nil + } + + if actualLinkedCertKeyname == nil && configuredLinkedCertKeyname == "" { + tflog.Debug(ctx, "actual and configured linked certificates both empty") + return nil + } + + // Unlink existing certificate if present + if err := r.unlinkCertificate(ctx, data, diags); err != nil { + return err + } + + // Link new certificate if configured + if configuredLinkedCertKeyname != "" { + tflog.Debug(ctx, fmt.Sprintf("Linking certkey: %s", configuredLinkedCertKeyname)) + sslCertkey := ssl.Sslcertkey{ + Certkey: sslcertkeyName, + Linkcertkeyname: configuredLinkedCertKeyname, + } + if err := r.client.ActOnResource(service.Sslcertkey.Type(), &sslCertkey, "link"); err != nil { + tflog.Error(ctx, fmt.Sprintf("Error linking certificate: %v", err)) + return err + } + } else { + tflog.Debug(ctx, "configured linked certkey is empty, nothing to do") + } + + return nil +} + +// Helper function to unlink certificate +func (r *SslCertKeyResource) unlinkCertificate(ctx context.Context, data *SslCertKeyResourceModel, diags *diag.Diagnostics) error { + sslcertkeyName := data.Certkey.ValueString() + if sslcertkeyName == "" { + sslcertkeyName = data.Id.ValueString() + } + + getResponseData, err := r.client.FindResource(service.Sslcertkey.Type(), sslcertkeyName) + if err != nil { + tflog.Error(ctx, fmt.Sprintf("Error finding sslcertkey %s", sslcertkeyName)) + data.Id = types.StringNull() + return err + } + + actualLinkedCertKeyname := getResponseData["linkcertkeyname"] + + if actualLinkedCertKeyname != nil { + tflog.Debug(ctx, fmt.Sprintf("Unlinking certkey: %s", actualLinkedCertKeyname)) + + sslCertkey := ssl.Sslcertkey{ + Certkey: sslcertkeyName, + } + if err := r.client.ActOnResource(service.Sslcertkey.Type(), &sslCertkey, "unlink"); err != nil { + tflog.Error(ctx, fmt.Sprintf("Error unlinking certificate: %v", err)) + return err + } + } else { + tflog.Debug(ctx, "actual linked certkey is nil, nothing to do") + } + + return nil +} diff --git a/citrixadc_framework/utils.go b/citrixadc_framework/utils.go deleted file mode 100644 index 06f176a57..000000000 --- a/citrixadc_framework/utils.go +++ /dev/null @@ -1,13 +0,0 @@ -package citrixadc_framework - -// intPtr returns a pointer to the provided int value -// This is useful for optional fields in structs that require *int -func intPtr(i int) *int { - return &i -} - -// boolPtr returns a pointer to the provided bool value -// This is useful for optional fields in structs that require *bool -func boolPtr(b bool) *bool { - return &b -} diff --git a/citrixadc_framework/utils/utils.go b/citrixadc_framework/utils/utils.go new file mode 100644 index 000000000..5e0562891 --- /dev/null +++ b/citrixadc_framework/utils/utils.go @@ -0,0 +1,32 @@ +package utils + +import ( + "fmt" + "strconv" +) + +// intPtr returns a pointer to the provided int value +// This is useful for optional fields in structs that require *int +func IntPtr(i int) *int { + return &i +} + +// boolPtr returns a pointer to the provided bool value +// This is useful for optional fields in structs that require *bool +func BoolPtr(b bool) *bool { + return &b +} + +// Helper function to convert interface{} to int64 +func ConvertToInt64(value interface{}) (int64, error) { + switch v := value.(type) { + case int: + return int64(v), nil + case int64: + return v, nil + case string: + return strconv.ParseInt(v, 10, 64) + default: + return 0, fmt.Errorf("cannot convert %T to int64", value) + } +} diff --git a/docs/data-sources/sslcertkey.md b/docs/data-sources/sslcertkey.md new file mode 100644 index 000000000..6c7cdae76 --- /dev/null +++ b/docs/data-sources/sslcertkey.md @@ -0,0 +1,65 @@ +--- +subcategory: "SSL" +--- + +# Data Source `sslcertkey` + +The sslcertkey data source allows you to retrieve information about the TLS certificate keys. + + +## Example usage + +```terraform +data "citrixadc_sslcertkey" "tf_sslcertkey" { + certkey = "servercert1" +} + +output "cert" { + value = data.citrixadc_sslcertkey.tf_sslcertkey.cert +} + +output "key" { + value = data.citrixadc_sslcertkey.tf_sslcertkey.key +} +``` + + +## Argument Reference + +* `certkey` - (Required) Name for the certificate and private-key pair. + +## Attribute Reference + +In addition to the arguments, the following attributes are available: + +* `cert` - Name of and, optionally, path to the X509 certificate file that is used to form the certificate-key pair. The certificate file should be present on the appliance's hard-disk drive or solid-state drive. Storing a certificate in any location other than the default might cause inconsistency in a high availability setup. /nsconfig/ssl/ is the default path. +* `key` - Name of and, optionally, path to the private-key file that is used to form the certificate-key pair. The certificate file should be present on the appliance's hard-disk drive or solid-state drive. Storing a certificate in any location other than the default might cause inconsistency in a high availability setup. /nsconfig/ssl/ is the default path. +* `password` - Passphrase that was used to encrypt the private-key. Use this option to load encrypted private-keys in PEM format. +* `fipskey` - Name of the FIPS key that was created inside the Hardware Security Module (HSM) of a FIPS appliance, or a key that was imported into the HSM. +* `hsmkey` - Name of the HSM key that was created in the External Hardware Security Module (HSM) of a FIPS appliance. +* `inform` - Input format of the certificate and the private-key files. The three formats supported by the appliance are: PEM - Privacy Enhanced Mail DER - Distinguished Encoding Rule PFX - Personal Information Exchange. Possible values: [ DER, PEM, PFX ] +* `passplain` - Pass phrase used to encrypt the private-key. Required when adding an encrypted private-key in PEM format. +* `expirymonitor` - Issue an alert when the certificate is about to expire. Possible values: [ ENABLED, DISABLED ] +* `notificationperiod` - Time, in number of days, before certificate expiration, at which to generate an alert that the certificate is about to expire. +* `bundle` - Parse the certificate chain as a single file after linking the server certificate to its issuer's certificate within the file. Possible values: [ YES, NO ] +* `linkcertkeyname` - Name of the Certificate Authority certificate-key pair to which to link a certificate-key pair. +* `nodomaincheck` - Override the check for matching domain names during a certificate update operation. +* `ocspstaplingcache` - Clear cached ocspStapling response in certkey. +* `deletecertkeyfilesonremoval` - This option is used to automatically delete certificate/key files from physical device when the added certkey is removed. When deleteCertKeyFilesOnRemoval option is used at rm certkey command, it overwrites the deleteCertKeyFilesOnRemoval setting used at add/set certkey command +* `deletefromdevice` - Delete cert/key file from file system. + + +## Attribute Reference + +In addition to the arguments, the following attributes are available: + +* `id` - The id of the sslcertkey. It has the same value as the `certkey` attribute. + + +## Import + +A sslcertkey can be imported using its certkey, e.g. + +```shell +terraform import citrixadc_sslcertkey.tf_sslcertkey tf_sslcertkey +``` diff --git a/docs/resources/sslcertkey.md b/docs/resources/sslcertkey.md index 0b8d3f742..8bc4f1484 100644 --- a/docs/resources/sslcertkey.md +++ b/docs/resources/sslcertkey.md @@ -7,7 +7,9 @@ subcategory: "SSL" The sslcertkey resource is used to create TLS certificate keys. -## Example usage +## Example Usage + +### Basic SSL Certificate without Passphrase ```hcl resource "citrixadc_sslcertkey" "tf_sslcertkey" { @@ -19,17 +21,82 @@ resource "citrixadc_sslcertkey" "tf_sslcertkey" { } ``` +### Using Legacy `passplain` (Backward Compatibility) + +The `passplain` attribute is maintained for backward compatibility but stores the passphrase in the state file: + +```hcl +resource "citrixadc_sslcertkey" "tf_sslcertkey_legacy" { + certkey = "tf_sslcertkey_legacy" + cert = "/nsconfig/ssl/certificate1.crt" + key = "/nsconfig/ssl/encrypted_key1.pem" + + # Legacy approach (passphrase stored in state file) + passplain = "my-secret-passphrase" +} +``` + +### SSL Certificate with Ephemeral Passphrase Support + +This example demonstrates using `passplain_wo` (write-only) for enhanced security. The passphrase is not stored in the Terraform state file. + +```hcl +variable "sslcertkey_passplain_wo" { + type = string + sensitive = true + description = "Passphrase for encrypted private key (not stored in state)" +} + +resource "citrixadc_sslcertkey" "tf_sslcertkey_encrypted" { + certkey = "tf_sslcertkey_encrypted" + cert = "/nsconfig/ssl/certificate1.crt" + key = "/nsconfig/ssl/encrypted_key1.pem" + notificationperiod = 40 + expirymonitor = "ENABLED" + + # Ephemeral passphrase (write-only, not stored in state) + passplain_wo = var.sslcertkey_passplain_wo + passplain_wo_version = 1 +} +``` + +### Updating the Passphrase + +To update the passphrase for an encrypted private key, increment the `passplain_wo_version` value: + +```hcl +variable "sslcertkey_passplain_wo" { + type = string + sensitive = true + description = "New passphrase for encrypted private key" +} + +resource "citrixadc_sslcertkey" "tf_sslcertkey_encrypted" { + certkey = "tf_sslcertkey_encrypted" + cert = "/nsconfig/ssl/certificate1.crt" + key = "/nsconfig/ssl/encrypted_key1.pem" + notificationperiod = 40 + expirymonitor = "ENABLED" + + # Update passphrase by incrementing version + passplain_wo = var.sslcertkey_passplain_wo + passplain_wo_version = 2 # Incremented from 1 to trigger update +} +``` + ## Argument Reference -* `certkey` - (Optional) Name for the certificate and private-key pair. -* `cert` - (Optional) Name of and, optionally, path to the X509 certificate file that is used to form the certificate-key pair. The certificate file should be present on the appliance's hard-disk drive or solid-state drive. Storing a certificate in any location other than the default might cause inconsistency in a high availability setup. /nsconfig/ssl/ is the default path. +* `certkey` - (Required) Name for the certificate and private-key pair. +* `cert` - (Required) Name of and, optionally, path to the X509 certificate file that is used to form the certificate-key pair. The certificate file should be present on the appliance's hard-disk drive or solid-state drive. Storing a certificate in any location other than the default might cause inconsistency in a high availability setup. /nsconfig/ssl/ is the default path. * `key` - (Optional) Name of and, optionally, path to the private-key file that is used to form the certificate-key pair. The certificate file should be present on the appliance's hard-disk drive or solid-state drive. Storing a certificate in any location other than the default might cause inconsistency in a high availability setup. /nsconfig/ssl/ is the default path. * `password` - (Optional) Passphrase that was used to encrypt the private-key. Use this option to load encrypted private-keys in PEM format. * `fipskey` - (Optional) Name of the FIPS key that was created inside the Hardware Security Module (HSM) of a FIPS appliance, or a key that was imported into the HSM. * `hsmkey` - (Optional) Name of the HSM key that was created in the External Hardware Security Module (HSM) of a FIPS appliance. * `inform` - (Optional) Input format of the certificate and the private-key files. The three formats supported by the appliance are: PEM - Privacy Enhanced Mail DER - Distinguished Encoding Rule PFX - Personal Information Exchange. Possible values: [ DER, PEM, PFX ] -* `passplain` - (Optional) Pass phrase used to encrypt the private-key. Required when adding an encrypted private-key in PEM format. +* `passplain` - (Optional) Pass phrase used to encrypt the private-key. Required when adding an encrypted private-key in PEM format. **Note:** This value is stored in the Terraform state file. For enhanced security, use `passplain_wo` instead. +* `passplain_wo` - (Optional, Write-Only) Pass phrase used to encrypt the private-key. Required when adding an encrypted private-key in PEM format. **Recommended over `passplain`** for security reasons as this value is not stored in the Terraform state file. Must be used together with `passplain_wo_version`. +* `passplain_wo_version` - (Optional) Version counter used to trigger updates when `passplain_wo` changes. Increment this value whenever you need to update the passphrase. Default: 1 * `expirymonitor` - (Optional) Issue an alert when the certificate is about to expire. Possible values: [ ENABLED, DISABLED ] * `notificationperiod` - (Optional) Time, in number of days, before certificate expiration, at which to generate an alert that the certificate is about to expire. * `bundle` - (Optional) Parse the certificate chain as a single file after linking the server certificate to its issuer's certificate within the file. Possible values: [ YES, NO ] @@ -39,6 +106,29 @@ resource "citrixadc_sslcertkey" "tf_sslcertkey" { * `deletecertkeyfilesonremoval` - (Optional) This option is used to automatically delete certificate/key files from physical device when the added certkey is removed. When deleteCertKeyFilesOnRemoval option is used at rm certkey command, it overwrites the deleteCertKeyFilesOnRemoval setting used at add/set certkey command * `deletefromdevice` - (Optional) Delete cert/key file from file system. +## Ephemeral Passphrase Support + +The `passplain_wo` (write-only) and `passplain_wo_version` attributes provide ephemeral passphrase support for enhanced security: + +### Why Use Ephemeral Passphrases? + +- **Security**: The passphrase is never stored in the Terraform state file +- **Compliance**: Meets security requirements that prohibit storing secrets in state +- **Best Practice**: Follows Terraform's recommended pattern for sensitive values that should not persist + +### How It Works + +1. **Initial Creation**: Provide the passphrase via `passplain_wo` and set `passplain_wo_version` to 1 +2. **Updating Passphrase**: Change the passphrase value and increment `passplain_wo_version` (e.g., to 2) +3. **Version Tracking**: Terraform uses the version change to detect when the passphrase needs updating + +### Important Notes + +- `passplain_wo` and `passplain_wo_version` must be used together +- The passphrase is sent to the Citrix ADC but never stored in Terraform state +- For backward compatibility, `passplain` is still supported but stores the value in state +- Do not use both `passplain` and `passplain_wo` simultaneously; choose one approach + ## Attribute Reference diff --git a/main.go b/main.go index b16cf5d90..7b1216b14 100644 --- a/main.go +++ b/main.go @@ -7,7 +7,7 @@ import ( "log" "github.com/citrix/terraform-provider-citrixadc/citrixadc" - "github.com/citrix/terraform-provider-citrixadc/citrixadc_framework" + "github.com/citrix/terraform-provider-citrixadc/citrixadc_framework/provider" "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov5" "github.com/hashicorp/terraform-plugin-go/tfprotov6" @@ -38,7 +38,7 @@ func main() { } // Create the Framework provider (already tf6) - frameworkProviderFunc := providerserver.NewProtocol6(citrixadc_framework.New(version)()) + frameworkProviderFunc := providerserver.NewProtocol6(provider.New(version)()) // Create the mux server providers := []func() tfprotov6.ProviderServer{ diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default/doc.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default/doc.go new file mode 100644 index 000000000..13d2a1d93 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default/doc.go @@ -0,0 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package int64default provides default values for types.Int64 attributes. +package int64default diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default/static_value.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default/static_value.go new file mode 100644 index 000000000..b01d2dcca --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default/static_value.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package int64default + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/defaults" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// StaticInt64 returns a static int64 value default handler. +// +// Use StaticInt64 if a static default value for a int64 should be set. +func StaticInt64(defaultVal int64) defaults.Int64 { + return staticInt64Default{ + defaultVal: defaultVal, + } +} + +// staticInt64Default is static value default handler that +// sets a value on an int64 attribute. +type staticInt64Default struct { + defaultVal int64 +} + +// Description returns a human-readable description of the default value handler. +func (d staticInt64Default) Description(_ context.Context) string { + return fmt.Sprintf("value defaults to %d", d.defaultVal) +} + +// MarkdownDescription returns a markdown description of the default value handler. +func (d staticInt64Default) MarkdownDescription(_ context.Context) string { + return fmt.Sprintf("value defaults to `%d`", d.defaultVal) +} + +// DefaultInt64 implements the static default value logic. +func (d staticInt64Default) DefaultInt64(_ context.Context, req defaults.Int64Request, resp *defaults.Int64Response) { + resp.PlanValue = types.Int64Value(d.defaultVal) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/doc.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/doc.go new file mode 100644 index 000000000..6bbbb6607 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/doc.go @@ -0,0 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package stringplanmodifier provides plan modifiers for types.String attributes. +package stringplanmodifier diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace.go new file mode 100644 index 000000000..e3adb4b97 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package stringplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplace returns a plan modifier that conditionally requires +// resource replacement if: +// +// - The resource is planned for update. +// - The plan and state values are not equal. +// +// Use RequiresReplaceIfConfigured if the resource replacement should +// only occur if there is a configuration value (ignore unconfigured drift +// detection changes). Use RequiresReplaceIf if the resource replacement +// should check provider-defined conditional logic. +func RequiresReplace() planmodifier.String { + return RequiresReplaceIf( + func(_ context.Context, _ planmodifier.StringRequest, resp *RequiresReplaceIfFuncResponse) { + resp.RequiresReplace = true + }, + "If the value of this attribute changes, Terraform will destroy and recreate the resource.", + "If the value of this attribute changes, Terraform will destroy and recreate the resource.", + ) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace_if.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace_if.go new file mode 100644 index 000000000..0afe6cebf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace_if.go @@ -0,0 +1,73 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package stringplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplaceIf returns a plan modifier that conditionally requires +// resource replacement if: +// +// - The resource is planned for update. +// - The plan and state values are not equal. +// - The given function returns true. Returning false will not unset any +// prior resource replacement. +// +// Use RequiresReplace if the resource replacement should always occur on value +// changes. Use RequiresReplaceIfConfigured if the resource replacement should +// occur on value changes, but only if there is a configuration value (ignore +// unconfigured drift detection changes). +func RequiresReplaceIf(f RequiresReplaceIfFunc, description, markdownDescription string) planmodifier.String { + return requiresReplaceIfModifier{ + ifFunc: f, + description: description, + markdownDescription: markdownDescription, + } +} + +// requiresReplaceIfModifier is an plan modifier that sets RequiresReplace +// on the attribute if a given function is true. +type requiresReplaceIfModifier struct { + ifFunc RequiresReplaceIfFunc + description string + markdownDescription string +} + +// Description returns a human-readable description of the plan modifier. +func (m requiresReplaceIfModifier) Description(_ context.Context) string { + return m.description +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m requiresReplaceIfModifier) MarkdownDescription(_ context.Context) string { + return m.markdownDescription +} + +// PlanModifyString implements the plan modification logic. +func (m requiresReplaceIfModifier) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) { + // Do not replace on resource creation. + if req.State.Raw.IsNull() { + return + } + + // Do not replace on resource destroy. + if req.Plan.Raw.IsNull() { + return + } + + // Do not replace if the plan and state values are equal. + if req.PlanValue.Equal(req.StateValue) { + return + } + + ifFuncResp := &RequiresReplaceIfFuncResponse{} + + m.ifFunc(ctx, req, ifFuncResp) + + resp.Diagnostics.Append(ifFuncResp.Diagnostics...) + resp.RequiresReplace = ifFuncResp.RequiresReplace +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace_if_configured.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace_if_configured.go new file mode 100644 index 000000000..e1bf461dc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace_if_configured.go @@ -0,0 +1,34 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package stringplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplaceIfConfigured returns a plan modifier that conditionally requires +// resource replacement if: +// +// - The resource is planned for update. +// - The plan and state values are not equal. +// - The configuration value is not null. +// +// Use RequiresReplace if the resource replacement should occur regardless of +// the presence of a configuration value. Use RequiresReplaceIf if the resource +// replacement should check provider-defined conditional logic. +func RequiresReplaceIfConfigured() planmodifier.String { + return RequiresReplaceIf( + func(_ context.Context, req planmodifier.StringRequest, resp *RequiresReplaceIfFuncResponse) { + if req.ConfigValue.IsNull() { + return + } + + resp.RequiresReplace = true + }, + "If the value of this attribute is configured and changes, Terraform will destroy and recreate the resource.", + "If the value of this attribute is configured and changes, Terraform will destroy and recreate the resource.", + ) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace_if_func.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace_if_func.go new file mode 100644 index 000000000..bde13cb3f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/requires_replace_if_func.go @@ -0,0 +1,25 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package stringplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplaceIfFunc is a conditional function used in the RequiresReplaceIf +// plan modifier to determine whether the attribute requires replacement. +type RequiresReplaceIfFunc func(context.Context, planmodifier.StringRequest, *RequiresReplaceIfFuncResponse) + +// RequiresReplaceIfFuncResponse is the response type for a RequiresReplaceIfFunc. +type RequiresReplaceIfFuncResponse struct { + // Diagnostics report errors or warnings related to this logic. An empty + // or unset slice indicates success, with no warnings or errors generated. + Diagnostics diag.Diagnostics + + // RequiresReplace should be enabled if the resource should be replaced. + RequiresReplace bool +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/use_state_for_unknown.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/use_state_for_unknown.go new file mode 100644 index 000000000..a6d77962e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier/use_state_for_unknown.go @@ -0,0 +1,55 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package stringplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// UseStateForUnknown returns a plan modifier that copies a known prior state +// value into the planned value. Use this when it is known that an unconfigured +// value will remain the same after a resource update. +// +// To prevent Terraform errors, the framework automatically sets unconfigured +// and Computed attributes to an unknown value "(known after apply)" on update. +// Using this plan modifier will instead display the prior state value in the +// plan, unless a prior plan modifier adjusts the value. +func UseStateForUnknown() planmodifier.String { + return useStateForUnknownModifier{} +} + +// useStateForUnknownModifier implements the plan modifier. +type useStateForUnknownModifier struct{} + +// Description returns a human-readable description of the plan modifier. +func (m useStateForUnknownModifier) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m useStateForUnknownModifier) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyString implements the plan modification logic. +func (m useStateForUnknownModifier) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) { + // Do nothing if there is no state (resource is being created). + if req.State.Raw.IsNull() { + return + } + + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value, otherwise interpolation gets messed up. + if req.ConfigValue.IsUnknown() { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5c42962f2..cc999efcf 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -251,7 +251,9 @@ github.com/hashicorp/terraform-plugin-framework/resource github.com/hashicorp/terraform-plugin-framework/resource/identityschema github.com/hashicorp/terraform-plugin-framework/resource/schema github.com/hashicorp/terraform-plugin-framework/resource/schema/defaults +github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier +github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier github.com/hashicorp/terraform-plugin-framework/schema/validator github.com/hashicorp/terraform-plugin-framework/tfsdk github.com/hashicorp/terraform-plugin-framework/types