diff --git a/internal/services/springcloud/client/client.go b/internal/services/springcloud/client/client.go index d1a053da15ec..84898060e26b 100644 --- a/internal/services/springcloud/client/client.go +++ b/internal/services/springcloud/client/client.go @@ -6,17 +6,18 @@ import ( ) type Client struct { - AppsClient *appplatform.AppsClient - BindingsClient *appplatform.BindingsClient - BuildServiceBuilderClient *appplatform.BuildServiceBuilderClient - CertificatesClient *appplatform.CertificatesClient - ConfigServersClient *appplatform.ConfigServersClient - CustomDomainsClient *appplatform.CustomDomainsClient - MonitoringSettingsClient *appplatform.MonitoringSettingsClient - DeploymentsClient *appplatform.DeploymentsClient - ServicesClient *appplatform.ServicesClient - ServiceRegistryClient *appplatform.ServiceRegistriesClient - StoragesClient *appplatform.StoragesClient + AppsClient *appplatform.AppsClient + BindingsClient *appplatform.BindingsClient + BuildServiceBuilderClient *appplatform.BuildServiceBuilderClient + CertificatesClient *appplatform.CertificatesClient + ConfigServersClient *appplatform.ConfigServersClient + ConfigurationServiceClient *appplatform.ConfigurationServicesClient + CustomDomainsClient *appplatform.CustomDomainsClient + MonitoringSettingsClient *appplatform.MonitoringSettingsClient + DeploymentsClient *appplatform.DeploymentsClient + ServicesClient *appplatform.ServicesClient + ServiceRegistryClient *appplatform.ServiceRegistriesClient + StoragesClient *appplatform.StoragesClient } func NewClient(o *common.ClientOptions) *Client { @@ -35,6 +36,9 @@ func NewClient(o *common.ClientOptions) *Client { configServersClient := appplatform.NewConfigServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&configServersClient.Client, o.ResourceManagerAuthorizer) + configurationServiceClient := appplatform.NewConfigurationServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&configurationServiceClient.Client, o.ResourceManagerAuthorizer) + customDomainsClient := appplatform.NewCustomDomainsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&customDomainsClient.Client, o.ResourceManagerAuthorizer) @@ -54,16 +58,17 @@ func NewClient(o *common.ClientOptions) *Client { o.ConfigureClient(&storageClient.Client, o.ResourceManagerAuthorizer) return &Client{ - AppsClient: &appsClient, - BindingsClient: &bindingsClient, - BuildServiceBuilderClient: &buildServiceBuilderClient, - CertificatesClient: &certificatesClient, - ConfigServersClient: &configServersClient, - CustomDomainsClient: &customDomainsClient, - DeploymentsClient: &deploymentsClient, - MonitoringSettingsClient: &monitoringSettingsClient, - ServicesClient: &servicesClient, - ServiceRegistryClient: &serviceRegistryClient, - StoragesClient: &storageClient, + AppsClient: &appsClient, + BindingsClient: &bindingsClient, + BuildServiceBuilderClient: &buildServiceBuilderClient, + CertificatesClient: &certificatesClient, + ConfigServersClient: &configServersClient, + ConfigurationServiceClient: &configurationServiceClient, + CustomDomainsClient: &customDomainsClient, + DeploymentsClient: &deploymentsClient, + MonitoringSettingsClient: &monitoringSettingsClient, + ServicesClient: &servicesClient, + ServiceRegistryClient: &serviceRegistryClient, + StoragesClient: &storageClient, } } diff --git a/internal/services/springcloud/parse/spring_cloud_configuration_service.go b/internal/services/springcloud/parse/spring_cloud_configuration_service.go new file mode 100644 index 000000000000..07c81febb594 --- /dev/null +++ b/internal/services/springcloud/parse/spring_cloud_configuration_service.go @@ -0,0 +1,75 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type SpringCloudConfigurationServiceId struct { + SubscriptionId string + ResourceGroup string + SpringName string + ConfigurationServiceName string +} + +func NewSpringCloudConfigurationServiceID(subscriptionId, resourceGroup, springName, configurationServiceName string) SpringCloudConfigurationServiceId { + return SpringCloudConfigurationServiceId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + SpringName: springName, + ConfigurationServiceName: configurationServiceName, + } +} + +func (id SpringCloudConfigurationServiceId) String() string { + segments := []string{ + fmt.Sprintf("Configuration Service Name %q", id.ConfigurationServiceName), + fmt.Sprintf("Spring Name %q", id.SpringName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Spring Cloud Configuration Service", segmentsStr) +} + +func (id SpringCloudConfigurationServiceId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.AppPlatform/Spring/%s/configurationServices/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.SpringName, id.ConfigurationServiceName) +} + +// SpringCloudConfigurationServiceID parses a SpringCloudConfigurationService ID into an SpringCloudConfigurationServiceId struct +func SpringCloudConfigurationServiceID(input string) (*SpringCloudConfigurationServiceId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := SpringCloudConfigurationServiceId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.SpringName, err = id.PopSegment("Spring"); err != nil { + return nil, err + } + if resourceId.ConfigurationServiceName, err = id.PopSegment("configurationServices"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/springcloud/parse/spring_cloud_configuration_service_test.go b/internal/services/springcloud/parse/spring_cloud_configuration_service_test.go new file mode 100644 index 000000000000..1fe17f3fdda5 --- /dev/null +++ b/internal/services/springcloud/parse/spring_cloud_configuration_service_test.go @@ -0,0 +1,128 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = SpringCloudConfigurationServiceId{} + +func TestSpringCloudConfigurationServiceIDFormatter(t *testing.T) { + actual := NewSpringCloudConfigurationServiceID("12345678-1234-9876-4563-123456789012", "resourceGroup1", "service1", "configurationService1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/configurationServices/configurationService1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestSpringCloudConfigurationServiceID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *SpringCloudConfigurationServiceId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing SpringName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/", + Error: true, + }, + + { + // missing value for SpringName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/", + Error: true, + }, + + { + // missing ConfigurationServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/", + Error: true, + }, + + { + // missing value for ConfigurationServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/configurationServices/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/configurationServices/configurationService1", + Expected: &SpringCloudConfigurationServiceId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resourceGroup1", + SpringName: "service1", + ConfigurationServiceName: "configurationService1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.APPPLATFORM/SPRING/SERVICE1/CONFIGURATIONSERVICES/CONFIGURATIONSERVICE1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := SpringCloudConfigurationServiceID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.SpringName != v.Expected.SpringName { + t.Fatalf("Expected %q but got %q for SpringName", v.Expected.SpringName, actual.SpringName) + } + if actual.ConfigurationServiceName != v.Expected.ConfigurationServiceName { + t.Fatalf("Expected %q but got %q for ConfigurationServiceName", v.Expected.ConfigurationServiceName, actual.ConfigurationServiceName) + } + } +} diff --git a/internal/services/springcloud/registration.go b/internal/services/springcloud/registration.go index 8aee935eb382..e5b41b930ad4 100644 --- a/internal/services/springcloud/registration.go +++ b/internal/services/springcloud/registration.go @@ -43,6 +43,7 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_spring_cloud_app_redis_association": resourceSpringCloudAppRedisAssociation(), "azurerm_spring_cloud_builder": resourceSpringCloudBuildServiceBuilder(), "azurerm_spring_cloud_certificate": resourceSpringCloudCertificate(), + "azurerm_spring_cloud_configuration_service": resourceSpringCloudConfigurationService(), "azurerm_spring_cloud_custom_domain": resourceSpringCloudCustomDomain(), "azurerm_spring_cloud_container_deployment": resourceSpringCloudContainerDeployment(), "azurerm_spring_cloud_java_deployment": resourceSpringCloudJavaDeployment(), diff --git a/internal/services/springcloud/resourceids.go b/internal/services/springcloud/resourceids.go index de3eae54a518..39f466bfb766 100644 --- a/internal/services/springcloud/resourceids.go +++ b/internal/services/springcloud/resourceids.go @@ -6,5 +6,6 @@ package springcloud //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudDeployment -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/apps/app1/deployments/deploy1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudCertificate -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/certificates/cert1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudCustomDomain -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1/apps/app1/domains/domain.com +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudConfigurationService -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/configurationServices/configurationService1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudService -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.AppPlatform/Spring/spring1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SpringCloudStorage -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/storages/storage1 diff --git a/internal/services/springcloud/spring_cloud_configuration_service_resource.go b/internal/services/springcloud/spring_cloud_configuration_service_resource.go new file mode 100644 index 000000000000..ef0875621595 --- /dev/null +++ b/internal/services/springcloud/spring_cloud_configuration_service_resource.go @@ -0,0 +1,326 @@ +package springcloud + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/appplatform/mgmt/2022-03-01-preview/appplatform" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceSpringCloudConfigurationService() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceSpringCloudConfigurationServiceCreateUpdate, + Read: resourceSpringCloudConfigurationServiceRead, + Update: resourceSpringCloudConfigurationServiceCreateUpdate, + Delete: resourceSpringCloudConfigurationServiceDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.SpringCloudConfigurationServiceID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "default", + }, false), + }, + + "spring_cloud_service_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.SpringCloudServiceID, + }, + + "repository": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "label": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "patterns": { + Type: pluginsdk.TypeSet, + Required: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + + "uri": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "host_key": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "host_key_algorithm": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "password": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "private_key": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "search_paths": { + Type: pluginsdk.TypeSet, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + + "strict_host_key_checking": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + + "username": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + } +} +func resourceSpringCloudConfigurationServiceCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + client := meta.(*clients.Client).AppPlatform.ConfigurationServiceClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + springId, err := parse.SpringCloudServiceID(d.Get("spring_cloud_service_id").(string)) + if err != nil { + return err + } + id := parse.NewSpringCloudConfigurationServiceID(subscriptionId, springId.ResourceGroup, springId.SpringName, d.Get("name").(string)) + + if d.IsNewResource() { + existing, err := client.Get(ctx, id.ResourceGroup, id.SpringName, id.ConfigurationServiceName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + } + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_spring_cloud_configuration_service", id.ID()) + } + } + + configurationServiceResource := appplatform.ConfigurationServiceResource{ + Properties: &appplatform.ConfigurationServiceProperties{ + Settings: &appplatform.ConfigurationServiceSettings{ + GitProperty: &appplatform.ConfigurationServiceGitProperty{ + Repositories: expandConfigurationServiceConfigurationServiceGitRepositoryArray(d.Get("repository").([]interface{})), + }, + }, + }, + } + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.SpringName, id.ConfigurationServiceName, configurationServiceResource) + if err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for creation/update of %s: %+v", id, err) + } + + d.SetId(id.ID()) + return resourceSpringCloudConfigurationServiceRead(d, meta) +} + +func resourceSpringCloudConfigurationServiceRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).AppPlatform.ConfigurationServiceClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.SpringCloudConfigurationServiceID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.SpringName, id.ConfigurationServiceName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] appplatform %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + d.Set("name", id.ConfigurationServiceName) + d.Set("spring_cloud_service_id", parse.NewSpringCloudServiceID(id.SubscriptionId, id.ResourceGroup, id.SpringName).ID()) + if props := resp.Properties; props != nil { + if props.Settings != nil && props.Settings.GitProperty != nil { + d.Set("repository", flattenConfigurationServiceConfigurationServiceGitRepositoryArray(props.Settings.GitProperty.Repositories, d.Get("repository").([]interface{}))) + } + } + return nil +} + +func resourceSpringCloudConfigurationServiceDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).AppPlatform.ConfigurationServiceClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.SpringCloudConfigurationServiceID(d.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.SpringName, id.ConfigurationServiceName) + if err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for deletion of %s: %+v", id, err) + } + return nil +} + +func expandConfigurationServiceConfigurationServiceGitRepositoryArray(input []interface{}) *[]appplatform.ConfigurationServiceGitRepository { + if len(input) == 0 { + return nil + } + results := make([]appplatform.ConfigurationServiceGitRepository, 0) + for _, item := range input { + v := item.(map[string]interface{}) + results = append(results, appplatform.ConfigurationServiceGitRepository{ + Name: utils.String(v["name"].(string)), + Patterns: utils.ExpandStringSlice(v["patterns"].(*pluginsdk.Set).List()), + URI: utils.String(v["uri"].(string)), + Label: utils.String(v["label"].(string)), + SearchPaths: utils.ExpandStringSlice(v["search_paths"].(*pluginsdk.Set).List()), + Username: utils.String(v["username"].(string)), + Password: utils.String(v["password"].(string)), + HostKey: utils.String(v["host_key"].(string)), + HostKeyAlgorithm: utils.String(v["host_key_algorithm"].(string)), + PrivateKey: utils.String(v["private_key"].(string)), + StrictHostKeyChecking: utils.Bool(v["strict_host_key_checking"].(bool)), + }) + } + return &results +} + +func flattenConfigurationServiceConfigurationServiceGitRepositoryArray(input *[]appplatform.ConfigurationServiceGitRepository, old []interface{}) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + oldItems := make(map[string]map[string]interface{}) + for _, item := range old { + v := item.(map[string]interface{}) + if name, ok := v["name"]; ok { + oldItems[name.(string)] = v + } + } + + for _, item := range *input { + var name string + if item.Name != nil { + name = *item.Name + } + var label string + if item.Label != nil { + label = *item.Label + } + var uri string + if item.URI != nil { + uri = *item.URI + } + + var strictHostKeyChecking bool + if item.StrictHostKeyChecking != nil { + strictHostKeyChecking = *item.StrictHostKeyChecking + } + + var hostKey string + var hostKeyAlgorithm string + var privateKey string + var username string + var password string + if oldItem, ok := oldItems[name]; ok { + if value, ok := oldItem["host_key"]; ok { + hostKey = value.(string) + } + if value, ok := oldItem["host_key_algorithm"]; ok { + hostKeyAlgorithm = value.(string) + } + if value, ok := oldItem["password"]; ok { + password = value.(string) + } + if value, ok := oldItem["private_key"]; ok { + privateKey = value.(string) + } + if value, ok := oldItem["username"]; ok { + username = value.(string) + } + } + results = append(results, map[string]interface{}{ + "name": name, + "label": label, + "patterns": utils.FlattenStringSlice(item.Patterns), + "uri": uri, + "host_key": hostKey, + "host_key_algorithm": hostKeyAlgorithm, + "password": password, + "private_key": privateKey, + "search_paths": utils.FlattenStringSlice(item.SearchPaths), + "strict_host_key_checking": strictHostKeyChecking, + "username": username, + }) + } + return results +} diff --git a/internal/services/springcloud/spring_cloud_configuration_service_resource_test.go b/internal/services/springcloud/spring_cloud_configuration_service_resource_test.go new file mode 100644 index 000000000000..655e1d6e5c91 --- /dev/null +++ b/internal/services/springcloud/spring_cloud_configuration_service_resource_test.go @@ -0,0 +1,205 @@ +package springcloud_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/parse" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type SpringCloudConfigurationServiceResource struct{} + +func TestAccSpringCloudConfigurationService_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_spring_cloud_configuration_service", "test") + r := SpringCloudConfigurationServiceResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSpringCloudConfigurationService_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_spring_cloud_configuration_service", "test") + r := SpringCloudConfigurationServiceResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccSpringCloudConfigurationService_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_spring_cloud_configuration_service", "test") + r := SpringCloudConfigurationServiceResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.complete(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("repository.0.password", "repository.0.username"), + }) +} + +func TestAccSpringCloudConfigurationService_sshAuth(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_spring_cloud_configuration_service", "test") + r := SpringCloudConfigurationServiceResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.sshAuth(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("repository.0.host_key", "repository.0.host_key_algorithm", "repository.0.private_key"), + }) +} + +func TestAccSpringCloudConfigurationService_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_spring_cloud_configuration_service", "test") + r := SpringCloudConfigurationServiceResource{} + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("repository.0.password", "repository.0.username"), + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r SpringCloudConfigurationServiceResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { + id, err := parse.SpringCloudConfigurationServiceID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.AppPlatform.ConfigurationServiceClient.Get(ctx, id.ResourceGroup, id.SpringName, id.ConfigurationServiceName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %q: %+v", id, err) + } + return utils.Bool(true), nil +} + +func (r SpringCloudConfigurationServiceResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-spring-%[2]d" + location = "%[1]s" +} + +resource "azurerm_spring_cloud_service" "test" { + name = "acctest-sc-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "E0" +} +`, data.Locations.Primary, data.RandomInteger) +} + +func (r SpringCloudConfigurationServiceResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_spring_cloud_configuration_service" "test" { + name = "default" + spring_cloud_service_id = azurerm_spring_cloud_service.test.id +} +`, template) +} + +func (r SpringCloudConfigurationServiceResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_spring_cloud_configuration_service" "import" { + name = azurerm_spring_cloud_configuration_service.test.name + spring_cloud_service_id = azurerm_spring_cloud_configuration_service.test.spring_cloud_service_id +} +`, config) +} + +func (r SpringCloudConfigurationServiceResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_spring_cloud_configuration_service" "test" { + name = "default" + spring_cloud_service_id = azurerm_spring_cloud_service.test.id + repository { + name = "fake" + label = "master" + patterns = ["app/dev"] + uri = "https://github.com/Azure-Samples/piggymetrics" + search_paths = ["dir1", "dir2"] + strict_host_key_checking = false + username = "adminuser" + password = "H@Sh1CoR3!" + } +} +`, template) +} + +func (r SpringCloudConfigurationServiceResource) sshAuth(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_spring_cloud_configuration_service" "test" { + name = "default" + spring_cloud_service_id = azurerm_spring_cloud_service.test.id + repository { + name = "fake" + label = "master" + patterns = ["app/dev"] + uri = "https://github.com/Azure-Samples/piggymetrics" + host_key = file("testdata/host_key") + host_key_algorithm = "ssh-rsa" + private_key = file("testdata/private_key") + search_paths = ["dir1", "dir2"] + strict_host_key_checking = false + } +} +`, template) +} diff --git a/internal/services/springcloud/validate/spring_cloud_configuration_service_id.go b/internal/services/springcloud/validate/spring_cloud_configuration_service_id.go new file mode 100644 index 000000000000..d121cb8ff1f3 --- /dev/null +++ b/internal/services/springcloud/validate/spring_cloud_configuration_service_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/springcloud/parse" +) + +func SpringCloudConfigurationServiceID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.SpringCloudConfigurationServiceID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/springcloud/validate/spring_cloud_configuration_service_id_test.go b/internal/services/springcloud/validate/spring_cloud_configuration_service_id_test.go new file mode 100644 index 000000000000..797b8a44a139 --- /dev/null +++ b/internal/services/springcloud/validate/spring_cloud_configuration_service_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestSpringCloudConfigurationServiceID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing SpringName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/", + Valid: false, + }, + + { + // missing value for SpringName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/", + Valid: false, + }, + + { + // missing ConfigurationServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/", + Valid: false, + }, + + { + // missing value for ConfigurationServiceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/configurationServices/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/configurationServices/configurationService1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.APPPLATFORM/SPRING/SERVICE1/CONFIGURATIONSERVICES/CONFIGURATIONSERVICE1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := SpringCloudConfigurationServiceID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/website/docs/r/spring_cloud_configuration_service.html.markdown b/website/docs/r/spring_cloud_configuration_service.html.markdown new file mode 100644 index 000000000000..fa6e702e873d --- /dev/null +++ b/website/docs/r/spring_cloud_configuration_service.html.markdown @@ -0,0 +1,107 @@ +--- +subcategory: "Spring Cloud" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_spring_cloud_configuration_service" +description: |- + Manages a Spring Cloud Configuration Service. +--- + +# azurerm_spring_cloud_configuration_service + +Manages a Spring Cloud Configuration Service. + +## Example Usage + +```hcl +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "example" { + name = "example" + location = "West Europe" +} + +resource "azurerm_spring_cloud_service" "example" { + name = "example" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + sku_name = "E0" +} + +resource "azurerm_spring_cloud_configuration_service" "example" { + name = "default" + spring_cloud_service_id = azurerm_spring_cloud_service.example.id + repository { + name = "fake" + label = "master" + patterns = ["app/dev"] + uri = "https://github.com/Azure-Samples/piggymetrics" + search_paths = ["dir1", "dir2"] + strict_host_key_checking = false + username = "adminuser" + password = "H@Sh1CoR3!" + } +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name which should be used for this Spring Cloud Configuration Service. Changing this forces a new Spring Cloud Configuration Service to be created. + +* `spring_cloud_service_id` - (Required) The ID of the Spring Cloud Service. Changing this forces a new Spring Cloud Configuration Service to be created. + +--- + +* `repository` - (Optional) One or more `repository` blocks as defined below. + +--- + +A `repository` block supports the following: + +* `label` - (Required) Specifies the label of the repository. + +* `name` - (Required) Specifies the name which should be used for this repository. + +* `patterns` - (Required) Specifies the collection of patterns of the repository. + +* `uri` - (Required) Specifies the URI of the repository. + +* `host_key` - (Optional) Specifies the SSH public key of git repository. + +* `host_key_algorithm` - (Optional) Specifies the SSH key algorithm of git repository. + +* `password` - (Optional) Specifies the password of git repository basic auth. + +* `private_key` - (Optional) Specifies the SSH private key of git repository. + +* `search_paths` - (Optional) Specifies a list of searching path of the repository + +* `strict_host_key_checking` - (Optional) Specifies whether enable the strict host key checking. + +* `username` - (Optional) Specifies the username of git repository basic auth. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Spring Cloud Configuration Service. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Spring Cloud Configuration Service. +* `read` - (Defaults to 5 minutes) Used when retrieving the Spring Cloud Configuration Service. +* `update` - (Defaults to 30 minutes) Used when updating the Spring Cloud Configuration Service. +* `delete` - (Defaults to 30 minutes) Used when deleting the Spring Cloud Configuration Service. + +## Import + +Spring Cloud Configuration Services can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_spring_cloud_configuration_service.example /subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resourceGroup1/providers/Microsoft.AppPlatform/Spring/service1/configurationServices/configurationService1 +```