Skip to content

Commit

Permalink
PDI-1813: Export block HCL generation for PingFederate (#130)
Browse files Browse the repository at this point in the history
* Add PF resource export pingfederate_idp_default_urls

* Add PF resource export pingfederate_idp_sp_connection and update ignoredError checking for Terraform testing

* Add PF resource export pingfederate_incoming_proxy_settings

* Add PF resource export pingfederate_kerberos_realm

* Add PF resource export pingfederate_local_identity_identity_profile

* Update ignoredError checking for Terraform testing
  • Loading branch information
erikostien-pingidentity authored Aug 19, 2024
1 parent 7c214b3 commit ab10521
Show file tree
Hide file tree
Showing 19 changed files with 660 additions and 7 deletions.
4 changes: 4 additions & 0 deletions internal/connector/common/resources_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (
"github.com/pingidentity/pingctl/internal/logger"
)

const (
SINGLETON_ID_COMMENT_DATA = "This resource is a singleton, so the value of 'ID' in the import block does not matter - it is just a placeholder and required by terraform."
)

func HandleClientResponse(response *http.Response, err error, apiFunctionName string, resourceType string) error {
l := logger.Get()

Expand Down
5 changes: 5 additions & 0 deletions internal/connector/pingfederate/pingfederate_connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ func (c *PingfederateConnector) Export(format, outputDir string, overwriteExport
resources.DataStore(&c.clientInfo),
resources.ExtendedProperties(&c.clientInfo),
resources.IDPAdapter(&c.clientInfo),
resources.IDPDefaultURLs(&c.clientInfo),
resources.IDPSPConnection(&c.clientInfo),
resources.IncomingProxySettings(&c.clientInfo),
resources.KerberosRealm(&c.clientInfo),
resources.LocalIdentityIdentityProfile(&c.clientInfo),
}

return common.WriteFiles(exportableResources, format, outputDir, overwriteExport)
Expand Down
34 changes: 33 additions & 1 deletion internal/connector/pingfederate/pingfederate_connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func TestPingFederateTerraformPlan(t *testing.T) {
resource: resources.AuthenticationSelector(PingFederateClientInfo),
ignoredErrors: []string{
"Error: Plugin did not respond",
"Error: Request cancelled",
},
},
{
Expand All @@ -74,14 +75,15 @@ func TestPingFederateTerraformPlan(t *testing.T) {
name: "PingFederateDataStore",
resource: resources.DataStore(PingFederateClientInfo),
ignoredErrors: []string{
"Error: Missing Configuration for Required Attribute",
"Error: 'password' and 'user_dn' must be set together",
},
},
{
name: "PingFederateExtendedProperties",
resource: resources.ExtendedProperties(PingFederateClientInfo),
ignoredErrors: []string{
"Error: Plugin did not respond",
"Error: Request cancelled",
},
},
{
Expand All @@ -92,6 +94,36 @@ func TestPingFederateTerraformPlan(t *testing.T) {
"Error: Reference to undeclared resource",
},
},
{
name: "PingFederateIDPDefaultURLs",
resource: resources.IDPDefaultURLs(PingFederateClientInfo),
ignoredErrors: nil,
},
{
name: "PingFederateIDPSPConnection",
resource: resources.IDPSPConnection(PingFederateClientInfo),
ignoredErrors: nil,
},
{
name: "PingFederateIncomingProxySettings",
resource: resources.IncomingProxySettings(PingFederateClientInfo),
ignoredErrors: []string{
"Error: Plugin did not respond",
"Error: Request cancelled",
},
},
{
name: "PingFederateKerberosRealm",
resource: resources.KerberosRealm(PingFederateClientInfo),
ignoredErrors: []string{
"Error: Property Required:",
},
},
{
name: "PingFederateLocalIdentityIdentityProfile",
resource: resources.LocalIdentityIdentityProfile(PingFederateClientInfo),
ignoredErrors: nil,
},
}

for _, tc := range testCases {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (r *PingFederateAuthenticationApiSettingsResource) ExportAll() (*[]connecto

commentData := map[string]string{
"Resource Type": r.ResourceType(),
"Singleton ID": "This resource is a singleton, so the value of 'ID' in the import block does not matter - it is just a placeholder and required by terraform.",
"Singleton ID": common.SINGLETON_ID_COMMENT_DATA,
}

importBlocks = append(importBlocks, connector.ImportBlock{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (r *PingFederateAuthenticationPoliciesResource) ExportAll() (*[]connector.I

commentData := map[string]string{
"Resource Type": r.ResourceType(),
"Singleton ID": "This resource is a singleton, so the value of 'ID' in the import block does not matter - it is just a placeholder and required by terraform.",
"Singleton ID": common.SINGLETON_ID_COMMENT_DATA,
}

importBlocks = append(importBlocks, connector.ImportBlock{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (r *PingFederateAuthenticationPoliciesSettingsResource) ExportAll() (*[]con

commentData := map[string]string{
"Resource Type": r.ResourceType(),
"Singleton ID": "This resource is a singleton, so the value of 'ID' in the import block does not matter - it is just a placeholder and required by terraform.",
"Singleton ID": common.SINGLETON_ID_COMMENT_DATA,
}

importBlocks = append(importBlocks, connector.ImportBlock{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (r *PingFederateExtendedPropertiesResource) ExportAll() (*[]connector.Impor

commentData := map[string]string{
"Resource Type": r.ResourceType(),
"Singleton ID": "This resource is a singleton, so the value of 'ID' in the import block does not matter - it is just a placeholder and required by terraform.",
"Singleton ID": common.SINGLETON_ID_COMMENT_DATA,
}

importBlocks = append(importBlocks, connector.ImportBlock{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package resources

import (
"github.com/pingidentity/pingctl/internal/connector"
"github.com/pingidentity/pingctl/internal/connector/common"
"github.com/pingidentity/pingctl/internal/logger"
)

// Verify that the resource satisfies the exportable resource interface
var (
_ connector.ExportableResource = &PingFederateExtendedPropertiesResource{}
)

type PingFederateIDPDefaultURLsResource struct {
clientInfo *connector.PingFederateClientInfo
}

// Utility method for creating a PingFederateIDPDefaultURLsResource
func IDPDefaultURLs(clientInfo *connector.PingFederateClientInfo) *PingFederateIDPDefaultURLsResource {
return &PingFederateIDPDefaultURLsResource{
clientInfo: clientInfo,
}
}

func (r *PingFederateIDPDefaultURLsResource) ExportAll() (*[]connector.ImportBlock, error) {
l := logger.Get()

importBlocks := []connector.ImportBlock{}

l.Debug().Msgf("Generating Import Blocks for all %s resources...", r.ResourceType())

idpDefaultURLsId := "idp_default_urls_singleton_id"
idpDefaultURLsName := "IDP Default URLs"

commentData := map[string]string{
"Resource Type": r.ResourceType(),
"Singleton ID": common.SINGLETON_ID_COMMENT_DATA,
}

importBlocks = append(importBlocks, connector.ImportBlock{
ResourceType: r.ResourceType(),
ResourceName: idpDefaultURLsName,
ResourceID: idpDefaultURLsId,
CommentInformation: common.GenerateCommentInformation(commentData),
})

return &importBlocks, nil
}

func (r *PingFederateIDPDefaultURLsResource) ResourceType() string {
return "pingfederate_idp_default_urls"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package resources_test

import (
"testing"

"github.com/pingidentity/pingctl/internal/connector"
"github.com/pingidentity/pingctl/internal/connector/pingfederate/resources"
"github.com/pingidentity/pingctl/internal/testing/testutils"
)

func TestPingFederateIDPDefaultURLsExport(t *testing.T) {
// Get initialized apiClient and resource
PingFederateClientInfo := testutils.GetPingFederateClientInfo(t)
resource := resources.IDPDefaultURLs(PingFederateClientInfo)

// Defined the expected ImportBlocks for the resource
expectedImportBlocks := []connector.ImportBlock{
{
ResourceType: "pingfederate_idp_default_urls",
ResourceName: "IDP Default URLs",
ResourceID: "idp_default_urls_singleton_id",
},
}

testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package resources

import (
"fmt"

"github.com/pingidentity/pingctl/internal/connector"
"github.com/pingidentity/pingctl/internal/connector/common"
"github.com/pingidentity/pingctl/internal/logger"
)

// Verify that the resource satisfies the exportable resource interface
var (
_ connector.ExportableResource = &PingFederateIDPSPConnectionResource{}
)

type PingFederateIDPSPConnectionResource struct {
clientInfo *connector.PingFederateClientInfo
}

// Utility method for creating a PingFederateIDPSPConnectionResource
func IDPSPConnection(clientInfo *connector.PingFederateClientInfo) *PingFederateIDPSPConnectionResource {
return &PingFederateIDPSPConnectionResource{
clientInfo: clientInfo,
}
}

func (r *PingFederateIDPSPConnectionResource) ExportAll() (*[]connector.ImportBlock, error) {
l := logger.Get()

l.Debug().Msgf("Fetching all %s resources...", r.ResourceType())

apiExecuteFunc := r.clientInfo.ApiClient.IdpSpConnectionsAPI.GetSpConnections(r.clientInfo.Context).Execute
apiFunctionName := "GetSpConnections"

spConnections, response, err := apiExecuteFunc()

err = common.HandleClientResponse(response, err, apiFunctionName, r.ResourceType())
if err != nil {
return nil, err
}

if spConnections == nil {
l.Error().Msgf("Returned %s() spConnections is nil.", apiFunctionName)
l.Error().Msgf("%s Response Code: %s\nResponse Body: %s", apiFunctionName, response.Status, response.Body)
return nil, fmt.Errorf("failed to fetch %s resources via %s()", r.ResourceType(), apiFunctionName)
}

spConnectionsItems, ok := spConnections.GetItemsOk()
if !ok {
l.Error().Msgf("Failed to get %s() spConnections items.", apiFunctionName)
l.Error().Msgf("%s Response Code: %s\nResponse Body: %s", apiFunctionName, response.Status, response.Body)
return nil, fmt.Errorf("failed to fetch %s resources via %s()", r.ResourceType(), apiFunctionName)
}

importBlocks := []connector.ImportBlock{}

l.Debug().Msgf("Generating Import Blocks for all %s resources...", r.ResourceType())

for _, spConnection := range spConnectionsItems {
spConnectionId, spConnectionIdOk := spConnection.GetIdOk()
spConnectionName, spConnectionNameOk := spConnection.GetNameOk()

if spConnectionIdOk && spConnectionNameOk {
commentData := map[string]string{
"Resource Type": r.ResourceType(),
"IDP SP Connection Resource ID": *spConnectionId,
"IDP SP Connection Resource Name": *spConnectionName,
}

importBlocks = append(importBlocks, connector.ImportBlock{
ResourceType: r.ResourceType(),
ResourceName: *spConnectionName,
ResourceID: *spConnectionId,
CommentInformation: common.GenerateCommentInformation(commentData),
})
}
}

return &importBlocks, nil
}

func (r *PingFederateIDPSPConnectionResource) ResourceType() string {
return "pingfederate_idp_sp_connection"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package resources_test

import (
"testing"

"github.com/pingidentity/pingctl/internal/connector"
"github.com/pingidentity/pingctl/internal/connector/pingfederate/resources"
"github.com/pingidentity/pingctl/internal/testing/testutils"
)

func TestPingFederateIDPSPConnectionExport(t *testing.T) {
// Get initialized apiClient and resource
PingFederateClientInfo := testutils.GetPingFederateClientInfo(t)
resource := resources.IDPSPConnection(PingFederateClientInfo)

// Defined the expected ImportBlocks for the resource
expectedImportBlocks := []connector.ImportBlock{
{
ResourceType: "pingfederate_idp_sp_connection",
ResourceName: "test",
ResourceID: "iIoQK.-GWcXI5kLp4KDNxQqAhDF",
},
}

testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package resources

import (
"github.com/pingidentity/pingctl/internal/connector"
"github.com/pingidentity/pingctl/internal/connector/common"
"github.com/pingidentity/pingctl/internal/logger"
)

// Verify that the resource satisfies the exportable resource interface
var (
_ connector.ExportableResource = &PingFederateIncomingProxySettingsResource{}
)

type PingFederateIncomingProxySettingsResource struct {
clientInfo *connector.PingFederateClientInfo
}

// Utility method for creating a PingFederateIncomingProxySettingsResource
func IncomingProxySettings(clientInfo *connector.PingFederateClientInfo) *PingFederateIncomingProxySettingsResource {
return &PingFederateIncomingProxySettingsResource{
clientInfo: clientInfo,
}
}

func (r *PingFederateIncomingProxySettingsResource) ExportAll() (*[]connector.ImportBlock, error) {
l := logger.Get()

importBlocks := []connector.ImportBlock{}

l.Debug().Msgf("Generating Import Blocks for all %s resources...", r.ResourceType())

incomingProxySettingsId := "incoming_proxy_settings_singleton_id"
incomingProxySettingsName := "Incoming Proxy Settings"

commentData := map[string]string{
"Resource Type": r.ResourceType(),
"Singleton ID": common.SINGLETON_ID_COMMENT_DATA,
}

importBlocks = append(importBlocks, connector.ImportBlock{
ResourceType: r.ResourceType(),
ResourceName: incomingProxySettingsName,
ResourceID: incomingProxySettingsId,
CommentInformation: common.GenerateCommentInformation(commentData),
})

return &importBlocks, nil
}

func (r *PingFederateIncomingProxySettingsResource) ResourceType() string {
return "pingfederate_incoming_proxy_settings"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package resources_test

import (
"testing"

"github.com/pingidentity/pingctl/internal/connector"
"github.com/pingidentity/pingctl/internal/connector/pingfederate/resources"
"github.com/pingidentity/pingctl/internal/testing/testutils"
)

func TestPingFederateIncomingProxySettingsExport(t *testing.T) {
// Get initialized apiClient and resource
PingFederateClientInfo := testutils.GetPingFederateClientInfo(t)
resource := resources.IncomingProxySettings(PingFederateClientInfo)

// Defined the expected ImportBlocks for the resource
expectedImportBlocks := []connector.ImportBlock{
{
ResourceType: "pingfederate_incoming_proxy_settings",
ResourceName: "Incoming Proxy Settings",
ResourceID: "incoming_proxy_settings_singleton_id",
},
}

testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks)
}
Loading

0 comments on commit ab10521

Please sign in to comment.