Skip to content

Commit b5d55c8

Browse files
authored
Add Data Sources for existing resources (#7)
1 parent ba1ae8f commit b5d55c8

19 files changed

+1429
-168
lines changed

example/main.tf

+24-9
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,15 @@ provider "pomerium" {
1414
shared_secret_b64 = "9OkZR6hwfmVD3a7Sfmgq58lUbFJGGz4hl/R9xbHFCAg="
1515
}
1616

17-
# resource "pomerium_namespace" "test_namespace" {
18-
# name = "test-namespace"
19-
# parent_id = "9d8dbd2c-8cce-4e66-9c1f-c490b4a07243"
20-
# }
21-
22-
locals {
23-
namespace_id = "9d8dbd2c-8cce-4e66-9c1f-c490b4a07243"
17+
# Create resources
18+
resource "pomerium_namespace" "test_namespace" {
19+
name = "test-namespace"
20+
parent_id = "9d8dbd2c-8cce-4e66-9c1f-c490b4a07243"
2421
}
2522

2623
resource "pomerium_policy" "test_policy" {
2724
name = "test-policy"
28-
namespace_id = local.namespace_id
25+
namespace_id = pomerium_namespace.test_namespace.id
2926
ppl = <<EOF
3027
- allow:
3128
and:
@@ -35,8 +32,26 @@ EOF
3532

3633
resource "pomerium_route" "test_route" {
3734
name = "test-route"
38-
namespace_id = local.namespace_id
35+
namespace_id = pomerium_namespace.test_namespace.id
3936
from = "https://verify-tf.localhost.pomerium.io"
4037
to = ["https://verify.pomerium.com"]
4138
policies = [pomerium_policy.test_policy.id]
4239
}
40+
41+
# Data source examples
42+
data "pomerium_namespace" "existing_namespace" {
43+
id = "9d8dbd2c-8cce-4e66-9c1f-c490b4a07243"
44+
}
45+
46+
data "pomerium_route" "existing_route" {
47+
id = pomerium_route.test_route.id
48+
}
49+
50+
# Output examples
51+
output "namespace_name" {
52+
value = data.pomerium_namespace.existing_namespace.name
53+
}
54+
55+
output "route_from" {
56+
value = data.pomerium_route.existing_route.from
57+
}

internal/provider/bootstrap_service_account.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"google.golang.org/grpc/status"
1212
)
1313

14-
func generateBootstrapServiceAccountToken(
14+
func GenerateBootstrapServiceAccountToken(
1515
sharedSecretB64 string,
1616
) (string, error) {
1717
sharedSecret, err := base64.StdEncoding.DecodeString(sharedSecretB64)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package provider_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/pomerium/enterprise-terraform-provider/internal/provider"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestGenerateBootstrapServiceAccountToken(t *testing.T) {
13+
t.Parallel()
14+
15+
tests := []struct {
16+
name string
17+
sharedSecret string
18+
expectError bool
19+
validateToken func(t *testing.T, token string)
20+
}{
21+
{
22+
name: "invalid base64",
23+
sharedSecret: "not-base64",
24+
expectError: true,
25+
},
26+
{
27+
name: "valid base64",
28+
sharedSecret: "dGVzdC1zZWNyZXQ=", // "test-secret" in base64
29+
expectError: false,
30+
validateToken: func(t *testing.T, token string) {
31+
// JWT format: header.payload.signature
32+
parts := strings.Split(token, ".")
33+
require.Len(t, parts, 3)
34+
},
35+
},
36+
}
37+
38+
for _, tt := range tests {
39+
t.Run(tt.name, func(t *testing.T) {
40+
t.Parallel()
41+
42+
token, err := provider.GenerateBootstrapServiceAccountToken(tt.sharedSecret)
43+
if tt.expectError {
44+
assert.Error(t, err)
45+
return
46+
}
47+
48+
require.NoError(t, err)
49+
assert.NotEmpty(t, token)
50+
51+
if tt.validateToken != nil {
52+
tt.validateToken(t, token)
53+
}
54+
})
55+
}
56+
}

internal/provider/helpers.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/datasource"
8+
"github.com/hashicorp/terraform-plugin-framework/path"
9+
"github.com/hashicorp/terraform-plugin-framework/resource"
10+
client "github.com/pomerium/enterprise-client-go"
11+
)
12+
13+
// ConfigureClient is a helper to configure resources and data sources with the API client
14+
func ConfigureClient(req any, resp any) *client.Client {
15+
var providerData any
16+
switch r := req.(type) {
17+
case datasource.ConfigureRequest:
18+
providerData = r.ProviderData
19+
case resource.ConfigureRequest:
20+
providerData = r.ProviderData
21+
}
22+
23+
if providerData == nil {
24+
return nil
25+
}
26+
27+
client, ok := providerData.(*client.Client)
28+
if !ok {
29+
switch r := resp.(type) {
30+
case *datasource.ConfigureResponse:
31+
r.Diagnostics.AddError(
32+
"Unexpected Data Source Configure Type",
33+
fmt.Sprintf("Expected *client.Client, got: %T.", providerData),
34+
)
35+
case *resource.ConfigureResponse:
36+
r.Diagnostics.AddError(
37+
"Unexpected Resource Configure Type",
38+
fmt.Sprintf("Expected *client.Client, got: %T.", providerData),
39+
)
40+
}
41+
return nil
42+
}
43+
44+
return client
45+
}
46+
47+
// ImportStatePassthroughID is a helper that implements the common import state pattern
48+
func ImportStatePassthroughID(req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
49+
resp.Diagnostics.Append(resp.State.SetAttribute(context.Background(), path.Root("id"), req.ID)...)
50+
}

internal/provider/helpers_test.go

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package provider_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/datasource"
7+
"github.com/hashicorp/terraform-plugin-framework/resource"
8+
client "github.com/pomerium/enterprise-client-go"
9+
"github.com/pomerium/enterprise-terraform-provider/internal/provider"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestConfigureClient(t *testing.T) {
14+
t.Parallel()
15+
16+
mockClient := &client.Client{}
17+
18+
tests := []struct {
19+
name string
20+
req any
21+
resp any
22+
expectedClient *client.Client
23+
expectError bool
24+
}{
25+
{
26+
name: "valid datasource request",
27+
req: datasource.ConfigureRequest{
28+
ProviderData: mockClient,
29+
},
30+
resp: &datasource.ConfigureResponse{},
31+
expectedClient: mockClient,
32+
expectError: false,
33+
},
34+
{
35+
name: "valid resource request",
36+
req: resource.ConfigureRequest{
37+
ProviderData: mockClient,
38+
},
39+
resp: &resource.ConfigureResponse{},
40+
expectedClient: mockClient,
41+
expectError: false,
42+
},
43+
{
44+
name: "nil provider data",
45+
req: datasource.ConfigureRequest{
46+
ProviderData: nil,
47+
},
48+
resp: &datasource.ConfigureResponse{},
49+
expectedClient: nil,
50+
expectError: false,
51+
},
52+
{
53+
name: "invalid provider data type - datasource",
54+
req: datasource.ConfigureRequest{
55+
ProviderData: "invalid",
56+
},
57+
resp: &datasource.ConfigureResponse{},
58+
expectedClient: nil,
59+
expectError: true,
60+
},
61+
{
62+
name: "invalid provider data type - resource",
63+
req: resource.ConfigureRequest{
64+
ProviderData: "invalid",
65+
},
66+
resp: &resource.ConfigureResponse{},
67+
expectedClient: nil,
68+
expectError: true,
69+
},
70+
}
71+
72+
for _, tt := range tests {
73+
t.Run(tt.name, func(t *testing.T) {
74+
t.Parallel()
75+
76+
client := provider.ConfigureClient(tt.req, tt.resp)
77+
assert.Equal(t, tt.expectedClient, client)
78+
79+
switch resp := tt.resp.(type) {
80+
case *datasource.ConfigureResponse:
81+
assert.Equal(t, tt.expectError, resp.Diagnostics.HasError())
82+
case *resource.ConfigureResponse:
83+
assert.Equal(t, tt.expectError, resp.Diagnostics.HasError())
84+
}
85+
})
86+
}
87+
}

0 commit comments

Comments
 (0)