Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ds/servicecatalog_launch_paths: New data source #19572

Merged
merged 11 commits into from
Jun 25, 2021
3 changes: 3 additions & 0 deletions .changelog/19572.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-notes:new-data-source
aws_servicecatalog_launch_paths
```
164 changes: 164 additions & 0 deletions aws/data_source_aws_servicecatalog_launch_paths.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package aws

import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/servicecatalog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
tfservicecatalog "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/servicecatalog"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/servicecatalog/waiter"
)

func dataSourceAwsServiceCatalogLaunchPaths() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsServiceCatalogLaunchPathsRead,

Schema: map[string]*schema.Schema{
"accept_language": {
Type: schema.TypeString,
Optional: true,
Default: "en",
ValidateFunc: validation.StringInSlice(tfservicecatalog.AcceptLanguage_Values(), false),
},
"product_id": {
Type: schema.TypeString,
Required: true,
},
"summaries": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"constraint_summaries": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"description": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"path_id": {
Type: schema.TypeString,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"tags": tagsSchemaComputed(),
},
},
},
},
}
}

func dataSourceAwsServiceCatalogLaunchPathsRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).scconn
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

summaries, err := waiter.LaunchPathsReady(conn, d.Get("accept_language").(string), d.Get("product_id").(string))

if err != nil {
return fmt.Errorf("error describing Service Catalog Launch Paths: %w", err)
}

if err := d.Set("summaries", flattenServiceCatalogLaunchPathSummaries(summaries, ignoreTagsConfig)); err != nil {
return fmt.Errorf("error setting summaries: %w", err)
}

d.SetId(d.Get("product_id").(string))

return nil
}

func flattenServiceCatalogLaunchPathSummary(apiObject *servicecatalog.LaunchPathSummary, ignoreTagsConfig *keyvaluetags.IgnoreConfig) map[string]interface{} {
if apiObject == nil {
return nil
}

tfMap := map[string]interface{}{}

if len(apiObject.ConstraintSummaries) > 0 {
tfMap["constraint_summaries"] = flattenServiceCatalogConstraintSummaries(apiObject.ConstraintSummaries)
}

if apiObject.Id != nil {
tfMap["path_id"] = aws.StringValue(apiObject.Id)
}

if apiObject.Name != nil {
tfMap["name"] = aws.StringValue(apiObject.Name)
}

tags := keyvaluetags.ServicecatalogKeyValueTags(apiObject.Tags)

tfMap["tags"] = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()

return tfMap
}

func flattenServiceCatalogLaunchPathSummaries(apiObjects []*servicecatalog.LaunchPathSummary, ignoreTagsConfig *keyvaluetags.IgnoreConfig) []interface{} {
if len(apiObjects) == 0 {
return nil
}

var tfList []interface{}

for _, apiObject := range apiObjects {
if apiObject == nil {
continue
}

tfList = append(tfList, flattenServiceCatalogLaunchPathSummary(apiObject, ignoreTagsConfig))
}

return tfList
}

func flattenServiceCatalogConstraintSummary(apiObject *servicecatalog.ConstraintSummary) map[string]interface{} {
if apiObject == nil {
return nil
}

tfMap := map[string]interface{}{}

if apiObject.Description != nil {
tfMap["description"] = aws.StringValue(apiObject.Description)
}

if apiObject.Type != nil {
tfMap["type"] = aws.StringValue(apiObject.Type)
}

return tfMap
}

func flattenServiceCatalogConstraintSummaries(apiObjects []*servicecatalog.ConstraintSummary) []interface{} {
if len(apiObjects) == 0 {
return nil
}

var tfList []interface{}

for _, apiObject := range apiObjects {
if apiObject == nil {
continue
}

tfList = append(tfList, flattenServiceCatalogConstraintSummary(apiObject))
}

return tfList
}
132 changes: 132 additions & 0 deletions aws/data_source_aws_servicecatalog_launch_paths_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package aws

import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/service/servicecatalog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccAWSServiceCatalogLaunchPathsDataSource_basic(t *testing.T) {
dataSourceName := "data.aws_servicecatalog_launch_paths.test"
resourceNameProduct := "aws_servicecatalog_product.test"
resourceNamePortfolio := "aws_servicecatalog_portfolio.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, servicecatalog.EndpointsID),
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccAWSServiceCatalogLaunchPathsDataSourceConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "accept_language", "en"),
resource.TestCheckResourceAttrPair(dataSourceName, "product_id", resourceNameProduct, "id"),
resource.TestCheckResourceAttr(dataSourceName, "summaries.#", "1"),
resource.TestCheckResourceAttrPair(dataSourceName, "summaries.0.name", resourceNamePortfolio, "name"),
resource.TestCheckResourceAttrSet(dataSourceName, "summaries.0.path_id"),
),
},
},
})
}

func testAccAWSServiceCatalogLaunchPathsDataSourceConfig_base(rName string) string {
return fmt.Sprintf(`
resource "aws_cloudformation_stack" "test" {
name = %[1]q

template_body = jsonencode({
AWSTemplateFormatVersion = "2010-09-09"

Resources = {
MyVPC = {
Type = "AWS::EC2::VPC"
Properties = {
CidrBlock = "10.1.0.0/16"
}
}
}

Outputs = {
VpcID = {
Description = "VPC ID"
Value = {
Ref = "MyVPC"
}
}
}
})
}

resource "aws_servicecatalog_product" "test" {
description = "beskrivning"
distributor = "distributör"
name = %[1]q
owner = "ägare"
type = "CLOUD_FORMATION_TEMPLATE"
support_description = "supportbeskrivning"
support_email = "[email protected]"
support_url = "http://example.com"

provisioning_artifact_parameters {
description = "artefaktbeskrivning"
name = %[1]q
template_physical_id = aws_cloudformation_stack.test.id
type = "CLOUD_FORMATION_TEMPLATE"
}

tags = {
Name = %[1]q
}
}

resource "aws_servicecatalog_portfolio" "test" {
name = %[1]q
provider_name = %[1]q
}

resource "aws_servicecatalog_product_portfolio_association" "test" {
portfolio_id = aws_servicecatalog_portfolio.test.id
product_id = aws_servicecatalog_product.test.id
}

data "aws_partition" "current" {}

resource "aws_iam_role" "test" {
name = %[1]q

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "servicecatalog.${data.aws_partition.current.dns_suffix}"
}
Sid = ""
}]
})
}

data "aws_caller_identity" "current" {}

resource "aws_servicecatalog_principal_portfolio_association" "test" {
portfolio_id = aws_servicecatalog_portfolio.test.id
principal_arn = data.aws_caller_identity.current.arn
}
`, rName)
}

func testAccAWSServiceCatalogLaunchPathsDataSourceConfig_basic(rName string) string {
return composeConfig(testAccAWSServiceCatalogLaunchPathsDataSourceConfig_base(rName), `
data "aws_servicecatalog_launch_paths" "test" {
product_id = aws_servicecatalog_product_portfolio_association.test.product_id

depends_on = [aws_servicecatalog_principal_portfolio_association.test]
}
`)
}
37 changes: 37 additions & 0 deletions aws/internal/service/servicecatalog/waiter/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,40 @@ func PrincipalPortfolioAssociationStatus(conn *servicecatalog.ServiceCatalog, ac
return output, servicecatalog.StatusAvailable, err
}
}

func LaunchPathsStatus(conn *servicecatalog.ServiceCatalog, acceptLanguage, productID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
input := &servicecatalog.ListLaunchPathsInput{
AcceptLanguage: aws.String(acceptLanguage),
ProductId: aws.String(productID),
}

var summaries []*servicecatalog.LaunchPathSummary

err := conn.ListLaunchPathsPages(input, func(page *servicecatalog.ListLaunchPathsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, summary := range page.LaunchPathSummaries {
if summary == nil {
continue
}

summaries = append(summaries, summary)
}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, servicecatalog.ErrCodeResourceNotFoundException) {
return nil, StatusNotFound, nil
}

if err != nil {
return nil, servicecatalog.StatusFailed, err
}

return summaries, servicecatalog.StatusAvailable, err
}
}
19 changes: 19 additions & 0 deletions aws/internal/service/servicecatalog/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const (
PrincipalPortfolioAssociationReadyTimeout = 3 * time.Minute
PrincipalPortfolioAssociationDeleteTimeout = 3 * time.Minute

LaunchPathsReadyTimeout = 3 * time.Minute

StatusNotFound = "NOT_FOUND"
StatusUnavailable = "UNAVAILABLE"

Expand Down Expand Up @@ -443,3 +445,20 @@ func PrincipalPortfolioAssociationDeleted(conn *servicecatalog.ServiceCatalog, a

return err
}

func LaunchPathsReady(conn *servicecatalog.ServiceCatalog, acceptLanguage, productID string) ([]*servicecatalog.LaunchPathSummary, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{StatusNotFound},
Target: []string{servicecatalog.StatusAvailable},
Refresh: LaunchPathsStatus(conn, acceptLanguage, productID),
Timeout: LaunchPathsReadyTimeout,
}

outputRaw, err := stateConf.WaitForState()

if output, ok := outputRaw.([]*servicecatalog.LaunchPathSummary); ok {
return output, err
}

return nil, err
}
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ func Provider() *schema.Provider {
"aws_secretsmanager_secret_rotation": dataSourceAwsSecretsManagerSecretRotation(),
"aws_secretsmanager_secret_version": dataSourceAwsSecretsManagerSecretVersion(),
"aws_servicecatalog_constraint": dataSourceAwsServiceCatalogConstraint(),
"aws_servicecatalog_launch_paths": dataSourceAwsServiceCatalogLaunchPaths(),
"aws_servicecatalog_portfolio": dataSourceAwsServiceCatalogPortfolio(),
"aws_servicecatalog_product": dataSourceAwsServiceCatalogProduct(),
"aws_servicequotas_service": dataSourceAwsServiceQuotasService(),
Expand Down
Loading