Skip to content

Commit 38bc617

Browse files
committed
fix: correct URL validation and centralize logic
1 parent f239e51 commit 38bc617

File tree

7 files changed

+123
-55
lines changed

7 files changed

+123
-55
lines changed

provider/app.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package provider
22

33
import (
44
"context"
5-
"net/url"
5+
"github.com/coder/terraform-provider-coder/v2/provider/helpers"
66
"regexp"
77

88
"github.com/google/uuid"
@@ -93,15 +93,9 @@ func appResource() *schema.Resource {
9393
Description: "A URL to an icon that will display in the dashboard. View built-in " +
9494
"icons [here](https://github.com/coder/coder/tree/main/site/static/icon). Use a " +
9595
"built-in icon with `\"${data.coder_workspace.me.access_url}/icon/<path>\"`.",
96-
ForceNew: true,
97-
Optional: true,
98-
ValidateFunc: func(i any, s string) ([]string, []error) {
99-
_, err := url.Parse(s)
100-
if err != nil {
101-
return nil, []error{err}
102-
}
103-
return nil, nil
104-
},
96+
ForceNew: true,
97+
Optional: true,
98+
ValidateFunc: helpers.ValidateURL,
10599
},
106100
"slug": {
107101
Type: schema.TypeString,

provider/app_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,4 +478,61 @@ func TestApp(t *testing.T) {
478478
})
479479
}
480480
})
481+
482+
t.Run("Icon", func(t *testing.T) {
483+
t.Parallel()
484+
485+
cases := []struct {
486+
name string
487+
icon string
488+
expectError *regexp.Regexp
489+
}{
490+
{
491+
name: "Empty",
492+
icon: "",
493+
},
494+
{
495+
name: "ValidURL",
496+
icon: "/icon/region.svg",
497+
},
498+
{
499+
name: "InvalidURL",
500+
icon: "/icon%.svg",
501+
expectError: regexp.MustCompile("invalid URL escape"),
502+
},
503+
}
504+
505+
for _, c := range cases {
506+
c := c
507+
508+
t.Run(c.name, func(t *testing.T) {
509+
t.Parallel()
510+
511+
config := fmt.Sprintf(`
512+
provider "coder" {}
513+
resource "coder_agent" "dev" {
514+
os = "linux"
515+
arch = "amd64"
516+
}
517+
resource "coder_app" "code-server" {
518+
agent_id = coder_agent.dev.id
519+
slug = "code-server"
520+
display_name = "Testing"
521+
url = "http://localhost:13337"
522+
open_in = "slim-window"
523+
icon = "%s"
524+
}
525+
`, c.icon)
526+
527+
resource.Test(t, resource.TestCase{
528+
ProviderFactories: coderFactory(),
529+
IsUnitTest: true,
530+
Steps: []resource.TestStep{{
531+
Config: config,
532+
ExpectError: c.expectError,
533+
}},
534+
})
535+
})
536+
}
537+
})
481538
}

provider/helpers/validation.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package helpers
2+
3+
import (
4+
"fmt"
5+
"net/url"
6+
)
7+
8+
// ValidateURL checks that the given value is a valid URL string.
9+
// Example: for `icon = "/icon/region.svg"`, value is `/icon/region.svg` and label is `icon`.
10+
func ValidateURL(value interface{}, label string) ([]string, []error) {
11+
val, ok := value.(string)
12+
if !ok {
13+
return nil, []error{fmt.Errorf("expected %q to be a string", label)}
14+
}
15+
if _, err := url.Parse(val); err != nil {
16+
return nil, []error{err}
17+
}
18+
return nil, nil
19+
}

provider/metadata.go

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ package provider
22

33
import (
44
"context"
5-
"net/url"
6-
5+
"github.com/coder/terraform-provider-coder/v2/provider/helpers"
76
"github.com/google/uuid"
87
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
98
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -56,15 +55,9 @@ func metadataResource() *schema.Resource {
5655
Description: "A URL to an icon that will display in the dashboard. View built-in " +
5756
"icons [here](https://github.com/coder/coder/tree/main/site/static/icon). Use a " +
5857
"built-in icon with `\"${data.coder_workspace.me.access_url}/icon/<path>\"`.",
59-
ForceNew: true,
60-
Optional: true,
61-
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
62-
_, err := url.Parse(s)
63-
if err != nil {
64-
return nil, []error{err}
65-
}
66-
return nil, nil
67-
},
58+
ForceNew: true,
59+
Optional: true,
60+
ValidateFunc: helpers.ValidateURL,
6861
},
6962
"daily_cost": {
7063
Type: schema.TypeInt,

provider/parameter.go

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"encoding/hex"
77
"encoding/json"
88
"fmt"
9-
"net/url"
9+
"github.com/coder/terraform-provider-coder/v2/provider/helpers"
1010
"os"
1111
"regexp"
1212
"strconv"
@@ -223,15 +223,9 @@ func parameterDataSource() *schema.Resource {
223223
Description: "A URL to an icon that will display in the dashboard. View built-in " +
224224
"icons [here](https://github.com/coder/coder/tree/main/site/static/icon). Use a " +
225225
"built-in icon with `\"${data.coder_workspace.me.access_url}/icon/<path>\"`.",
226-
ForceNew: true,
227-
Optional: true,
228-
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
229-
_, err := url.Parse(s)
230-
if err != nil {
231-
return nil, []error{err}
232-
}
233-
return nil, nil
234-
},
226+
ForceNew: true,
227+
Optional: true,
228+
ValidateFunc: helpers.ValidateURL,
235229
},
236230
"option": {
237231
Type: schema.TypeList,
@@ -263,15 +257,9 @@ func parameterDataSource() *schema.Resource {
263257
Description: "A URL to an icon that will display in the dashboard. View built-in " +
264258
"icons [here](https://github.com/coder/coder/tree/main/site/static/icon). Use a " +
265259
"built-in icon with `\"${data.coder_workspace.me.access_url}/icon/<path>\"`.",
266-
ForceNew: true,
267-
Optional: true,
268-
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
269-
_, err := url.Parse(s)
270-
if err != nil {
271-
return nil, []error{err}
272-
}
273-
return nil, nil
274-
},
260+
ForceNew: true,
261+
Optional: true,
262+
ValidateFunc: helpers.ValidateURL,
275263
},
276264
},
277265
},

provider/parameter_test.go

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,23 +227,19 @@ func TestParameter(t *testing.T) {
227227
data "coder_parameter" "region" {
228228
name = "Region"
229229
type = "string"
230-
default = "2"
230+
default = "1"
231231
option {
232232
name = "1"
233233
value = "1"
234234
icon = "/icon/code.svg"
235235
description = "Something!"
236236
}
237-
option {
238-
name = "2"
239-
value = "2"
240-
}
241237
}
242238
`,
243239
Check: func(state *terraform.ResourceState) {
244240
for key, expected := range map[string]string{
245241
"name": "Region",
246-
"option.#": "2",
242+
"option.#": "1",
247243
"option.0.name": "1",
248244
"option.0.value": "1",
249245
"option.0.icon": "/icon/code.svg",
@@ -665,7 +661,33 @@ data "coder_parameter" "region" {
665661
}
666662
`,
667663
ExpectError: regexp.MustCompile("ephemeral parameter requires the default property"),
668-
}} {
664+
}, {
665+
Name: "InvalidIconURL",
666+
Config: `
667+
data "coder_parameter" "region" {
668+
name = "Region"
669+
type = "string"
670+
icon = "/icon%.svg"
671+
}
672+
`,
673+
ExpectError: regexp.MustCompile("invalid URL escape"),
674+
}, {
675+
Name: "OptionInvalidIconURL",
676+
Config: `
677+
data "coder_parameter" "region" {
678+
name = "Region"
679+
type = "string"
680+
option {
681+
name = "1"
682+
value = "1"
683+
icon = "/icon%.svg"
684+
description = "Something!"
685+
}
686+
}
687+
`,
688+
ExpectError: regexp.MustCompile("invalid URL escape"),
689+
},
690+
} {
669691
tc := tc
670692
t.Run(tc.Name, func(t *testing.T) {
671693
t.Parallel()

provider/provider.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package provider
22

33
import (
44
"context"
5+
"github.com/coder/terraform-provider-coder/v2/provider/helpers"
56
"net/url"
67
"reflect"
78
"strings"
@@ -26,14 +27,8 @@ func New() *schema.Provider {
2627
Optional: true,
2728
// The "CODER_AGENT_URL" environment variable is used by default
2829
// as the Access URL when generating scripts.
29-
DefaultFunc: schema.EnvDefaultFunc("CODER_AGENT_URL", "https://mydeployment.coder.com"),
30-
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
31-
_, err := url.Parse(s)
32-
if err != nil {
33-
return nil, []error{err}
34-
}
35-
return nil, nil
36-
},
30+
DefaultFunc: schema.EnvDefaultFunc("CODER_AGENT_URL", "https://mydeployment.coder.com"),
31+
ValidateFunc: helpers.ValidateURL,
3732
},
3833
},
3934
ConfigureContextFunc: func(c context.Context, resourceData *schema.ResourceData) (interface{}, diag.Diagnostics) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy