-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #228 from tosuke/framework-role-metadata
Reimplement role metadata with tf-framework
- Loading branch information
Showing
9 changed files
with
638 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package mackerel | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/mackerelio/mackerel-client-go" | ||
) | ||
|
||
type RoleMetadataModel struct { | ||
ID types.String `tfsdk:"id"` | ||
ServiceName types.String `tfsdk:"service"` | ||
RoleName types.String `tfsdk:"role"` | ||
Namespace types.String `tfsdk:"namespace"` | ||
MetadataJSON jsontypes.Normalized `tfsdk:"metadata_json"` | ||
} | ||
|
||
func roleMetadataID(serviceName, roleName, namespace string) string { | ||
return fmt.Sprintf("%s:%s/%s", serviceName, roleName, namespace) | ||
} | ||
|
||
func parseRoleMetadataID(id string) (serviceName, roleName, namespace string, err error) { | ||
sn, rest, foundColon := strings.Cut(id, ":") | ||
rn, ns, foundSlash := strings.Cut(rest, "/") | ||
if !foundColon || !foundSlash { | ||
return "", "", "", fmt.Errorf("The ID is expected to have `<service>:<role>/<namespace>` format, but got: '%s'.", id) | ||
} | ||
return sn, rn, ns, nil | ||
} | ||
|
||
func ReadRoleMetadata(ctx context.Context, client *Client, serviceName, roleName, namespace string) (RoleMetadataModel, error) { | ||
return readRoleMetadata(client, serviceName, roleName, namespace) | ||
} | ||
|
||
type roleMetadataReader interface { | ||
GetRoleMetaData(serviceName, roleName, namespace string) (*mackerel.RoleMetaDataResp, error) | ||
} | ||
|
||
func readRoleMetadata(client roleMetadataReader, serviceName, roleName, namespace string) (RoleMetadataModel, error) { | ||
metadataResp, err := client.GetRoleMetaData(serviceName, roleName, namespace) | ||
if err != nil { | ||
return RoleMetadataModel{}, err | ||
} | ||
|
||
metadataJSON, err := json.Marshal(metadataResp.RoleMetaData) | ||
if err != nil { | ||
return RoleMetadataModel{}, fmt.Errorf("failed to marshal result: %w", err) | ||
} | ||
|
||
id := roleMetadataID(serviceName, roleName, namespace) | ||
return RoleMetadataModel{ | ||
ID: types.StringValue(id), | ||
ServiceName: types.StringValue(serviceName), | ||
RoleName: types.StringValue(roleName), | ||
Namespace: types.StringValue(namespace), | ||
MetadataJSON: jsontypes.NewNormalizedValue(string(metadataJSON)), | ||
}, nil | ||
} | ||
|
||
func ImportRoleMetadata(id string) (RoleMetadataModel, error) { | ||
serviceName, roleName, namespace, err := parseRoleMetadataID(id) | ||
if err != nil { | ||
return RoleMetadataModel{}, err | ||
} | ||
return RoleMetadataModel{ | ||
ID: types.StringValue(id), | ||
ServiceName: types.StringValue(serviceName), | ||
RoleName: types.StringValue(roleName), | ||
Namespace: types.StringValue(namespace), | ||
}, nil | ||
} | ||
|
||
func (m *RoleMetadataModel) Create(ctx context.Context, client *Client) error { | ||
return m.create(client) | ||
} | ||
|
||
func (m *RoleMetadataModel) create(client roleMetadataUpdator) error { | ||
if err := m.update(client); err != nil { | ||
return err | ||
} | ||
|
||
m.ID = types.StringValue( | ||
roleMetadataID(m.ServiceName.ValueString(), m.RoleName.ValueString(), m.Namespace.ValueString()), | ||
) | ||
|
||
return nil | ||
} | ||
|
||
func (m *RoleMetadataModel) Read(ctx context.Context, client *Client) error { | ||
data, err := readRoleMetadata( | ||
client, | ||
m.ServiceName.ValueString(), | ||
m.RoleName.ValueString(), | ||
m.Namespace.ValueString(), | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
m.ID = data.ID // computed | ||
m.MetadataJSON = data.MetadataJSON | ||
return nil | ||
} | ||
|
||
func (m RoleMetadataModel) Update(ctx context.Context, client *Client) error { | ||
return m.update(client) | ||
} | ||
|
||
type roleMetadataUpdator interface { | ||
PutRoleMetaData(serviceName, roleName, namespace string, metadata mackerel.RoleMetaData) error | ||
} | ||
|
||
func (m *RoleMetadataModel) update(client roleMetadataUpdator) error { | ||
var metadata mackerel.RoleMetaData | ||
if err := json.Unmarshal([]byte(m.MetadataJSON.ValueString()), &metadata); err != nil { | ||
return fmt.Errorf("failed to unmarshal metadata: %w", err) | ||
} | ||
if err := client.PutRoleMetaData( | ||
m.ServiceName.ValueString(), | ||
m.RoleName.ValueString(), | ||
m.Namespace.ValueString(), | ||
metadata, | ||
); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func (m RoleMetadataModel) Delete(_ context.Context, client *Client) error { | ||
if err := client.DeleteRoleMetaData( | ||
m.ServiceName.ValueString(), | ||
m.RoleName.ValueString(), | ||
m.Namespace.ValueString(), | ||
); err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
package mackerel | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/mackerelio/mackerel-client-go" | ||
) | ||
|
||
func Test_ReadRoleMetadata(t *testing.T) { | ||
t.Parallel() | ||
|
||
defaultClient := func(serviceName, roleName, namespace string) (*mackerel.RoleMetaDataResp, error) { | ||
if serviceName != "service" || roleName != "role" || namespace != "namespace" { | ||
return nil, fmt.Errorf("no metadata found") | ||
} | ||
return &mackerel.RoleMetaDataResp{ | ||
RoleMetaData: map[string]any{"v": 1}, | ||
}, nil | ||
} | ||
|
||
cases := map[string]struct { | ||
inClient roleMetadataReaderFunc | ||
inServiceName string | ||
inRoleName string | ||
inNamespace string | ||
|
||
wants RoleMetadataModel | ||
}{ | ||
"basic": { | ||
inClient: defaultClient, | ||
inServiceName: "service", | ||
inRoleName: "role", | ||
inNamespace: "namespace", | ||
|
||
wants: RoleMetadataModel{ | ||
ID: types.StringValue("service:role/namespace"), | ||
ServiceName: types.StringValue("service"), | ||
RoleName: types.StringValue("role"), | ||
Namespace: types.StringValue("namespace"), | ||
MetadataJSON: jsontypes.NewNormalizedValue(`{"v":1}`), | ||
}, | ||
}, | ||
} | ||
|
||
for name, tt := range cases { | ||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
data, err := readRoleMetadata(tt.inClient, tt.inServiceName, tt.inRoleName, tt.inNamespace) | ||
if err != nil { | ||
t.Errorf("unexpected error: %+v", err) | ||
return | ||
} | ||
|
||
if diff := cmp.Diff(data, tt.wants); diff != "" { | ||
t.Error(diff) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func Test_ImportRoleMetadata(t *testing.T) { | ||
t.Parallel() | ||
|
||
cases := map[string]struct { | ||
inID string | ||
|
||
wants RoleMetadataModel | ||
wantErr bool | ||
}{ | ||
"valid": { | ||
inID: "service:role/namespace", | ||
|
||
wants: RoleMetadataModel{ | ||
ID: types.StringValue("service:role/namespace"), | ||
ServiceName: types.StringValue("service"), | ||
RoleName: types.StringValue("role"), | ||
Namespace: types.StringValue("namespace"), | ||
}, | ||
}, | ||
"invalid": { | ||
inID: "invalidid", | ||
|
||
wantErr: true, | ||
}, | ||
} | ||
|
||
for name, tt := range cases { | ||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
data, err := ImportRoleMetadata(tt.inID) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("unexpected error: %+v", err) | ||
} | ||
if err != nil { | ||
return | ||
} | ||
|
||
if diff := cmp.Diff(data, tt.wants); diff != "" { | ||
t.Error(diff) | ||
} | ||
}) | ||
|
||
} | ||
} | ||
|
||
func Test_RoleMetadata_Create(t *testing.T) { | ||
t.Parallel() | ||
|
||
cases := map[string]struct { | ||
in RoleMetadataModel | ||
inClient roleMetadataUpdatorFunc | ||
|
||
wants RoleMetadataModel | ||
}{ | ||
"basic": { | ||
in: RoleMetadataModel{ | ||
ServiceName: types.StringValue("service"), | ||
RoleName: types.StringValue("role"), | ||
Namespace: types.StringValue("namespace"), | ||
MetadataJSON: jsontypes.NewNormalizedValue(`{"v":1}`), | ||
}, | ||
inClient: func(serviceName, roleName, namespace string, metadata mackerel.RoleMetaData) error { | ||
if serviceName != "service" { | ||
return fmt.Errorf("unexpected service name: %s", serviceName) | ||
} | ||
if roleName != "role" { | ||
return fmt.Errorf("unexpected role name: %s", roleName) | ||
} | ||
if namespace != "namespace" { | ||
return fmt.Errorf("unexpected namespace: %s", namespace) | ||
} | ||
if diff := cmp.Diff(metadata, map[string]any{"v": 1.}); diff != "" { | ||
return fmt.Errorf("unexpected metadata: %s", diff) | ||
} | ||
return nil | ||
}, | ||
|
||
wants: RoleMetadataModel{ | ||
ID: types.StringValue("service:role/namespace"), | ||
ServiceName: types.StringValue("service"), | ||
RoleName: types.StringValue("role"), | ||
Namespace: types.StringValue("namespace"), | ||
MetadataJSON: jsontypes.NewNormalizedValue(`{"v":1}`), | ||
}, | ||
}, | ||
} | ||
|
||
for name, tt := range cases { | ||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
data := tt.in | ||
if err := data.create(tt.inClient); err != nil { | ||
t.Errorf("unexpected error: %+v", err) | ||
return | ||
} | ||
|
||
if diff := cmp.Diff(data, tt.wants); diff != "" { | ||
t.Error(diff) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
type roleMetadataReaderFunc func(serviceName, roleName, namespace string) (*mackerel.RoleMetaDataResp, error) | ||
|
||
func (f roleMetadataReaderFunc) GetRoleMetaData(serviceName, roleName, namespace string) (*mackerel.RoleMetaDataResp, error) { | ||
return f(serviceName, roleName, namespace) | ||
} | ||
|
||
type roleMetadataUpdatorFunc func(serviceName, roleName, namespace string, metadata mackerel.RoleMetaData) error | ||
|
||
func (f roleMetadataUpdatorFunc) PutRoleMetaData(serviceName, roleName, namespace string, metadata mackerel.RoleMetaData) error { | ||
return f(serviceName, roleName, namespace, metadata) | ||
} |
Oops, something went wrong.