Skip to content

Commit 42790b6

Browse files
committed
feat(deploy): add utils to deploy a workspace
1 parent ef531a5 commit 42790b6

File tree

6 files changed

+225
-1
lines changed

6 files changed

+225
-1
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ generate-client:
2626
rm -rf ${OPENAPI_DIR}
2727
openapi-generator-cli generate -g go -o ${OPENAPI_DIR} -i https://codesphere.com/api/docs \
2828
--additional-properties=isGoSubmodule=true,withGoMod=false,packageName=openapi_client \
29-
--type-mappings=integer=int \
29+
--type-mappings=integer=int \
3030
--template-dir openapi-template \
3131
--skip-validate-spec # TODO: remove once the Codesphere openapi spec is fixed
3232
# Remove all non-go files

api/client.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,31 @@ func (c *Client) ListWorkspaces(teamId int) ([]Workspace, error) {
140140
workspaces, _, err := c.api.WorkspacesAPI.WorkspacesListWorkspaces(c.ctx, float32(teamId)).Execute()
141141
return workspaces, err
142142
}
143+
144+
func (c *Client) WorkspaceStatus(workspaceId int) (*WorkspaceStatus, error) {
145+
status, _, err := c.api.WorkspacesAPI.WorkspacesGetWorkspaceStatus(c.ctx, float32(workspaceId)).Execute()
146+
return status, err
147+
}
148+
149+
func (c *Client) CreateWorkspace(args CreateWorkspaceArgs) (*Workspace, error) {
150+
workspace, _, err := c.api.WorkspacesAPI.
151+
WorkspacesCreateWorkspace(c.ctx).
152+
WorkspacesCreateWorkspaceRequest(args).
153+
Execute()
154+
return workspace, err
155+
}
156+
157+
func (c *Client) SetEnvVarOnWorkspace(workspaceId int, envVars map[string]string) error {
158+
vars := []openapi_client.WorkspacesListEnvVars200ResponseInner{}
159+
for k, v := range envVars {
160+
vars = append(vars, openapi_client.WorkspacesListEnvVars200ResponseInner{
161+
Name: k,
162+
Value: v,
163+
})
164+
}
165+
_, err := c.api.WorkspacesAPI.
166+
WorkspacesSetEnvVar(c.ctx, float32(workspaceId)).
167+
WorkspacesListEnvVars200ResponseInner(vars).
168+
Execute()
169+
return err
170+
}

api/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ type Domain = openapi.DomainsGetDomain200Response
1313
type DomainVerificationStatus = openapi.DomainsGetDomain200ResponseDomainVerificationStatus
1414
type UpdateDomainArgs = openapi.DomainsGetDomain200ResponseCustomConfig
1515
type PathToWorkspaces = map[string][]*Workspace
16+
1617
type Workspace = openapi.WorkspacesGetWorkspace200Response
18+
type WorkspaceStatus = openapi.WorkspacesGetWorkspaceStatus200Response
19+
type CreateWorkspaceArgs = openapi.WorkspacesCreateWorkspaceRequest
1720
type WorkspacePlan = openapi.MetadataGetWorkspacePlans200ResponseInner
1821

1922
// TODO: remove the conversion once the api is fixed

pkg/cs/errors.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package cs
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
type TimedOut struct {
9+
msg string
10+
}
11+
12+
func (e *TimedOut) Error() string {
13+
return e.msg
14+
}
15+
16+
func NewTimedOut(operation string, timeout time.Duration) *TimedOut {
17+
return &TimedOut{
18+
msg: fmt.Sprintf("%s timed out after %s", operation, timeout.String()),
19+
}
20+
}
21+
22+
type NotFound struct {
23+
msg string
24+
}
25+
26+
func (e *NotFound) Error() string {
27+
return e.msg
28+
}
29+
30+
func NewNotFound(msg string) *NotFound {
31+
return &NotFound{
32+
msg: msg,
33+
}
34+
}
35+
36+
type Duplicated struct {
37+
msg string
38+
}
39+
40+
func (e *Duplicated) Error() string {
41+
return e.msg
42+
}
43+
44+
func NewDuplicated(msg string) *Duplicated {
45+
return &Duplicated{
46+
msg: msg,
47+
}
48+
}

pkg/cs/util.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"net/http"
1212
"os"
1313
"strings"
14+
15+
"github.com/codesphere-cloud/cs-go/api"
1416
)
1517

1618
type Step struct {
@@ -94,3 +96,55 @@ func GetRoleName(role int) string {
9496
}
9597
return "Admin"
9698
}
99+
100+
// Fetches the team for a given team name.
101+
// If the name is ambigous an error is thrown.
102+
//
103+
// Returns [NotFound] if no plan with the given Id could be found
104+
// Returns [Duplicated] if no plan with the given Id could be found
105+
func TeamIdByName(
106+
client *api.Client,
107+
name string,
108+
) (api.Team, error) {
109+
teams, err := client.ListTeams()
110+
if err != nil {
111+
return api.Team{}, err
112+
}
113+
114+
matchingTeams := []api.Team{}
115+
for _, t := range teams {
116+
if t.Name == name {
117+
matchingTeams = append(matchingTeams, t)
118+
}
119+
}
120+
121+
if len(matchingTeams) == 0 {
122+
return api.Team{}, NewNotFound(fmt.Sprintf("No team with name %s found", name))
123+
}
124+
125+
if len(matchingTeams) > 1 {
126+
return api.Team{}, NewDuplicated(fmt.Sprintf("Multiple teams (%v) with the name %s found.", matchingTeams, name))
127+
}
128+
129+
return matchingTeams[0], nil
130+
}
131+
132+
// Fetches the workspace plan for a given name.
133+
//
134+
// Returns [NotFound] if no plan with the given Id could be found
135+
func PlanByName(
136+
client *api.Client,
137+
name string,
138+
) (api.WorkspacePlan, error) {
139+
plans, err := client.ListWorkspacePlans()
140+
if err != nil {
141+
return api.WorkspacePlan{}, err
142+
}
143+
144+
for _, p := range plans {
145+
if p.Title == name {
146+
return p, nil
147+
}
148+
}
149+
return api.WorkspacePlan{}, NewNotFound(fmt.Sprintf("No team with name %s found", name))
150+
}

pkg/cs/workspace.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package cs
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/codesphere-cloud/cs-go/api"
8+
)
9+
10+
type WaitForWorkspaceRunningOptions struct {
11+
Timeout time.Duration
12+
Delay time.Duration
13+
}
14+
15+
// Waits for a given workspace to be running.
16+
//
17+
// Returns [TimedOut] error if the workspace does not become running in time.
18+
func WaitForWorkspaceRunning(
19+
client *api.Client,
20+
workspace *api.Workspace,
21+
opts WaitForWorkspaceRunningOptions,
22+
) error {
23+
timeout := opts.Timeout
24+
if timeout == 0 {
25+
timeout = 20 * time.Minute
26+
}
27+
delay := opts.Delay
28+
if delay == 0 {
29+
delay = 5 * time.Second
30+
}
31+
32+
maxWaitTime := time.Now().Add(timeout)
33+
for time.Now().Before(maxWaitTime) {
34+
status, err := client.WorkspaceStatus(workspace.Id)
35+
36+
if err != nil {
37+
// TODO: log error and retry until timeout is reached.
38+
return err
39+
}
40+
if status.IsRunning {
41+
return nil
42+
}
43+
time.Sleep(delay)
44+
}
45+
46+
return NewTimedOut(
47+
fmt.Sprintf("Waiting for workspace %s(%d) to be ready", workspace.Name, workspace.Id),
48+
timeout)
49+
}
50+
51+
type DeployWorkspaceArgs struct {
52+
TeamId int
53+
PlanId int
54+
Name string
55+
EnvVars map[string]string
56+
VpnConfigName *string
57+
58+
Timeout time.Duration
59+
}
60+
61+
// Deploys a workspace with the given configuration.
62+
//
63+
// Returns [TimedOut] error if the timeout is reached
64+
func DeployWorkspace(
65+
client api.Client,
66+
args DeployWorkspaceArgs,
67+
) error {
68+
workspace, err := client.CreateWorkspace(api.CreateWorkspaceArgs{
69+
TeamId: args.TeamId,
70+
Name: args.Name,
71+
PlanId: args.PlanId,
72+
IsPrivateRepo: true,
73+
GitUrl: nil,
74+
InitialBranch: nil,
75+
SourceWorkspaceId: nil,
76+
WelcomeMessage: nil,
77+
Replicas: 1,
78+
VpnConfig: args.VpnConfigName,
79+
})
80+
if err != nil {
81+
return err
82+
}
83+
WaitForWorkspaceRunning(&client, workspace, WaitForWorkspaceRunningOptions{Timeout: args.Timeout})
84+
85+
if len(args.EnvVars) != 0 {
86+
if err := client.SetEnvVarOnWorkspace(workspace.Id, args.EnvVars); err != nil {
87+
return err
88+
}
89+
}
90+
return nil
91+
}

0 commit comments

Comments
 (0)