/
provider.go
158 lines (140 loc) · 4.88 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
150
151
152
153
154
155
156
157
158
package vercel
import (
"context"
"fmt"
"os"
"regexp"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/vercel/terraform-provider-vercel/client"
)
type vercelProvider struct{}
// New instantiates a new instance of a vercel terraform provider.
func New() provider.Provider {
return &vercelProvider{}
}
func (p *vercelProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "vercel"
}
// Schema returns the schema information for the provider configuration itself.
func (p *vercelProvider) Schema(_ context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Description: `
The Vercel provider is used to interact with resources supported by Vercel.
The provider needs to be configured with the proper credentials before it can be used.
Use the navigation to the left to read about the available resources.
`,
Attributes: map[string]schema.Attribute{
"api_token": schema.StringAttribute{
Optional: true,
Description: "The Vercel API Token to use. This can also be specified with the `VERCEL_API_TOKEN` shell environment variable. Tokens can be created from your [Vercel settings](https://vercel.com/account/tokens).",
Sensitive: true,
},
"team": schema.StringAttribute{
Optional: true,
Description: "The default Vercel Team to use when creating resources. This can be provided as either a team slug, or team ID. The slug and ID are both available from the Team Settings page in the Vercel dashboard.",
},
},
}
}
func (p *vercelProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
newAliasResource,
newDNSRecordResource,
newDeploymentResource,
newEdgeConfigResource,
newEdgeConfigSchemaResource,
newEdgeConfigTokenResource,
newLogDrainResource,
newProjectDomainResource,
newProjectEnvironmentVariableResource,
newProjectResource,
newSharedEnvironmentVariableResource,
newWebhookResource,
}
}
func (p *vercelProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
newAliasDataSource,
newDeploymentDataSource,
newEdgeConfigDataSource,
newEdgeConfigSchemaDataSource,
newEdgeConfigTokenDataSource,
newEndpointVerificationDataSource,
newFileDataSource,
newPrebuiltProjectDataSource,
newProjectDataSource,
newProjectDirectoryDataSource,
newSharedEnvironmentVariableDataSource,
newLogDrainDataSource,
}
}
type providerData struct {
APIToken types.String `tfsdk:"api_token"`
Team types.String `tfsdk:"team"`
}
// apiTokenRe is a regex for an API access token. We use this to validate that the
// token provided matches the expected format.
var apiTokenRe = regexp.MustCompile("[0-9a-zA-Z]{24}")
// Configure takes a provider and applies any configuration. In the context of Vercel
// this allows us to set up an API token.
func (p *vercelProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var config providerData
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// User must provide an api_token to the provider
var apiToken string
if config.APIToken.IsUnknown() {
resp.Diagnostics.AddWarning(
"Unable to create client",
"Cannot use unknown value as api_token",
)
return
}
if config.APIToken.IsNull() {
apiToken = os.Getenv("VERCEL_API_TOKEN")
} else {
apiToken = config.APIToken.ValueString()
}
if apiToken == "" {
resp.Diagnostics.AddError(
"Unable to find api_token",
"api_token cannot be an empty string",
)
return
}
if !apiTokenRe.MatchString(apiToken) {
resp.Diagnostics.AddError(
"Invalid api_token",
"api_token (VERCEL_API_TOKEN) must be 24 characters and only contain characters 0-9 and a-f (all lowercased)",
)
return
}
vercelClient := client.New(apiToken)
if config.Team.ValueString() != "" {
res, err := vercelClient.GetTeam(ctx, config.Team.ValueString())
if client.NotFound(err) {
resp.Diagnostics.AddError(
"Vercel Team not found",
"You provided a `team` field on the Vercel provider, but the team could not be found. Please check the team slug or ID is correct and that your api_token has access to the team.",
)
return
}
if err != nil {
resp.Diagnostics.AddError(
"Unexpected error reading Vercel Team",
fmt.Sprintf("Could not read Vercel Team %s, unexpected error: %s", config.Team.ValueString(), err),
)
return
}
vercelClient = vercelClient.WithTeamID(res.ID)
}
resp.DataSourceData = vercelClient
resp.ResourceData = vercelClient
}