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

Add Data Sources for existing resources #7

Merged
merged 11 commits into from
Dec 12, 2024
33 changes: 24 additions & 9 deletions example/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,15 @@ provider "pomerium" {
shared_secret_b64 = "9OkZR6hwfmVD3a7Sfmgq58lUbFJGGz4hl/R9xbHFCAg="
}

# resource "pomerium_namespace" "test_namespace" {
# name = "test-namespace"
# parent_id = "9d8dbd2c-8cce-4e66-9c1f-c490b4a07243"
# }

locals {
namespace_id = "9d8dbd2c-8cce-4e66-9c1f-c490b4a07243"
# Create resources
resource "pomerium_namespace" "test_namespace" {
name = "test-namespace"
parent_id = "9d8dbd2c-8cce-4e66-9c1f-c490b4a07243"
}

resource "pomerium_policy" "test_policy" {
name = "test-policy"
namespace_id = local.namespace_id
namespace_id = pomerium_namespace.test_namespace.id
ppl = <<EOF
- allow:
and:
Expand All @@ -35,8 +32,26 @@ EOF

resource "pomerium_route" "test_route" {
name = "test-route"
namespace_id = local.namespace_id
namespace_id = pomerium_namespace.test_namespace.id
from = "https://verify-tf.localhost.pomerium.io"
to = ["https://verify.pomerium.com"]
policies = [pomerium_policy.test_policy.id]
}

# Data source examples
data "pomerium_namespace" "existing_namespace" {
id = "9d8dbd2c-8cce-4e66-9c1f-c490b4a07243"
}

data "pomerium_route" "existing_route" {
id = pomerium_route.test_route.id
}

# Output examples
output "namespace_name" {
value = data.pomerium_namespace.existing_namespace.name
}

output "route_from" {
value = data.pomerium_route.existing_route.from
}
2 changes: 1 addition & 1 deletion internal/provider/bootstrap_service_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"google.golang.org/grpc/status"
)

func generateBootstrapServiceAccountToken(
func GenerateBootstrapServiceAccountToken(
sharedSecretB64 string,
) (string, error) {
sharedSecret, err := base64.StdEncoding.DecodeString(sharedSecretB64)
Expand Down
56 changes: 56 additions & 0 deletions internal/provider/bootstrap_service_account_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package provider_test

import (
"strings"
"testing"

"github.com/pomerium/enterprise-terraform-provider/internal/provider"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGenerateBootstrapServiceAccountToken(t *testing.T) {
t.Parallel()

tests := []struct {
name string
sharedSecret string
expectError bool
validateToken func(t *testing.T, token string)
}{
{
name: "invalid base64",
sharedSecret: "not-base64",
expectError: true,
},
{
name: "valid base64",
sharedSecret: "dGVzdC1zZWNyZXQ=", // "test-secret" in base64
expectError: false,
validateToken: func(t *testing.T, token string) {
// JWT format: header.payload.signature
parts := strings.Split(token, ".")
require.Len(t, parts, 3)
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

token, err := provider.GenerateBootstrapServiceAccountToken(tt.sharedSecret)
if tt.expectError {
assert.Error(t, err)
return
}

require.NoError(t, err)
assert.NotEmpty(t, token)

if tt.validateToken != nil {
tt.validateToken(t, token)
}
})
}
}
50 changes: 50 additions & 0 deletions internal/provider/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package provider

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
client "github.com/pomerium/enterprise-client-go"
)

// ConfigureClient is a helper to configure resources and data sources with the API client
func ConfigureClient(req any, resp any) *client.Client {
var providerData any
switch r := req.(type) {
case datasource.ConfigureRequest:
providerData = r.ProviderData
case resource.ConfigureRequest:
providerData = r.ProviderData
}

if providerData == nil {
return nil
}

client, ok := providerData.(*client.Client)
if !ok {
switch r := resp.(type) {
case *datasource.ConfigureResponse:
r.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *client.Client, got: %T.", providerData),
)
case *resource.ConfigureResponse:
r.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *client.Client, got: %T.", providerData),
)
}
return nil
}

return client
}

// ImportStatePassthroughID is a helper that implements the common import state pattern
func ImportStatePassthroughID(req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resp.Diagnostics.Append(resp.State.SetAttribute(context.Background(), path.Root("id"), req.ID)...)
}
87 changes: 87 additions & 0 deletions internal/provider/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package provider_test

import (
"testing"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/resource"
client "github.com/pomerium/enterprise-client-go"
"github.com/pomerium/enterprise-terraform-provider/internal/provider"
"github.com/stretchr/testify/assert"
)

func TestConfigureClient(t *testing.T) {
t.Parallel()

mockClient := &client.Client{}

tests := []struct {
name string
req any
resp any
expectedClient *client.Client
expectError bool
}{
{
name: "valid datasource request",
req: datasource.ConfigureRequest{
ProviderData: mockClient,
},
resp: &datasource.ConfigureResponse{},
expectedClient: mockClient,
expectError: false,
},
{
name: "valid resource request",
req: resource.ConfigureRequest{
ProviderData: mockClient,
},
resp: &resource.ConfigureResponse{},
expectedClient: mockClient,
expectError: false,
},
{
name: "nil provider data",
req: datasource.ConfigureRequest{
ProviderData: nil,
},
resp: &datasource.ConfigureResponse{},
expectedClient: nil,
expectError: false,
},
{
name: "invalid provider data type - datasource",
req: datasource.ConfigureRequest{
ProviderData: "invalid",
},
resp: &datasource.ConfigureResponse{},
expectedClient: nil,
expectError: true,
},
{
name: "invalid provider data type - resource",
req: resource.ConfigureRequest{
ProviderData: "invalid",
},
resp: &resource.ConfigureResponse{},
expectedClient: nil,
expectError: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

client := provider.ConfigureClient(tt.req, tt.resp)
assert.Equal(t, tt.expectedClient, client)

switch resp := tt.resp.(type) {
case *datasource.ConfigureResponse:
assert.Equal(t, tt.expectError, resp.Diagnostics.HasError())
case *resource.ConfigureResponse:
assert.Equal(t, tt.expectError, resp.Diagnostics.HasError())
}
})
}
}
Loading
Loading