Skip to content

Commit 1d4544f

Browse files
authored
Optimize GetModuleContent performance (#148)
1 parent ff9c1b4 commit 1d4544f

File tree

9 files changed

+574
-414
lines changed

9 files changed

+574
-414
lines changed

plugin/fromproto/fromproto.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,18 @@ func Config(config *proto.ApplyGlobalConfig_Config) *tflint.Config {
183183
return &tflint.Config{Rules: rules, DisabledByDefault: config.DisabledByDefault}
184184
}
185185

186+
// GetModuleContentOption converts proto.GetModuleContent_Option to tflint.GetModuleContentOption
187+
func GetModuleContentOption(opts *proto.GetModuleContent_Option) tflint.GetModuleContentOption {
188+
if opts == nil {
189+
return tflint.GetModuleContentOption{}
190+
}
191+
192+
return tflint.GetModuleContentOption{
193+
ModuleCtx: ModuleCtxType(opts.ModuleCtx),
194+
Hint: GetModuleContentHint(opts.Hint),
195+
}
196+
}
197+
186198
// ModuleCtxType converts proto.ModuleCtxType to tflint.ModuleCtxType
187199
func ModuleCtxType(ty proto.ModuleCtxType) tflint.ModuleCtxType {
188200
switch ty {
@@ -197,6 +209,17 @@ func ModuleCtxType(ty proto.ModuleCtxType) tflint.ModuleCtxType {
197209
}
198210
}
199211

212+
// GetModuleContentHint converts proto.GetModuleContent_Hint to tflint.GetModuleContentHint
213+
func GetModuleContentHint(hint *proto.GetModuleContent_Hint) tflint.GetModuleContentHint {
214+
if hint == nil {
215+
return tflint.GetModuleContentHint{}
216+
}
217+
218+
return tflint.GetModuleContentHint{
219+
ResourceType: hint.ResourceType,
220+
}
221+
}
222+
200223
// Error converts gRPC error status to wrapped error
201224
func Error(err error) error {
202225
if err == nil {

plugin/host2plugin/host2plugin_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,8 +486,8 @@ func (s *mockServer) EmitIssue(rule tflint.Rule, message string, location hcl.Ra
486486
return nil
487487
}
488488

489-
func (s *mockServer) GetFiles(tflint.ModuleCtxType) map[string]*hcl.File {
490-
return map[string]*hcl.File{}
489+
func (s *mockServer) GetFiles(tflint.ModuleCtxType) map[string][]byte {
490+
return map[string][]byte{}
491491
}
492492

493493
func TestCheck(t *testing.T) {

plugin/plugin2host/client.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ var _ tflint.Runner = &GRPCClient{}
3030
// GetResourceContent gets the contents of resources based on the schema.
3131
// This is shorthand of GetModuleContent for resources
3232
func (c *GRPCClient) GetResourceContent(name string, inner *hclext.BodySchema, opts *tflint.GetModuleContentOption) (*hclext.BodyContent, error) {
33+
if opts == nil {
34+
opts = &tflint.GetModuleContentOption{}
35+
}
36+
opts.Hint.ResourceType = name
37+
3338
body, err := c.GetModuleContent(&hclext.BodySchema{
3439
Blocks: []hclext.BlockSchema{
3540
{Type: "resource", LabelNames: []string{"type", "name"}, Body: inner},
@@ -59,7 +64,7 @@ func (c *GRPCClient) GetModuleContent(schema *hclext.BodySchema, opts *tflint.Ge
5964

6065
req := &proto.GetModuleContent_Request{
6166
Schema: toproto.BodySchema(schema),
62-
Option: &proto.GetModuleContent_Option{ModuleCtx: toproto.ModuleCtxType(opts.ModuleCtx)},
67+
Option: toproto.GetModuleContentOption(opts),
6368
}
6469
resp, err := c.Client.GetModuleContent(context.Background(), req)
6570
if err != nil {

plugin/plugin2host/plugin2host_test.go

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type mockServer struct {
3636
type mockServerImpl struct {
3737
getModuleContent func(*hclext.BodySchema, tflint.GetModuleContentOption) (*hclext.BodyContent, hcl.Diagnostics)
3838
getFile func(string) (*hcl.File, error)
39-
getFiles func() map[string]*hcl.File
39+
getFiles func() map[string][]byte
4040
getRuleConfigContent func(string, *hclext.BodySchema) (*hclext.BodyContent, *hcl.File, error)
4141
evaluateExpr func(hcl.Expression, tflint.EvaluateExprOption) (cty.Value, error)
4242
emitIssue func(tflint.Rule, string, hcl.Range) error
@@ -60,11 +60,11 @@ func (s *mockServer) GetFile(filename string) (*hcl.File, error) {
6060
return nil, nil
6161
}
6262

63-
func (s *mockServer) GetFiles(tflint.ModuleCtxType) map[string]*hcl.File {
63+
func (s *mockServer) GetFiles(tflint.ModuleCtxType) map[string][]byte {
6464
if s.impl.getFiles != nil {
6565
return s.impl.getFiles()
6666
}
67-
return map[string]*hcl.File{}
67+
return map[string][]byte{}
6868
}
6969

7070
func (s *mockServer) GetRuleConfigContent(name string, schema *hclext.BodySchema) (*hclext.BodyContent, *hcl.File, error) {
@@ -96,8 +96,8 @@ func TestGetResourceContent(t *testing.T) {
9696
neverHappend := func(err error) bool { return err != nil }
9797

9898
// default getFileImpl function
99-
files := map[string]*hcl.File{}
100-
fileExists := func() map[string]*hcl.File {
99+
files := map[string][]byte{}
100+
fileExists := func() map[string][]byte {
101101
return files
102102
}
103103

@@ -107,15 +107,15 @@ func TestGetResourceContent(t *testing.T) {
107107
if diags.HasErrors() {
108108
panic(diags)
109109
}
110-
files[filename] = file
110+
files[filename] = file.Bytes
111111
return file
112112
}
113113
jsonFile := func(filename string, code string) *hcl.File {
114114
file, diags := json.Parse([]byte(code), filename)
115115
if diags.HasErrors() {
116116
panic(diags)
117117
}
118-
files[filename] = file
118+
files[filename] = file.Bytes
119119
return file
120120
}
121121

@@ -215,6 +215,34 @@ resource "aws_instance" "foo" {
215215
},
216216
ErrCheck: neverHappend,
217217
},
218+
{
219+
Name: "get content with options",
220+
Args: func() (string, *hclext.BodySchema, *tflint.GetModuleContentOption) {
221+
return "aws_instance", &hclext.BodySchema{}, &tflint.GetModuleContentOption{
222+
ModuleCtx: tflint.RootModuleCtxType,
223+
}
224+
},
225+
ServerImpl: func(schema *hclext.BodySchema, opts tflint.GetModuleContentOption) (*hclext.BodyContent, hcl.Diagnostics) {
226+
if opts.ModuleCtx != tflint.RootModuleCtxType {
227+
return &hclext.BodyContent{}, hcl.Diagnostics{
228+
&hcl.Diagnostic{Severity: hcl.DiagError, Summary: "unexpected moduleCtx options"},
229+
}
230+
}
231+
if opts.Hint.ResourceType != "aws_instance" {
232+
return &hclext.BodyContent{}, hcl.Diagnostics{
233+
&hcl.Diagnostic{Severity: hcl.DiagError, Summary: "unexpected hint options"},
234+
}
235+
}
236+
return &hclext.BodyContent{}, hcl.Diagnostics{}
237+
},
238+
Want: func(resource string, schema *hclext.BodySchema, opts *tflint.GetModuleContentOption) (*hclext.BodyContent, hcl.Diagnostics) {
239+
return &hclext.BodyContent{
240+
Attributes: hclext.Attributes{},
241+
Blocks: hclext.Blocks{},
242+
}, hcl.Diagnostics{}
243+
},
244+
ErrCheck: neverHappend,
245+
},
218246
}
219247

220248
for _, test := range tests {
@@ -252,8 +280,8 @@ func TestGetModuleContent(t *testing.T) {
252280
neverHappend := func(err error) bool { return err != nil }
253281

254282
// default getFileImpl function
255-
files := map[string]*hcl.File{}
256-
fileExists := func() map[string]*hcl.File {
283+
files := map[string][]byte{}
284+
fileExists := func() map[string][]byte {
257285
return files
258286
}
259287

@@ -263,15 +291,15 @@ func TestGetModuleContent(t *testing.T) {
263291
if diags.HasErrors() {
264292
panic(diags)
265293
}
266-
files[filename] = file
294+
files[filename] = file.Bytes
267295
return file
268296
}
269297
jsonFile := func(filename string, code string) *hcl.File {
270298
file, diags := json.Parse([]byte(code), filename)
271299
if diags.HasErrors() {
272300
panic(diags)
273301
}
274-
files[filename] = file
302+
files[filename] = file.Bytes
275303
return file
276304
}
277305

@@ -359,12 +387,20 @@ resource "aws_instance" "foo" {
359387
{
360388
Name: "get content with options",
361389
Args: func() (*hclext.BodySchema, *tflint.GetModuleContentOption) {
362-
return &hclext.BodySchema{}, &tflint.GetModuleContentOption{ModuleCtx: tflint.RootModuleCtxType}
390+
return &hclext.BodySchema{}, &tflint.GetModuleContentOption{
391+
ModuleCtx: tflint.RootModuleCtxType,
392+
Hint: tflint.GetModuleContentHint{ResourceType: "aws_instance"},
393+
}
363394
},
364395
ServerImpl: func(schema *hclext.BodySchema, opts tflint.GetModuleContentOption) (*hclext.BodyContent, hcl.Diagnostics) {
365396
if opts.ModuleCtx != tflint.RootModuleCtxType {
366397
return &hclext.BodyContent{}, hcl.Diagnostics{
367-
&hcl.Diagnostic{Severity: hcl.DiagError, Summary: "unexpected options"},
398+
&hcl.Diagnostic{Severity: hcl.DiagError, Summary: "unexpected moduleCtx options"},
399+
}
400+
}
401+
if opts.Hint.ResourceType != "aws_instance" {
402+
return &hclext.BodyContent{}, hcl.Diagnostics{
403+
&hcl.Diagnostic{Severity: hcl.DiagError, Summary: "unexpected hint options"},
368404
}
369405
}
370406
return &hclext.BodyContent{}, hcl.Diagnostics{}
@@ -608,19 +644,19 @@ func TestGetFiles(t *testing.T) {
608644

609645
tests := []struct {
610646
Name string
611-
ServerImpl func() map[string]*hcl.File
647+
ServerImpl func() map[string][]byte
612648
Want map[string]*hcl.File
613649
ErrCheck func(error) bool
614650
}{
615651
{
616652
Name: "HCL files",
617-
ServerImpl: func() map[string]*hcl.File {
618-
return map[string]*hcl.File{
619-
"test1.tf": hclFile("test1.tf", `
653+
ServerImpl: func() map[string][]byte {
654+
return map[string][]byte{
655+
"test1.tf": []byte(`
620656
resource "aws_instance" "foo" {
621657
instance_type = "t2.micro"
622658
}`),
623-
"test2.tf": hclFile("test2.tf", `
659+
"test2.tf": []byte(`
624660
resource "aws_s3_bucket" "bar" {
625661
bucket = "baz"
626662
}`),
@@ -640,9 +676,9 @@ resource "aws_s3_bucket" "bar" {
640676
},
641677
{
642678
Name: "JSON files",
643-
ServerImpl: func() map[string]*hcl.File {
644-
return map[string]*hcl.File{
645-
"test1.tf.json": jsonFile("test1.tf.json", `
679+
ServerImpl: func() map[string][]byte {
680+
return map[string][]byte{
681+
"test1.tf.json": []byte(`
646682
{
647683
"resource": {
648684
"aws_instance": {
@@ -652,7 +688,7 @@ resource "aws_s3_bucket" "bar" {
652688
}
653689
}
654690
}`),
655-
"test2.tf.json": jsonFile("test2.tf.json", `
691+
"test2.tf.json": []byte(`
656692
{
657693
"resource": {
658694
"aws_s3_bucket": {

plugin/plugin2host/server.go

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ var _ proto.RunnerServer = &GRPCServer{}
3030
type Server interface {
3131
GetModuleContent(*hclext.BodySchema, tflint.GetModuleContentOption) (*hclext.BodyContent, hcl.Diagnostics)
3232
GetFile(string) (*hcl.File, error)
33-
GetFiles(tflint.ModuleCtxType) map[string]*hcl.File
33+
// For performance, GetFiles returns map[string][]bytes instead of map[string]*hcl.File.
34+
GetFiles(tflint.ModuleCtxType) map[string][]byte
3435
GetRuleConfigContent(string, *hclext.BodySchema) (*hclext.BodyContent, *hcl.File, error)
3536
EvaluateExpr(hcl.Expression, tflint.EvaluateExprOption) (cty.Value, error)
3637
EmitIssue(rule tflint.Rule, message string, location hcl.Range) error
@@ -45,20 +46,16 @@ func (s *GRPCServer) GetModuleContent(ctx context.Context, req *proto.GetModuleC
4546
return nil, status.Error(codes.InvalidArgument, "option should not be null")
4647
}
4748

48-
moduleCtx := fromproto.ModuleCtxType(req.Option.ModuleCtx)
49-
body, diags := s.Impl.GetModuleContent(fromproto.BodySchema(req.Schema), tflint.GetModuleContentOption{ModuleCtx: moduleCtx})
49+
opts := fromproto.GetModuleContentOption(req.Option)
50+
body, diags := s.Impl.GetModuleContent(fromproto.BodySchema(req.Schema), opts)
5051
if diags.HasErrors() {
5152
return nil, toproto.Error(codes.FailedPrecondition, diags)
5253
}
5354
if body == nil {
5455
return nil, status.Error(codes.FailedPrecondition, "response body is empty")
5556
}
5657

57-
sources := map[string][]byte{}
58-
for name, file := range s.Impl.GetFiles(moduleCtx) {
59-
sources[name] = file.Bytes
60-
}
61-
content := toproto.BodyContent(body, sources)
58+
content := toproto.BodyContent(body, s.Impl.GetFiles(opts.ModuleCtx))
6259

6360
return &proto.GetModuleContent_Response{Content: content}, nil
6461
}
@@ -80,13 +77,7 @@ func (s *GRPCServer) GetFile(ctx context.Context, req *proto.GetFile_Request) (*
8077

8178
// GetFiles returns bytes of hcl.File in the self module context.
8279
func (s *GRPCServer) GetFiles(ctx context.Context, req *proto.GetFiles_Request) (*proto.GetFiles_Response, error) {
83-
files := s.Impl.GetFiles(tflint.SelfModuleCtxType)
84-
85-
resp := map[string][]byte{}
86-
for name, file := range files {
87-
resp[name] = file.Bytes
88-
}
89-
return &proto.GetFiles_Response{Files: resp}, nil
80+
return &proto.GetFiles_Response{Files: s.Impl.GetFiles(tflint.SelfModuleCtxType)}, nil
9081
}
9182

9283
// GetRuleConfigContent returns BodyContent based on the rule name and config schema.

0 commit comments

Comments
 (0)