-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathprovider.go
149 lines (138 loc) · 4.87 KB
/
provider.go
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
package provider
import (
"context"
"net/url"
"reflect"
"strings"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"golang.org/x/xerrors"
)
type config struct {
URL *url.URL
}
// New returns a new Terraform provider.
func New() *schema.Provider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"url": {
Type: schema.TypeString,
Description: "The URL to access Coder.",
Optional: true,
// The "CODER_AGENT_URL" environment variable is used by default
// as the Access URL when generating scripts.
DefaultFunc: schema.EnvDefaultFunc("CODER_AGENT_URL", "https://mydeployment.coder.com"),
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
_, err := url.Parse(s)
if err != nil {
return nil, []error{err}
}
return nil, nil
},
},
"feature_use_managed_variables": {
Type: schema.TypeBool,
Description: "Feature: use managed Terraform variables. The feature flag is not used anymore as Terraform variables are now exclusively utilized for template-wide variables.",
Default: true,
Optional: true,
Deprecated: "Terraform variables are now exclusively utilized for template-wide variables after the removal of support for legacy parameters.",
},
},
ConfigureContextFunc: func(c context.Context, resourceData *schema.ResourceData) (interface{}, diag.Diagnostics) {
rawURL, ok := resourceData.Get("url").(string)
if !ok {
return nil, diag.Errorf("unexpected type %q for url", reflect.TypeOf(resourceData.Get("url")).String())
}
if rawURL == "" {
return nil, diag.Errorf("CODER_AGENT_URL must not be empty; got %q", rawURL)
}
parsed, err := url.Parse(resourceData.Get("url").(string))
if err != nil {
return nil, diag.FromErr(err)
}
rawHost, ok := resourceData.Get("host").(string)
if ok && rawHost != "" {
rawPort := parsed.Port()
if rawPort != "" && !strings.Contains(rawHost, ":") {
rawHost += ":" + rawPort
}
parsed.Host = rawHost
}
return config{
URL: parsed,
}, nil
},
DataSourcesMap: map[string]*schema.Resource{
"coder_workspace": workspaceDataSource(),
"coder_workspace_tags": workspaceTagDataSource(),
"coder_provisioner": provisionerDataSource(),
"coder_parameter": parameterDataSource(),
"coder_git_auth": gitAuthDataSource(),
"coder_external_auth": externalAuthDataSource(),
"coder_workspace_owner": workspaceOwnerDataSource(),
},
ResourcesMap: map[string]*schema.Resource{
"coder_agent": agentResource(),
"coder_agent_instance": agentInstanceResource(),
"coder_app": appResource(),
"coder_metadata": metadataResource(),
"coder_script": scriptResource(),
"coder_env": envResource(),
},
}
}
// populateIsNull reads the raw plan for a coder_metadata resource being created,
// figures out which items have null "value"s, and augments them by setting the
// "is_null" field to true. This ugly hack is necessary because terraform-plugin-sdk
// is designed around a old version of Terraform that didn't support nullable fields,
// and it doesn't correctly propagate null values for primitive types.
// Returns an interface{} representing the new value of the "item" field, or an error.
func populateIsNull(resourceData *schema.ResourceData) (result interface{}, err error) {
// The cty package reports type mismatches by panicking
defer func() {
if r := recover(); r != nil {
err = xerrors.Errorf("panic while handling coder_metadata: %#v", r)
}
}()
rawPlan := resourceData.GetRawPlan()
items := rawPlan.GetAttr("item").AsValueSlice()
var resultItems []interface{}
for _, item := range items {
key := valueAsString(item.GetAttr("key"))
resultItem := map[string]interface{}{
"key": key,
"value": valueAsString(item.GetAttr("value")),
"sensitive": valueAsBool(item.GetAttr("sensitive")),
}
if item.GetAttr("value").IsNull() {
resultItem["is_null"] = true
}
resultItems = append(resultItems, resultItem)
}
return resultItems, nil
}
// valueAsString takes a cty.Value that may be a string or null, and converts it to a Go string,
// which will be empty if the input value was null.
// or a nil interface{}
func valueAsString(value cty.Value) string {
if value.IsNull() {
return ""
}
return value.AsString()
}
// valueAsString takes a cty.Value that may be a boolean or null, and converts it to either a Go bool
// or a nil interface{}
func valueAsBool(value cty.Value) interface{} {
if value.IsNull() {
return nil
}
return value.True()
}
// errorAsDiagnostic transforms a Go error to a diag.Diagnostics object representing a fatal error.
func errorAsDiagnostics(err error) diag.Diagnostics {
return []diag.Diagnostic{{
Severity: diag.Error,
Summary: err.Error(),
}}
}