Skip to content

Commit 51f2b8f

Browse files
furkhatFurkhat Kasymov Genii Uuludvoros-castai
authored
feat: datasource castai_impersonation_service_account (#623)
* feat: castai_impersonation_service_account to use for secretless onboarding (currently only AKS) * re generate sdk --------- Co-authored-by: Furkhat Kasymov Genii Uulu <furkhat@cast.ai> Co-authored-by: Daniel Voros <daniel.voros@cast.ai>
1 parent 5188973 commit 51f2b8f

File tree

4 files changed

+192
-7
lines changed

4 files changed

+192
-7
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
10+
"github.com/castai/terraform-provider-castai/castai/sdk"
11+
)
12+
13+
const (
14+
FieldImpersonationServiceAccountId = "id"
15+
)
16+
17+
func dataSourceImpersonationServiceAccount() *schema.Resource {
18+
return &schema.Resource{
19+
ReadContext: dataSourceImpersonationServiceAccountRead,
20+
Description: "Retrieve impersonation service account ID for AKS clusters",
21+
Schema: map[string]*schema.Schema{
22+
FieldImpersonationServiceAccountId: {
23+
Type: schema.TypeString,
24+
Computed: true,
25+
Description: "ID of the impersonation service account",
26+
},
27+
},
28+
}
29+
}
30+
31+
func dataSourceImpersonationServiceAccountRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
32+
client := meta.(*ProviderConfig).api
33+
34+
provider := "aks"
35+
resp, err := client.ExternalClusterAPIImpersonationServiceAccountWithResponse(ctx,
36+
sdk.ExternalclusterV1ImpersonationServiceAccountRequest{
37+
Provider: &provider,
38+
})
39+
if err := sdk.CheckOKResponse(resp, err); err != nil {
40+
return diag.FromErr(fmt.Errorf("retrieving impersonation service account: %w", err))
41+
}
42+
43+
if resp.JSON200 == nil {
44+
return diag.FromErr(fmt.Errorf("empty response received from impersonation service account API"))
45+
}
46+
47+
if resp.JSON200.Id != nil {
48+
data.SetId(*resp.JSON200.Id)
49+
if err := data.Set(FieldImpersonationServiceAccountId, *resp.JSON200.Id); err != nil {
50+
return diag.FromErr(fmt.Errorf("setting id: %w", err))
51+
}
52+
}
53+
54+
return nil
55+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package castai
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"io"
7+
"net/http"
8+
"testing"
9+
10+
"github.com/golang/mock/gomock"
11+
"github.com/hashicorp/go-cty/cty"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
13+
"github.com/stretchr/testify/require"
14+
15+
"github.com/castai/terraform-provider-castai/castai/sdk"
16+
mock_sdk "github.com/castai/terraform-provider-castai/castai/sdk/mock"
17+
)
18+
19+
func TestImpersonationServiceAccountDataSourceRead(t *testing.T) {
20+
t.Parallel()
21+
22+
tests := []struct {
23+
name string
24+
responseBody string
25+
statusCode int
26+
expectedID string
27+
expectedError bool
28+
errorContains string
29+
}{
30+
{
31+
name: "successful response with id and email",
32+
responseBody: `{
33+
"id": "test-service-account-id",
34+
"email": "test@example.com"
35+
}`,
36+
statusCode: 200,
37+
expectedID: "test-service-account-id",
38+
expectedError: false,
39+
},
40+
{
41+
name: "successful response with id only",
42+
responseBody: `{
43+
"id": "another-service-account-id"
44+
}`,
45+
statusCode: 200,
46+
expectedID: "another-service-account-id",
47+
expectedError: false,
48+
},
49+
{
50+
name: "api error response",
51+
responseBody: `{"message": "Internal server error"}`,
52+
statusCode: 500,
53+
expectedError: true,
54+
errorContains: "retrieving impersonation service account",
55+
},
56+
{
57+
name: "unauthorized response",
58+
responseBody: `{"message": "Unauthorized"}`,
59+
statusCode: 401,
60+
expectedError: true,
61+
errorContains: "retrieving impersonation service account",
62+
},
63+
}
64+
65+
for _, tt := range tests {
66+
t.Run(tt.name, func(t *testing.T) {
67+
r := require.New(t)
68+
mockClient := mock_sdk.NewMockClientInterface(gomock.NewController(t))
69+
70+
ctx := context.Background()
71+
provider := &ProviderConfig{
72+
api: &sdk.ClientWithResponses{
73+
ClientInterface: mockClient,
74+
},
75+
}
76+
77+
body := io.NopCloser(bytes.NewReader([]byte(tt.responseBody)))
78+
79+
mockClient.EXPECT().
80+
ExternalClusterAPIImpersonationServiceAccount(gomock.Any(), gomock.Any(), gomock.Any()).
81+
Return(&http.Response{
82+
StatusCode: tt.statusCode,
83+
Body: body,
84+
Header: map[string][]string{"Content-Type": {"json"}},
85+
}, nil)
86+
87+
state := terraform.NewInstanceStateShimmedFromValue(cty.ObjectVal(map[string]cty.Value{}), 0)
88+
89+
resource := dataSourceImpersonationServiceAccount()
90+
data := resource.Data(state)
91+
92+
result := resource.ReadContext(ctx, data, provider)
93+
94+
if tt.expectedError {
95+
r.True(result.HasError())
96+
if tt.errorContains != "" {
97+
r.Contains(result[0].Summary, tt.errorContains)
98+
}
99+
} else {
100+
r.Nil(result)
101+
r.False(result.HasError())
102+
r.Equal(tt.expectedID, data.Id())
103+
r.Equal(tt.expectedID, data.Get(FieldImpersonationServiceAccountId))
104+
}
105+
})
106+
}
107+
}

castai/provider.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,14 @@ func Provider(version string) *schema.Provider {
7777
},
7878

7979
DataSourcesMap: map[string]*schema.Resource{
80-
"castai_eks_settings": dataSourceEKSSettings(),
81-
"castai_gke_user_policies": dataSourceGKEPolicies(),
82-
"castai_organization": dataSourceOrganization(),
83-
"castai_rebalancing_schedule": dataSourceRebalancingSchedule(),
84-
"castai_hibernation_schedule": dataSourceHibernationSchedule(),
85-
"castai_workload_scaling_policy_order": dataSourceWorkloadScalingPolicyOrder(),
86-
"castai_cache_group": dataSourceCacheGroup(),
80+
"castai_eks_settings": dataSourceEKSSettings(),
81+
"castai_gke_user_policies": dataSourceGKEPolicies(),
82+
"castai_organization": dataSourceOrganization(),
83+
"castai_rebalancing_schedule": dataSourceRebalancingSchedule(),
84+
"castai_hibernation_schedule": dataSourceHibernationSchedule(),
85+
"castai_workload_scaling_policy_order": dataSourceWorkloadScalingPolicyOrder(),
86+
"castai_cache_group": dataSourceCacheGroup(),
87+
"castai_impersonation_service_account": dataSourceImpersonationServiceAccount(),
8788
},
8889

8990
ConfigureContextFunc: providerConfigure(version),

docs/data-sources/impersonation_service_account.md

Lines changed: 22 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)