-
Notifications
You must be signed in to change notification settings - Fork 97
/
Copy pathserver.go
425 lines (323 loc) · 15.3 KB
/
server.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
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
package fwserver
import (
"context"
"fmt"
"sync"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
"github.com/hashicorp/terraform-plugin-framework/internal/logging"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/resource"
)
// Server implements the framework provider server. Protocol specific
// implementations wrap this handling along with calling all request and
// response type conversions.
type Server struct {
Provider provider.Provider
// DataSourceConfigureData is the
// [provider.ConfigureResponse.DataSourceData] field value which is passed
// to [datasource.ConfigureRequest.ProviderData].
DataSourceConfigureData any
// ResourceConfigureData is the
// [provider.ConfigureResponse.ResourceData] field value which is passed
// to [resource.ConfigureRequest.ProviderData].
ResourceConfigureData any
// dataSourceSchemas is the cached DataSource Schemas for RPCs that need to
// convert configuration data from the protocol. If not found, it will be
// fetched from the DataSourceType.GetSchema() method.
dataSourceSchemas map[string]fwschema.Schema
// dataSourceSchemasDiags is the cached Diagnostics obtained while populating
// dataSourceSchemas. This is to ensure any warnings or errors are also
// returned appropriately when fetching dataSourceSchemas.
dataSourceSchemasDiags diag.Diagnostics
// dataSourceSchemasMutex is a mutex to protect concurrent dataSourceSchemas
// access from race conditions.
dataSourceSchemasMutex sync.Mutex
// dataSourceFuncs is the cached DataSource functions for RPCs that need to
// access data sources. If not found, it will be fetched from the
// Provider.DataSources() method.
dataSourceFuncs map[string]func() datasource.DataSource
// dataSourceTypesDiags is the cached Diagnostics obtained while populating
// dataSourceTypes. This is to ensure any warnings or errors are also
// returned appropriately when fetching dataSourceTypes.
dataSourceTypesDiags diag.Diagnostics
// dataSourceTypesMutex is a mutex to protect concurrent dataSourceTypes
// access from race conditions.
dataSourceTypesMutex sync.Mutex
// providerSchema is the cached Provider Schema for RPCs that need to
// convert configuration data from the protocol. If not found, it will be
// fetched from the Provider.GetSchema() method.
providerSchema fwschema.Schema
// providerSchemaDiags is the cached Diagnostics obtained while populating
// providerSchema. This is to ensure any warnings or errors are also
// returned appropriately when fetching providerSchema.
providerSchemaDiags diag.Diagnostics
// providerSchemaMutex is a mutex to protect concurrent providerSchema
// access from race conditions.
providerSchemaMutex sync.Mutex
// providerMetaSchema is the cached Provider Meta Schema for RPCs that need
// to convert configuration data from the protocol. If not found, it will
// be fetched from the Provider.GetMetaSchema() method.
providerMetaSchema fwschema.Schema
// providerMetaSchemaDiags is the cached Diagnostics obtained while populating
// providerMetaSchema. This is to ensure any warnings or errors are also
// returned appropriately when fetching providerMetaSchema.
providerMetaSchemaDiags diag.Diagnostics
// providerMetaSchemaMutex is a mutex to protect concurrent providerMetaSchema
// access from race conditions.
providerMetaSchemaMutex sync.Mutex
// providerTypeName is the type name of the provider, if the provider
// implemented the Metadata method.
providerTypeName string
// resourceSchemas is the cached Resource Schemas for RPCs that need to
// convert configuration data from the protocol. If not found, it will be
// fetched from the ResourceType.GetSchema() method.
resourceSchemas map[string]fwschema.Schema
// resourceSchemasDiags is the cached Diagnostics obtained while populating
// resourceSchemas. This is to ensure any warnings or errors are also
// returned appropriately when fetching resourceSchemas.
resourceSchemasDiags diag.Diagnostics
// resourceSchemasMutex is a mutex to protect concurrent resourceSchemas
// access from race conditions.
resourceSchemasMutex sync.Mutex
// resourceFuncs is the cached Resource functions for RPCs that need to
// access resources. If not found, it will be fetched from the
// Provider.Resources() method.
resourceFuncs map[string]func() resource.Resource
// resourceTypesDiags is the cached Diagnostics obtained while populating
// resourceTypes. This is to ensure any warnings or errors are also
// returned appropriately when fetching resourceTypes.
resourceTypesDiags diag.Diagnostics
// resourceTypesMutex is a mutex to protect concurrent resourceTypes
// access from race conditions.
resourceTypesMutex sync.Mutex
}
// DataSource returns the DataSource for a given type name.
func (s *Server) DataSource(ctx context.Context, typeName string) (datasource.DataSource, diag.Diagnostics) {
dataSourceFuncs, diags := s.DataSourceFuncs(ctx)
dataSourceFunc, ok := dataSourceFuncs[typeName]
if !ok {
diags.AddError(
"Data Source Type Not Found",
fmt.Sprintf("No data source type named %q was found in the provider.", typeName),
)
return nil, diags
}
return dataSourceFunc(), diags
}
// DataSourceFuncs returns a map of DataSource functions. The results are cached
// on first use.
func (s *Server) DataSourceFuncs(ctx context.Context) (map[string]func() datasource.DataSource, diag.Diagnostics) {
logging.FrameworkTrace(ctx, "Checking DataSourceTypes lock")
s.dataSourceTypesMutex.Lock()
defer s.dataSourceTypesMutex.Unlock()
if s.dataSourceFuncs != nil {
return s.dataSourceFuncs, s.dataSourceTypesDiags
}
s.dataSourceFuncs = make(map[string]func() datasource.DataSource)
logging.FrameworkDebug(ctx, "Calling provider defined Provider DataSources")
dataSourceFuncsSlice := s.Provider.DataSources(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider DataSources")
for _, dataSourceFunc := range dataSourceFuncsSlice {
dataSource := dataSourceFunc()
dataSourceTypeNameReq := datasource.MetadataRequest{
ProviderTypeName: s.providerTypeName,
}
dataSourceTypeNameResp := datasource.MetadataResponse{}
dataSource.Metadata(ctx, dataSourceTypeNameReq, &dataSourceTypeNameResp)
if dataSourceTypeNameResp.TypeName == "" {
s.dataSourceTypesDiags.AddError(
"Data Source Type Name Missing",
fmt.Sprintf("The %T DataSource returned an empty string from the Metadata method. ", dataSource)+
"This is always an issue with the provider and should be reported to the provider developers.",
)
continue
}
logging.FrameworkTrace(ctx, "Found data source type", map[string]interface{}{logging.KeyDataSourceType: dataSourceTypeNameResp.TypeName})
if _, ok := s.dataSourceFuncs[dataSourceTypeNameResp.TypeName]; ok {
s.dataSourceTypesDiags.AddError(
"Duplicate Data Source Type Defined",
fmt.Sprintf("The %s data source type name was returned for multiple data sources. ", dataSourceTypeNameResp.TypeName)+
"Data source type names must be unique. "+
"This is always an issue with the provider and should be reported to the provider developers.",
)
continue
}
s.dataSourceFuncs[dataSourceTypeNameResp.TypeName] = dataSourceFunc
}
return s.dataSourceFuncs, s.dataSourceTypesDiags
}
// DataSourceSchema returns the Schema associated with the DataSourceType for
// the given type name.
func (s *Server) DataSourceSchema(ctx context.Context, typeName string) (fwschema.Schema, diag.Diagnostics) {
dataSourceSchemas, diags := s.DataSourceSchemas(ctx)
dataSourceSchema, ok := dataSourceSchemas[typeName]
if !ok {
diags.AddError(
"Data Source Schema Not Found",
fmt.Sprintf("No data source type named %q was found in the provider to fetch the schema. ", typeName)+
"This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.",
)
return nil, diags
}
return dataSourceSchema, diags
}
// DataSourceSchemas returns the map of DataSourceType Schemas. The results are
// cached on first use.
func (s *Server) DataSourceSchemas(ctx context.Context) (map[string]fwschema.Schema, diag.Diagnostics) {
logging.FrameworkTrace(ctx, "Checking DataSourceSchemas lock")
s.dataSourceSchemasMutex.Lock()
defer s.dataSourceSchemasMutex.Unlock()
if s.dataSourceSchemas != nil {
return s.dataSourceSchemas, s.dataSourceSchemasDiags
}
s.dataSourceSchemas = map[string]fwschema.Schema{}
dataSourceFuncs, diags := s.DataSourceFuncs(ctx)
s.dataSourceSchemasDiags = diags
for dataSourceTypeName, dataSourceFunc := range dataSourceFuncs {
dataSource := dataSourceFunc()
logging.FrameworkDebug(ctx, "Calling provider defined DataSource GetSchema", map[string]interface{}{logging.KeyDataSourceType: dataSourceTypeName})
schema, diags := dataSource.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined DataSource GetSchema", map[string]interface{}{logging.KeyDataSourceType: dataSourceTypeName})
s.dataSourceSchemasDiags.Append(diags...)
if s.dataSourceSchemasDiags.HasError() {
return s.dataSourceSchemas, s.dataSourceSchemasDiags
}
s.dataSourceSchemas[dataSourceTypeName] = schema
}
return s.dataSourceSchemas, s.dataSourceSchemasDiags
}
// ProviderSchema returns the Schema associated with the Provider. The Schema
// and Diagnostics are cached on first use.
func (s *Server) ProviderSchema(ctx context.Context) (fwschema.Schema, diag.Diagnostics) {
logging.FrameworkTrace(ctx, "Checking ProviderSchema lock")
s.providerSchemaMutex.Lock()
defer s.providerSchemaMutex.Unlock()
if s.providerSchema != nil {
return s.providerSchema, s.providerSchemaDiags
}
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetSchema")
providerSchema, diags := s.Provider.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider GetSchema")
s.providerSchema = &providerSchema
s.providerSchemaDiags = diags
return s.providerSchema, s.providerSchemaDiags
}
// ProviderMetaSchema returns the Meta Schema associated with the Provider, if
// it implements the ProviderWithMetaSchema interface. The Schema and
// Diagnostics are cached on first use.
func (s *Server) ProviderMetaSchema(ctx context.Context) (fwschema.Schema, diag.Diagnostics) {
providerWithProviderMeta, ok := s.Provider.(provider.ProviderWithMetaSchema)
if !ok {
return nil, nil
}
logging.FrameworkTrace(ctx, "Provider implements ProviderWithMetaSchema")
logging.FrameworkTrace(ctx, "Checking ProviderMetaSchema lock")
s.providerMetaSchemaMutex.Lock()
defer s.providerMetaSchemaMutex.Unlock()
if s.providerMetaSchema != nil {
return s.providerMetaSchema, s.providerMetaSchemaDiags
}
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetMetaSchema")
providerMetaSchema, diags := providerWithProviderMeta.GetMetaSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider GetMetaSchema")
s.providerMetaSchema = &providerMetaSchema
s.providerMetaSchemaDiags = diags
return s.providerMetaSchema, s.providerMetaSchemaDiags
}
// Resource returns the Resource for a given type name.
func (s *Server) Resource(ctx context.Context, typeName string) (resource.Resource, diag.Diagnostics) {
resourceFuncs, diags := s.ResourceFuncs(ctx)
resourceFunc, ok := resourceFuncs[typeName]
if !ok {
diags.AddError(
"Resource Type Not Found",
fmt.Sprintf("No resource type named %q was found in the provider.", typeName),
)
return nil, diags
}
return resourceFunc(), diags
}
// ResourceFuncs returns a map of Resource functions. The results are cached
// on first use.
func (s *Server) ResourceFuncs(ctx context.Context) (map[string]func() resource.Resource, diag.Diagnostics) {
logging.FrameworkTrace(ctx, "Checking ResourceTypes lock")
s.resourceTypesMutex.Lock()
defer s.resourceTypesMutex.Unlock()
if s.resourceFuncs != nil {
return s.resourceFuncs, s.resourceTypesDiags
}
s.resourceFuncs = make(map[string]func() resource.Resource)
logging.FrameworkDebug(ctx, "Calling provider defined Provider Resources")
resourceFuncsSlice := s.Provider.Resources(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider Resources")
for _, resourceFunc := range resourceFuncsSlice {
res := resourceFunc()
resourceTypeNameReq := resource.MetadataRequest{
ProviderTypeName: s.providerTypeName,
}
resourceTypeNameResp := resource.MetadataResponse{}
res.Metadata(ctx, resourceTypeNameReq, &resourceTypeNameResp)
if resourceTypeNameResp.TypeName == "" {
s.resourceTypesDiags.AddError(
"Resource Type Name Missing",
fmt.Sprintf("The %T Resource returned an empty string from the Metadata method. ", res)+
"This is always an issue with the provider and should be reported to the provider developers.",
)
continue
}
logging.FrameworkTrace(ctx, "Found resource type", map[string]interface{}{logging.KeyResourceType: resourceTypeNameResp.TypeName})
if _, ok := s.resourceFuncs[resourceTypeNameResp.TypeName]; ok {
s.resourceTypesDiags.AddError(
"Duplicate Resource Type Defined",
fmt.Sprintf("The %s resource type name was returned for multiple resources. ", resourceTypeNameResp.TypeName)+
"Resource type names must be unique. "+
"This is always an issue with the provider and should be reported to the provider developers.",
)
continue
}
s.resourceFuncs[resourceTypeNameResp.TypeName] = resourceFunc
}
return s.resourceFuncs, s.resourceTypesDiags
}
// ResourceSchema returns the Schema associated with the ResourceType for
// the given type name.
func (s *Server) ResourceSchema(ctx context.Context, typeName string) (fwschema.Schema, diag.Diagnostics) {
resourceSchemas, diags := s.ResourceSchemas(ctx)
resourceSchema, ok := resourceSchemas[typeName]
if !ok {
diags.AddError(
"Resource Schema Not Found",
fmt.Sprintf("No resource type named %q was found in the provider to fetch the schema. ", typeName)+
"This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.",
)
return nil, diags
}
return resourceSchema, diags
}
// ResourceSchemas returns the map of ResourceType Schemas. The results are
// cached on first use.
func (s *Server) ResourceSchemas(ctx context.Context) (map[string]fwschema.Schema, diag.Diagnostics) {
logging.FrameworkTrace(ctx, "Checking ResourceSchemas lock")
s.resourceSchemasMutex.Lock()
defer s.resourceSchemasMutex.Unlock()
if s.resourceSchemas != nil {
return s.resourceSchemas, s.resourceSchemasDiags
}
s.resourceSchemas = map[string]fwschema.Schema{}
resourceFuncs, diags := s.ResourceFuncs(ctx)
s.resourceSchemasDiags = diags
for resourceTypeName, resourceFunc := range resourceFuncs {
res := resourceFunc()
logging.FrameworkDebug(ctx, "Calling provider defined Resource GetSchema", map[string]interface{}{logging.KeyResourceType: resourceTypeName})
schema, diags := res.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Resource GetSchema", map[string]interface{}{logging.KeyResourceType: resourceTypeName})
s.resourceSchemasDiags.Append(diags...)
if s.resourceSchemasDiags.HasError() {
return s.resourceSchemas, s.resourceSchemasDiags
}
s.resourceSchemas[resourceTypeName] = schema
}
return s.resourceSchemas, s.resourceSchemasDiags
}