2022-06-08 14:14:57 +00:00
package cli
import (
"fmt"
2023-03-07 14:14:58 +00:00
"net/http"
2023-07-20 13:35:41 +00:00
"strings"
2022-06-08 14:14:57 +00:00
"time"
"golang.org/x/xerrors"
2023-09-07 21:28:22 +00:00
"github.com/coder/pretty"
2024-03-15 16:24:38 +00:00
"github.com/coder/serpent"
2023-09-07 21:28:22 +00:00
2023-08-18 18:55:43 +00:00
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
2022-06-08 14:14:57 +00:00
)
2024-03-17 14:45:26 +00:00
func ( r * RootCmd ) templateEdit ( ) * serpent . Command {
2023-11-20 19:16:18 +00:00
const deprecatedFlagName = "deprecated"
2022-06-08 14:14:57 +00:00
var (
2023-10-13 16:57:18 +00:00
name string
displayName string
description string
icon string
defaultTTL time . Duration
2024-02-13 07:00:35 +00:00
activityBump time . Duration
2023-10-13 16:57:18 +00:00
autostopRequirementDaysOfWeek [ ] string
autostopRequirementWeeks int64
autostartRequirementDaysOfWeek [ ] string
failureTTL time . Duration
2023-11-22 00:20:01 +00:00
dormancyThreshold time . Duration
dormancyAutoDeletion time . Duration
2023-10-13 16:57:18 +00:00
allowUserCancelWorkspaceJobs bool
allowUserAutostart bool
allowUserAutostop bool
2023-10-19 22:16:15 +00:00
requireActiveVersion bool
2023-11-20 19:16:18 +00:00
deprecationMessage string
2024-01-05 21:04:14 +00:00
disableEveryone bool
2022-06-08 14:14:57 +00:00
)
2023-03-23 22:42:20 +00:00
client := new ( codersdk . Client )
2022-06-08 14:14:57 +00:00
2024-03-17 14:45:26 +00:00
cmd := & serpent . Command {
2023-03-23 22:42:20 +00:00
Use : "edit <template>" ,
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 ) ,
) ,
2022-06-08 14:14:57 +00:00
Short : "Edit the metadata of a template by name." ,
2024-03-15 16:24:38 +00:00
Handler : func ( inv * serpent . Invocation ) error {
2023-08-29 18:35:05 +00:00
unsetAutostopRequirementDaysOfWeek := len ( autostopRequirementDaysOfWeek ) == 1 && autostopRequirementDaysOfWeek [ 0 ] == "none"
2023-10-19 22:16:15 +00:00
requiresScheduling := ( len ( autostopRequirementDaysOfWeek ) > 0 && ! unsetAutostopRequirementDaysOfWeek ) ||
2023-08-29 18:35:05 +00:00
autostopRequirementWeeks > 0 ||
2023-07-20 13:35:41 +00:00
! allowUserAutostart ||
! allowUserAutostop ||
failureTTL != 0 ||
2023-11-22 00:20:01 +00:00
dormancyThreshold != 0 ||
dormancyAutoDeletion != 0 ||
2023-10-13 16:57:18 +00:00
len ( autostartRequirementDaysOfWeek ) > 0
2023-10-19 22:16:15 +00:00
requiresEntitlement := requiresScheduling || requireActiveVersion
2023-07-20 13:35:41 +00:00
if requiresEntitlement {
2023-03-23 22:42:20 +00:00
entitlements , err := client . Entitlements ( inv . Context ( ) )
2023-10-19 22:16:15 +00:00
if cerr , ok := codersdk . AsError ( err ) ; ok && cerr . StatusCode ( ) == http . StatusNotFound {
return xerrors . Errorf ( "your deployment appears to be an AGPL deployment, so you cannot set enterprise-only flags" )
2023-03-07 14:14:58 +00:00
} else if err != nil {
return xerrors . Errorf ( "get entitlements: %w" , err )
}
2023-10-19 22:16:15 +00:00
if requiresScheduling && ! entitlements . Features [ codersdk . FeatureAdvancedTemplateScheduling ] . Enabled {
2024-03-20 15:37:57 +00:00
return xerrors . Errorf ( "your license is not entitled to use advanced template scheduling, so you cannot set --failure-ttl, --inactivityTTL, --allow-user-autostart=false or --allow-user-autostop=false" )
2023-03-07 14:14:58 +00:00
}
2023-10-19 22:16:15 +00:00
if requireActiveVersion {
if ! entitlements . Features [ codersdk . FeatureAccessControl ] . Enabled {
return xerrors . Errorf ( "your license is not entitled to use enterprise access control, so you cannot set --require-active-version" )
}
}
2023-03-07 14:14:58 +00:00
}
2024-02-26 16:03:49 +00:00
organization , err := CurrentOrganization ( r , inv , client )
2022-06-08 14:14:57 +00:00
if err != nil {
return xerrors . Errorf ( "get current organization: %w" , err )
}
2023-03-23 22:42:20 +00:00
template , err := client . TemplateByName ( inv . Context ( ) , organization . ID , inv . Args [ 0 ] )
2022-06-08 14:14:57 +00:00
if err != nil {
return xerrors . Errorf ( "get workspace template: %w" , err )
}
2024-01-11 22:18:46 +00:00
// Default values
if ! userSetOption ( inv , "description" ) {
description = template . Description
2023-07-20 13:35:41 +00:00
}
2024-01-11 22:18:46 +00:00
if ! userSetOption ( inv , "icon" ) {
icon = template . Icon
2023-10-13 16:57:18 +00:00
}
2024-01-11 22:18:46 +00:00
if ! userSetOption ( inv , "display-name" ) {
displayName = template . DisplayName
2023-07-20 13:35:41 +00:00
}
2024-01-11 22:18:46 +00:00
if ! userSetOption ( inv , "default-ttl" ) {
defaultTTL = time . Duration ( template . DefaultTTLMillis ) * time . Millisecond
}
2024-02-13 07:00:35 +00:00
if ! userSetOption ( inv , "activity-bump" ) {
activityBump = time . Duration ( template . ActivityBumpMillis ) * time . Millisecond
}
2024-01-11 22:18:46 +00:00
if ! userSetOption ( inv , "allow-user-autostop" ) {
allowUserAutostop = template . AllowUserAutostop
}
if ! userSetOption ( inv , "allow-user-autostart" ) {
allowUserAutostart = template . AllowUserAutostart
}
if ! userSetOption ( inv , "allow-user-cancel-workspace-jobs" ) {
allowUserCancelWorkspaceJobs = template . AllowUserCancelWorkspaceJobs
}
if ! userSetOption ( inv , "failure-ttl" ) {
2023-11-22 00:20:01 +00:00
failureTTL = time . Duration ( template . FailureTTLMillis ) * time . Millisecond
}
2024-01-11 22:18:46 +00:00
if ! userSetOption ( inv , "dormancy-threshold" ) {
2023-11-22 00:20:01 +00:00
dormancyThreshold = time . Duration ( template . TimeTilDormantMillis ) * time . Millisecond
}
2024-01-11 22:18:46 +00:00
if ! userSetOption ( inv , "dormancy-auto-deletion" ) {
2023-11-22 00:20:01 +00:00
dormancyAutoDeletion = time . Duration ( template . TimeTilDormantAutoDeleteMillis ) * time . Millisecond
}
2023-07-20 13:35:41 +00:00
2024-01-11 22:18:46 +00:00
if ! userSetOption ( inv , "require-active-version" ) {
requireActiveVersion = template . RequireActiveVersion
2023-11-21 00:14:30 +00:00
}
2024-01-11 22:18:46 +00:00
if ! userSetOption ( inv , "autostop-requirement-weekdays" ) {
autostopRequirementDaysOfWeek = template . AutostopRequirement . DaysOfWeek
2023-11-21 00:14:30 +00:00
}
2024-01-11 22:18:46 +00:00
if unsetAutostopRequirementDaysOfWeek {
autostopRequirementDaysOfWeek = [ ] string { }
}
if ! userSetOption ( inv , "autostop-requirement-weeks" ) {
autostopRequirementWeeks = template . AutostopRequirement . Weeks
}
if len ( autostartRequirementDaysOfWeek ) == 1 && autostartRequirementDaysOfWeek [ 0 ] == "all" {
// Set it to every day of the week
autostartRequirementDaysOfWeek = [ ] string { "monday" , "tuesday" , "wednesday" , "thursday" , "friday" , "saturday" , "sunday" }
} else if ! userSetOption ( inv , "autostart-requirement-weekdays" ) {
autostartRequirementDaysOfWeek = template . AutostartRequirement . DaysOfWeek
} else if len ( autostartRequirementDaysOfWeek ) == 0 {
autostartRequirementDaysOfWeek = [ ] string { }
2023-11-21 00:14:30 +00:00
}
2023-11-20 19:16:18 +00:00
var deprecated * string
2024-01-11 22:18:46 +00:00
if userSetOption ( inv , "deprecated" ) {
2023-11-20 19:16:18 +00:00
deprecated = & deprecationMessage
}
2024-01-11 22:18:46 +00:00
var disableEveryoneGroup bool
if userSetOption ( inv , "private" ) {
disableEveryoneGroup = disableEveryone
}
2022-06-08 14:14:57 +00:00
req := codersdk . UpdateTemplateMeta {
2024-02-13 07:00:35 +00:00
Name : name ,
DisplayName : displayName ,
Description : description ,
Icon : icon ,
DefaultTTLMillis : defaultTTL . Milliseconds ( ) ,
ActivityBumpMillis : activityBump . Milliseconds ( ) ,
2023-08-29 18:35:05 +00:00
AutostopRequirement : & codersdk . TemplateAutostopRequirement {
DaysOfWeek : autostopRequirementDaysOfWeek ,
Weeks : autostopRequirementWeeks ,
2023-07-20 13:35:41 +00:00
} ,
2023-10-13 16:57:18 +00:00
AutostartRequirement : & codersdk . TemplateAutostartRequirement {
DaysOfWeek : autostartRequirementDaysOfWeek ,
} ,
2023-11-22 00:20:01 +00:00
FailureTTLMillis : failureTTL . Milliseconds ( ) ,
TimeTilDormantMillis : dormancyThreshold . Milliseconds ( ) ,
TimeTilDormantAutoDeleteMillis : dormancyAutoDeletion . Milliseconds ( ) ,
AllowUserCancelWorkspaceJobs : allowUserCancelWorkspaceJobs ,
AllowUserAutostart : allowUserAutostart ,
AllowUserAutostop : allowUserAutostop ,
RequireActiveVersion : requireActiveVersion ,
DeprecationMessage : deprecated ,
2024-01-11 22:18:46 +00:00
DisableEveryoneGroupAccess : disableEveryoneGroup ,
2022-06-08 14:14:57 +00:00
}
2023-03-23 22:42:20 +00:00
_ , err = client . UpdateTemplateMeta ( inv . Context ( ) , template . ID , req )
2022-06-08 14:14:57 +00:00
if err != nil {
return xerrors . Errorf ( "update template metadata: %w" , err )
}
2023-09-07 21:28:22 +00:00
_ , _ = fmt . Fprintf ( inv . Stdout , "Updated template metadata at %s!\n" , pretty . Sprint ( cliui . DefaultStyles . DateTimeStamp , time . Now ( ) . Format ( time . Stamp ) ) )
2022-06-08 14:14:57 +00:00
return nil
} ,
}
2024-03-15 16:24:38 +00:00
cmd . Options = serpent . OptionSet {
2023-03-23 22:42:20 +00:00
{
Flag : "name" ,
Description : "Edit the template name." ,
2024-03-15 16:24:38 +00:00
Value : serpent . StringOf ( & name ) ,
2023-03-23 22:42:20 +00:00
} ,
{
Flag : "display-name" ,
Description : "Edit the template display name." ,
2024-03-15 16:24:38 +00:00
Value : serpent . StringOf ( & displayName ) ,
2023-03-23 22:42:20 +00:00
} ,
{
Flag : "description" ,
Description : "Edit the template description." ,
2024-03-15 16:24:38 +00:00
Value : serpent . StringOf ( & description ) ,
2023-03-23 22:42:20 +00:00
} ,
2023-11-20 19:16:18 +00:00
{
Name : deprecatedFlagName ,
Flag : "deprecated" ,
Description : "Sets the template as deprecated. Must be a message explaining why the template is deprecated." ,
2024-03-15 16:24:38 +00:00
Value : serpent . StringOf ( & deprecationMessage ) ,
2023-11-20 19:16:18 +00:00
} ,
2023-03-23 22:42:20 +00:00
{
Flag : "icon" ,
Description : "Edit the template icon path." ,
2024-03-15 16:24:38 +00:00
Value : serpent . StringOf ( & icon ) ,
2023-03-23 22:42:20 +00:00
} ,
{
Flag : "default-ttl" ,
2023-08-25 17:39:12 +00:00
Description : "Edit the template default time before shutdown - workspaces created from this template default to this value. Maps to \"Default autostop\" in the UI." ,
2024-03-15 16:24:38 +00:00
Value : serpent . DurationOf ( & defaultTTL ) ,
2023-03-23 22:42:20 +00:00
} ,
2024-02-13 07:00:35 +00:00
{
Flag : "activity-bump" ,
Description : "Edit the template activity bump - workspaces created from this template will have their shutdown time bumped by this value when activity is detected. Maps to \"Activity bump\" in the UI." ,
2024-03-15 16:24:38 +00:00
Value : serpent . DurationOf ( & activityBump ) ,
2024-02-13 07:00:35 +00:00
} ,
2023-10-13 16:57:18 +00:00
{
Flag : "autostart-requirement-weekdays" ,
// workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the autostop requirement for the template), pass 'none'.
Description : "Edit the template autostart requirement weekdays - workspaces created from this template can only autostart on the given weekdays. To unset this value for the template (and allow autostart on all days), pass 'all'." ,
2024-03-15 16:24:38 +00:00
Value : serpent . Validate ( serpent . StringArrayOf ( & autostartRequirementDaysOfWeek ) , func ( value * serpent . StringArray ) error {
2023-10-13 16:57:18 +00:00
v := value . GetSlice ( )
if len ( v ) == 1 && v [ 0 ] == "all" {
return nil
}
_ , err := codersdk . WeekdaysToBitmap ( v )
if err != nil {
return xerrors . Errorf ( "invalid autostart requirement days of week %q: %w" , strings . Join ( v , "," ) , err )
}
return nil
} ) ,
} ,
2023-07-20 13:35:41 +00:00
{
2023-08-29 18:35:05 +00:00
Flag : "autostop-requirement-weekdays" ,
Description : "Edit the template autostop requirement weekdays - workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the autostop requirement for the template), pass 'none'." ,
2024-03-15 16:24:38 +00:00
Value : serpent . Validate ( serpent . StringArrayOf ( & autostopRequirementDaysOfWeek ) , func ( value * serpent . StringArray ) error {
2023-07-20 13:35:41 +00:00
v := value . GetSlice ( )
if len ( v ) == 1 && v [ 0 ] == "none" {
return nil
}
_ , err := codersdk . WeekdaysToBitmap ( v )
if err != nil {
2023-08-29 18:35:05 +00:00
return xerrors . Errorf ( "invalid autostop requirement days of week %q: %w" , strings . Join ( v , "," ) , err )
2023-07-20 13:35:41 +00:00
}
return nil
} ) ,
} ,
{
2023-08-29 18:35:05 +00:00
Flag : "autostop-requirement-weeks" ,
Description : "Edit the template autostop requirement weeks - workspaces created from this template must be restarted on an n-weekly basis." ,
2024-03-20 15:37:57 +00:00
Value : serpent . Int64Of ( & autostopRequirementWeeks ) ,
2023-07-20 13:35:41 +00:00
} ,
2023-05-10 19:57:11 +00:00
{
Flag : "failure-ttl" ,
2023-08-25 17:39:12 +00:00
Description : "Specify a failure TTL for workspaces created from this template. It is the amount of time after a failed \"start\" build before coder automatically schedules a \"stop\" build to cleanup.This licensed feature's default is 0h (off). Maps to \"Failure cleanup\" in the UI." ,
2023-05-10 19:57:11 +00:00
Default : "0h" ,
2024-03-15 16:24:38 +00:00
Value : serpent . DurationOf ( & failureTTL ) ,
2023-05-10 19:57:11 +00:00
} ,
{
2023-11-22 00:20:01 +00:00
Flag : "dormancy-threshold" ,
Description : "Specify a duration workspaces may be inactive prior to being moved to the dormant state. This licensed feature's default is 0h (off). Maps to \"Dormancy threshold\" in the UI." ,
Default : "0h" ,
2024-03-15 16:24:38 +00:00
Value : serpent . DurationOf ( & dormancyThreshold ) ,
2023-11-22 00:20:01 +00:00
} ,
{
Flag : "dormancy-auto-deletion" ,
Description : "Specify a duration workspaces may be in the dormant state prior to being deleted. This licensed feature's default is 0h (off). Maps to \"Dormancy Auto-Deletion\" in the UI." ,
2023-05-10 19:57:11 +00:00
Default : "0h" ,
2024-03-15 16:24:38 +00:00
Value : serpent . DurationOf ( & dormancyAutoDeletion ) ,
2023-05-10 19:57:11 +00:00
} ,
2023-03-23 22:42:20 +00:00
{
Flag : "allow-user-cancel-workspace-jobs" ,
Description : "Allow users to cancel in-progress workspace jobs." ,
Default : "true" ,
2024-03-15 16:24:38 +00:00
Value : serpent . BoolOf ( & allowUserCancelWorkspaceJobs ) ,
2023-03-23 22:42:20 +00:00
} ,
2023-04-04 12:48:35 +00:00
{
Flag : "allow-user-autostart" ,
Description : "Allow users to configure autostart for workspaces on this template. This can only be disabled in enterprise." ,
Default : "true" ,
2024-03-15 16:24:38 +00:00
Value : serpent . BoolOf ( & allowUserAutostart ) ,
2023-04-04 12:48:35 +00:00
} ,
{
Flag : "allow-user-autostop" ,
Description : "Allow users to customize the autostop TTL for workspaces on this template. This can only be disabled in enterprise." ,
Default : "true" ,
2024-03-15 16:24:38 +00:00
Value : serpent . BoolOf ( & allowUserAutostop ) ,
2023-04-04 12:48:35 +00:00
} ,
2023-10-19 22:16:15 +00:00
{
Flag : "require-active-version" ,
Description : "Requires workspace builds to use the active template version. This setting does not apply to template admins. This is an enterprise-only feature." ,
2024-03-15 16:24:38 +00:00
Value : serpent . BoolOf ( & requireActiveVersion ) ,
2023-10-19 22:16:15 +00:00
Default : "false" ,
} ,
2024-01-05 21:04:14 +00:00
{
Flag : "private" ,
Description : "Disable the default behavior of granting template access to the 'everyone' group. " +
"The template permissions must be updated to allow non-admin users to use this template." ,
2024-03-15 16:24:38 +00:00
Value : serpent . BoolOf ( & disableEveryone ) ,
2024-01-05 21:04:14 +00:00
Default : "false" ,
} ,
2023-03-23 22:42:20 +00:00
cliui . SkipPromptOption ( ) ,
}
2022-06-08 14:14:57 +00:00
return cmd
}