mirror of
https://github.com/coder/coder.git
synced 2025-07-21 01:28:49 +00:00
fix!: enforce regex for agent names (#16641)
Underscores and double hyphens are now blocked. The regex is almost the exact same as the `coder_app` `slug` regex, but uppercase characters are still permitted.
This commit is contained in:
@ -984,6 +984,7 @@ func TestInvalidTerraformAddress(t *testing.T) {
|
||||
require.Equal(t, state.Resources[0].ModulePath, "invalid terraform address")
|
||||
}
|
||||
|
||||
//nolint:tparallel
|
||||
func TestAppSlugValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
@ -1001,31 +1002,116 @@ func TestAppSlugValidation(t *testing.T) {
|
||||
tfPlanGraph, err := os.ReadFile(filepath.Join(dir, "multiple-apps.tfplan.dot"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Change all slugs to be invalid.
|
||||
cases := []struct {
|
||||
slug string
|
||||
errContains string
|
||||
}{
|
||||
{slug: "$$$ invalid slug $$$", errContains: "does not match regex"},
|
||||
{slug: "invalid--slug", errContains: "does not match regex"},
|
||||
{slug: "invalid_slug", errContains: "does not match regex"},
|
||||
{slug: "Invalid-slug", errContains: "does not match regex"},
|
||||
{slug: "valid", errContains: ""},
|
||||
}
|
||||
|
||||
//nolint:paralleltest
|
||||
for i, c := range cases {
|
||||
c := c
|
||||
t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) {
|
||||
// Change the first app slug to match the current case.
|
||||
for _, resource := range tfPlan.PlannedValues.RootModule.Resources {
|
||||
if resource.Type == "coder_app" {
|
||||
resource.AttributeValues["slug"] = c.slug
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_, err := terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), logger)
|
||||
if c.errContains != "" {
|
||||
require.ErrorContains(t, err, c.errContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppSlugDuplicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
|
||||
// nolint:dogsled
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
|
||||
dir := filepath.Join(filepath.Dir(filename), "testdata", "multiple-apps")
|
||||
tfPlanRaw, err := os.ReadFile(filepath.Join(dir, "multiple-apps.tfplan.json"))
|
||||
require.NoError(t, err)
|
||||
var tfPlan tfjson.Plan
|
||||
err = json.Unmarshal(tfPlanRaw, &tfPlan)
|
||||
require.NoError(t, err)
|
||||
tfPlanGraph, err := os.ReadFile(filepath.Join(dir, "multiple-apps.tfplan.dot"))
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, resource := range tfPlan.PlannedValues.RootModule.Resources {
|
||||
if resource.Type == "coder_app" {
|
||||
resource.AttributeValues["slug"] = "$$$ invalid slug $$$"
|
||||
resource.AttributeValues["slug"] = "dev"
|
||||
}
|
||||
}
|
||||
|
||||
state, err := terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), logger)
|
||||
require.Nil(t, state)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "invalid app slug")
|
||||
|
||||
// Change all slugs to be identical and valid.
|
||||
for _, resource := range tfPlan.PlannedValues.RootModule.Resources {
|
||||
if resource.Type == "coder_app" {
|
||||
resource.AttributeValues["slug"] = "valid"
|
||||
}
|
||||
}
|
||||
|
||||
state, err = terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), logger)
|
||||
require.Nil(t, state)
|
||||
_, err = terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), logger)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "duplicate app slug")
|
||||
}
|
||||
|
||||
//nolint:tparallel
|
||||
func TestAgentNameInvalid(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
|
||||
// nolint:dogsled
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
|
||||
dir := filepath.Join(filepath.Dir(filename), "testdata", "multiple-agents")
|
||||
tfPlanRaw, err := os.ReadFile(filepath.Join(dir, "multiple-agents.tfplan.json"))
|
||||
require.NoError(t, err)
|
||||
var tfPlan tfjson.Plan
|
||||
err = json.Unmarshal(tfPlanRaw, &tfPlan)
|
||||
require.NoError(t, err)
|
||||
tfPlanGraph, err := os.ReadFile(filepath.Join(dir, "multiple-agents.tfplan.dot"))
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
errContains string
|
||||
}{
|
||||
{name: "bad--name", errContains: "does not match regex"},
|
||||
{name: "bad_name", errContains: "contains underscores"}, // custom error for underscores
|
||||
{name: "valid-name-123", errContains: ""},
|
||||
{name: "valid", errContains: ""},
|
||||
{name: "UppercaseValid", errContains: ""},
|
||||
}
|
||||
|
||||
//nolint:paralleltest
|
||||
for i, c := range cases {
|
||||
c := c
|
||||
t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) {
|
||||
// Change the first agent name to match the current case.
|
||||
for _, resource := range tfPlan.PlannedValues.RootModule.Resources {
|
||||
if resource.Type == "coder_agent" {
|
||||
resource.Name = c.name
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_, err := terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), logger)
|
||||
if c.errContains != "" {
|
||||
require.ErrorContains(t, err, c.errContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentNameDuplicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
|
Reference in New Issue
Block a user