Skip to content

Commit 3bebbe1

Browse files
authored
1 parent fec4af7 commit 3bebbe1

File tree

8 files changed

+231
-23
lines changed

8 files changed

+231
-23
lines changed

example/main.tf

+2-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ terraform {
22
required_providers {
33
pomerium = {
44
source = "pomerium/pomerium"
5-
version = "0.0.7"
5+
version = "0.0.8"
66
}
77
}
88
}
@@ -249,15 +249,5 @@ data "pomerium_route" "existing_route" {
249249
id = pomerium_route.test_route.id
250250
}
251251

252-
# Output examples
253-
output "namespace_name" {
254-
value = data.pomerium_namespace.existing_namespace.name
255-
}
256-
257-
# output "route_from" {
258-
# value = data.pomerium_route.existing_route.from
259-
# }
252+
data "pomerium_routes" "all_routes" {}
260253

261-
output "all_namespaces" {
262-
value = data.pomerium_namespaces.all_namespaces.namespaces
263-
}

internal/provider/enum.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package provider
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/diag"
7+
"github.com/hashicorp/terraform-plugin-framework/types"
8+
"google.golang.org/protobuf/reflect/protoreflect"
9+
)
10+
11+
// GetValidEnumValues returns a list of valid enum values for a given protobuf enum type.
12+
// it includes zero value as well to match its use in the current api
13+
func GetValidEnumValues[T protoreflect.Enum]() []string {
14+
var values []string
15+
var v T
16+
descriptor := v.Descriptor()
17+
for i := 0; i < descriptor.Values().Len(); i++ {
18+
values = append(values, string(descriptor.Values().Get(i).Name()))
19+
}
20+
return values
21+
}
22+
23+
// EnumValueToPBWithDefault converts a string to a protobuf enum value.
24+
func EnumValueToPBWithDefault[T interface {
25+
~int32
26+
protoreflect.Enum
27+
}](
28+
dst *T,
29+
src types.String,
30+
defaultValue T,
31+
diagnostics *diag.Diagnostics,
32+
) {
33+
if src.IsNull() || src.ValueString() == "" {
34+
*dst = defaultValue
35+
return
36+
}
37+
38+
var v T
39+
enumValue := v.Descriptor().Values().ByName(protoreflect.Name(src.ValueString()))
40+
if enumValue == nil {
41+
diagnostics.AddError(
42+
"InvalidEnumValue",
43+
fmt.Sprintf("The provided %s enum value %q is not valid.", v.Descriptor().FullName(), src.ValueString()),
44+
)
45+
return
46+
}
47+
48+
*dst = T(enumValue.Number())
49+
}
50+
51+
func EnumValueFromPB[T interface {
52+
~int32
53+
protoreflect.Enum
54+
}](
55+
src T,
56+
) types.String {
57+
v := src.Descriptor().Values().ByNumber(protoreflect.EnumNumber(src))
58+
if v == nil {
59+
return types.StringNull()
60+
}
61+
return types.StringValue(string(v.Name()))
62+
}

internal/provider/enum_test.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package provider_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/diag"
7+
"github.com/hashicorp/terraform-plugin-framework/types"
8+
"github.com/pomerium/enterprise-client-go/pb"
9+
"github.com/pomerium/enterprise-terraform-provider/internal/provider"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestEnumValueToPB(t *testing.T) {
15+
t.Parallel()
16+
17+
defaultValue := pb.IssuerFormat(-1)
18+
tests := []struct {
19+
name types.String
20+
expect pb.IssuerFormat
21+
expectError bool
22+
}{
23+
{types.StringValue("IssuerHostOnly"), pb.IssuerFormat_IssuerHostOnly, false},
24+
{types.StringValue("IssuerURI"), pb.IssuerFormat_IssuerURI, false},
25+
{types.StringValue("InvalidInexistentTest"), pb.IssuerFormat(-2), true},
26+
{types.StringNull(), defaultValue, false},
27+
{types.StringValue(""), defaultValue, false},
28+
}
29+
30+
for _, tt := range tests {
31+
t.Run(tt.name.String(), func(t *testing.T) {
32+
var got pb.IssuerFormat
33+
var diagnostics diag.Diagnostics
34+
provider.EnumValueToPBWithDefault(&got, tt.name, defaultValue, &diagnostics)
35+
if tt.expectError {
36+
assert.True(t, diagnostics.HasError())
37+
} else {
38+
require.False(t, diagnostics.HasError(), diagnostics.Errors())
39+
assert.Equal(t, tt.expect, got)
40+
}
41+
})
42+
}
43+
}
44+
45+
func TestEnumValueFromPB(t *testing.T) {
46+
t.Parallel()
47+
48+
tests := []struct {
49+
name pb.IssuerFormat
50+
expect types.String
51+
}{
52+
{pb.IssuerFormat_IssuerHostOnly, types.StringValue("IssuerHostOnly")},
53+
{pb.IssuerFormat_IssuerURI, types.StringValue("IssuerURI")},
54+
{pb.IssuerFormat(-1), types.StringNull()},
55+
}
56+
57+
for _, tt := range tests {
58+
t.Run(tt.expect.String(), func(t *testing.T) {
59+
got := provider.EnumValueFromPB(tt.name)
60+
assert.Equal(t, tt.expect, got)
61+
})
62+
}
63+
}

internal/provider/route.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import (
55
"fmt"
66

77
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
8-
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
99
"github.com/hashicorp/terraform-plugin-framework/path"
1010
"github.com/hashicorp/terraform-plugin-framework/resource"
1111
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1212
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
1313
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
14+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1415
"github.com/hashicorp/terraform-plugin-framework/types"
1516
"github.com/hashicorp/terraform-plugin-log/tflog"
1617
client "github.com/pomerium/enterprise-client-go"
@@ -200,11 +201,12 @@ func (r *RouteResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
200201
Computed: true,
201202
},
202203
"jwt_groups_filter": JWTGroupsFilterSchema,
203-
"jwt_issuer_format": schema.ObjectAttribute{
204-
Description: "JWT issuer format configuration.",
204+
"jwt_issuer_format": schema.StringAttribute{
205205
Optional: true,
206-
AttributeTypes: map[string]attr.Type{
207-
"format": types.StringType,
206+
Computed: true,
207+
Description: "Format for JWT issuer strings. Use 'IssuerHostOnly' for hostname without scheme or trailing slash, or 'IssuerURI' for complete URI including scheme and trailing slash.",
208+
Validators: []validator.String{
209+
stringvalidator.OneOf(GetValidEnumValues[pb.IssuerFormat]()...),
208210
},
209211
},
210212
"rewrite_response_headers": schema.SetNestedAttribute{

internal/provider/route_data_source.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import (
55
"fmt"
66

77
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
8-
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
99
"github.com/hashicorp/terraform-plugin-framework/datasource"
1010
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
11+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1112
"github.com/hashicorp/terraform-plugin-framework/types"
1213

1314
client "github.com/pomerium/enterprise-client-go"
@@ -167,11 +168,12 @@ func getRouteDataSourceAttributes(idRequired bool) map[string]schema.Attribute {
167168
Description: "Show error details.",
168169
},
169170
"jwt_groups_filter": JWTGroupsFilterSchema,
170-
"jwt_issuer_format": schema.ObjectAttribute{
171-
Description: "JWT issuer format configuration.",
171+
"jwt_issuer_format": schema.StringAttribute{
172+
Optional: true,
172173
Computed: true,
173-
AttributeTypes: map[string]attr.Type{
174-
"format": types.StringType,
174+
Description: "Format for JWT issuer strings. Use 'IssuerHostOnly' for hostname without scheme or trailing slash, or 'IssuerURI' for complete URI including scheme and trailing slash.",
175+
Validators: []validator.String{
176+
stringvalidator.OneOf(GetValidEnumValues[pb.IssuerFormat]()...),
175177
},
176178
},
177179
"rewrite_response_headers": schema.SetNestedAttribute{

internal/provider/route_model.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type RouteModel struct {
2626
IDPClientID types.String `tfsdk:"idp_client_id"`
2727
IDPClientSecret types.String `tfsdk:"idp_client_secret"`
2828
JWTGroupsFilter types.Object `tfsdk:"jwt_groups_filter"`
29-
JWTIssuerFormat types.Object `tfsdk:"jwt_issuer_format"`
29+
JWTIssuerFormat types.String `tfsdk:"jwt_issuer_format"`
3030
KubernetesServiceAccountToken types.String `tfsdk:"kubernetes_service_account_token"`
3131
KubernetesServiceAccountTokenFile types.String `tfsdk:"kubernetes_service_account_token_file"`
3232
LogoURL types.String `tfsdk:"logo_url"`
@@ -174,6 +174,7 @@ func ConvertRouteToPB(
174174
pbRoute.EnableGoogleCloudServerlessAuthentication = src.EnableGoogleCloudServerlessAuthentication.ValueBool()
175175
}
176176
pbRoute.KubernetesServiceAccountTokenFile = src.KubernetesServiceAccountTokenFile.ValueStringPointer()
177+
EnumValueToPBWithDefault(&pbRoute.JwtIssuerFormat, src.JWTIssuerFormat, pb.IssuerFormat_IssuerHostOnly, &diagnostics)
177178

178179
pbRoute.RewriteResponseHeaders = rewriteHeadersToPB(src.RewriteResponseHeaders)
179180

@@ -232,6 +233,7 @@ func ConvertRouteFromPB(
232233
}
233234
dst.KubernetesServiceAccountTokenFile = types.StringPointerValue(src.KubernetesServiceAccountTokenFile)
234235

236+
dst.JWTIssuerFormat = EnumValueFromPB(src.JwtIssuerFormat)
235237
dst.RewriteResponseHeaders = rewriteHeadersFromPB(src.RewriteResponseHeaders)
236238

237239
return diagnostics

internal/provider/route_model_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package provider_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/types"
8+
"github.com/pomerium/enterprise-client-go/pb"
9+
"github.com/pomerium/enterprise-terraform-provider/internal/provider"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestConvertRouteFromPB(t *testing.T) {
15+
t.Run("jwt_issuer_format", func(t *testing.T) {
16+
testCases := []struct {
17+
name string
18+
input pb.IssuerFormat
19+
expected string
20+
isNull bool
21+
}{
22+
{
23+
name: "host_only",
24+
input: pb.IssuerFormat_IssuerHostOnly,
25+
expected: "IssuerHostOnly",
26+
},
27+
{
28+
name: "uri",
29+
input: pb.IssuerFormat_IssuerURI,
30+
expected: "IssuerURI",
31+
},
32+
{
33+
name: "invalid value",
34+
input: pb.IssuerFormat(999),
35+
isNull: true,
36+
},
37+
}
38+
39+
for _, tc := range testCases {
40+
t.Run(tc.name, func(t *testing.T) {
41+
m := &provider.RouteModel{}
42+
r := &pb.Route{
43+
JwtIssuerFormat: tc.input,
44+
}
45+
diags := provider.ConvertRouteFromPB(m, r)
46+
require.False(t, diags.HasError())
47+
if tc.isNull {
48+
assert.True(t, m.JWTIssuerFormat.IsNull())
49+
} else {
50+
assert.Equal(t, tc.expected, m.JWTIssuerFormat.ValueString())
51+
}
52+
})
53+
}
54+
})
55+
}
56+
57+
func TestConvertRouteToPB(t *testing.T) {
58+
t.Run("jwt_issuer_format", func(t *testing.T) {
59+
testCases := []struct {
60+
name string
61+
input string
62+
expected pb.IssuerFormat
63+
expectError bool
64+
}{
65+
{"host_only", "IssuerHostOnly", pb.IssuerFormat_IssuerHostOnly, false},
66+
{"uri", "IssuerURI", pb.IssuerFormat_IssuerURI, false},
67+
{"invalid_value", "invalid_value", -1, true},
68+
}
69+
70+
for _, tc := range testCases {
71+
t.Run(tc.name, func(t *testing.T) {
72+
m := &provider.RouteModel{
73+
JWTIssuerFormat: types.StringValue(tc.input),
74+
}
75+
r, diag := provider.ConvertRouteToPB(context.Background(), m)
76+
if tc.expectError {
77+
require.True(t, diag.HasError())
78+
} else {
79+
require.False(t, diag.HasError())
80+
assert.Equal(t, tc.expected, r.JwtIssuerFormat)
81+
}
82+
})
83+
}
84+
})
85+
}

internal/provider/route_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func TestConvertRoute(t *testing.T) {
5050
EnableGoogleCloudServerlessAuthentication: true,
5151
TlsCustomCaKeyPairId: P("custom-ca-1"),
5252
KubernetesServiceAccountTokenFile: P("/path/to/token"),
53+
JwtIssuerFormat: pb.IssuerFormat_IssuerURI,
5354
}
5455

5556
var actual provider.RouteResourceModel
@@ -108,6 +109,7 @@ func TestConvertRoute(t *testing.T) {
108109
EnableGoogleCloudServerlessAuthentication: types.BoolValue(true),
109110
TLSCustomCAKeyPairID: types.StringValue("custom-ca-1"),
110111
KubernetesServiceAccountTokenFile: types.StringValue("/path/to/token"),
112+
JWTIssuerFormat: types.StringValue("IssuerURI"),
111113
}
112114

113115
if diff := cmp.Diff(expected, actual); diff != "" {

0 commit comments

Comments
 (0)