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
14 changes: 9 additions & 5 deletions api/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ type DeployWorkspaceArgs struct {
PlanId int
Name string
EnvVars map[string]string
VpnConfigName string
VpnConfigName *string //must be nil to use default

IsPrivateRepo bool
GitUrl *string //must be nil to use default
Branch *string //must be nil to use default

Timeout time.Duration
}
Expand All @@ -108,13 +112,13 @@ func (client Client) DeployWorkspace(args DeployWorkspaceArgs) (*Workspace, erro
TeamId: args.TeamId,
Name: args.Name,
PlanId: args.PlanId,
IsPrivateRepo: true,
GitUrl: nil,
InitialBranch: nil,
IsPrivateRepo: args.IsPrivateRepo,
GitUrl: args.GitUrl,
InitialBranch: args.Branch,
SourceWorkspaceId: nil,
WelcomeMessage: nil,
Replicas: 1,
VpnConfig: &args.VpnConfigName,
VpnConfig: args.VpnConfigName,
})
if err != nil {
return nil, err
Expand Down
2 changes: 2 additions & 0 deletions cli/cmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type Client interface {
GetWorkspace(workspaceId int) (api.Workspace, error)
SetEnvVarOnWorkspace(workspaceId int, vars map[string]string) error
ExecCommand(workspaceId int, command string, workdir string, env map[string]string) (string, string, error)
ListWorkspacePlans() ([]api.WorkspacePlan, error)
DeployWorkspace(args api.DeployWorkspaceArgs) (*api.Workspace, error)
}

func NewClient(opts GlobalOptions) (Client, error) {
Expand Down
149 changes: 149 additions & 0 deletions cli/cmd/create-workspace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package cmd

import (
"errors"
"fmt"
"os"
"time"

"github.com/codesphere-cloud/cs-go/api"
"github.com/codesphere-cloud/cs-go/pkg/cs"
"github.com/codesphere-cloud/cs-go/pkg/out"
"github.com/spf13/cobra"
)

// CreateWorkspaceCmd represents the workspace command
type CreateWorkspaceCmd struct {
cmd *cobra.Command
Opts CreateWorkspaceOpts
}

type CreateWorkspaceOpts struct {
GlobalOptions
Repo *string
Vpn *string
Env *[]string
Plan *int
Private *bool
Timeout *time.Duration
Branch *string
}

func (c *CreateWorkspaceCmd) RunE(_ *cobra.Command, args []string) error {
client, err := NewClient(c.Opts.GlobalOptions)
if err != nil {
return fmt.Errorf("failed to create Codesphere client: %w", err)
}

teamId, err := c.Opts.GetTeamId()
if err != nil {
return fmt.Errorf("failed to get team ID: %w", err)
}

if len(args) != 1 {
return errors.New("workspace name not set")
}
wsName := args[0]

ws, err := c.CreateWorkspace(client, teamId, wsName)
if err != nil {
return fmt.Errorf("failed to create workspace: %w", err)
}

giturl := ""
if ws.GitUrl.Get() != nil {
giturl = *ws.GitUrl.Get()
}
branch := ""
if ws.InitialBranch.Get() != nil {
branch = *ws.InitialBranch.Get()
}
fmt.Println("Workspace created:")
fmt.Printf("\nID: %d\n", ws.Id)
fmt.Printf("Name: %s\n", ws.Name)
fmt.Printf("Team ID: %d\n", ws.TeamId)
fmt.Printf("Git Repository: %s\n", giturl)
fmt.Printf("Branch: %s\n", branch)
fmt.Printf("To open it in the Codesphere IDE run '%s open workspace -w %d'", os.Args[0], ws.Id)

return nil
}

func AddCreateWorkspaceCmd(create *cobra.Command, opts GlobalOptions) {
workspace := CreateWorkspaceCmd{
cmd: &cobra.Command{
Use: "workspace",
Short: "Create a workspace",
Args: cobra.RangeArgs(1, 1),
Long: out.Long(`Create a workspace in Codesphere.

Specify a (private) git repository or start an empty workspace.
Environment variables can be set to initialize the workspace with a specific environment.
The command will wait for the workspace to become running or a timeout is reached.

To decide which plan suits your needs, run 'cs list plans'
`),
Example: out.FormatExampleCommands("create workspace my-workspace", []out.Example{
{Cmd: "-p 20", Desc: "Create an empty workspace, using plan 20"},
{Cmd: "-r https://github.com/codesphere-cloud/landingpage-temp.git", Desc: "Create a workspace from a git repository"},
{Cmd: "-r https://github.com/codesphere-cloud/landingpage-temp.git -e DEPLOYMENT=prod -e A=B", Desc: "Create a workspace and set environment variables"},
{Cmd: "-r https://github.com/codesphere-cloud/landingpage-temp.git --vpn myVpn", Desc: "Create a workspace and connect to VPN myVpn"},
{Cmd: "-r https://github.com/codesphere-cloud/landingpage-temp.git --timeout 30s", Desc: "Create a workspace and wait 30 seconds for it to become running"},
{Cmd: "-r https://github.com/codesphere-cloud/landingpage-temp.git -b staging", Desc: "Create a workspace from branch 'staging'"},
{Cmd: "-r https://github.com/my-org/my-private-project.git -P", Desc: "Create a workspace from a private git repository"},
}),
},
Opts: CreateWorkspaceOpts{GlobalOptions: opts},
}
workspace.Opts.Repo = workspace.cmd.Flags().StringP("repository", "r", "", "Git repository to create the workspace from")
workspace.Opts.Vpn = workspace.cmd.Flags().String("vpn", "", "Vpn config to use")
workspace.Opts.Env = workspace.cmd.Flags().StringArrayP("env", "e", []string{}, "Environment variables to set in the workspace in key=value form (e.g. --env DEPLOYMENT=prod)")
workspace.Opts.Plan = workspace.cmd.Flags().IntP("plan", "p", 8, "Plan ID for the workspace")
workspace.Opts.Private = workspace.cmd.Flags().BoolP("private", "P", false, "Use private repository")
workspace.Opts.Timeout = workspace.cmd.Flags().Duration("timeout", 10*time.Minute, "Time to wait for the workspace to start (e.g. 5m for 5 minutes)")
workspace.Opts.Branch = workspace.cmd.Flags().StringP("branch", "b", "", "branch to check out")

create.AddCommand(workspace.cmd)
workspace.cmd.RunE = workspace.RunE
}

func (c *CreateWorkspaceCmd) CreateWorkspace(client Client, teamId int, wsName string) (*api.Workspace, error) {
envVars, err := cs.ArgToEnvVarMap(*c.Opts.Env)
if err != nil {
return nil, fmt.Errorf("failed to parse environment variables: %w", err)
}

args := api.DeployWorkspaceArgs{
TeamId: teamId,
PlanId: *c.Opts.Plan,
Name: wsName,
EnvVars: envVars,

IsPrivateRepo: *c.Opts.Private,

Timeout: *c.Opts.Timeout,
}

if c.Opts.Repo != nil && *c.Opts.Repo != "" {
validatedUrl, err := cs.ValidateUrl(*c.Opts.Repo)
if err != nil {
return nil, fmt.Errorf("validation of repository URL failed: %w", err)
}
args.GitUrl = &validatedUrl
}

if c.Opts.Vpn != nil && *c.Opts.Vpn != "" {
args.VpnConfigName = c.Opts.Vpn
}

if c.Opts.Branch != nil && *c.Opts.Branch != "" {
args.Branch = c.Opts.Branch
}

ws, err := client.DeployWorkspace(args)
if err != nil {
return nil, fmt.Errorf("failed to create workspace: %w", err)
}

return ws, nil
}
24 changes: 24 additions & 0 deletions cli/cmd/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cmd

import (
"github.com/spf13/cobra"
)

// CreateCmd represents the create command
type CreateCmd struct {
cmd *cobra.Command
}

func AddCreateCmd(rootCmd *cobra.Command, opts GlobalOptions) {
create := CreateCmd{
cmd: &cobra.Command{
Use: "create",
Short: "Create codesphere resource",
Long: `Create codesphere resources like workspaces.`,
},
}
rootCmd.AddCommand(create.cmd)

// Add child commands here
AddCreateWorkspaceCmd(create.cmd, opts)
}
110 changes: 110 additions & 0 deletions cli/cmd/create_workspace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package cmd_test

import (
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

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

var _ = Describe("CreateWorkspace", func() {
var (
mockEnv *cmd.MockEnv
mockClient *cmd.MockClient
c *cmd.CreateWorkspaceCmd
teamId int
wsName string
env []string
repoStr string
repo *string
vpn *string
vpnStr string
plan int
private bool
timeout time.Duration
branchStr string
branch *string
deployArgs api.DeployWorkspaceArgs
)

BeforeEach(func() {
env = []string{}
repoStr = "https://fake-git.com/my/repo.git"
repo = nil
vpnStr = "MyVpn"
vpn = nil
plan = 8
private = false
timeout = 30 * time.Second
branchStr = "fake-branch"
branch = nil
})

JustBeforeEach(func() {
mockClient = cmd.NewMockClient(GinkgoT())
mockEnv = cmd.NewMockEnv(GinkgoT())
wsName = "foo-workspace"
teamId = 21
c = &cmd.CreateWorkspaceCmd{
Opts: cmd.CreateWorkspaceOpts{
GlobalOptions: cmd.GlobalOptions{
Env: mockEnv,
TeamId: &teamId,
},
Env: &env,
Repo: repo,
Vpn: vpn,
Plan: &plan,
Private: &private,
Timeout: &timeout,
Branch: branch,
},
}
envMap, err := cs.ArgToEnvVarMap(env)
Expect(err).ToNot(HaveOccurred())
deployArgs = api.DeployWorkspaceArgs{
Name: wsName,
TeamId: teamId,
EnvVars: envMap,
GitUrl: repo,
VpnConfigName: vpn,
PlanId: plan,
IsPrivateRepo: private,
Timeout: timeout,
Branch: branch,
}
})

Context("Minimal values are set", func() {

It("Creates the workspace", func() {
mockClient.EXPECT().DeployWorkspace(deployArgs).Return(&api.Workspace{Name: wsName}, nil)
ws, err := c.CreateWorkspace(mockClient, teamId, wsName)
Expect(err).ToNot(HaveOccurred())
Expect(ws.Name).To(Equal(wsName))
})
})

Context("All values are set", func() {
BeforeEach(func() {
env = []string{"foo=bla", "blib=blub"}
repo = &repoStr
vpn = &vpnStr
private = true
timeout = 120 * time.Second
branch = &branchStr
wsName = "different-name"
})
It("Creates the workspace and passes expected arguments", func() {
mockClient.EXPECT().DeployWorkspace(deployArgs).Return(&api.Workspace{Name: wsName}, nil)
ws, err := c.CreateWorkspace(mockClient, teamId, wsName)
Expect(err).ToNot(HaveOccurred())
Expect(ws.Name).To(Equal(wsName))
})
})

})
4 changes: 2 additions & 2 deletions cli/cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func AddExecCmd(rootCmd *cobra.Command, opts GlobalOptions) {
Use: "exec",
Args: cobra.MinimumNArgs(1),
Short: "Run a command in Codesphere workspace",
Long: `Run a command in a Codesphere workspace.
Output will be printed to STDOUT, errors to STDERR.`,
Long: out.Long(`Run a command in a Codesphere workspace.
Output will be printed to STDOUT, errors to STDERR.`),
Example: out.FormatExampleCommands("exec", []out.Example{
{Cmd: "-- echo hello world", Desc: "Print `hello world`"},
{Cmd: "-- find .", Desc: "List all files in workspace"},
Expand Down
Loading