Skip to content

Commit 4010218

Browse files
authored
Add k6bundle provider function (#2363)
* Add k6bundle provider function * Update docs * Reduce permissions on test files
1 parent f24ea10 commit 4010218

File tree

6 files changed

+204
-0
lines changed

6 files changed

+204
-0
lines changed

docs/functions/k6bundle.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "k6bundle function - terraform-provider-grafana"
4+
subcategory: ""
5+
description: |-
6+
Bundle multi-file JavaScript/TypeScript k6 tests
7+
---
8+
9+
# function: k6bundle
10+
11+
Takes a file path to a JavaScript or TypeScript k6 test and bundles it using ESbuild. Returns the bundled JavaScript code as a string.
12+
13+
14+
15+
## Signature
16+
17+
<!-- signature generated by tfplugindocs -->
18+
```text
19+
k6bundle(file_path string) string
20+
```
21+
22+
## Arguments
23+
24+
<!-- arguments generated by tfplugindocs -->
25+
1. `file_path` (String) Path to the JavaScript or TypeScript file to bundle
26+

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ require (
8080
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
8181
github.com/elazarl/goproxy v1.7.2 // indirect
8282
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
83+
github.com/evanw/esbuild v0.25.10 // indirect
8384
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
8485
github.com/getkin/kin-openapi v0.133.0 // indirect
8586
github.com/go-logr/logr v1.4.3 // indirect

go.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
7878
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
7979
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
8080
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
81+
github.com/evanw/esbuild v0.25.10 h1:8cl6FntLWO4AbqXWqMWgYrvdm8lLSFm5HjU/HY2N27E=
82+
github.com/evanw/esbuild v0.25.10/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
8183
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
8284
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
8385
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
@@ -571,6 +573,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
571573
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
572574
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
573575
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
576+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
574577
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
575578
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
576579
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

internal/functions/k6bundle.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package functions
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/evanw/esbuild/pkg/api"
10+
"github.com/hashicorp/terraform-plugin-framework/function"
11+
)
12+
13+
var _ function.Function = &K6BundleFunction{}
14+
15+
type K6BundleFunction struct{}
16+
17+
func NewK6BundleFunction() function.Function {
18+
return &K6BundleFunction{}
19+
}
20+
21+
func (f *K6BundleFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) {
22+
resp.Name = "k6bundle"
23+
}
24+
25+
func (f *K6BundleFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) {
26+
resp.Definition = function.Definition{
27+
Summary: "Bundle multi-file JavaScript/TypeScript k6 tests",
28+
Description: "Takes a file path to a JavaScript or TypeScript k6 test and bundles it using ESbuild. Returns the bundled JavaScript code as a string.",
29+
30+
Parameters: []function.Parameter{
31+
function.StringParameter{
32+
Name: "file_path",
33+
Description: "Path to the JavaScript or TypeScript file to bundle",
34+
},
35+
},
36+
Return: function.StringReturn{},
37+
}
38+
}
39+
40+
func (f *K6BundleFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) {
41+
var filePath string
42+
43+
resp.Error = function.ConcatFuncErrors(resp.Error, req.Arguments.Get(ctx, &filePath))
44+
if resp.Error != nil {
45+
return
46+
}
47+
48+
if _, err := os.Stat(filePath); os.IsNotExist(err) {
49+
resp.Error = function.ConcatFuncErrors(resp.Error,
50+
function.NewFuncError(fmt.Sprintf("File does not exist: %s", filePath)))
51+
return
52+
}
53+
54+
absPath, err := filepath.Abs(filePath)
55+
if err != nil {
56+
resp.Error = function.ConcatFuncErrors(resp.Error,
57+
function.NewFuncError(fmt.Sprintf("Failed to get absolute path: %v", err)))
58+
return
59+
}
60+
61+
result := api.Build(api.BuildOptions{
62+
EntryPoints: []string{absPath},
63+
Bundle: true,
64+
Platform: api.PlatformNode,
65+
Target: api.ES2017,
66+
Format: api.FormatCommonJS,
67+
External: []string{
68+
"k6",
69+
"k6/*",
70+
"https",
71+
"https/*",
72+
},
73+
Write: false, // Don't write to disk, return the content
74+
})
75+
76+
if len(result.Errors) > 0 {
77+
var errorMsg string
78+
for _, buildErr := range result.Errors {
79+
errorMsg += fmt.Sprintf("ESbuild error: %s\n", buildErr.Text)
80+
}
81+
resp.Error = function.ConcatFuncErrors(resp.Error,
82+
function.NewFuncError(errorMsg))
83+
return
84+
}
85+
86+
if len(result.OutputFiles) == 0 {
87+
resp.Error = function.ConcatFuncErrors(resp.Error,
88+
function.NewFuncError("ESbuild produced no output"))
89+
return
90+
}
91+
92+
bundledCode := string(result.OutputFiles[0].Contents)
93+
94+
resp.Error = function.ConcatFuncErrors(resp.Error, resp.Result.Set(ctx, bundledCode))
95+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package functions_test
2+
3+
import (
4+
"context"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
"testing"
9+
10+
"github.com/grafana/terraform-provider-grafana/v4/internal/functions"
11+
"github.com/hashicorp/terraform-plugin-framework/attr"
12+
"github.com/hashicorp/terraform-plugin-framework/function"
13+
"github.com/hashicorp/terraform-plugin-framework/types"
14+
)
15+
16+
func TestK6BundleFunction_Basic(t *testing.T) {
17+
tempDir := t.TempDir()
18+
19+
libFile := filepath.Join(tempDir, "utils.js")
20+
if err := os.WriteFile(libFile, []byte(`export const sum = (a, b) => a + b;`), 0600); err != nil {
21+
t.Fatalf("Failed to create lib file: %v", err)
22+
}
23+
24+
testFile := filepath.Join(tempDir, "test.js")
25+
testContent := `
26+
import { check } from 'k6';
27+
import { sum } from './utils.js';
28+
29+
export default function() {
30+
const result = sum(1, 2);
31+
check(result, { 'sum works': (r) => r === 3 });
32+
}
33+
`
34+
if err := os.WriteFile(testFile, []byte(testContent), 0600); err != nil {
35+
t.Fatalf("Failed to create test file: %v", err)
36+
}
37+
38+
f := functions.NewK6BundleFunction()
39+
args := function.NewArgumentsData([]attr.Value{types.StringValue(testFile)})
40+
req := function.RunRequest{Arguments: args}
41+
resp := &function.RunResponse{Result: function.NewResultData(types.StringUnknown())}
42+
43+
f.Run(context.Background(), req, resp)
44+
45+
if resp.Error != nil {
46+
t.Fatalf("Unexpected error: %v", resp.Error)
47+
}
48+
49+
bundledCode := resp.Result.Value().(types.String).ValueString()
50+
if !strings.Contains(bundledCode, "sum") || !strings.Contains(bundledCode, "check") {
51+
t.Error("Bundled code should contain both sum function and k6 check")
52+
}
53+
}
54+
55+
func TestK6BundleFunction_FileNotFound(t *testing.T) {
56+
f := functions.NewK6BundleFunction()
57+
args := function.NewArgumentsData([]attr.Value{types.StringValue("/nonexistent.js")})
58+
req := function.RunRequest{Arguments: args}
59+
resp := &function.RunResponse{Result: function.NewResultData(types.StringUnknown())}
60+
61+
f.Run(context.Background(), req, resp)
62+
63+
if resp.Error == nil {
64+
t.Fatal("Expected error for nonexistent file")
65+
}
66+
if !strings.Contains(resp.Error.Error(), "File does not exist") {
67+
t.Errorf("Expected 'File does not exist' error, got: %s", resp.Error.Error())
68+
}
69+
}

pkg/provider/framework_provider.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import (
1010

1111
"github.com/hashicorp/terraform-plugin-framework/attr"
1212
"github.com/hashicorp/terraform-plugin-framework/datasource"
13+
"github.com/hashicorp/terraform-plugin-framework/function"
1314
"github.com/hashicorp/terraform-plugin-framework/provider"
1415
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
1516
"github.com/hashicorp/terraform-plugin-framework/resource"
1617
"github.com/hashicorp/terraform-plugin-framework/types"
18+
19+
"github.com/grafana/terraform-provider-grafana/v4/internal/functions"
1720
)
1821

1922
type ProviderConfig struct {
@@ -316,6 +319,13 @@ func (p *frameworkProvider) Resources(_ context.Context) []func() resource.Resou
316319
return pluginFrameworkResources()
317320
}
318321

322+
// Functions defines the functions implemented in the provider.
323+
func (p *frameworkProvider) Functions(_ context.Context) []func() function.Function {
324+
return []func() function.Function{
325+
functions.NewK6BundleFunction,
326+
}
327+
}
328+
319329
// FrameworkProvider returns a terraform-plugin-framework Provider.
320330
// This is the recommended way forward for new resources.
321331
func FrameworkProvider(version string) provider.Provider {

0 commit comments

Comments
 (0)