feat: NormalizeSetData (#11)
* feat: NormalizeSetData & NormalizeCatalogData - Add support for always normalizing host set filters as an array. This implementation follows exactly what we do for AWS as well for consistency - No-op for NormalizeCatalogData
This commit is contained in:
1 parent
ba53ae16b2
commit
beaa93d3d8
2 files changed
+186
No files matched your search
@@ -18,6 +18,8 @@ import (
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
// HostPlugin implements the HostPluginServiceServer interface for the
|
||||
@@ -270,6 +272,42 @@ func (p *HostPlugin) OnDeleteCatalog(ctx context.Context, req *pb.OnDeleteCatalo
|
||||
return &pb.OnDeleteCatalogResponse{}, nil
|
||||
}
|
||||
|
||||
// NormalizeCatalogData is called to normalize the catalog data.
|
||||
func (p *HostPlugin) NormalizeCatalogData(ctx context.Context, req *pb.NormalizeCatalogDataRequest) (*pb.NormalizeCatalogDataResponse, error) {
|
||||
// No-op, Catalog fields do not require any normalization
|
||||
return &pb.NormalizeCatalogDataResponse{Attributes: req.Attributes}, nil
|
||||
}
|
||||
|
||||
// NormalizeSetData currently ensures that "filters" is an array value, even
|
||||
// though it's accepted as a string value for CLI UX reasons
|
||||
func (p *HostPlugin) NormalizeSetData(ctx context.Context, req *pb.NormalizeSetDataRequest) (*pb.NormalizeSetDataResponse, error) {
|
||||
if req.Attributes == nil {
|
||||
return &pb.NormalizeSetDataResponse{}, nil
|
||||
}
|
||||
|
||||
val := req.Attributes.Fields["filters"]
|
||||
if val == nil {
|
||||
return &pb.NormalizeSetDataResponse{Attributes: req.Attributes}, nil
|
||||
}
|
||||
|
||||
switch val.GetKind().(type) {
|
||||
case *structpb.Value_ListValue:
|
||||
return &pb.NormalizeSetDataResponse{Attributes: req.Attributes}, nil
|
||||
case *structpb.Value_StringValue:
|
||||
stringVal := val.Kind.(*structpb.Value_StringValue)
|
||||
retAttrs := proto.Clone(req.Attributes).(*structpb.Struct)
|
||||
retAttrs.Fields["filters"] = structpb.NewListValue(
|
||||
&structpb.ListValue{
|
||||
Values: []*structpb.Value{
|
||||
structpb.NewStringValue(stringVal.StringValue),
|
||||
},
|
||||
})
|
||||
return &pb.NormalizeSetDataResponse{Attributes: retAttrs}, nil
|
||||
default:
|
||||
return &pb.NormalizeSetDataResponse{}, status.Error(codes.InvalidArgument, "attribute.filters must be a string or list")
|
||||
}
|
||||
}
|
||||
|
||||
// OnCreateSet is called when a dynamic host set is created.
|
||||
func (p *HostPlugin) OnCreateSet(_ context.Context, req *pb.OnCreateSetRequest) (*pb.OnCreateSetResponse, error) {
|
||||
if err := validateSet(req.GetSet()); err != nil {
|
||||
|
||||
@@ -1190,3 +1190,151 @@ func TestUpdateSet(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeSetData(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
p := &HostPlugin{}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pb.NormalizeSetDataRequest
|
||||
expectedRsp *pb.NormalizeSetDataResponse
|
||||
expectedErrContains string
|
||||
}{
|
||||
{
|
||||
name: "nil attributes",
|
||||
req: &pb.NormalizeSetDataRequest{},
|
||||
expectedRsp: &pb.NormalizeSetDataResponse{
|
||||
Attributes: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil filters",
|
||||
req: &pb.NormalizeSetDataRequest{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{},
|
||||
},
|
||||
},
|
||||
expectedRsp: &pb.NormalizeSetDataResponse{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filters not a string",
|
||||
req: &pb.NormalizeSetDataRequest{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"filters": structpb.NewListValue(&structpb.ListValue{
|
||||
Values: []*structpb.Value{
|
||||
structpb.NewStringValue("status = RUNNING"),
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRsp: &pb.NormalizeSetDataResponse{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"filters": structpb.NewListValue(&structpb.ListValue{
|
||||
Values: []*structpb.Value{
|
||||
structpb.NewStringValue("status = RUNNING"),
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filters is a string",
|
||||
req: &pb.NormalizeSetDataRequest{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"filters": structpb.NewStringValue("status = RUNNING"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRsp: &pb.NormalizeSetDataResponse{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"filters": structpb.NewListValue(&structpb.ListValue{
|
||||
Values: []*structpb.Value{
|
||||
structpb.NewStringValue("status = RUNNING"),
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filters with multiple values",
|
||||
req: &pb.NormalizeSetDataRequest{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"filters": structpb.NewStringValue(`(cpuPlatform = "Intel Skylake") OR (cpuPlatform = "Intel Broadwell") AND (scheduling.automaticRestart = true)`),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRsp: &pb.NormalizeSetDataResponse{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"filters": structpb.NewListValue(&structpb.ListValue{
|
||||
Values: []*structpb.Value{
|
||||
structpb.NewStringValue(`(cpuPlatform = "Intel Skylake") OR (cpuPlatform = "Intel Broadwell") AND (scheduling.automaticRestart = true)`),
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filters is an number",
|
||||
req: &pb.NormalizeSetDataRequest{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"filters": structpb.NewNumberValue(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrContains: "attribute.filters must be a string or list",
|
||||
},
|
||||
{
|
||||
name: "filters is an boolean",
|
||||
req: &pb.NormalizeSetDataRequest{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"filters": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrContains: "attribute.filters must be a string or list",
|
||||
},
|
||||
{
|
||||
name: "filters is null",
|
||||
req: &pb.NormalizeSetDataRequest{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"filters": structpb.NewNullValue(),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrContains: "attribute.filters must be a string or list",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
actual, err := p.NormalizeSetData(ctx, tc.req)
|
||||
if tc.expectedErrContains != "" {
|
||||
require.ErrorContains(err, tc.expectedErrContains)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
require.Empty(cmp.Diff(tc.expectedRsp, actual, protocmp.Transform()))
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user