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

Commit 7d89f9e

Browse files
author
Faris Huskovic
committed
Change coder create | edit envs image flag to take image name and use defaults from image
1 parent 6f8b9b8 commit 7d89f9e

File tree

5 files changed

+258
-70
lines changed

5 files changed

+258
-70
lines changed

ci/integration/envs_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestEnvsCLI(t *testing.T) {
4949
// Invalid environment name should fail.
5050
c.Run(ctx, "coder envs create --image "+ubuntuImgID+" this-IS-an-invalid-EnvironmentName").Assert(t,
5151
tcli.Error(),
52-
tcli.StderrMatches(regexp.QuoteMeta("environment name must conform to regex ^[a-z0-9]([a-z0-9-]+)?")),
52+
tcli.StderrMatches(regexp.QuoteMeta("fatal: image not found - did you forget to import this image?\n\n")),
5353
)
5454

5555
// TODO(Faris) : uncomment this when we can safely purge the environments
@@ -63,7 +63,7 @@ func TestEnvsCLI(t *testing.T) {
6363
// Image does not exist should fail.
6464
c.Run(ctx, "coder envs create --image does-not-exist env-will-not-be-created").Assert(t,
6565
tcli.Error(),
66-
tcli.StderrMatches(regexp.QuoteMeta("does not exist")),
66+
tcli.StderrMatches(regexp.QuoteMeta("fatal: image not found - did you forget to import this image?\n\n")),
6767
)
6868
})
6969
}

coder-sdk/image.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type Image struct {
1313
Description string `json:"description"`
1414
URL string `json:"url"` // User-supplied URL for image.
1515
DefaultCPUCores float32 `json:"default_cpu_cores"`
16-
DefaultMemoryGB int `json:"default_memory_gb"`
16+
DefaultMemoryGB float32 `json:"default_memory_gb"`
1717
DefaultDiskGB int `json:"default_disk_gb"`
1818
Deprecated bool `json:"deprecated"`
1919
}
@@ -47,3 +47,13 @@ func (c Client) ImportImage(ctx context.Context, orgID string, req ImportImageRe
4747
}
4848
return &img, nil
4949
}
50+
51+
// GetOrganizationImages returns all of the images imported for org.
52+
func (c Client) GetOrganizationImages(ctx context.Context, org string) ([]*Image, error) {
53+
var imgs []*Image
54+
55+
if err := c.requestBody(ctx, http.MethodGet, "/api/orgs/"+org+"/images", nil, &imgs); err != nil {
56+
return nil, err
57+
}
58+
return imgs, nil
59+
}

internal/cmd/ceapi.go

+92
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"context"
55
"fmt"
6+
"strings"
67

78
"cdr.dev/coder-cli/coder-sdk"
89
"cdr.dev/coder-cli/internal/clog"
@@ -81,3 +82,94 @@ func findEnv(ctx context.Context, client *coder.Client, envName, userEmail strin
8182
clog.Tipf("run \"coder envs ls\" to view your environments"),
8283
)
8384
}
85+
86+
func findImg(ctx context.Context, client *coder.Client, email, imgName string) (*coder.Image, error) {
87+
userImgs, err := lookupUserImgs(ctx, client, email, imgName)
88+
if err != nil {
89+
return nil, err
90+
}
91+
92+
numImgs := len(userImgs)
93+
94+
if numImgs == 0 {
95+
return nil, xerrors.New("image not found - did you forget to import this image?")
96+
}
97+
98+
if numImgs > 1 {
99+
var lines []string
100+
101+
for _, img := range userImgs {
102+
lines = append(lines, img.Repository)
103+
}
104+
105+
clog.LogInfo(
106+
fmt.Sprintf("Found %d possible matches for %q.", numImgs, imgName),
107+
clog.Tipf("Did you mean?\n\t%s", strings.Join(lines, "\n\t")),
108+
)
109+
110+
return nil, xerrors.New("please refine your search")
111+
}
112+
return userImgs[0], nil
113+
}
114+
115+
func getImgs(ctx context.Context, client *coder.Client, userEmail string) ([]*coder.Image, error) {
116+
u, err := client.UserByEmail(ctx, userEmail)
117+
if err != nil {
118+
return nil, err
119+
}
120+
121+
orgs, err := client.Organizations(ctx)
122+
if err != nil {
123+
return nil, err
124+
}
125+
126+
orgs = lookupUserOrgs(u, orgs)
127+
var importedImgs []*coder.Image
128+
129+
// Get all of the imported images for all of the orgs the user belongs to.
130+
for _, org := range orgs {
131+
imgs, err := client.GetOrganizationImages(ctx, org.ID)
132+
if err != nil {
133+
return nil, err
134+
}
135+
importedImgs = append(importedImgs, imgs...)
136+
}
137+
return importedImgs, nil
138+
}
139+
140+
// returns all images that contain imgName as a substring and have been imported by the user.
141+
func lookupUserImgs(ctx context.Context, client *coder.Client, email, imgName string) ([]*coder.Image, error) {
142+
switch {
143+
case client == nil:
144+
return nil, xerrors.New("nil client")
145+
case email == "":
146+
return nil, xerrors.New("user email unset")
147+
case imgName == "":
148+
return nil, xerrors.New("image name unset")
149+
}
150+
151+
// Get all imported images for each org the user belongs to.
152+
imgs, err := getImgs(ctx, client, email)
153+
if err != nil {
154+
return nil, err
155+
}
156+
157+
var userImgs []*coder.Image
158+
159+
// The user may provide an image thats not an exact match
160+
// to one of their imported images but they may be close.
161+
// We can assist the user by collecting images that contain
162+
// the user provided image flag value as a substring.
163+
for _, img := range imgs {
164+
if strings.Contains(img.Repository, imgName) {
165+
userImgs = append(userImgs, img)
166+
}
167+
// If it's an exact match we can overwrite the slice and exit the loop
168+
// since we won't need the fuzzy matched images anymore.
169+
if img.Repository == imgName {
170+
userImgs = []*coder.Image{img}
171+
break
172+
}
173+
}
174+
return userImgs, nil
175+
}

0 commit comments

Comments
 (0)