2022-03-22 19:17:50 +00:00
package cli
import (
2022-06-24 16:30:22 +00:00
"fmt"
2023-10-19 22:16:15 +00:00
"net/http"
2022-03-22 19:17:50 +00:00
"time"
2023-07-13 10:56:02 +00:00
"golang.org/x/xerrors"
2023-08-18 18:55:43 +00:00
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
2024-03-15 16:24:38 +00:00
"github.com/coder/serpent"
2022-03-22 19:17:50 +00:00
)
2024-03-17 14:45:26 +00:00
func ( r * RootCmd ) start ( ) * serpent . Command {
2023-07-13 10:56:02 +00:00
var parameterFlags workspaceParameterFlags
2023-03-23 22:42:20 +00:00
client := new ( codersdk . Client )
2024-03-17 14:45:26 +00:00
cmd := & serpent . Command {
2022-05-09 22:42:02 +00:00
Annotations : workspaceCommand ,
Use : "start <workspace>" ,
2022-09-19 16:36:18 +00:00
Short : "Start a workspace" ,
2024-03-15 16:24:38 +00:00
Middleware : serpent . Chain (
serpent . RequireNArgs ( 1 ) ,
2023-03-23 22:42:20 +00:00
r . InitClient ( client ) ,
) ,
2024-03-15 16:24:38 +00:00
Options : serpent . OptionSet { cliui . SkipPromptOption ( ) } ,
Handler : func ( inv * serpent . Invocation ) error {
2023-03-23 22:42:20 +00:00
workspace , err := namedWorkspace ( inv . Context ( ) , client , inv . Args [ 0 ] )
2022-03-22 19:17:50 +00:00
if err != nil {
return err
}
2024-01-03 09:24:37 +00:00
var build codersdk . WorkspaceBuild
switch workspace . LatestBuild . Status {
case codersdk . WorkspaceStatusRunning :
_ , _ = fmt . Fprintf (
inv . Stdout , "\nThe %s workspace is already running!\n" ,
cliui . Keyword ( workspace . Name ) ,
)
return nil
case codersdk . WorkspaceStatusStarting :
_ , _ = fmt . Fprintf (
inv . Stdout , "\nThe %s workspace is already starting.\n" ,
cliui . Keyword ( workspace . Name ) ,
)
build = workspace . LatestBuild
default :
build , err = startWorkspace ( inv , client , workspace , parameterFlags , WorkspaceStart )
// It's possible for a workspace build to fail due to the template requiring starting
// workspaces with the active version.
if cerr , ok := codersdk . AsError ( err ) ; ok && cerr . StatusCode ( ) == http . StatusForbidden {
2024-01-23 17:01:44 +00:00
_ , _ = fmt . Fprintln ( inv . Stdout , "Unable to start the workspace with the template version from the last build. Policy may require you to restart with the current active template version." )
2024-01-03 09:24:37 +00:00
build , err = startWorkspace ( inv , client , workspace , parameterFlags , WorkspaceUpdate )
if err != nil {
return xerrors . Errorf ( "start workspace with active template version: %w" , err )
}
} else if err != nil {
return err
2023-10-19 22:16:15 +00:00
}
2022-03-22 19:17:50 +00:00
}
2022-06-24 16:30:22 +00:00
2023-03-23 22:42:20 +00:00
err = cliui . WorkspaceBuild ( inv . Context ( ) , inv . Stdout , client , build . ID )
2022-06-24 16:30:22 +00:00
if err != nil {
return err
}
2023-09-07 21:28:22 +00:00
_ , _ = fmt . Fprintf (
inv . Stdout , "\nThe %s workspace has been started at %s!\n" ,
cliui . Keyword ( workspace . Name ) , cliui . Timestamp ( time . Now ( ) ) ,
)
2022-06-24 16:30:22 +00:00
return nil
2022-03-22 19:17:50 +00:00
} ,
}
2023-07-13 10:56:02 +00:00
2023-11-02 19:41:34 +00:00
cmd . Options = append ( cmd . Options , parameterFlags . allOptions ( ) ... )
2023-08-09 11:00:25 +00:00
2023-11-02 19:41:34 +00:00
return cmd
2023-07-13 10:56:02 +00:00
}
2024-03-15 16:24:38 +00:00
func buildWorkspaceStartRequest ( inv * serpent . Invocation , client * codersdk . Client , workspace codersdk . Workspace , parameterFlags workspaceParameterFlags , action WorkspaceCLIAction ) ( codersdk . CreateWorkspaceBuildRequest , error ) {
2023-11-02 19:41:34 +00:00
version := workspace . LatestBuild . TemplateVersionID
2024-01-23 17:01:44 +00:00
2023-11-02 19:41:34 +00:00
if workspace . AutomaticUpdates == codersdk . AutomaticUpdatesAlways || action == WorkspaceUpdate {
version = workspace . TemplateActiveVersionID
if version != workspace . LatestBuild . TemplateVersionID {
action = WorkspaceUpdate
}
}
2023-07-13 10:56:02 +00:00
2023-11-02 19:41:34 +00:00
lastBuildParameters , err := client . WorkspaceBuildParameters ( inv . Context ( ) , workspace . LatestBuild . ID )
2023-07-13 10:56:02 +00:00
if err != nil {
2023-11-02 19:41:34 +00:00
return codersdk . CreateWorkspaceBuildRequest { } , err
2023-07-13 10:56:02 +00:00
}
2023-11-02 19:41:34 +00:00
buildOptions , err := asWorkspaceBuildParameters ( parameterFlags . buildOptions )
2023-07-13 10:56:02 +00:00
if err != nil {
2023-11-02 19:41:34 +00:00
return codersdk . CreateWorkspaceBuildRequest { } , xerrors . Errorf ( "unable to parse build options: %w" , err )
2023-07-13 10:56:02 +00:00
}
2023-11-02 19:41:34 +00:00
cliRichParameters , err := asWorkspaceBuildParameters ( parameterFlags . richParameters )
2023-10-19 22:16:15 +00:00
if err != nil {
2024-04-29 18:23:54 +00:00
return codersdk . CreateWorkspaceBuildRequest { } , xerrors . Errorf ( "unable to parse rich parameters: %w" , err )
}
cliRichParameterDefaults , err := asWorkspaceBuildParameters ( parameterFlags . richParameterDefaults )
if err != nil {
return codersdk . CreateWorkspaceBuildRequest { } , xerrors . Errorf ( "unable to parse rich parameter defaults: %w" , err )
2023-10-19 22:16:15 +00:00
}
2023-11-02 19:41:34 +00:00
buildParameters , err := prepWorkspaceBuild ( inv , client , prepWorkspaceBuildArgs {
Action : action ,
TemplateVersionID : version ,
NewWorkspaceName : workspace . Name ,
LastBuildParameters : lastBuildParameters ,
2024-04-29 18:23:54 +00:00
PromptBuildOptions : parameterFlags . promptBuildOptions ,
BuildOptions : buildOptions ,
PromptRichParameters : parameterFlags . promptRichParameters ,
RichParameters : cliRichParameters ,
RichParameterFile : parameterFlags . richParameterFile ,
RichParameterDefaults : cliRichParameterDefaults ,
2023-10-19 22:16:15 +00:00
} )
if err != nil {
2023-11-02 19:41:34 +00:00
return codersdk . CreateWorkspaceBuildRequest { } , err
2023-10-19 22:16:15 +00:00
}
2023-11-02 19:41:34 +00:00
return codersdk . CreateWorkspaceBuildRequest {
2023-10-19 22:16:15 +00:00
Transition : codersdk . WorkspaceTransitionStart ,
RichParameterValues : buildParameters ,
2023-11-02 19:41:34 +00:00
TemplateVersionID : version ,
} , nil
}
2024-03-15 16:24:38 +00:00
func startWorkspace ( inv * serpent . Invocation , client * codersdk . Client , workspace codersdk . Workspace , parameterFlags workspaceParameterFlags , action WorkspaceCLIAction ) ( codersdk . WorkspaceBuild , error ) {
2024-01-16 22:42:04 +00:00
if workspace . DormantAt != nil {
_ , _ = fmt . Fprintln ( inv . Stdout , "Activating dormant workspace..." )
err := client . UpdateWorkspaceDormancy ( inv . Context ( ) , workspace . ID , codersdk . UpdateWorkspaceDormancy {
Dormant : false ,
} )
if err != nil {
return codersdk . WorkspaceBuild { } , xerrors . Errorf ( "activate workspace: %w" , err )
}
}
2023-11-02 19:41:34 +00:00
req , err := buildWorkspaceStartRequest ( inv , client , workspace , parameterFlags , action )
2023-10-19 22:16:15 +00:00
if err != nil {
return codersdk . WorkspaceBuild { } , err
}
2023-11-02 19:41:34 +00:00
build , err := client . CreateWorkspaceBuild ( inv . Context ( ) , workspace . ID , req )
if err != nil {
return codersdk . WorkspaceBuild { } , xerrors . Errorf ( "create workspace build: %w" , err )
}
2023-10-19 22:16:15 +00:00
return build , nil
}