Skip to content

Commit

Permalink
Add idle timeout to cs create parameters (cli#4741)
Browse files Browse the repository at this point in the history
Co-authored-by: Mislav Marohnić <[email protected]>
  • Loading branch information
reybard and mislav authored Nov 18, 2021
1 parent 235fdcd commit 90313fb
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 17 deletions.
32 changes: 23 additions & 9 deletions internal/codespaces/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,14 +461,17 @@ func (a *API) GetCodespacesMachines(ctx context.Context, repoID int, branch, loc

// CreateCodespaceParams are the required parameters for provisioning a Codespace.
type CreateCodespaceParams struct {
RepositoryID int
Branch, Machine, Location string
RepositoryID int
IdleTimeoutMinutes int
Branch string
Machine string
Location string
}

// CreateCodespace creates a codespace with the given parameters and returns a non-nil error if it
// fails to create.
func (a *API) CreateCodespace(ctx context.Context, params *CreateCodespaceParams) (*Codespace, error) {
codespace, err := a.startCreate(ctx, params.RepositoryID, params.Machine, params.Branch, params.Location)
codespace, err := a.startCreate(ctx, params)
if err != errProvisioningInProgress {
return codespace, err
}
Expand Down Expand Up @@ -503,10 +506,11 @@ func (a *API) CreateCodespace(ctx context.Context, params *CreateCodespaceParams
}

type startCreateRequest struct {
RepositoryID int `json:"repository_id"`
Ref string `json:"ref"`
Location string `json:"location"`
Machine string `json:"machine"`
RepositoryID int `json:"repository_id"`
IdleTimeoutMinutes int `json:"idle_timeout_minutes"`
Ref string `json:"ref"`
Location string `json:"location"`
Machine string `json:"machine"`
}

var errProvisioningInProgress = errors.New("provisioning in progress")
Expand All @@ -515,8 +519,18 @@ var errProvisioningInProgress = errors.New("provisioning in progress")
// It may return success or an error, or errProvisioningInProgress indicating that the operation
// did not complete before the GitHub API's time limit for RPCs (10s), in which case the caller
// must poll the server to learn the outcome.
func (a *API) startCreate(ctx context.Context, repoID int, machine, branch, location string) (*Codespace, error) {
requestBody, err := json.Marshal(startCreateRequest{repoID, branch, location, machine})
func (a *API) startCreate(ctx context.Context, params *CreateCodespaceParams) (*Codespace, error) {
if params == nil {
return nil, errors.New("startCreate missing parameters")
}

requestBody, err := json.Marshal(startCreateRequest{
RepositoryID: params.RepositoryID,
IdleTimeoutMinutes: params.IdleTimeoutMinutes,
Ref: params.Branch,
Location: params.Location,
Machine: params.Machine,
})
if err != nil {
return nil, fmt.Errorf("error marshaling request: %w", err)
}
Expand Down
20 changes: 12 additions & 8 deletions pkg/cmd/codespace/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"time"

"github.com/AlecAivazis/survey/v2"
"github.com/cli/cli/v2/internal/codespaces"
Expand All @@ -12,10 +13,11 @@ import (
)

type createOptions struct {
repo string
branch string
machine string
showStatus bool
repo string
branch string
machine string
showStatus bool
idleTimeout time.Duration
}

func newCreateCmd(app *App) *cobra.Command {
Expand All @@ -34,6 +36,7 @@ func newCreateCmd(app *App) *cobra.Command {
createCmd.Flags().StringVarP(&opts.branch, "branch", "b", "", "repository branch")
createCmd.Flags().StringVarP(&opts.machine, "machine", "m", "", "hardware specifications for the VM")
createCmd.Flags().BoolVarP(&opts.showStatus, "status", "s", false, "show status of post-create command and dotfiles")
createCmd.Flags().DurationVar(&opts.idleTimeout, "idle-timeout", 30*time.Minute, "allowed inactivity before codespace is stopped, e.g. \"10m\", \"1h\"")

return createCmd
}
Expand Down Expand Up @@ -101,10 +104,11 @@ func (a *App) Create(ctx context.Context, opts createOptions) error {

a.StartProgressIndicatorWithLabel("Creating codespace")
codespace, err := a.apiClient.CreateCodespace(ctx, &api.CreateCodespaceParams{
RepositoryID: repository.ID,
Branch: branch,
Machine: machine,
Location: locationResult.Location,
RepositoryID: repository.ID,
Branch: branch,
Machine: machine,
Location: locationResult.Location,
IdleTimeoutMinutes: int(opts.idleTimeout.Minutes()),
})
a.StopProgressIndicator()
if err != nil {
Expand Down
83 changes: 83 additions & 0 deletions pkg/cmd/codespace/create_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,92 @@
package codespace

import (
"context"
"fmt"
"testing"
"time"

"github.com/cli/cli/v2/internal/codespaces/api"
"github.com/cli/cli/v2/pkg/iostreams"
)

func TestApp_Create(t *testing.T) {
type fields struct {
apiClient apiClient
}
tests := []struct {
name string
fields fields
opts createOptions
wantErr bool
wantStdout string
wantStderr string
}{
{
name: "create codespace with default branch and 30m idle timeout",
fields: fields{
apiClient: &apiClientMock{
GetCodespaceRegionLocationFunc: func(ctx context.Context) (string, error) {
return "EUROPE", nil
},
GetRepositoryFunc: func(ctx context.Context, nwo string) (*api.Repository, error) {
return &api.Repository{
ID: 1234,
FullName: nwo,
DefaultBranch: "main",
}, nil
},
GetCodespacesMachinesFunc: func(ctx context.Context, repoID int, branch, location string) ([]*api.Machine, error) {
return []*api.Machine{
{
Name: "GIGA",
DisplayName: "Gigabits of a machine",
},
}, nil
},
CreateCodespaceFunc: func(ctx context.Context, params *api.CreateCodespaceParams) (*api.Codespace, error) {
if params.Branch != "main" {
return nil, fmt.Errorf("got branch %q, want %q", params.Branch, "main")
}
if params.IdleTimeoutMinutes != 30 {
return nil, fmt.Errorf("idle timeout minutes was %v", params.IdleTimeoutMinutes)
}
return &api.Codespace{
Name: "monalisa-dotfiles-abcd1234",
}, nil
},
},
},
opts: createOptions{
repo: "monalisa/dotfiles",
branch: "",
machine: "GIGA",
showStatus: false,
idleTimeout: 30 * time.Minute,
},
wantStdout: "monalisa-dotfiles-abcd1234\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
io, _, stdout, stderr := iostreams.Test()
a := &App{
io: io,
apiClient: tt.fields.apiClient,
}
if err := a.Create(context.Background(), tt.opts); (err != nil) != tt.wantErr {
t.Errorf("App.Create() error = %v, wantErr %v", err, tt.wantErr)
}
if got := stdout.String(); got != tt.wantStdout {
t.Errorf("stdout = %v, want %v", got, tt.wantStdout)
}
if got := stderr.String(); got != tt.wantStderr {
t.Errorf("stderr = %v, want %v", got, tt.wantStderr)
}
})
}
}

func TestBuildDisplayName(t *testing.T) {
tests := []struct {
name string
Expand Down

0 comments on commit 90313fb

Please sign in to comment.