Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 87b9aaf

Browse files
committed
Add envs create command
1 parent 8917fb8 commit 87b9aaf

File tree

5 files changed

+161
-7
lines changed

5 files changed

+161
-7
lines changed

ci/integration/envs_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package integration
2+
3+
import (
4+
"context"
5+
"regexp"
6+
"testing"
7+
8+
"cdr.dev/coder-cli/ci/tcli"
9+
)
10+
11+
// From Coder organization images
12+
const ubuntuImgID = "5f443b16-30652892427b955601330fa5"
13+
14+
func TestEnvsCLI(t *testing.T) {
15+
t.Parallel()
16+
17+
run(t, "coder-cli-env-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) {
18+
headlessLogin(ctx, t, c)
19+
20+
// Ensure binary is present.
21+
c.Run(ctx, "which coder").Assert(t,
22+
tcli.Success(),
23+
tcli.StdoutMatches("/usr/sbin/coder"),
24+
tcli.StderrEmpty(),
25+
)
26+
27+
// Minimum args not received.
28+
c.Run(ctx, "coder envs create").Assert(t,
29+
tcli.StderrMatches(regexp.QuoteMeta("accepts 1 arg(s), received 0")),
30+
tcli.Error(),
31+
)
32+
33+
// Successfully output help.
34+
c.Run(ctx, "coder envs create --help").Assert(t,
35+
tcli.Success(),
36+
tcli.StdoutMatches(regexp.QuoteMeta("Create a new environment under the active user.")),
37+
tcli.StderrEmpty(),
38+
)
39+
40+
// TODO(Faris) : uncomment this when we can safely purge the environments
41+
// the integrations tests would create in the sidecar
42+
// Successfully create environment.
43+
// c.Run(ctx, "coder envs create --image "+ubuntuImgID+" test-ubuntu").Assert(t,
44+
// tcli.Success(),
45+
// // why does flog.Success write to stderr?
46+
// tcli.StderrMatches(regexp.QuoteMeta("Successfully created environment \"test-ubuntu\"")),
47+
// )
48+
49+
// Invalid environment name should fail.
50+
c.Run(ctx, "coder envs create --image "+ubuntuImgID+" this-IS-an-invalid-EnvironmentName").Assert(t,
51+
tcli.Error(),
52+
tcli.StderrMatches(regexp.QuoteMeta("environment name must conform to regex ^[a-z0-9]([a-z0-9-]+)?")),
53+
)
54+
55+
// TODO(Faris) : uncomment this when we can safely purge the environments
56+
// the integrations tests would create in the sidecar
57+
// Successfully provision environment with fractional resource amounts
58+
// c.Run(ctx, fmt.Sprintf(`coder envs create -i %s -c 1.2 -m 1.4 non-whole-resource-amounts`, ubuntuImgID)).Assert(t,
59+
// tcli.Success(),
60+
// tcli.StderrMatches(regexp.QuoteMeta("Successfully created environment \"non-whole-resource-amounts\"")),
61+
// )
62+
63+
// Image does not exist should fail.
64+
c.Run(ctx, "coder envs create --image does-not-exist env-will-not-be-created").Assert(t,
65+
tcli.Error(),
66+
tcli.StderrMatches(regexp.QuoteMeta("does not exist")),
67+
)
68+
})
69+
}

ci/steps/gendocs.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ go run ./cmd/coder gen-docs ./docs
1313

1414
# remove cobra footer from each file
1515
for filename in ./docs/*.md; do
16-
trimmed=$(head -n -1 "$filename")
16+
trimmed=$(head -n $(( $(wc -l "$filename" | awk '{print $1}') - 1 )) "$filename")
1717
echo "$trimmed" >$filename
1818
done
1919

coder-sdk/env.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ type CreateEnvironmentRequest struct {
7878
ImageID string `json:"image_id"`
7979
ImageTag string `json:"image_tag"`
8080
CPUCores float32 `json:"cpu_cores"`
81-
MemoryGB int `json:"memory_gb"`
81+
MemoryGB float32 `json:"memory_gb"`
8282
DiskGB int `json:"disk_gb"`
8383
GPUs int `json:"gpus"`
8484
Services []string `json:"services"`

internal/cmd/cmd.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ var verbose bool = false
1313
// Make constructs the "coder" root command
1414
func Make() *cobra.Command {
1515
app := &cobra.Command{
16-
Use: "coder",
17-
Short: "coder provides a CLI for working with an existing Coder Enterprise installation",
18-
SilenceErrors: true,
19-
SilenceUsage: true,
16+
Use: "coder",
17+
Short: "coder provides a CLI for working with an existing Coder Enterprise installation",
18+
SilenceErrors: true,
19+
SilenceUsage: true,
20+
DisableAutoGenTag: true,
2021
}
2122

2223
app.AddCommand(

internal/cmd/envs.go

+85-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ import (
1414
"golang.org/x/xerrors"
1515
)
1616

17+
const (
18+
defaultOrg = "default"
19+
defaultImgTag = "latest"
20+
defaultCPUCores float32 = 1
21+
defaultMemGB float32 = 1
22+
defaultDiskGB = 10
23+
defaultGPUs = 0
24+
)
25+
1726
func envsCommand() *cobra.Command {
1827
var outputFmt string
1928
var user string
@@ -64,9 +73,9 @@ func envsCommand() *cobra.Command {
6473
lsCmd.Flags().StringVarP(&outputFmt, "output", "o", "human", "human | json")
6574
cmd.AddCommand(lsCmd)
6675
cmd.AddCommand(stopEnvCommand(&user))
67-
6876
cmd.AddCommand(watchBuildLogCommand())
6977
cmd.AddCommand(rebuildEnvCommand())
78+
cmd.AddCommand(createEnvCommand())
7079
return cmd
7180
}
7281

@@ -125,3 +134,78 @@ coder envs --user charlie@coder.com ls -o json \
125134
},
126135
}
127136
}
137+
138+
func createEnvCommand() *cobra.Command {
139+
var (
140+
org string
141+
img string
142+
tag string
143+
follow bool
144+
)
145+
146+
cmd := &cobra.Command{
147+
Use: "create [environment_name]",
148+
Short: "create a new environment.",
149+
Args: cobra.ExactArgs(1),
150+
Hidden: true,
151+
Long: "Create a new environment under the active user.",
152+
Example: `# create a new environment using default resource amounts
153+
coder envs create --image id-of-imported-image my-env-name
154+
155+
# create a new environment using custom resource amounts
156+
coder envs create --cores 4 --disk 100 --memory 8 --image id-of-imported-image --org id-of-existing-organization my-env-name
157+
158+
# using short-hand flags.
159+
coder envs create -c 4 -d 100 -m 8 -i id-of-imported-image -o id-of-existing-organization my-env-name`,
160+
RunE: func(cmd *cobra.Command, args []string) error {
161+
if img == "" {
162+
return xerrors.New("image id unset")
163+
}
164+
// ExactArgs(1) ensures our name value can't panic on an out of bounds.
165+
createReq := &coder.CreateEnvironmentRequest{
166+
Name: args[0],
167+
ImageID: img,
168+
ImageTag: tag,
169+
}
170+
// We're explicitly ignoring errors for these because all of these flags
171+
// have a non-zero-value default value set already.
172+
createReq.CPUCores, _ = cmd.Flags().GetFloat32("cpu")
173+
createReq.MemoryGB, _ = cmd.Flags().GetFloat32("memory")
174+
createReq.DiskGB, _ = cmd.Flags().GetInt("disk")
175+
createReq.GPUs, _ = cmd.Flags().GetInt("gpus")
176+
177+
client, err := newClient()
178+
if err != nil {
179+
return err
180+
}
181+
182+
env, err := client.CreateEnvironment(cmd.Context(), org, *createReq)
183+
if err != nil {
184+
return xerrors.Errorf("create environment: %w", err)
185+
}
186+
187+
clog.LogSuccess(
188+
"creating environment...",
189+
clog.BlankLine,
190+
clog.Tip(`run "coder envs watch-build %q" to trail the build logs`, args[0]),
191+
)
192+
193+
if follow {
194+
if err := trailBuildLogs(cmd.Context(), client, env.ID); err != nil {
195+
return err
196+
}
197+
}
198+
return nil
199+
},
200+
}
201+
cmd.Flags().StringVarP(&org, "org", "o", defaultOrg, "ID of the organization the environment should be created under.")
202+
cmd.Flags().StringVarP(&tag, "tag", "t", defaultImgTag, "tag of the image the environment will be based off of.")
203+
cmd.Flags().Float32P("cpu", "c", defaultCPUCores, "number of cpu cores the environment should be provisioned with.")
204+
cmd.Flags().Float32P("memory", "m", defaultMemGB, "GB of RAM an environment should be provisioned with.")
205+
cmd.Flags().IntP("disk", "d", defaultDiskGB, "GB of disk storage an environment should be provisioned with.")
206+
cmd.Flags().IntP("gpus", "g", defaultGPUs, "number GPUs an environment should be provisioned with.")
207+
cmd.Flags().StringVarP(&img, "image", "i", "", "ID of the image to base the environment off of.")
208+
cmd.Flags().BoolVar(&follow, "follow", false, "follow buildlog after initiating rebuild")
209+
cmd.MarkFlagRequired("image")
210+
return cmd
211+
}

0 commit comments

Comments
 (0)