Skip to content

Commit 30034dc

Browse files
authored
feat: add cache group data source (#610)
1 parent 71add20 commit 30034dc

5 files changed

Lines changed: 350 additions & 0 deletions

File tree

castai/data_source_cache_group.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package castai
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
10+
11+
"github.com/castai/terraform-provider-castai/castai/sdk"
12+
)
13+
14+
const FieldCacheGroupEndpointConnectionString = "connection_string"
15+
16+
func dataSourceCacheGroup() *schema.Resource {
17+
return &schema.Resource{
18+
ReadContext: dataSourceCacheGroupRead,
19+
Description: "Retrieve CAST AI DBO Cache Group by ID.",
20+
Schema: map[string]*schema.Schema{
21+
"id": {
22+
Type: schema.TypeString,
23+
Required: true,
24+
ValidateDiagFunc: validation.ToDiagFunc(validation.IsUUID),
25+
Description: "The unique identifier of the cache group.",
26+
},
27+
FieldCacheGroupName: {
28+
Type: schema.TypeString,
29+
Computed: true,
30+
Description: "Display name for the cache group.",
31+
},
32+
FieldCacheGroupProtocolType: {
33+
Type: schema.TypeString,
34+
Computed: true,
35+
Description: "Database protocol type. Values: MySQL or PostgreSQL.",
36+
},
37+
FieldCacheGroupDirectMode: {
38+
Type: schema.TypeBool,
39+
Computed: true,
40+
Description: "Direct mode for the cache group.",
41+
},
42+
FieldCacheGroupEndpoints: {
43+
Type: schema.TypeList,
44+
Computed: true,
45+
Description: "Connection endpoints for the cache group.",
46+
Elem: &schema.Resource{
47+
Schema: map[string]*schema.Schema{
48+
FieldCacheGroupEndpointHostname: {
49+
Type: schema.TypeString,
50+
Computed: true,
51+
Description: "Database instance hostname.",
52+
},
53+
FieldCacheGroupEndpointPort: {
54+
Type: schema.TypeInt,
55+
Computed: true,
56+
Description: "Database instance port.",
57+
},
58+
FieldCacheGroupEndpointName: {
59+
Type: schema.TypeString,
60+
Computed: true,
61+
Description: "Name for the endpoint.",
62+
},
63+
FieldCacheGroupEndpointConnectionString: {
64+
Type: schema.TypeString,
65+
Computed: true,
66+
Description: "Connection string for the endpoint.",
67+
},
68+
},
69+
},
70+
},
71+
},
72+
}
73+
}
74+
75+
func dataSourceCacheGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
76+
client := meta.(*ProviderConfig).api
77+
78+
id := d.Get("id").(string)
79+
80+
resp, err := client.DboAPIGetCacheGroupWithResponse(ctx, id, &sdk.DboAPIGetCacheGroupParams{})
81+
if err != nil {
82+
return diag.FromErr(fmt.Errorf("retrieving cache group: %w", err))
83+
}
84+
85+
if err := sdk.CheckOKResponse(resp, err); err != nil {
86+
return diag.FromErr(fmt.Errorf("retrieving cache group: %w", err))
87+
}
88+
89+
if resp.JSON200 == nil {
90+
return diag.FromErr(fmt.Errorf("cache group data not returned from API"))
91+
}
92+
93+
cacheGroup := resp.JSON200
94+
d.SetId(id)
95+
96+
name := ""
97+
if cacheGroup.Name != nil {
98+
name = *cacheGroup.Name
99+
}
100+
if err := d.Set(FieldCacheGroupName, name); err != nil {
101+
return diag.FromErr(fmt.Errorf("setting name: %w", err))
102+
}
103+
104+
if err := d.Set(FieldCacheGroupProtocolType, string(cacheGroup.ProtocolType)); err != nil {
105+
return diag.FromErr(fmt.Errorf("setting protocol_type: %w", err))
106+
}
107+
108+
if cacheGroup.DirectMode != nil {
109+
if err := d.Set(FieldCacheGroupDirectMode, *cacheGroup.DirectMode); err != nil {
110+
return diag.FromErr(fmt.Errorf("setting direct_mode: %w", err))
111+
}
112+
}
113+
114+
if cacheGroup.Endpoints != nil {
115+
endpoints := flattenEndpointsDataSource(*cacheGroup.Endpoints)
116+
if err := d.Set(FieldCacheGroupEndpoints, endpoints); err != nil {
117+
return diag.FromErr(fmt.Errorf("setting endpoints: %w", err))
118+
}
119+
}
120+
121+
return nil
122+
}
123+
124+
func flattenEndpointsDataSource(endpoints []sdk.DboV1Endpoint) []interface{} {
125+
if len(endpoints) == 0 {
126+
return nil
127+
}
128+
129+
result := make([]interface{}, 0, len(endpoints))
130+
for _, endpoint := range endpoints {
131+
item := map[string]any{
132+
FieldCacheGroupEndpointHostname: endpoint.Hostname,
133+
FieldCacheGroupEndpointPort: endpoint.Port,
134+
}
135+
136+
if endpoint.Suffix != nil {
137+
item[FieldCacheGroupEndpointName] = *endpoint.Suffix
138+
}
139+
140+
if endpoint.ConnectionString != nil {
141+
item[FieldCacheGroupEndpointConnectionString] = *endpoint.ConnectionString
142+
}
143+
144+
result = append(result, item)
145+
}
146+
147+
return result
148+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package castai
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"io"
8+
"net/http"
9+
"testing"
10+
11+
"github.com/golang/mock/gomock"
12+
"github.com/hashicorp/go-cty/cty"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
14+
"github.com/stretchr/testify/require"
15+
16+
"github.com/castai/terraform-provider-castai/castai/sdk"
17+
mock_sdk "github.com/castai/terraform-provider-castai/castai/sdk/mock"
18+
)
19+
20+
func TestCacheGroupDataSourceRead(t *testing.T) {
21+
t.Parallel()
22+
23+
r := require.New(t)
24+
mockClient := mock_sdk.NewMockClientInterface(gomock.NewController(t))
25+
26+
ctx := context.Background()
27+
provider := &ProviderConfig{
28+
api: &sdk.ClientWithResponses{
29+
ClientInterface: mockClient,
30+
},
31+
}
32+
33+
cacheGroupID := "cache-group-123"
34+
name := "test-cache-group"
35+
protocolType := "MySQL"
36+
directMode := true
37+
suffix := "primary"
38+
connectionString := "mysql://db.example.com:3306"
39+
40+
readResponse := sdk.DboV1CacheGroup{
41+
Id: &cacheGroupID,
42+
ProtocolType: sdk.DboV1CacheGroupProtocolType(protocolType),
43+
Name: &name,
44+
DirectMode: &directMode,
45+
Endpoints: &[]sdk.DboV1Endpoint{
46+
{
47+
Hostname: "db.example.com",
48+
Port: 3306,
49+
Suffix: &suffix,
50+
ConnectionString: &connectionString,
51+
},
52+
},
53+
}
54+
55+
readBody, _ := json.Marshal(readResponse)
56+
mockClient.EXPECT().
57+
DboAPIGetCacheGroup(ctx, cacheGroupID, gomock.Any()).
58+
Return(&http.Response{
59+
StatusCode: http.StatusOK,
60+
Header: http.Header{"Content-Type": []string{"application/json"}},
61+
Body: io.NopCloser(bytes.NewReader(readBody)),
62+
}, nil)
63+
64+
state := terraform.NewInstanceStateShimmedFromValue(cty.ObjectVal(map[string]cty.Value{}), 0)
65+
66+
resource := dataSourceCacheGroup()
67+
data := resource.Data(state)
68+
r.NoError(data.Set("id", cacheGroupID))
69+
70+
result := resource.ReadContext(ctx, data, provider)
71+
r.Nil(result)
72+
r.False(result.HasError())
73+
r.Equal(`ID = cache-group-123
74+
direct_mode = true
75+
endpoints.# = 1
76+
endpoints.0.connection_string = mysql://db.example.com:3306
77+
endpoints.0.hostname = db.example.com
78+
endpoints.0.name = primary
79+
endpoints.0.port = 3306
80+
name = test-cache-group
81+
protocol_type = MySQL
82+
Tainted = false
83+
`, data.State().String())
84+
}
85+
86+
func TestCacheGroupDataSourceReadEmptyName(t *testing.T) {
87+
t.Parallel()
88+
89+
r := require.New(t)
90+
mockClient := mock_sdk.NewMockClientInterface(gomock.NewController(t))
91+
92+
ctx := context.Background()
93+
provider := &ProviderConfig{
94+
api: &sdk.ClientWithResponses{
95+
ClientInterface: mockClient,
96+
},
97+
}
98+
99+
cacheGroupID := "cache-group-123"
100+
protocolType := "PostgreSQL"
101+
102+
readResponse := sdk.DboV1CacheGroup{
103+
Id: &cacheGroupID,
104+
ProtocolType: sdk.DboV1CacheGroupProtocolType(protocolType),
105+
Name: nil,
106+
}
107+
108+
readBody, _ := json.Marshal(readResponse)
109+
mockClient.EXPECT().
110+
DboAPIGetCacheGroup(ctx, cacheGroupID, gomock.Any()).
111+
Return(&http.Response{
112+
StatusCode: http.StatusOK,
113+
Header: http.Header{"Content-Type": []string{"application/json"}},
114+
Body: io.NopCloser(bytes.NewReader(readBody)),
115+
}, nil)
116+
117+
state := terraform.NewInstanceStateShimmedFromValue(cty.ObjectVal(map[string]cty.Value{}), 0)
118+
119+
resource := dataSourceCacheGroup()
120+
data := resource.Data(state)
121+
r.NoError(data.Set("id", cacheGroupID))
122+
123+
result := resource.ReadContext(ctx, data, provider)
124+
r.Nil(result)
125+
r.False(result.HasError())
126+
r.Equal("ID = cache-group-123\nname = \nprotocol_type = PostgreSQL\nTainted = false\n", data.State().String())
127+
}
128+
129+
func TestCacheGroupDataSourceReadNotFound(t *testing.T) {
130+
t.Parallel()
131+
132+
r := require.New(t)
133+
mockClient := mock_sdk.NewMockClientInterface(gomock.NewController(t))
134+
135+
ctx := context.Background()
136+
provider := &ProviderConfig{
137+
api: &sdk.ClientWithResponses{
138+
ClientInterface: mockClient,
139+
},
140+
}
141+
142+
cacheGroupID := "non-existent-id"
143+
144+
mockClient.EXPECT().
145+
DboAPIGetCacheGroup(ctx, cacheGroupID, gomock.Any()).
146+
Return(&http.Response{
147+
StatusCode: http.StatusNotFound,
148+
Header: http.Header{"Content-Type": []string{"application/json"}},
149+
Body: io.NopCloser(bytes.NewReader([]byte(`{"message": "not found"}`))),
150+
}, nil)
151+
152+
state := terraform.NewInstanceStateShimmedFromValue(cty.ObjectVal(map[string]cty.Value{}), 0)
153+
154+
resource := dataSourceCacheGroup()
155+
data := resource.Data(state)
156+
r.NoError(data.Set("id", cacheGroupID))
157+
158+
result := resource.ReadContext(ctx, data, provider)
159+
r.True(result.HasError())
160+
}

castai/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func Provider(version string) *schema.Provider {
8383
"castai_rebalancing_schedule": dataSourceRebalancingSchedule(),
8484
"castai_hibernation_schedule": dataSourceHibernationSchedule(),
8585
"castai_workload_scaling_policy_order": dataSourceWorkloadScalingPolicyOrder(),
86+
"castai_cache_group": dataSourceCacheGroup(),
8687
},
8788

8889
ConfigureContextFunc: providerConfigure(version),

castai/sdk/api.gen.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/data-sources/cache_group.md

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)