@@ -178,6 +178,198 @@ func New() *schema.Provider {
178178 },
179179 },
180180 },
181+ "coder_parameter" : {
182+ Description : "Use this data source to configure editable options for workspaces." ,
183+ ReadContext : func (ctx context.Context , rd * schema.ResourceData , i interface {}) diag.Diagnostics {
184+ rd .SetId (uuid .NewString ())
185+
186+ name := rd .Get ("name" ).(string )
187+ typ := rd .Get ("type" ).(string )
188+ var value string
189+ rawDefaultValue , ok := rd .GetOk ("default" )
190+ if ok {
191+ defaultValue := rawDefaultValue .(string )
192+ err := valueIsType (typ , defaultValue )
193+ if err != nil {
194+ return err
195+ }
196+ value = defaultValue
197+ }
198+ envValue , ok := os .LookupEnv (fmt .Sprintf ("CODER_PARAMETER_%s" , name ))
199+ if ok {
200+ value = envValue
201+ }
202+ rd .Set ("value" , value )
203+
204+ rawOptions , exists := rd .GetOk ("option" )
205+ if exists {
206+ rawArrayOptions , valid := rawOptions .([]interface {})
207+ if ! valid {
208+ return diag .Errorf ("options is of wrong type %T" , rawArrayOptions )
209+ }
210+ optionDisplayNames := map [string ]interface {}{}
211+ optionValues := map [string ]interface {}{}
212+ for _ , rawOption := range rawArrayOptions {
213+ option , valid := rawOption .(map [string ]interface {})
214+ if ! valid {
215+ return diag .Errorf ("option is of wrong type %T" , rawOption )
216+ }
217+ rawName , ok := option ["name" ]
218+ if ! ok {
219+ return diag .Errorf ("no name for %+v" , option )
220+ }
221+ displayName , ok := rawName .(string )
222+ if ! ok {
223+ return diag .Errorf ("display name is of wrong type %T" , displayName )
224+ }
225+ _ , exists := optionDisplayNames [displayName ]
226+ if exists {
227+ return diag .Errorf ("multiple options cannot have the same display name %q" , displayName )
228+ }
229+
230+ rawValue , ok := option ["value" ]
231+ if ! ok {
232+ return diag .Errorf ("no value for %+v\n " , option )
233+ }
234+ value , ok := rawValue .(string )
235+ if ! ok {
236+ return diag .Errorf ("" )
237+ }
238+ _ , exists = optionValues [value ]
239+ if exists {
240+ return diag .Errorf ("multiple options cannot have the same value %q" , value )
241+ }
242+ err := valueIsType (typ , value )
243+ if err != nil {
244+ return err
245+ }
246+
247+ optionValues [value ] = nil
248+ optionDisplayNames [displayName ] = nil
249+ }
250+ }
251+
252+ return nil
253+ },
254+ Schema : map [string ]* schema.Schema {
255+ "value" : {
256+ Type : schema .TypeString ,
257+ Computed : true ,
258+ Description : "The output value of a parameter." ,
259+ },
260+ "name" : {
261+ Type : schema .TypeString ,
262+ Required : true ,
263+ Description : "The name of the parameter as it appears in the interface. If this is changed, the parameter will need to be re-updated by developers." ,
264+ },
265+ "description" : {
266+ Type : schema .TypeString ,
267+ Optional : true ,
268+ Description : "Explain what the parameter does." ,
269+ },
270+ "type" : {
271+ Type : schema .TypeString ,
272+ Default : "string" ,
273+ Optional : true ,
274+ ValidateFunc : validation .StringInSlice ([]string {"number" , "string" , "bool" }, false ),
275+ },
276+ "immutable" : {
277+ Type : schema .TypeBool ,
278+ Optional : true ,
279+ Description : "Whether the value can be changed after it's set initially." ,
280+ },
281+ "default" : {
282+ Type : schema .TypeString ,
283+ Optional : true ,
284+ Description : "A default value for the parameter." ,
285+ ExactlyOneOf : []string {"option" },
286+ },
287+ "icon" : {
288+ Type : schema .TypeString ,
289+ Description : "A URL to an icon that will display in the dashboard. View built-in " +
290+ "icons here: https://github.com/coder/coder/tree/main/site/static/icon. Use a " +
291+ "built-in icon with `data.coder_workspace.me.access_url + \" /icon/<path>\" `." ,
292+ ForceNew : true ,
293+ Optional : true ,
294+ ValidateFunc : func (i interface {}, s string ) ([]string , []error ) {
295+ _ , err := url .Parse (s )
296+ if err != nil {
297+ return nil , []error {err }
298+ }
299+ return nil , nil
300+ },
301+ },
302+ "option" : {
303+ Type : schema .TypeList ,
304+ Description : "Each \" option\" block defines a single displayable value for a user to select." ,
305+ ForceNew : true ,
306+ Optional : true ,
307+ MaxItems : 64 ,
308+ Elem : & schema.Resource {
309+ Schema : map [string ]* schema.Schema {
310+ "name" : {
311+ Type : schema .TypeString ,
312+ Description : "The display name of this value in the UI." ,
313+ ForceNew : true ,
314+ Required : true ,
315+ },
316+ "description" : {
317+ Type : schema .TypeString ,
318+ Description : "Add a description to select this item." ,
319+ ForceNew : true ,
320+ Optional : true ,
321+ },
322+ "value" : {
323+ Type : schema .TypeString ,
324+ Description : "The value of this option." ,
325+ ForceNew : true ,
326+ Required : true ,
327+ },
328+ "icon" : {
329+ Type : schema .TypeString ,
330+ Description : "A URL to an icon that will display in the dashboard. View built-in " +
331+ "icons here: https://github.com/coder/coder/tree/main/site/static/icon. Use a " +
332+ "built-in icon with `data.coder_workspace.me.access_url + \" /icon/<path>\" `." ,
333+ ForceNew : true ,
334+ Optional : true ,
335+ ValidateFunc : func (i interface {}, s string ) ([]string , []error ) {
336+ _ , err := url .Parse (s )
337+ if err != nil {
338+ return nil , []error {err }
339+ }
340+ return nil , nil
341+ },
342+ },
343+ },
344+ },
345+ },
346+ "validation" : {
347+ Type : schema .TypeSet ,
348+ MaxItems : 1 ,
349+ Optional : true ,
350+ Elem : & schema.Resource {
351+ Schema : map [string ]* schema.Schema {
352+ "min" : {
353+ Type : schema .TypeInt ,
354+ Optional : true ,
355+ Default : 0 ,
356+ Description : "The minimum for a number to be." ,
357+ },
358+ "max" : {
359+ Type : schema .TypeInt ,
360+ Optional : true ,
361+ Description : "The maximum for a number to be." ,
362+ },
363+ "regex" : {
364+ Type : schema .TypeString ,
365+ ExactlyOneOf : []string {"min" , "max" },
366+ Optional : true ,
367+ },
368+ },
369+ },
370+ },
371+ },
372+ },
181373 "coder_provisioner" : {
182374 Description : "Use this data source to get information about the Coder provisioner." ,
183375 ReadContext : func (c context.Context , rd * schema.ResourceData , i interface {}) diag.Diagnostics {
@@ -339,8 +531,8 @@ func New() *schema.Provider {
339531 "icon" : {
340532 Type : schema .TypeString ,
341533 Description : "A URL to an icon that will display in the dashboard. View built-in " +
342- "icons here: https://github.com/coder/coder/tree/main/site/static/icons . Use a " +
343- "built-in icon with `data.coder_workspace.me.access_url + \" /icons /<path>\" `." ,
534+ "icons here: https://github.com/coder/coder/tree/main/site/static/icon . Use a " +
535+ "built-in icon with `data.coder_workspace.me.access_url + \" /icon /<path>\" `." ,
344536 ForceNew : true ,
345537 Optional : true ,
346538 ValidateFunc : func (i interface {}, s string ) ([]string , []error ) {
@@ -445,7 +637,7 @@ func New() *schema.Provider {
445637 Type : schema .TypeString ,
446638 Description : "A URL to an icon that will display in the dashboard. View built-in " +
447639 "icons here: https://github.com/coder/coder/tree/main/site/static/icon. Use a " +
448- "built-in icon with `data.coder_workspace.me.access_url + \" /icons /<path>\" `." ,
640+ "built-in icon with `data.coder_workspace.me.access_url + \" /icon /<path>\" `." ,
449641 ForceNew : true ,
450642 Optional : true ,
451643 ValidateFunc : func (i interface {}, s string ) ([]string , []error ) {
@@ -499,6 +691,26 @@ func New() *schema.Provider {
499691 }
500692}
501693
694+ func valueIsType (typ , value string ) diag.Diagnostics {
695+ switch typ {
696+ case "number" :
697+ _ , err := strconv .ParseFloat (value , 64 )
698+ if err != nil {
699+ return diag .Errorf ("%q is not a number" , value )
700+ }
701+ case "bool" :
702+ _ , err := strconv .ParseBool (value )
703+ if err != nil {
704+ return diag .Errorf ("%q is not a bool" , value )
705+ }
706+ case "string" :
707+ // Anything is a string!
708+ default :
709+ return diag .Errorf ("invalid type %q" , typ )
710+ }
711+ return nil
712+ }
713+
502714// updateInitScript fetches parameters from a "coder_agent" to produce the
503715// agent script from environment variables.
504716func updateInitScript (resourceData * schema.ResourceData , i interface {}) diag.Diagnostics {
0 commit comments