-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathtemplatecreate.go
More file actions
251 lines (223 loc) · 8.54 KB
/
templatecreate.go
File metadata and controls
251 lines (223 loc) · 8.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
package cli
import (
"fmt"
"net/http"
"time"
"unicode/utf8"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/pretty"
"github.com/coder/serpent"
)
func (r *RootCmd) templateCreate() *serpent.Command {
var (
provisioner string
provisionerTags []string
variablesFile string
commandLineVariables []string
disableEveryone bool
requireActiveVersion bool
defaultTTL time.Duration
failureTTL time.Duration
dormancyThreshold time.Duration
dormancyAutoDeletion time.Duration
uploadFlags templateUploadFlags
orgContext = NewOrganizationContext()
)
cmd := &serpent.Command{
Use: "create [name]",
Short: "DEPRECATED: Create a template from the current directory or as specified by flag",
Middleware: serpent.Chain(
serpent.RequireRangeArgs(0, 1),
cliui.DeprecationWarning(
"Use `coder templates push` command for creating and updating templates. \n"+
"Use `coder templates edit` command for editing template settings. ",
),
),
Handler: func(inv *serpent.Invocation) error {
client, err := r.InitClient(inv)
if err != nil {
return err
}
isTemplateSchedulingOptionsSet := failureTTL != 0 || dormancyThreshold != 0 || dormancyAutoDeletion != 0
if isTemplateSchedulingOptionsSet || requireActiveVersion {
entitlements, err := client.Entitlements(inv.Context())
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")
} else if err != nil {
return xerrors.Errorf("get entitlements: %w", err)
}
if isTemplateSchedulingOptionsSet {
if !entitlements.Features[codersdk.FeatureAdvancedTemplateScheduling].Enabled {
return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --failure-ttl, or --inactivity-ttl")
}
}
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")
}
}
}
organization, err := orgContext.Selected(inv, client)
if err != nil {
return err
}
templateName, err := uploadFlags.templateName(inv)
if err != nil {
return err
}
if utf8.RuneCountInString(templateName) > 32 {
return xerrors.Errorf("Template name must be no more than 32 characters")
}
_, err = client.TemplateByName(inv.Context(), organization.ID, templateName)
if err == nil {
return xerrors.Errorf("A template already exists named %q!", templateName)
}
err = uploadFlags.checkForLockfile(inv)
if err != nil {
return xerrors.Errorf("check for lockfile: %w", err)
}
message := uploadFlags.templateMessage(inv)
var varsFiles []string
if !uploadFlags.stdin(inv) {
varsFiles, err = codersdk.DiscoverVarsFiles(uploadFlags.directory)
if err != nil {
return err
}
if len(varsFiles) > 0 {
_, _ = fmt.Fprintln(inv.Stdout, "Auto-discovered Terraform tfvars files. Make sure to review and clean up any unused files.")
}
}
// Confirm upload of the directory.
resp, err := uploadFlags.upload(inv, client)
if err != nil {
return err
}
tags, err := ParseProvisionerTags(provisionerTags)
if err != nil {
return err
}
userVariableValues, err := codersdk.ParseUserVariableValues(
varsFiles,
variablesFile,
commandLineVariables)
if err != nil {
return err
}
job, err := createValidTemplateVersion(inv, createValidTemplateVersionArgs{
Message: message,
Client: client,
Organization: organization,
Provisioner: codersdk.ProvisionerType(provisioner),
FileID: resp.ID,
ProvisionerTags: tags,
UserVariableValues: userVariableValues,
})
if err != nil {
return err
}
if !uploadFlags.stdin(inv) {
_, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: "Confirm create?",
IsConfirm: true,
})
if err != nil {
return err
}
}
createReq := codersdk.CreateTemplateRequest{
Name: templateName,
VersionID: job.ID,
DefaultTTLMillis: ptr.Ref(defaultTTL.Milliseconds()),
FailureTTLMillis: ptr.Ref(failureTTL.Milliseconds()),
TimeTilDormantMillis: ptr.Ref(dormancyThreshold.Milliseconds()),
TimeTilDormantAutoDeleteMillis: ptr.Ref(dormancyAutoDeletion.Milliseconds()),
DisableEveryoneGroupAccess: disableEveryone,
RequireActiveVersion: requireActiveVersion,
}
template, err := client.CreateTemplate(inv.Context(), organization.ID, createReq)
if err != nil {
return err
}
_, _ = fmt.Fprintln(inv.Stdout, "\n"+pretty.Sprint(cliui.DefaultStyles.Wrap,
"The "+pretty.Sprint(
cliui.DefaultStyles.Keyword, templateName)+" template has been created at "+
pretty.Sprint(cliui.DefaultStyles.DateTimeStamp, time.Now().Format(time.Stamp))+"! "+
"Developers can provision a workspace with this template using:")+"\n")
_, _ = fmt.Fprintln(inv.Stdout, " "+pretty.Sprint(cliui.DefaultStyles.Code, fmt.Sprintf("coder create --template=%q --org=%q [workspace name]", templateName, template.OrganizationName)))
_, _ = fmt.Fprintln(inv.Stdout)
return nil
},
}
cmd.Options = serpent.OptionSet{
{
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.",
Value: serpent.BoolOf(&disableEveryone),
},
{
Flag: "variables-file",
Description: "Specify a file path with values for Terraform-managed variables.",
Value: serpent.StringOf(&variablesFile),
},
{
Flag: "variable",
Description: "Specify a set of values for Terraform-managed variables.",
Value: serpent.StringArrayOf(&commandLineVariables),
},
{
Flag: "var",
Description: "Alias of --variable.",
Value: serpent.StringArrayOf(&commandLineVariables),
},
{
Flag: "provisioner-tag",
Description: "Specify a set of tags to target provisioner daemons.",
Value: serpent.StringArrayOf(&provisionerTags),
},
{
Flag: "default-ttl",
Description: "Specify a default TTL for workspaces created from this template. It is the default time before shutdown - workspaces created from this template default to this value. Maps to \"Default autostop\" in the UI.",
Default: "24h",
Value: serpent.DurationOf(&defaultTTL),
},
{
Flag: "failure-ttl",
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.",
Default: "0h",
Value: serpent.DurationOf(&failureTTL),
},
{
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",
Value: serpent.DurationOf(&dormancyThreshold),
},
{
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.",
Default: "0h",
Value: serpent.DurationOf(&dormancyAutoDeletion),
},
{
Flag: "test.provisioner",
Description: "Customize the provisioner backend.",
Default: "terraform",
Value: serpent.StringOf(&provisioner),
Hidden: true,
},
{
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. See https://coder.com/docs/admin/templates/managing-templates#require-automatic-updates-enterprise for more details.",
Value: serpent.BoolOf(&requireActiveVersion),
Default: "false",
},
cliui.SkipPromptOption(),
}
orgContext.AttachOptions(cmd)
cmd.Options = append(cmd.Options, uploadFlags.options()...)
return cmd
}