Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit289f021

Browse files
authored
feat: add scaletest Runner for dynamicparameters load gen (#19890)
relates tocoder/internal#912Adds a new scaletest Runner to generate dynamic parameters load.A later PR will add the CLI command, including creating the template & version.
1 parent5317d30 commit289f021

File tree

10 files changed

+458
-2
lines changed

10 files changed

+458
-2
lines changed

‎.github/workflows/typos.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[default]
22
extend-ignore-identifiers-re = ["gho_.*"]
3+
extend-ignore-re = ["(#|//)\\s*spellchecker:ignore-next-line\\n.*"]
34

45
[default.extend-identifiers]
56
alog ="alog"

‎coderd/coderdtest/dynamicparameters.go‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ type DynamicParameterTemplateParams struct {
2020
Plan json.RawMessage
2121
ModulesArchive []byte
2222

23+
// ExtraFiles are additional files to include in the template, beyond the MainTF.
24+
ExtraFilesmap[string][]byte
25+
2326
// Uses a zip archive instead of a tar
2427
Zipbool
2528

@@ -36,9 +39,17 @@ type DynamicParameterTemplateParams struct {
3639
funcDynamicParameterTemplate(t*testing.T,client*codersdk.Client,org uuid.UUID,argsDynamicParameterTemplateParams) (codersdk.Template, codersdk.TemplateVersion) {
3740
t.Helper()
3841

39-
files:=echo.WithExtraFiles(map[string][]byte{
42+
// Start with main.tf
43+
extraFiles:=map[string][]byte{
4044
"main.tf": []byte(args.MainTF),
41-
})
45+
}
46+
47+
// Add any additional files
48+
forname,content:=rangeargs.ExtraFiles {
49+
extraFiles[name]=content
50+
}
51+
52+
files:=echo.WithExtraFiles(extraFiles)
4253
files.ProvisionPlan= []*proto.Response{{
4354
Type:&proto.Response_Plan{
4455
Plan:&proto.PlanComplete{

‎provisioner/echo/serve.go‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"os"
99
"path/filepath"
10+
"slices"
1011
"strings"
1112
"text/template"
1213

@@ -359,9 +360,26 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response
359360
}
360361
}
361362
}
363+
dirs:= []string{}
362364
forname,content:=rangeresponses.ExtraFiles {
363365
logger.Debug(ctx,"extra file",slog.F("name",name))
364366

367+
// We need to add directories before any files that use them. But, we only need to do this
368+
// once.
369+
dir:=filepath.Dir(name)
370+
ifdir!="."&&!slices.Contains(dirs,dir) {
371+
logger.Debug(ctx,"adding extra file directory",slog.F("dir",dir))
372+
dirs=append(dirs,dir)
373+
err:=writer.WriteHeader(&tar.Header{
374+
Name:dir,
375+
Mode:0o755,
376+
Typeflag:tar.TypeDir,
377+
})
378+
iferr!=nil {
379+
returnnil,err
380+
}
381+
}
382+
365383
err:=writer.WriteHeader(&tar.Header{
366384
Name:name,
367385
Size:int64(len(content)),
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package dynamicparameters
2+
3+
import"github.com/google/uuid"
4+
5+
typeConfigstruct {
6+
TemplateVersion uuid.UUID`json:"template_version"`
7+
SessionTokenstring`json:"session_token"`
8+
Metrics*Metrics`json:"-"`
9+
MetricLabelValues []string`json:"metric_label_values"`
10+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dynamicparameters
2+
3+
import"github.com/prometheus/client_golang/prometheus"
4+
5+
typeMetricsstruct {
6+
LatencyInitialResponseSeconds prometheus.HistogramVec
7+
LatencyChangeResponseSeconds prometheus.HistogramVec
8+
}
9+
10+
funcNewMetrics(reg prometheus.Registerer,labelNames...string)*Metrics {
11+
m:=&Metrics{
12+
LatencyInitialResponseSeconds:*prometheus.NewHistogramVec(prometheus.HistogramOpts{
13+
Namespace:"coderd",
14+
Subsystem:"scaletest",
15+
Name:"dynamic_parameters_latency_initial_response_seconds",
16+
Help:"Time in seconds to get the initial dynamic parameters response from start of request.",
17+
},labelNames),
18+
LatencyChangeResponseSeconds:*prometheus.NewHistogramVec(prometheus.HistogramOpts{
19+
Namespace:"coderd",
20+
Subsystem:"scaletest",
21+
Name:"dynamic_parameters_latency_change_response_seconds",
22+
Help:"Time in seconds to between sending a dynamic parameters change request and receiving the response.",
23+
},labelNames),
24+
}
25+
reg.MustRegister(m.LatencyInitialResponseSeconds)
26+
reg.MustRegister(m.LatencyChangeResponseSeconds)
27+
returnm
28+
}

‎scaletest/dynamicparameters/run.go‎

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package dynamicparameters
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"slices"
8+
"time"
9+
10+
"golang.org/x/xerrors"
11+
12+
"github.com/coder/coder/v2/codersdk"
13+
"github.com/coder/coder/v2/scaletest/harness"
14+
"github.com/coder/websocket"
15+
)
16+
17+
typeRunnerstruct {
18+
client*codersdk.Client
19+
cfgConfig
20+
}
21+
22+
var_ harness.Runnable=&Runner{}
23+
24+
funcNewRunner(client*codersdk.Client,cfgConfig)*Runner {
25+
clone:=codersdk.New(client.URL)
26+
clone.HTTPClient=client.HTTPClient
27+
clone.SetLogger(client.Logger())
28+
clone.SetSessionToken(cfg.SessionToken)
29+
return&Runner{
30+
client:clone,
31+
cfg:cfg,
32+
}
33+
}
34+
35+
// Run executes the dynamic parameters test, which:
36+
//
37+
// 1. connects to the dynamic parameters stream
38+
// 2. waits for the initial response
39+
// 3. sends a change request
40+
// 4. waits for the change response
41+
// 5. closes the stream
42+
func (r*Runner)Run(ctx context.Context,_string,logs io.Writer) (retErrerror) {
43+
startTime:=time.Now()
44+
stream,err:=r.client.TemplateVersionDynamicParameters(ctx,codersdk.Me,r.cfg.TemplateVersion)
45+
iferr!=nil {
46+
returnxerrors.Errorf("connect to dynamic parameters stream: %w",err)
47+
}
48+
deferstream.Close(websocket.StatusNormalClosure)
49+
respCh:=stream.Chan()
50+
51+
varinitTime time.Time
52+
select {
53+
case<-ctx.Done():
54+
returnctx.Err()
55+
caseresp,ok:=<-respCh:
56+
if!ok {
57+
returnxerrors.Errorf("dynamic parameters stream closed before initial response")
58+
}
59+
initTime=time.Now()
60+
r.cfg.Metrics.LatencyInitialResponseSeconds.
61+
WithLabelValues(r.cfg.MetricLabelValues...).
62+
Observe(initTime.Sub(startTime).Seconds())
63+
_,_=fmt.Fprintf(logs,"initial response: %+v\n",resp)
64+
if!slices.ContainsFunc(resp.Parameters,func(p codersdk.PreviewParameter)bool {
65+
returnp.Name=="zero"
66+
}) {
67+
returnxerrors.Errorf("missing expected parameter: 'zero'")
68+
}
69+
iferr:=checkNoDiagnostics(resp);err!=nil {
70+
returnxerrors.Errorf("unexpected initial response diagnostics: %w",err)
71+
}
72+
}
73+
74+
err=stream.Send(codersdk.DynamicParametersRequest{
75+
ID:1,
76+
Inputs:map[string]string{
77+
"zero":"B",
78+
},
79+
})
80+
iferr!=nil {
81+
returnxerrors.Errorf("send change request: %w",err)
82+
}
83+
select {
84+
case<-ctx.Done():
85+
returnctx.Err()
86+
caseresp,ok:=<-respCh:
87+
if!ok {
88+
returnxerrors.Errorf("dynamic parameters stream closed before change response")
89+
}
90+
_,_=fmt.Fprintf(logs,"change response: %+v\n",resp)
91+
r.cfg.Metrics.LatencyChangeResponseSeconds.
92+
WithLabelValues(r.cfg.MetricLabelValues...).
93+
Observe(time.Since(initTime).Seconds())
94+
ifresp.ID!=1 {
95+
returnxerrors.Errorf("unexpected response ID: %d",resp.ID)
96+
}
97+
iferr:=checkNoDiagnostics(resp);err!=nil {
98+
returnxerrors.Errorf("unexpected change response diagnostics: %w",err)
99+
}
100+
returnnil
101+
}
102+
}
103+
104+
funccheckNoDiagnostics(resp codersdk.DynamicParametersResponse)error {
105+
iflen(resp.Diagnostics)!=0 {
106+
returnxerrors.Errorf("unexpected response diagnostics: %v",resp.Diagnostics)
107+
}
108+
for_,param:=rangeresp.Parameters {
109+
iflen(param.Diagnostics)!=0 {
110+
returnxerrors.Errorf("unexpected parameter diagnostics for '%s': %v",param.Name,param.Diagnostics)
111+
}
112+
}
113+
returnnil
114+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package dynamicparameters_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/prometheus/client_golang/prometheus"
8+
"github.com/stretchr/testify/require"
9+
10+
"cdr.dev/slog"
11+
"github.com/coder/coder/v2/coderd/coderdtest"
12+
"github.com/coder/coder/v2/scaletest/dynamicparameters"
13+
"github.com/coder/coder/v2/testutil"
14+
)
15+
16+
funcTestRun(t*testing.T) {
17+
t.Parallel()
18+
ctx:=testutil.Context(t,testutil.WaitLong)
19+
20+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
21+
client.SetLogger(testutil.Logger(t).Leveled(slog.LevelDebug))
22+
first:=coderdtest.CreateFirstUser(t,client)
23+
userClient,_:=coderdtest.CreateAnotherUser(t,client,first.OrganizationID)
24+
orgID:=first.OrganizationID
25+
26+
dynamicParametersTerraformSource,err:=dynamicparameters.TemplateContent()
27+
require.NoError(t,err)
28+
29+
template,version:=coderdtest.DynamicParameterTemplate(t,client,orgID, coderdtest.DynamicParameterTemplateParams{
30+
MainTF:dynamicParametersTerraformSource,
31+
Plan:nil,
32+
ModulesArchive:nil,
33+
StaticParams:nil,
34+
ExtraFiles:dynamicparameters.GetModuleFiles(),
35+
})
36+
37+
reg:=prometheus.NewRegistry()
38+
cfg:= dynamicparameters.Config{
39+
TemplateVersion:version.ID,
40+
SessionToken:userClient.SessionToken(),
41+
Metrics:dynamicparameters.NewMetrics(reg,"template","test_label_name"),
42+
MetricLabelValues: []string{template.Name,"test_label_value"},
43+
}
44+
runner:=dynamicparameters.NewRunner(userClient,cfg)
45+
varlogs strings.Builder
46+
err=runner.Run(ctx,t.Name(),&logs)
47+
t.Log("Runner logs:\n\n"+logs.String())
48+
require.NoError(t,err)
49+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package dynamicparameters
2+
3+
import (
4+
_"embed"
5+
"encoding/json"
6+
"strings"
7+
"text/template"
8+
9+
"github.com/coder/coder/v2/cryptorand"
10+
)
11+
12+
//go:embed tf/main.tf
13+
vartemplateContentstring
14+
15+
funcTemplateContent() (string,error) {
16+
randomString,err:=cryptorand.String(8)
17+
iferr!=nil {
18+
return"",err
19+
}
20+
tmpl,err:=template.New("workspace-template").Parse(templateContent)
21+
iferr!=nil {
22+
return"",err
23+
}
24+
varresult strings.Builder
25+
err=tmpl.Execute(&result,map[string]string{
26+
"RandomString":randomString,
27+
})
28+
iferr!=nil {
29+
return"",err
30+
}
31+
returnresult.String(),nil
32+
}
33+
34+
//go:embed tf/modules/two/main.tf
35+
varmoduleTwoMainTFstring
36+
37+
// GetModuleFiles returns a map of module files to be used with ExtraFiles
38+
funcGetModuleFiles()map[string][]byte {
39+
// Create the modules.json that Terraform needs to see the module
40+
modulesJSON:=struct {
41+
Modules []struct {
42+
Keystring`json:"Key"`
43+
Sourcestring`json:"Source"`
44+
Dirstring`json:"Dir"`
45+
}`json:"Modules"`
46+
}{
47+
Modules: []struct {
48+
Keystring`json:"Key"`
49+
Sourcestring`json:"Source"`
50+
Dirstring`json:"Dir"`
51+
}{
52+
{
53+
Key:"",
54+
Source:"",
55+
Dir:".",
56+
},
57+
{
58+
Key:"two",
59+
Source:"./modules/two",
60+
Dir:"modules/two",
61+
},
62+
},
63+
}
64+
65+
modulesJSONBytes,err:=json.Marshal(modulesJSON)
66+
iferr!=nil {
67+
panic(err)// This should never happen with static data
68+
}
69+
70+
returnmap[string][]byte{
71+
"modules/two/main.tf": []byte(moduleTwoMainTF),
72+
".terraform/modules/modules.json":modulesJSONBytes,
73+
}
74+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp