From f799e203cb60349f3b4d8b30493b658a2d62881f Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Mon, 8 Mar 2021 21:23:18 -0600 Subject: [PATCH 1/5] fix: use proper API type for resource pool list --- coder-sdk/interface.go | 4 +-- coder-sdk/workspace_providers.go | 19 ++++++---- internal/cmd/envs.go | 60 +++++++++++++++++++++----------- internal/cmd/providers.go | 6 ++-- internal/coderutil/env.go | 10 +++--- internal/coderutil/provider.go | 21 +++++++++++ 6 files changed, 83 insertions(+), 37 deletions(-) create mode 100644 internal/coderutil/provider.go diff --git a/coder-sdk/interface.go b/coder-sdk/interface.go index 1ab51dae..82971340 100644 --- a/coder-sdk/interface.go +++ b/coder-sdk/interface.go @@ -203,10 +203,10 @@ type Client interface { APIVersion(ctx context.Context) (string, error) // WorkspaceProviderByID fetches a workspace provider entity by its unique ID. - WorkspaceProviderByID(ctx context.Context, id string) (*WorkspaceProvider, error) + WorkspaceProviderByID(ctx context.Context, id string) (*KubernetesProvider, error) // WorkspaceProviders fetches all workspace providers known to the Coder control plane. - WorkspaceProviders(ctx context.Context) ([]WorkspaceProvider, error) + WorkspaceProviders(ctx context.Context) (*WorkspaceProviders, error) // CreateWorkspaceProvider creates a new WorkspaceProvider entity. CreateWorkspaceProvider(ctx context.Context, req CreateWorkspaceProviderReq) (*CreateWorkspaceProviderRes, error) diff --git a/coder-sdk/workspace_providers.go b/coder-sdk/workspace_providers.go index e834d79b..db2ba5ee 100644 --- a/coder-sdk/workspace_providers.go +++ b/coder-sdk/workspace_providers.go @@ -5,8 +5,13 @@ import ( "net/http" ) -// WorkspaceProvider defines an entity capable of deploying and acting as an ingress for Coder environments. -type WorkspaceProvider struct { +// WorkspaceProviders defines all available Coder workspace provider targets. +type WorkspaceProviders struct { + Kubernetes []KubernetesProvider `json:"kubernetes"` +} + +// KubernetesProvider defines an entity capable of deploying and acting as an ingress for Coder environments. +type KubernetesProvider struct { ID string `json:"id" table:"-"` Name string `json:"name" table:"Name"` Status WorkspaceProviderStatus `json:"status" table:"Status"` @@ -32,8 +37,8 @@ const ( ) // WorkspaceProviderByID fetches a workspace provider entity by its unique ID. -func (c *DefaultClient) WorkspaceProviderByID(ctx context.Context, id string) (*WorkspaceProvider, error) { - var wp WorkspaceProvider +func (c *DefaultClient) WorkspaceProviderByID(ctx context.Context, id string) (*KubernetesProvider, error) { + var wp KubernetesProvider err := c.requestBody(ctx, http.MethodGet, "/api/private/resource-pools/"+id, nil, &wp) if err != nil { return nil, err @@ -42,13 +47,13 @@ func (c *DefaultClient) WorkspaceProviderByID(ctx context.Context, id string) (* } // WorkspaceProviders fetches all workspace providers known to the Coder control plane. -func (c *DefaultClient) WorkspaceProviders(ctx context.Context) ([]WorkspaceProvider, error) { - var providers []WorkspaceProvider +func (c *DefaultClient) WorkspaceProviders(ctx context.Context) (*WorkspaceProviders, error) { + var providers WorkspaceProviders err := c.requestBody(ctx, http.MethodGet, "/api/private/resource-pools", nil, &providers) if err != nil { return nil, err } - return providers, nil + return &providers, nil } // CreateWorkspaceProviderReq defines the request parameters for creating a new workspace provider entity. diff --git a/internal/cmd/envs.go b/internal/cmd/envs.go index e824aa65..277a4807 100644 --- a/internal/cmd/envs.go +++ b/internal/cmd/envs.go @@ -152,15 +152,16 @@ coder envs --user charlie@coder.com ls -o json \ func createEnvCmd() *cobra.Command { var ( - org string - cpu float32 - memory float32 - disk int - gpus int - img string - tag string - follow bool - useCVM bool + org string + cpu float32 + memory float32 + disk int + gpus int + img string + tag string + follow bool + useCVM bool + providerName string ) cmd := &cobra.Command{ @@ -199,9 +200,17 @@ coder envs create my-new-powerful-env --cpu 12 --disk 100 --memory 16 --image ub return err } - provider, err := coderutil.DefaultWorkspaceProvider(ctx, client) - if err != nil { - return xerrors.Errorf("default workspace provider: %w", err) + var provider *coder.KubernetesProvider + if providerName == "" { + provider, err = coderutil.DefaultWorkspaceProvider(ctx, client) + if err != nil { + return xerrors.Errorf("default workspace provider: %w", err) + } + } else { + provider, err = coderutil.ProviderByName(ctx, client, providerName) + if err != nil { + return xerrors.Errorf("provider by name: %w", err) + } } // ExactArgs(1) ensures our name value can't panic on an out of bounds. @@ -258,6 +267,7 @@ coder envs create my-new-powerful-env --cpu 12 --disk 100 --memory 16 --image ub cmd.Flags().IntVarP(&disk, "disk", "d", 0, "GB of disk storage an environment should be provisioned with.") cmd.Flags().IntVarP(&gpus, "gpus", "g", 0, "number GPUs an environment should be provisioned with.") cmd.Flags().StringVarP(&img, "image", "i", "", "name of the image to base the environment off of.") + cmd.Flags().StringVar(&providerName, "provider", "", "name of Workspace Provider with which to create the environment") cmd.Flags().BoolVar(&follow, "follow", false, "follow buildlog after initiating rebuild") cmd.Flags().BoolVar(&useCVM, "container-based-vm", false, "deploy the environment as a Container-based VM") _ = cmd.MarkFlagRequired("image") @@ -266,11 +276,12 @@ coder envs create my-new-powerful-env --cpu 12 --disk 100 --memory 16 --image ub func createEnvFromRepoCmd() *cobra.Command { var ( - ref string - repo string - follow bool - filepath string - org string + ref string + repo string + follow bool + filepath string + org string + providerName string ) cmd := &cobra.Command{ @@ -347,9 +358,17 @@ coder envs create-from-repo -f coder.yaml`, return xerrors.Errorf("parse environment template config: %w", err) } - provider, err := coderutil.DefaultWorkspaceProvider(ctx, client) - if err != nil { - return xerrors.Errorf("default workspace provider: %w", err) + var provider *coder.KubernetesProvider + if providerName == "" { + provider, err = coderutil.DefaultWorkspaceProvider(ctx, client) + if err != nil { + return xerrors.Errorf("default workspace provider: %w", err) + } + } else { + provider, err = coderutil.ProviderByName(ctx, client, providerName) + if err != nil { + return xerrors.Errorf("provider by name: %w", err) + } } env, err := client.CreateEnvironment(ctx, coder.CreateEnvironmentRequest{ @@ -382,6 +401,7 @@ coder envs create-from-repo -f coder.yaml`, cmd.Flags().StringVarP(&ref, "ref", "", "master", "git reference to pull template from. May be a branch, tag, or commit hash.") cmd.Flags().StringVarP(&repo, "repo-url", "r", "", "URL of the git repository to pull the config from. Config file must live in '.coder/coder.yaml'.") cmd.Flags().BoolVar(&follow, "follow", false, "follow buildlog after initiating rebuild") + cmd.Flags().StringVar(&providerName, "provider", "", "name of Workspace Provider with which to create the environment") return cmd } diff --git a/internal/cmd/providers.go b/internal/cmd/providers.go index 8d5ff12b..e771dcf1 100644 --- a/internal/cmd/providers.go +++ b/internal/cmd/providers.go @@ -86,8 +86,8 @@ coder providers ls`, return xerrors.Errorf("list workspace providers: %w", err) } - err = tablewriter.WriteTable(len(wps), func(i int) interface{} { - return wps[i] + err = tablewriter.WriteTable(len(wps.Kubernetes), func(i int) interface{} { + return wps.Kubernetes[i] }) if err != nil { return xerrors.Errorf("write table: %w", err) @@ -122,7 +122,7 @@ coder providers rm my-workspace-provider`, name := wpName egroup.Go(func() error { var id string - for _, wp := range wps { + for _, wp := range wps.Kubernetes { if wp.Name == name { id = wp.ID } diff --git a/internal/coderutil/env.go b/internal/coderutil/env.go index aca70c4c..bffa87d3 100644 --- a/internal/coderutil/env.go +++ b/internal/coderutil/env.go @@ -32,7 +32,7 @@ func DialEnvWsep(ctx context.Context, client coder.Client, env *coder.Environmen // EnvWithWorkspaceProvider composes an Environment entity with its associated WorkspaceProvider. type EnvWithWorkspaceProvider struct { Env coder.Environment - WorkspaceProvider coder.WorkspaceProvider + WorkspaceProvider coder.KubernetesProvider } // EnvsWithProvider performs the composition of each Environment with its associated WorkspaceProvider. @@ -42,8 +42,8 @@ func EnvsWithProvider(ctx context.Context, client coder.Client, envs []coder.Env if err != nil { return nil, err } - providerMap := make(map[string]coder.WorkspaceProvider, len(providers)) - for _, p := range providers { + providerMap := make(map[string]coder.KubernetesProvider, len(providers.Kubernetes)) + for _, p := range providers.Kubernetes { providerMap[p.ID] = p } for _, e := range envs { @@ -60,12 +60,12 @@ func EnvsWithProvider(ctx context.Context, client coder.Client, envs []coder.Env } // DefaultWorkspaceProvider returns the default provider with which to create environments. -func DefaultWorkspaceProvider(ctx context.Context, c coder.Client) (*coder.WorkspaceProvider, error) { +func DefaultWorkspaceProvider(ctx context.Context, c coder.Client) (*coder.KubernetesProvider, error) { provider, err := c.WorkspaceProviders(ctx) if err != nil { return nil, err } - for _, p := range provider { + for _, p := range provider.Kubernetes { if p.Local { return &p, nil } diff --git a/internal/coderutil/provider.go b/internal/coderutil/provider.go new file mode 100644 index 00000000..5364add8 --- /dev/null +++ b/internal/coderutil/provider.go @@ -0,0 +1,21 @@ +package coderutil + +import ( + "context" + + "cdr.dev/coder-cli/coder-sdk" +) + +// ProviderByName searches linearly for a workspace provider by its name. +func ProviderByName(ctx context.Context, client coder.Client, name string) (*coder.KubernetesProvider, error) { + providers, err := client.WorkspaceProviders(ctx) + if err != nil { + return nil, err + } + for _, p := range providers.Kubernetes { + if p.Name == name { + return &p, nil + } + } + return nil, coder.ErrNotFound +} From 7ffefd061183ecf3b27c3c824810f1707ad6fff3 Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Mon, 8 Mar 2021 21:25:14 -0600 Subject: [PATCH 2/5] fixup! fix: use proper API type for resource pool list --- internal/cmd/envs.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/internal/cmd/envs.go b/internal/cmd/envs.go index 277a4807..012d1f23 100644 --- a/internal/cmd/envs.go +++ b/internal/cmd/envs.go @@ -200,17 +200,9 @@ coder envs create my-new-powerful-env --cpu 12 --disk 100 --memory 16 --image ub return err } - var provider *coder.KubernetesProvider - if providerName == "" { - provider, err = coderutil.DefaultWorkspaceProvider(ctx, client) - if err != nil { - return xerrors.Errorf("default workspace provider: %w", err) - } - } else { - provider, err = coderutil.ProviderByName(ctx, client, providerName) - if err != nil { - return xerrors.Errorf("provider by name: %w", err) - } + provider, err := coderutil.DefaultWorkspaceProvider(ctx, client) + if err != nil { + return xerrors.Errorf("default workspace provider: %w", err) } // ExactArgs(1) ensures our name value can't panic on an out of bounds. From eb89c606b5ee7550b265c07fcc42d6f6f5cd3198 Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Mon, 8 Mar 2021 21:26:27 -0600 Subject: [PATCH 3/5] fixup! fix: use proper API type for resource pool list --- internal/cmd/envs.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/cmd/envs.go b/internal/cmd/envs.go index 012d1f23..aaa4b27e 100644 --- a/internal/cmd/envs.go +++ b/internal/cmd/envs.go @@ -200,9 +200,17 @@ coder envs create my-new-powerful-env --cpu 12 --disk 100 --memory 16 --image ub return err } - provider, err := coderutil.DefaultWorkspaceProvider(ctx, client) - if err != nil { - return xerrors.Errorf("default workspace provider: %w", err) + var provider *coder.KubernetesProvider + if providerName == "" { + provider, err = coderutil.DefaultWorkspaceProvider(ctx, client) + if err != nil { + return xerrors.Errorf("default workspace provider: %w", err) + } + } else { + provider, err = coderutil.ProviderByName(ctx, client, providerName) + if err != nil { + return xerrors.Errorf("provider by name: %w", err) + } } // ExactArgs(1) ensures our name value can't panic on an out of bounds. @@ -350,17 +358,9 @@ coder envs create-from-repo -f coder.yaml`, return xerrors.Errorf("parse environment template config: %w", err) } - var provider *coder.KubernetesProvider - if providerName == "" { - provider, err = coderutil.DefaultWorkspaceProvider(ctx, client) - if err != nil { - return xerrors.Errorf("default workspace provider: %w", err) - } - } else { - provider, err = coderutil.ProviderByName(ctx, client, providerName) - if err != nil { - return xerrors.Errorf("provider by name: %w", err) - } + provider, err := coderutil.DefaultWorkspaceProvider(ctx, client) + if err != nil { + return xerrors.Errorf("default workspace provider: %w", err) } env, err := client.CreateEnvironment(ctx, coder.CreateEnvironmentRequest{ From a56005fd086446f161279a11fcee967896ad351b Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Mon, 8 Mar 2021 21:26:44 -0600 Subject: [PATCH 4/5] fixup! fix: use proper API type for resource pool list --- docs/coder_envs_create.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/coder_envs_create.md b/docs/coder_envs_create.md index d728e34b..9e6ca581 100644 --- a/docs/coder_envs_create.md +++ b/docs/coder_envs_create.md @@ -30,6 +30,7 @@ coder envs create my-new-powerful-env --cpu 12 --disk 100 --memory 16 --image ub -i, --image string name of the image to base the environment off of. -m, --memory float32 GB of RAM an environment should be provisioned with. -o, --org string name of the organization the environment should be created under. + --provider string name of Workspace Provider with which to create the environment -t, --tag string tag of the image the environment will be based off of. (default "latest") ``` From 109ee8d9436ee4669d677fafbe9de9fb33e1c15e Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Mon, 8 Mar 2021 21:32:29 -0600 Subject: [PATCH 5/5] fixup! fix: use proper API type for resource pool list --- coder-sdk/workspace_providers.go | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/coder-sdk/workspace_providers.go b/coder-sdk/workspace_providers.go index db2ba5ee..cfd79860 100644 --- a/coder-sdk/workspace_providers.go +++ b/coder-sdk/workspace_providers.go @@ -12,19 +12,24 @@ type WorkspaceProviders struct { // KubernetesProvider defines an entity capable of deploying and acting as an ingress for Coder environments. type KubernetesProvider struct { - ID string `json:"id" table:"-"` - Name string `json:"name" table:"Name"` - Status WorkspaceProviderStatus `json:"status" table:"Status"` - Local bool `json:"local" table:"-"` - ClusterAddress string `json:"cluster_address" table:"Cluster Address"` - DefaultNamespace string `json:"default_namespace" table:"Namespace"` - StorageClass string `json:"storage_class" table:"Storage Class"` - ClusterDomainSuffix string `json:"cluster_domain_suffix" table:"Cluster Domain Suffix"` - EnvproxyAccessURL string `json:"envproxy_access_url" validate:"required" table:"Access URL"` - DevurlHost string `json:"devurl_host" table:"Devurl Host"` - SSHEnabled bool `json:"ssh_enabled" table:"SSH Enabled"` - NamespaceWhitelist []string `json:"namespace_whitelist" table:"Namespace Allowlist"` - OrgWhitelist []string `json:"org_whitelist" table:"-"` + ID string `json:"id" table:"-"` + Name string `json:"name" table:"Name"` + Status WorkspaceProviderStatus `json:"status" table:"Status"` + Local bool `json:"local" table:"-"` + EnvproxyAccessURL string `json:"envproxy_access_url" validate:"required" table:"Access URL"` + DevurlHost string `json:"devurl_host" table:"Devurl Host"` + OrgWhitelist []string `json:"org_whitelist" table:"-"` + KubeProviderConfig `json:"config"` +} + +// KubeProviderConfig defines Kubernetes-specific configuration options. +type KubeProviderConfig struct { + ClusterAddress string `json:"cluster_address" table:"Cluster Address"` + DefaultNamespace string `json:"default_namespace" table:"Namespace"` + StorageClass string `json:"storage_class" table:"Storage Class"` + ClusterDomainSuffix string `json:"cluster_domain_suffix" table:"Cluster Domain Suffix"` + SSHEnabled bool `json:"ssh_enabled" table:"SSH Enabled"` + NamespaceWhitelist []string `json:"namespace_whitelist" table:"Namespace Allowlist"` } // WorkspaceProviderStatus represents the configuration state of a workspace provider.