Skip to content

11 files changed

+937
-2
lines changed

example/main.tf

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ resource "pomerium_namespace" "test_namespace" {
2020
parent_id = "9d8dbd2c-8cce-4e66-9c1f-c490b4a07243"
2121
}
2222

23+
resource "pomerium_settings" "settings" {
24+
installation_id = "localhost-dev"
25+
}
26+
2327
resource "pomerium_policy" "test_policy" {
2428
name = "test-policy"
2529
namespace_id = pomerium_namespace.test_namespace.id

go.mod

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212
github.com/rs/zerolog v1.33.0
1313
github.com/stretchr/testify v1.10.0
1414
google.golang.org/grpc v1.68.0
15+
google.golang.org/protobuf v1.35.2
1516
)
1617

1718
require (
@@ -26,7 +27,6 @@ require (
2627
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
2728
github.com/fatih/color v1.14.1 // indirect
2829
github.com/golang/protobuf v1.5.4 // indirect
29-
github.com/google/go-cmp v0.6.0 // indirect
3030
github.com/google/uuid v1.6.0 // indirect
3131
github.com/hashicorp/errwrap v1.1.0 // indirect
3232
github.com/hashicorp/go-hclog v1.5.0 // indirect
@@ -69,7 +69,6 @@ require (
6969
golang.org/x/tools v0.25.0 // indirect
7070
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
7171
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f // indirect
72-
google.golang.org/protobuf v1.35.2 // indirect
7372
gopkg.in/yaml.v3 v3.0.1 // indirect
7473
sigs.k8s.io/yaml v1.4.0 // indirect
7574
)

internal/provider/convert.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework/diag"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
"github.com/pomerium/enterprise-client-go/pb"
11+
"google.golang.org/protobuf/types/known/durationpb"
12+
)
13+
14+
func FromStringSlice(slice []string) types.List {
15+
if slice == nil {
16+
return types.ListNull(types.StringType)
17+
}
18+
fields := make([]attr.Value, 0)
19+
for _, v := range slice {
20+
fields = append(fields, types.StringValue(v))
21+
}
22+
return types.ListValueMust(types.StringType, fields)
23+
}
24+
25+
// FromStringList converts a Settings_StringList to a types.List
26+
func FromStringList(sl *pb.Settings_StringList) types.List {
27+
if sl == nil {
28+
return types.ListNull(types.StringType)
29+
}
30+
return FromStringSlice(sl.Values)
31+
}
32+
33+
// FromStringMap converts a map[string]string to a types.Map
34+
func FromStringMap(m map[string]string) types.Map {
35+
if m == nil {
36+
return types.MapNull(types.StringType)
37+
}
38+
elements := make(map[string]attr.Value)
39+
for k, v := range m {
40+
elements[k] = types.StringValue(v)
41+
}
42+
return types.MapValueMust(types.StringType, elements)
43+
}
44+
45+
// ToStringList converts a types.List to Settings_StringList and handles diagnostics internally
46+
func ToStringList(ctx context.Context, dst **pb.Settings_StringList, list types.List, diagnostics *diag.Diagnostics) {
47+
// Handle null list case first
48+
if list.IsNull() {
49+
*dst = nil
50+
return
51+
}
52+
53+
var values []string
54+
diagnostics.Append(list.ElementsAs(ctx, &values, false)...)
55+
if !diagnostics.HasError() {
56+
*dst = &pb.Settings_StringList{Values: values}
57+
}
58+
}
59+
60+
// ToStringMap converts a types.Map to map[string]string and handles diagnostics internally
61+
func ToStringMap(ctx context.Context, dst *map[string]string, m types.Map, diagnostics *diag.Diagnostics) {
62+
if m.IsNull() {
63+
*dst = nil
64+
return
65+
}
66+
67+
result := make(map[string]string)
68+
diagnostics.Append(m.ElementsAs(ctx, &result, false)...)
69+
if !diagnostics.HasError() {
70+
*dst = result
71+
}
72+
}
73+
74+
// ToStringSlice converts a types.List to string slice and handles diagnostics internally
75+
func ToStringSlice(ctx context.Context, dst *[]string, list types.List, diagnostics *diag.Diagnostics) {
76+
*dst = make([]string, 0)
77+
if !list.IsNull() {
78+
var values []string
79+
diagnostics.Append(list.ElementsAs(ctx, &values, false)...)
80+
if !diagnostics.HasError() {
81+
*dst = values
82+
}
83+
}
84+
}
85+
86+
// ToDuration converts a types.String containing a duration to a durationpb.Duration and handles diagnostics internally
87+
func ToDuration(dst **durationpb.Duration, src types.String, field string, diagnostics *diag.Diagnostics) {
88+
if src.IsNull() {
89+
*dst = nil
90+
return
91+
}
92+
93+
if d, err := time.ParseDuration(src.ValueString()); err == nil {
94+
*dst = durationpb.New(d)
95+
} else {
96+
diagnostics.AddError("invalid "+field, err.Error())
97+
}
98+
}
99+
100+
// FromDuration converts a durationpb.Duration to a types.String
101+
func FromDuration(d *durationpb.Duration) types.String {
102+
if d == nil {
103+
return types.StringNull()
104+
}
105+
return types.StringValue(d.AsDuration().String())
106+
}

internal/provider/convert_test.go

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package provider_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/hashicorp/terraform-plugin-framework/attr"
9+
"github.com/hashicorp/terraform-plugin-framework/diag"
10+
"github.com/hashicorp/terraform-plugin-framework/types"
11+
"github.com/pomerium/enterprise-client-go/pb"
12+
"github.com/pomerium/enterprise-terraform-provider/internal/provider"
13+
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/require"
15+
"google.golang.org/protobuf/types/known/durationpb"
16+
)
17+
18+
func TestFromStringSlice(t *testing.T) {
19+
tests := []struct {
20+
name string
21+
input []string
22+
expected types.List
23+
}{
24+
{
25+
name: "nil slice",
26+
input: nil,
27+
expected: types.ListNull(types.StringType),
28+
},
29+
{
30+
name: "empty slice",
31+
input: []string{},
32+
expected: types.ListValueMust(types.StringType, []attr.Value{}),
33+
},
34+
{
35+
name: "normal slice",
36+
input: []string{"a", "b", "c"},
37+
expected: types.ListValueMust(types.StringType, []attr.Value{
38+
types.StringValue("a"),
39+
types.StringValue("b"),
40+
types.StringValue("c"),
41+
}),
42+
},
43+
}
44+
45+
for _, tt := range tests {
46+
t.Run(tt.name, func(t *testing.T) {
47+
result := provider.FromStringSlice(tt.input)
48+
assert.Equal(t, tt.expected, result)
49+
})
50+
}
51+
}
52+
53+
func TestFromDurationP(t *testing.T) {
54+
tests := []struct {
55+
name string
56+
input *durationpb.Duration
57+
expected types.String
58+
}{
59+
{
60+
name: "nil duration",
61+
input: nil,
62+
expected: types.StringNull(),
63+
},
64+
{
65+
name: "zero duration",
66+
input: durationpb.New(0),
67+
expected: types.StringValue("0s"),
68+
},
69+
{
70+
name: "normal duration",
71+
input: durationpb.New(time.Hour + time.Minute),
72+
expected: types.StringValue("1h1m0s"),
73+
},
74+
}
75+
76+
for _, tt := range tests {
77+
t.Run(tt.name, func(t *testing.T) {
78+
result := provider.FromDuration(tt.input)
79+
assert.Equal(t, tt.expected, result)
80+
})
81+
}
82+
}
83+
84+
func TestToStringList(t *testing.T) {
85+
ctx := context.Background()
86+
tests := []struct {
87+
name string
88+
input types.List
89+
expectError bool
90+
validate func(*testing.T, *pb.Settings_StringList)
91+
}{
92+
{
93+
name: "null list",
94+
input: types.ListNull(types.StringType),
95+
validate: func(t *testing.T, s *pb.Settings_StringList) {
96+
assert.Nil(t, s)
97+
},
98+
},
99+
{
100+
name: "empty list",
101+
input: types.ListValueMust(types.StringType, []attr.Value{}),
102+
validate: func(t *testing.T, s *pb.Settings_StringList) {
103+
require.NotNil(t, s)
104+
assert.Empty(t, s.Values)
105+
},
106+
},
107+
{
108+
name: "valid list",
109+
input: types.ListValueMust(types.StringType, []attr.Value{
110+
types.StringValue("value1"),
111+
types.StringValue("value2"),
112+
}),
113+
validate: func(t *testing.T, s *pb.Settings_StringList) {
114+
require.NotNil(t, s)
115+
assert.Equal(t, []string{"value1", "value2"}, s.Values)
116+
},
117+
},
118+
}
119+
120+
for _, tt := range tests {
121+
t.Run(tt.name, func(t *testing.T) {
122+
var result *pb.Settings_StringList
123+
diagnostics := diag.Diagnostics{}
124+
provider.ToStringList(ctx, &result, tt.input, &diagnostics)
125+
126+
if tt.expectError {
127+
assert.True(t, diagnostics.HasError())
128+
return
129+
}
130+
131+
assert.False(t, diagnostics.HasError())
132+
if tt.validate != nil {
133+
tt.validate(t, result)
134+
}
135+
})
136+
}
137+
}

internal/provider/help/settings.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Global Pomerium Settings
2+
3+
The settings are global object.
4+

internal/provider/helpers_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,8 @@ func TestConfigureClient(t *testing.T) {
8585
})
8686
}
8787
}
88+
89+
// helper function to create pointers
90+
func ptr[T any](v T) *T {
91+
return &v
92+
}

internal/provider/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ func (p *PomeriumProvider) Resources(_ context.Context) []func() resource.Resour
122122
NewNamespaceResource,
123123
NewRouteResource,
124124
NewPolicyResource,
125+
NewSettingsResource,
125126
NewServiceAccountResource,
126127
}
127128
}

0 commit comments

Comments
 (0)