diff --git a/ci/integration/envs_test.go b/ci/integration/envs_test.go index 133e4243..cfa96983 100644 --- a/ci/integration/envs_test.go +++ b/ci/integration/envs_test.go @@ -5,6 +5,7 @@ import ( "fmt" "math" "net/url" + "os" "regexp" "testing" "time" @@ -36,6 +37,61 @@ func cleanupEnv(t *testing.T, client *coder.Client, envID string) func() { } } +// this is a stopgap until we have support for a `coder images` subcommand +// until then, we want can use the *coder.Client to ensure our integration tests +// work on fresh deployments. +func ensureImageImported(ctx context.Context, t *testing.T, client *coder.Client, img string) { + orgs, err := client.Organizations(ctx) + assert.Success(t, "get orgs", err) + + var org *coder.Organization +search: + for _, o := range orgs { + for _, m := range o.Members { + if m.Email == os.Getenv("CODER_EMAIL") { + o := o + org = &o + break search + } + } + } + if org == nil { + slogtest.Fatal(t, "failed to find org of current user") + return // help the linter out a bit + } + + registries, err := client.Registries(ctx, org.ID) + assert.Success(t, "get registries", err) + + var dockerhubID string + for _, r := range registries { + if r.Registry == "index.docker.io" { + dockerhubID = r.ID + } + } + assert.True(t, "docker hub registry found", dockerhubID != "") + + imgs, err := client.OrganizationImages(ctx, org.ID) + assert.Success(t, "get org images", err) + found := false + for _, i := range imgs { + if i.Repository == img { + found = true + } + } + if !found { + // ignore this error for now as it causes a race with other parallel tests + _, _ = client.ImportImage(ctx, org.ID, coder.ImportImageReq{ + RegistryID: &dockerhubID, + Repository: img, + Tag: "latest", + DefaultCPUCores: 2.5, + DefaultDiskGB: 22, + DefaultMemoryGB: 3, + }) + } +} + func TestEnvsCLI(t *testing.T) { t.Parallel() @@ -68,6 +124,8 @@ func TestEnvsCLI(t *testing.T) { tcli.Error(), ) + ensureImageImported(ctx, t, client, "ubuntu") + name := randString(10) cpu := 2.3 c.Run(ctx, fmt.Sprintf("coder envs create %s --image ubuntu --cpu %f", name, cpu)).Assert(t, @@ -103,6 +161,8 @@ func TestEnvsCLI(t *testing.T) { headlessLogin(ctx, t, c) client := cleanupClient(ctx, t) + ensureImageImported(ctx, t, client, "ubuntu") + name := randString(10) c.Run(ctx, fmt.Sprintf("coder envs create %s --image ubuntu --follow", name)).Assert(t, tcli.Success(), diff --git a/coder-sdk/registries.go b/coder-sdk/registries.go new file mode 100644 index 00000000..c85910fe --- /dev/null +++ b/coder-sdk/registries.go @@ -0,0 +1,53 @@ +package coder + +import ( + "context" + "net/http" + "time" +) + +// Registry defines an image registry configuration. +type Registry struct { + ID string `json:"id"` + OrganizationID string `json:"organization_id"` + FriendlyName string `json:"friendly_name"` + Registry string `json:"registry"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// Registries fetches all registries in an organization. +func (c Client) Registries(ctx context.Context, orgID string) ([]Registry, error) { + var r []Registry + if err := c.requestBody(ctx, http.MethodGet, "/api/orgs/"+orgID+"/registries", nil, &r); err != nil { + return nil, err + } + return r, nil +} + +// RegistryByID fetches a registry resource by its ID. +func (c Client) RegistryByID(ctx context.Context, orgID, registryID string) (*Registry, error) { + var r Registry + if err := c.requestBody(ctx, http.MethodGet, "/api/orgs/"+orgID+"/registries/"+registryID, nil, &r); err != nil { + return nil, err + } + return &r, nil +} + +// UpdateRegistryReq defines the requests parameters for a partial update of a registry resource. +type UpdateRegistryReq struct { + Registry *string `json:"registry"` + FriendlyName *string `json:"friendly_name"` + Username *string `json:"username"` + Password *string `json:"password"` +} + +// UpdateRegistry applies a partial update to a registry resource. +func (c Client) UpdateRegistry(ctx context.Context, orgID, registryID string, req UpdateRegistryReq) error { + return c.requestBody(ctx, http.MethodPatch, "/api/orgs/"+orgID+"/registries/"+registryID, req, nil) +} + +// DeleteRegistry deletes a registry resource by its ID. +func (c Client) DeleteRegistry(ctx context.Context, orgID, registryID string) error { + return c.requestBody(ctx, http.MethodDelete, "/api/orgs/"+orgID+"/registries/"+registryID, nil, nil) +} diff --git a/internal/cmd/envs.go b/internal/cmd/envs.go index 99e1776a..4dff187b 100644 --- a/internal/cmd/envs.go +++ b/internal/cmd/envs.go @@ -62,7 +62,7 @@ func lsEnvsCommand(user *string) *cobra.Command { } if len(envs) < 1 { clog.LogInfo("no environments found") - return nil + envs = []coder.Environment{} // ensures that json output still marshals } switch outputFmt {