mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
Relates to CODAGT-713 Depends on #27515 This adds `--agents-allowed` to `coder templates create` and `coder templates edit`. Template creation defaults the option to true, matching the per-template API and database default, while template editing only changes the value when the flag is explicitly supplied so unrelated edits preserve the existing setting. The generated CLI help and reference documentation include the new option. #27518 updates the Coder Agents platform controls documentation to describe the completed per-template model.
373 lines
10 KiB
Go
373 lines
10 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/coder/coder/v2/cli/clitest"
|
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/rbac"
|
|
"github.com/coder/coder/v2/provisioner/echo"
|
|
"github.com/coder/coder/v2/provisionersdk/proto"
|
|
"github.com/coder/coder/v2/testutil"
|
|
"github.com/coder/coder/v2/testutil/expecter"
|
|
)
|
|
|
|
func TestCliTemplateCreate(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("Create", func(t *testing.T) {
|
|
t.Parallel()
|
|
logger := testutil.Logger(t)
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
coderdtest.CreateFirstUser(t, client)
|
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
|
args := []string{
|
|
"templates",
|
|
"create",
|
|
"my-template",
|
|
"--directory", source,
|
|
"--test.provisioner", string(database.ProvisionerTypeEcho),
|
|
"--default-ttl", "24h",
|
|
}
|
|
inv, root := clitest.New(t, args...)
|
|
clitest.SetupConfig(t, client, root)
|
|
stdout := expecter.NewAttachedToInvocation(t, inv)
|
|
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
|
|
|
|
clitest.Start(t, inv)
|
|
|
|
matches := []struct {
|
|
match string
|
|
write string
|
|
}{
|
|
{match: "Upload", write: "yes"},
|
|
{match: "compute.main"},
|
|
{match: "smith (linux, i386)"},
|
|
{match: "Confirm create?", write: "yes"},
|
|
}
|
|
for _, m := range matches {
|
|
stdout.ExpectMatch(ctx, m.match)
|
|
if len(m.write) > 0 {
|
|
stdin.WriteLine(m.write)
|
|
}
|
|
}
|
|
})
|
|
t.Run("AgentsAllowed", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
owner := coderdtest.CreateFirstUser(t, client)
|
|
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin())
|
|
|
|
for _, tt := range []struct {
|
|
name string
|
|
flag string
|
|
agentsAllowed bool
|
|
}{
|
|
{
|
|
name: "DefaultTrue",
|
|
agentsAllowed: true,
|
|
},
|
|
{
|
|
name: "False",
|
|
flag: "--agents-allowed=false",
|
|
agentsAllowed: false,
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
templateName := coderdtest.RandomUsername(t)
|
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
|
args := []string{
|
|
"templates",
|
|
"create",
|
|
templateName,
|
|
"--yes",
|
|
"--directory", source,
|
|
"--test.provisioner", string(database.ProvisionerTypeEcho),
|
|
}
|
|
if tt.flag != "" {
|
|
args = append(args, tt.flag)
|
|
}
|
|
inv, root := clitest.New(t, args...)
|
|
clitest.SetupConfig(t, templateAdmin, root)
|
|
|
|
require.NoError(t, inv.Run())
|
|
|
|
template, err := client.TemplateByName(t.Context(), owner.OrganizationID, templateName)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.agentsAllowed, template.AgentsAllowed)
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("CreateNoLockfile", func(t *testing.T) {
|
|
t.Parallel()
|
|
logger := testutil.Logger(t)
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
coderdtest.CreateFirstUser(t, client)
|
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
|
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
|
args := []string{
|
|
"templates",
|
|
"create",
|
|
"my-template",
|
|
"--directory", source,
|
|
"--test.provisioner", string(database.ProvisionerTypeEcho),
|
|
"--default-ttl", "24h",
|
|
}
|
|
inv, root := clitest.New(t, args...)
|
|
clitest.SetupConfig(t, client, root)
|
|
stdout := expecter.NewAttachedToInvocation(t, inv)
|
|
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
|
|
|
|
execDone := make(chan error)
|
|
go func() {
|
|
execDone <- inv.Run()
|
|
}()
|
|
|
|
matches := []struct {
|
|
match string
|
|
write string
|
|
}{
|
|
{match: "No .terraform.lock.hcl file found"},
|
|
{match: "Upload", write: "no"},
|
|
}
|
|
for _, m := range matches {
|
|
stdout.ExpectMatch(ctx, m.match)
|
|
if len(m.write) > 0 {
|
|
stdin.WriteLine(m.write)
|
|
}
|
|
}
|
|
|
|
// cmd should error once we say no.
|
|
require.Error(t, <-execDone)
|
|
})
|
|
t.Run("CreateNoLockfileIgnored", func(t *testing.T) {
|
|
t.Parallel()
|
|
logger := testutil.Logger(t)
|
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
coderdtest.CreateFirstUser(t, client)
|
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
|
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
|
args := []string{
|
|
"templates",
|
|
"create",
|
|
"my-template",
|
|
"--directory", source,
|
|
"--test.provisioner", string(database.ProvisionerTypeEcho),
|
|
"--default-ttl", "24h",
|
|
"--ignore-lockfile",
|
|
}
|
|
inv, root := clitest.New(t, args...)
|
|
clitest.SetupConfig(t, client, root)
|
|
stdout := expecter.NewAttachedToInvocation(t, inv)
|
|
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
|
|
|
|
execDone := make(chan error)
|
|
go func() {
|
|
execDone <- inv.Run()
|
|
}()
|
|
|
|
{
|
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
|
|
defer cancel()
|
|
|
|
stdout.ExpectNoMatchBefore(ctx, "No .terraform.lock.hcl file found", "Upload")
|
|
stdin.WriteLine("no")
|
|
}
|
|
|
|
// cmd should error once we say no.
|
|
require.Error(t, <-execDone)
|
|
})
|
|
|
|
t.Run("CreateStdin", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
coderdtest.CreateFirstUser(t, client)
|
|
source, err := echo.Tar(completeWithAgent())
|
|
require.NoError(t, err)
|
|
|
|
args := []string{
|
|
"templates",
|
|
"create",
|
|
"my-template",
|
|
"--directory", "-",
|
|
"--test.provisioner", string(database.ProvisionerTypeEcho),
|
|
"--default-ttl", "24h",
|
|
}
|
|
inv, root := clitest.New(t, args...)
|
|
clitest.SetupConfig(t, client, root)
|
|
inv.Stdin = bytes.NewReader(source)
|
|
|
|
require.NoError(t, inv.Run())
|
|
})
|
|
|
|
t.Run("Recreate template with same name (create, delete, create)", func(t *testing.T) {
|
|
t.Parallel()
|
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
coderdtest.CreateFirstUser(t, client)
|
|
|
|
create := func() error {
|
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
|
args := []string{
|
|
"templates",
|
|
"create",
|
|
"my-template",
|
|
"--yes",
|
|
"--directory", source,
|
|
"--test.provisioner", string(database.ProvisionerTypeEcho),
|
|
}
|
|
inv, root := clitest.New(t, args...)
|
|
clitest.SetupConfig(t, client, root)
|
|
|
|
return inv.Run()
|
|
}
|
|
del := func() error {
|
|
args := []string{
|
|
"templates",
|
|
"delete",
|
|
"my-template",
|
|
"--yes",
|
|
}
|
|
inv, root := clitest.New(t, args...)
|
|
clitest.SetupConfig(t, client, root)
|
|
|
|
return inv.Run()
|
|
}
|
|
|
|
err := create()
|
|
require.NoError(t, err, "Template must be created without error")
|
|
err = del()
|
|
require.NoError(t, err, "Template must be deleted without error")
|
|
err = create()
|
|
require.NoError(t, err, "Template must be recreated without error")
|
|
})
|
|
|
|
t.Run("WithVariablesFileWithTheRequiredValue", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
logger := testutil.Logger(t)
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
coderdtest.CreateFirstUser(t, client)
|
|
|
|
templateVariables := []*proto.TemplateVariable{
|
|
{
|
|
Name: "first_variable",
|
|
Description: "This is the first variable.",
|
|
Type: "string",
|
|
Required: true,
|
|
Sensitive: true,
|
|
},
|
|
{
|
|
Name: "second_variable",
|
|
Description: "This is the second variable",
|
|
Type: "string",
|
|
DefaultValue: "abc",
|
|
Required: false,
|
|
Sensitive: true,
|
|
},
|
|
}
|
|
source := clitest.CreateTemplateVersionSource(t,
|
|
createEchoResponsesWithTemplateVariables(templateVariables))
|
|
tempDir := t.TempDir()
|
|
removeTmpDirUntilSuccessAfterTest(t, tempDir)
|
|
variablesFile, _ := os.CreateTemp(tempDir, "variables*.yaml")
|
|
_, _ = variablesFile.WriteString(`first_variable: foobar`)
|
|
inv, root := clitest.New(t, "templates", "create", "my-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--variables-file", variablesFile.Name())
|
|
clitest.SetupConfig(t, client, root)
|
|
stdout := expecter.NewAttachedToInvocation(t, inv)
|
|
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
|
|
|
|
clitest.Start(t, inv)
|
|
|
|
matches := []struct {
|
|
match string
|
|
write string
|
|
}{
|
|
{match: "Upload", write: "yes"},
|
|
{match: "Confirm create?", write: "yes"},
|
|
}
|
|
for _, m := range matches {
|
|
stdout.ExpectMatch(ctx, m.match)
|
|
if len(m.write) > 0 {
|
|
stdin.WriteLine(m.write)
|
|
}
|
|
}
|
|
})
|
|
t.Run("WithVariableOption", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
logger := testutil.Logger(t)
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
|
coderdtest.CreateFirstUser(t, client)
|
|
|
|
templateVariables := []*proto.TemplateVariable{
|
|
{
|
|
Name: "first_variable",
|
|
Description: "This is the first variable.",
|
|
Type: "string",
|
|
Required: true,
|
|
Sensitive: true,
|
|
},
|
|
}
|
|
source := clitest.CreateTemplateVersionSource(t,
|
|
createEchoResponsesWithTemplateVariables(templateVariables))
|
|
inv, root := clitest.New(t, "templates", "create", "my-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--variable", "first_variable=foobar")
|
|
clitest.SetupConfig(t, client, root)
|
|
stdout := expecter.NewAttachedToInvocation(t, inv)
|
|
stdin := testutil.NewWriterAttachedToInvocation(t, logger.Named("stdin"), inv)
|
|
|
|
clitest.Start(t, inv)
|
|
|
|
matches := []struct {
|
|
match string
|
|
write string
|
|
}{
|
|
{match: "Upload", write: "yes"},
|
|
{match: "Confirm create?", write: "yes"},
|
|
}
|
|
for _, m := range matches {
|
|
stdout.ExpectMatch(ctx, m.match)
|
|
if len(m.write) > 0 {
|
|
stdin.WriteLine(m.write)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("RequireActiveVersionInvalid", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{
|
|
IncludeProvisionerDaemon: true,
|
|
})
|
|
coderdtest.CreateFirstUser(t, client)
|
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
|
args := []string{
|
|
"templates",
|
|
"create",
|
|
"my-template",
|
|
"--directory", source,
|
|
"--test.provisioner", string(database.ProvisionerTypeEcho),
|
|
"--require-active-version",
|
|
}
|
|
inv, root := clitest.New(t, args...)
|
|
clitest.SetupConfig(t, client, root)
|
|
|
|
err := inv.Run()
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "your deployment appears to be an AGPL deployment, so you cannot set enterprise-only flags")
|
|
})
|
|
}
|