Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ generate-client:
rm -rf ${OPENAPI_DIR}
openapi-generator-cli generate -g go -o ${OPENAPI_DIR} -i https://codesphere.com/api/docs \
--additional-properties=isGoSubmodule=true,withGoMod=false,packageName=openapi_client \
--type-mappings=integer=int \
--type-mappings=integer=int \
--template-dir openapi-template \
--skip-validate-spec # TODO: remove once the Codesphere openapi spec is fixed
# Remove all non-go files
Expand Down
28 changes: 28 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,31 @@ func (c *Client) ListWorkspaces(teamId int) ([]Workspace, error) {
workspaces, _, err := c.api.WorkspacesAPI.WorkspacesListWorkspaces(c.ctx, float32(teamId)).Execute()
return workspaces, err
}

func (c *Client) WorkspaceStatus(workspaceId int) (*WorkspaceStatus, error) {
status, _, err := c.api.WorkspacesAPI.WorkspacesGetWorkspaceStatus(c.ctx, float32(workspaceId)).Execute()
return status, err
}

func (c *Client) CreateWorkspace(args CreateWorkspaceArgs) (*Workspace, error) {
workspace, _, err := c.api.WorkspacesAPI.
WorkspacesCreateWorkspace(c.ctx).
WorkspacesCreateWorkspaceRequest(args).
Execute()
return workspace, err
}

func (c *Client) SetEnvVarOnWorkspace(workspaceId int, envVars map[string]string) error {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure on the naming here. WorkspaceSetEnvVars sounded a bit strange

vars := []openapi_client.WorkspacesListEnvVars200ResponseInner{}
for k, v := range envVars {
vars = append(vars, openapi_client.WorkspacesListEnvVars200ResponseInner{
Name: k,
Value: v,
})
}
_, err := c.api.WorkspacesAPI.
WorkspacesSetEnvVar(c.ctx, float32(workspaceId)).
WorkspacesListEnvVars200ResponseInner(vars).
Execute()
return err
}
3 changes: 3 additions & 0 deletions api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ type Domain = openapi.DomainsGetDomain200Response
type DomainVerificationStatus = openapi.DomainsGetDomain200ResponseDomainVerificationStatus
type UpdateDomainArgs = openapi.DomainsGetDomain200ResponseCustomConfig
type PathToWorkspaces = map[string][]*Workspace

type Workspace = openapi.WorkspacesGetWorkspace200Response
type WorkspaceStatus = openapi.WorkspacesGetWorkspaceStatus200Response
type CreateWorkspaceArgs = openapi.WorkspacesCreateWorkspaceRequest
type WorkspacePlan = openapi.MetadataGetWorkspacePlans200ResponseInner

// TODO: remove the conversion once the api is fixed
Expand Down
48 changes: 48 additions & 0 deletions pkg/cs/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cs

import (
"fmt"
"time"
)

type TimedOut struct {
msg string
}

func (e *TimedOut) Error() string {
return e.msg
}

func NewTimedOut(operation string, timeout time.Duration) *TimedOut {
return &TimedOut{
msg: fmt.Sprintf("%s timed out after %s", operation, timeout.String()),
}
}

type NotFound struct {
msg string
}

func (e *NotFound) Error() string {
return e.msg
}

func NewNotFound(msg string) *NotFound {
return &NotFound{
msg: msg,
}
}

type Duplicated struct {
msg string
}

func (e *Duplicated) Error() string {
return e.msg
}

func NewDuplicated(msg string) *Duplicated {
return &Duplicated{
msg: msg,
}
}
54 changes: 54 additions & 0 deletions pkg/cs/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"net/http"
"os"
"strings"

"github.com/codesphere-cloud/cs-go/api"
)

type Step struct {
Expand Down Expand Up @@ -94,3 +96,55 @@ func GetRoleName(role int) string {
}
return "Admin"
}

// Fetches the team for a given team name.
// If the name is ambigous an error is thrown.
//
// Returns [NotFound] if no plan with the given Id could be found
// Returns [Duplicated] if no plan with the given Id could be found
func TeamIdByName(
client *api.Client,
name string,
) (api.Team, error) {
teams, err := client.ListTeams()
if err != nil {
return api.Team{}, err
}

matchingTeams := []api.Team{}
for _, t := range teams {
if t.Name == name {
matchingTeams = append(matchingTeams, t)
}
}

if len(matchingTeams) == 0 {
return api.Team{}, NewNotFound(fmt.Sprintf("No team with name %s found", name))
}

if len(matchingTeams) > 1 {
return api.Team{}, NewDuplicated(fmt.Sprintf("Multiple teams (%v) with the name %s found.", matchingTeams, name))
}

return matchingTeams[0], nil
}

// Fetches the workspace plan for a given name.
//
// Returns [NotFound] if no plan with the given Id could be found
func PlanByName(
client *api.Client,
name string,
) (api.WorkspacePlan, error) {
plans, err := client.ListWorkspacePlans()
if err != nil {
return api.WorkspacePlan{}, err
}

for _, p := range plans {
if p.Title == name {
return p, nil
}
}
return api.WorkspacePlan{}, NewNotFound(fmt.Sprintf("No team with name %s found", name))
}
93 changes: 93 additions & 0 deletions pkg/cs/workspace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package cs

import (
"fmt"
"time"

"github.com/codesphere-cloud/cs-go/api"
)

type WaitForWorkspaceRunningOptions struct {
Timeout time.Duration
Delay time.Duration
}

// Waits for a given workspace to be running.
//
// Returns [TimedOut] error if the workspace does not become running in time.
func WaitForWorkspaceRunning(
client *api.Client,
workspace *api.Workspace,
opts WaitForWorkspaceRunningOptions,
) error {
timeout := opts.Timeout
if timeout == 0 {
timeout = 20 * time.Minute
}
delay := opts.Delay
if delay == 0 {
delay = 5 * time.Second
}

maxWaitTime := time.Now().Add(timeout)
for time.Now().Before(maxWaitTime) {
status, err := client.WorkspaceStatus(workspace.Id)

if err != nil {
// TODO: log error and retry until timeout is reached.
return err
}
if status.IsRunning {
return nil
}
time.Sleep(delay)
}

return NewTimedOut(
fmt.Sprintf("Waiting for workspace %s(%d) to be ready", workspace.Name, workspace.Id),
timeout)
}

type DeployWorkspaceArgs struct {
TeamId int
PlanId int
Name string
EnvVars map[string]string
VpnConfigName *string

Timeout time.Duration
}

// Deploys a workspace with the given configuration.
//
// Returns [TimedOut] error if the timeout is reached
func DeployWorkspace(
client api.Client,
args DeployWorkspaceArgs,
) error {
workspace, err := client.CreateWorkspace(api.CreateWorkspaceArgs{
TeamId: args.TeamId,
Name: args.Name,
PlanId: args.PlanId,
IsPrivateRepo: true,
GitUrl: nil,
InitialBranch: nil,
SourceWorkspaceId: nil,
WelcomeMessage: nil,
Replicas: 1,
VpnConfig: args.VpnConfigName,
})
if err != nil {
return err
}
if err := WaitForWorkspaceRunning(&client, workspace, WaitForWorkspaceRunningOptions{Timeout: args.Timeout}); err != nil {
return err
}

if len(args.EnvVars) != 0 {
if err := client.SetEnvVarOnWorkspace(workspace.Id, args.EnvVars); err != nil {
return err
}
}
return nil
}