Skip to content

Commit

Permalink
Feat: add new OIDC credentials creation and assigment (Azure data sou…
Browse files Browse the repository at this point in the history
…rce) (#773)
  • Loading branch information
TomerHeber committed Dec 26, 2023
1 parent 7ad25a2 commit fcf27bc
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 72 deletions.
66 changes: 0 additions & 66 deletions env0/data_aws_oidc_credentials.go

This file was deleted.

5 changes: 0 additions & 5 deletions env0/data_aws_oidc_credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ func TestAwsOidcCredentialDataSource(t *testing.T) {
Type: string(client.AwsAssumedRoleCredentialsType),
}

oidcSub := "oidc sub 123345 !!!"

byName := map[string]interface{}{"name": credentials.Name}
byId := map[string]interface{}{"id": credentials.Id}

Expand All @@ -46,7 +44,6 @@ func TestAwsOidcCredentialDataSource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", credentials.Id),
resource.TestCheckResourceAttr(accessor, "name", credentials.Name),
resource.TestCheckResourceAttr(accessor, "oidc_sub", oidcSub),
),
},
},
Expand All @@ -67,14 +64,12 @@ func TestAwsOidcCredentialDataSource(t *testing.T) {
mockGetCredentials := func(returnValue client.Credentials) func(mockFunc *client.MockApiClientInterface) {
return func(mock *client.MockApiClientInterface) {
mock.EXPECT().CloudCredentials(credentials.Id).AnyTimes().Return(returnValue, nil)
mock.EXPECT().OidcSub().AnyTimes().Return(oidcSub, nil)
}
}

mockListCredentials := func(returnValue []client.Credentials) func(mockFunc *client.MockApiClientInterface) {
return func(mock *client.MockApiClientInterface) {
mock.EXPECT().CloudCredentialsList().AnyTimes().Return(returnValue, nil)
mock.EXPECT().OidcSub().AnyTimes().Return(oidcSub, nil)
}
}

Expand Down
119 changes: 119 additions & 0 deletions env0/data_azure_oidc_credentials_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package env0

import (
"fmt"
"regexp"
"testing"

"github.com/env0/terraform-provider-env0/client"
"github.com/env0/terraform-provider-env0/client/http"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAzureOidcCredentialDataSource(t *testing.T) {
credentials := client.Credentials{
Id: "id0",
Name: "name0",
Type: string(client.AzureOidcCredentialsType),
}

credentialsOther1 := client.Credentials{
Id: "id1",
Name: "name1",
Type: string(client.AzureOidcCredentialsType),
}

credentialsOther2 := client.Credentials{
Id: "id2",
Name: "name2",
Type: string(client.AzureServicePrincipalCredentialsType),
}

byName := map[string]interface{}{"name": credentials.Name}
byId := map[string]interface{}{"id": credentials.Id}

resourceType := "env0_azure_oidc_credentials"
resourceName := "test_azure_oidc_credentials"
accessor := dataSourceAccessor(resourceType, resourceName)

getValidTestCase := func(input map[string]interface{}) resource.TestCase {
return resource.TestCase{
Steps: []resource.TestStep{
{
Config: dataSourceConfigCreate(resourceType, resourceName, input),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", credentials.Id),
resource.TestCheckResourceAttr(accessor, "name", credentials.Name),
),
},
},
}
}

getErrorTestCase := func(input map[string]interface{}, expectedError string) resource.TestCase {
return resource.TestCase{
Steps: []resource.TestStep{
{
Config: dataSourceConfigCreate(resourceType, resourceName, input),
ExpectError: regexp.MustCompile(expectedError),
},
},
}
}

mockGetCredentials := func(returnValue client.Credentials) func(mockFunc *client.MockApiClientInterface) {
return func(mock *client.MockApiClientInterface) {
mock.EXPECT().CloudCredentials(credentials.Id).AnyTimes().Return(returnValue, nil)
}
}

mockListCredentials := func(returnValue []client.Credentials) func(mockFunc *client.MockApiClientInterface) {
return func(mock *client.MockApiClientInterface) {
mock.EXPECT().CloudCredentialsList().AnyTimes().Return(returnValue, nil)
}
}

t.Run("by id", func(t *testing.T) {
runUnitTest(t,
getValidTestCase(byId),
mockGetCredentials(credentials),
)
})

t.Run("by name", func(t *testing.T) {
runUnitTest(t,
getValidTestCase(byName),
mockListCredentials([]client.Credentials{credentials, credentialsOther1, credentialsOther2}),
)
})

t.Run("throw error when no name or id is supplied", func(t *testing.T) {
runUnitTest(t,
getErrorTestCase(map[string]interface{}{}, "one of `id,name` must be specified"),
func(mock *client.MockApiClientInterface) {},
)
})

t.Run("throw error when by name and more than one is returned", func(t *testing.T) {
runUnitTest(t,
getErrorTestCase(byName, "found multiple credentials"),
mockListCredentials([]client.Credentials{credentials, credentialsOther1, credentialsOther2, credentials}),
)
})

t.Run("Throw error when by name and not found", func(t *testing.T) {
runUnitTest(t,
getErrorTestCase(byName, "not found"),
mockListCredentials([]client.Credentials{credentialsOther1, credentialsOther2}),
)
})

t.Run("Throw error when by id and not found", func(t *testing.T) {
runUnitTest(t,
getErrorTestCase(byId, fmt.Sprintf("id %s not found", credentials.Id)),
func(mock *client.MockApiClientInterface) {
mock.EXPECT().CloudCredentials(credentials.Id).AnyTimes().Return(client.Credentials{}, http.NewMockFailedResponseError(404))
},
)
})
}
55 changes: 55 additions & 0 deletions env0/data_oidc_credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package env0

import (
"context"
"fmt"

"github.com/env0/terraform-provider-env0/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataOidcCredentials(credentialsType CloudType) *schema.Resource {
return &schema.Resource{
ReadContext: dataOidcCredentialRead(credentialsType),

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Description: fmt.Sprintf("the name of the %s oidc credentials", credentialsType),
Optional: true,
ExactlyOneOf: []string{"name", "id"},
},
"id": {
Type: schema.TypeString,
Description: fmt.Sprintf("the id of the %s oidc credentials", credentialsType),
Optional: true,
ExactlyOneOf: []string{"name", "id"},
},
},
}
}

func dataOidcCredentialRead(credentialsType CloudType) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var credentials client.Credentials
var err error

id, ok := d.GetOk("id")
if ok {
credentials, err = getCredentialsById(id.(string), credentialsTypeToPrefixList[credentialsType], meta)
} else {
credentials, err = getCredentialsByName(d.Get("name").(string), credentialsTypeToPrefixList[credentialsType], meta)
}

if err != nil {
return DataGetFailure(fmt.Sprintf("%s oidc credentials", credentialsType), id, err)
}

if err := writeResourceData(&credentials, d); err != nil {
return diag.Errorf("schema resource data serialization failed: %v", err)
}

return nil
}
}
10 changes: 10 additions & 0 deletions env0/data_organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ func dataOrganization() *schema.Resource {
Description: "is the organization self hosted",
Computed: true,
},
"oidc_sub": {
Type: schema.TypeString,
Computed: true,
Description: "the jwt oidc sub",
},
},
}
}
Expand All @@ -49,5 +54,10 @@ func dataOrganizationRead(ctx context.Context, d *schema.ResourceData, meta inte
return diag.Errorf("schema resource data serialization failed: %v", err)
}

oidcSub, err := apiClient.OidcSub()
if err == nil {
d.Set("oidc_sub", oidcSub)
}

return nil
}
4 changes: 4 additions & 0 deletions env0/data_organization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ func TestUnitOrganizationData(t *testing.T) {
IsSelfHostedK8s: true,
}

oidcSub := "sub"

testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Expand All @@ -30,12 +32,14 @@ func TestUnitOrganizationData(t *testing.T) {
resource.TestCheckResourceAttr(accessor, "created_by", organization.CreatedBy),
resource.TestCheckResourceAttr(accessor, "role", organization.Role),
resource.TestCheckResourceAttr(accessor, "is_self_hosted", strconv.FormatBool(organization.IsSelfHostedK8s)),
resource.TestCheckResourceAttr(accessor, "oidc_sub", oidcSub),
),
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {
mock.EXPECT().Organization().AnyTimes().Return(organization, nil)
mock.EXPECT().OidcSub().AnyTimes().Return(oidcSub, nil)
})
}
3 changes: 2 additions & 1 deletion env0/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ func Provider(version string) plugin.ProviderFunc {
"env0_azure_cost_credentials": dataCredentials(AZURE_COST_TYPE),
"env0_google_cost_credentials": dataCredentials(GCP_COST_TYPE),
"env0_aws_credentials": dataCredentials(AWS_TYPE),
"env0_aws_oidc_credentials": dataAwsOidcCredentials(),
"env0_aws_oidc_credentials": dataOidcCredentials(AWS_OIDC_TYPE),
"env0_gcp_credentials": dataCredentials(GCP_TYPE),
"env0_azure_credentials": dataCredentials(AZURE_TYPE),
"env0_azure_oidc_credentials": dataOidcCredentials(AZURE_OIDC_TYPE),
"env0_team": dataTeam(),
"env0_teams": dataTeams(),
"env0_environment": dataEnvironment(),
Expand Down
5 changes: 5 additions & 0 deletions tests/integration/006_aws_credentials/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ resource "env0_aws_oidc_credentials" "oidc_credentials" {
duration = 7200
}

data "env0_aws_oidc_credentials" "oidc_credentials" {
name = "Test Oidc Credentials ${random_string.random.result}"
depends_on = [env0_aws_oidc_credentials.oidc_credentials]
}

output "name_by_arn" {
value = replace(data.env0_aws_credentials.my_role_by_arn.name, random_string.random.result, "")
}
Expand Down
5 changes: 5 additions & 0 deletions tests/integration/016_azure_credentials/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ resource "env0_azure_oidc_credentials" "oidc_credentials" {
tenant_id = "tenant_id"
}

data "env0_azure_oidc_credentials" "oidc_credentials" {
name = "test azure oidc credentials ${random_string.random.result}"
depends_on = [env0_azure_oidc_credentials.oidc_credentials]
}

data "env0_azure_credentials" "azure_cred" {
name = env0_azure_credentials.azure_cred.name
}
Expand Down

0 comments on commit fcf27bc

Please sign in to comment.