This repository has been archived by the owner on Dec 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 38
/
resource.go
305 lines (255 loc) · 11.7 KB
/
resource.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
/*
Copyright 2021 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"context"
"fmt"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
)
// SetIdentifierArgumentsFn sets the name of the resource in Terraform attributes map,
// i.e. Main HCL file.
type SetIdentifierArgumentsFn func(base map[string]interface{}, externalName string)
// NopSetIdentifierArgument does nothing. It's useful for cases where the external
// name is calculated by provider and doesn't have any effect on spec fields.
var NopSetIdentifierArgument SetIdentifierArgumentsFn = func(_ map[string]interface{}, _ string) {}
// GetIDFn returns the ID to be used in TF State file, i.e. "id" field in
// terraform.tfstate.
type GetIDFn func(ctx context.Context, externalName string, parameters map[string]interface{}, providerConfig map[string]interface{}) (string, error)
// ExternalNameAsID returns the name to be used as ID in TF State file.
var ExternalNameAsID GetIDFn = func(_ context.Context, externalName string, _ map[string]interface{}, _ map[string]interface{}) (string, error) {
return externalName, nil
}
// GetExternalNameFn returns the external name extracted from the TF State.
type GetExternalNameFn func(tfstate map[string]interface{}) (string, error)
// IDAsExternalName returns the TF State ID as external name.
var IDAsExternalName GetExternalNameFn = func(tfstate map[string]interface{}) (string, error) {
if id, ok := tfstate["id"].(string); ok && id != "" {
return id, nil
}
return "", errors.New("cannot find id in tfstate")
}
// AdditionalConnectionDetailsFn functions adds custom keys to connection details
// secret using input terraform attributes
type AdditionalConnectionDetailsFn func(attr map[string]interface{}) (map[string][]byte, error)
// NopAdditionalConnectionDetails does nothing, when no additional connection
// details configuration function provided.
var NopAdditionalConnectionDetails AdditionalConnectionDetailsFn = func(_ map[string]interface{}) (map[string][]byte, error) {
return nil, nil
}
// ExternalName contains all information that is necessary for naming operations,
// such as removal of those fields from spec schema and calling Configure function
// to fill attributes with information given in external name.
type ExternalName struct {
// SetIdentifierArgumentFn sets the name of the resource in Terraform argument
// map. In many cases, there is a field called "name" in the HCL schema, however,
// there are cases like RDS DB Cluster where the name field in HCL is called
// "cluster_identifier". This function is the place that you can take external
// name and assign it to that specific key for that resource type.
SetIdentifierArgumentFn SetIdentifierArgumentsFn
// GetExternalNameFn returns the external name extracted from TF State. In most cases,
// "id" field contains all the information you need. You'll need to extract
// the format that is decided for external name annotation to use.
// For example the following is an Azure resource ID:
// /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1
// The function should return "mygroup1" so that it can be used to set external
// name if it was not set already.
GetExternalNameFn GetExternalNameFn
// GetIDFn returns the string that will be used as "id" key in TF state. In
// many cases, external name format is the same as "id" but when it is not
// we may need information from other places to construct it. For example,
// the following is an Azure resource ID:
// /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1
// The function here should use information from supplied arguments to
// construct this ID, i.e. "mygroup1" from external name, subscription ID
// from providerConfig, and others from parameters map if needed.
GetIDFn GetIDFn
// OmittedFields are the ones you'd like to be removed from the schema since
// they are specified via external name. For example, if you set
// "cluster_identifier" in SetIdentifierArgumentFn, then you need to omit
// that field.
// You can omit only the top level fields.
// No field is omitted by default.
OmittedFields []string
// DisableNameInitializer allows you to specify whether the name initializer
// that sets external name to metadata.name if none specified should be disabled.
// It needs to be disabled for resources whose external identifier is randomly
// assigned by the provider, like AWS VPC where it gets vpc-21kn123 identifier
// and not let you name it.
DisableNameInitializer bool
}
// References represents reference resolver configurations for the fields of a
// given resource. Key should be the field path of the field to be referenced.
type References map[string]Reference
// Reference represents the Crossplane options used to generate
// reference resolvers for fields
type Reference struct {
// Type is the type name of the CRD if it is in the same package or
// <package-path>.<type-name> if it is in a different package.
Type string
// Extractor is the function to be used to extract value from the
// referenced type. Defaults to getting external name.
// Optional
Extractor string
// RefFieldName is the field name for the Reference field. Defaults to
// <field-name>Ref or <field-name>Refs.
// Optional
RefFieldName string
// SelectorFieldName is the field name for the Selector field. Defaults to
// <field-name>Selector.
// Optional
SelectorFieldName string
}
// Sensitive represents configurations to handle sensitive information
type Sensitive struct {
// AdditionalConnectionDetailsFn is the path for function adding additional
// connection details keys
AdditionalConnectionDetailsFn AdditionalConnectionDetailsFn
// fieldPaths keeps the mapping of sensitive fields in Terraform schema with
// terraform field path as key and xp field path as value.
fieldPaths map[string]string
}
// LateInitializer represents configurations that control
// late-initialization behaviour
type LateInitializer struct {
// IgnoredFields are the field paths to be skipped during
// late-initialization. Similar to other configurations, these paths are
// Terraform field paths concatenated with dots. For example, if we want to
// ignore "ebs" block in "aws_launch_template", we should add
// "block_device_mappings.ebs".
IgnoredFields []string
// ignoredCanonicalFieldPaths are the Canonical field paths to be skipped
// during late-initialization. This is filled using the `IgnoredFields`
// field which keeps Terraform paths by converting them to Canonical paths.
ignoredCanonicalFieldPaths []string
}
// GetIgnoredCanonicalFields returns the ignoredCanonicalFields
func (l *LateInitializer) GetIgnoredCanonicalFields() []string {
return l.ignoredCanonicalFieldPaths
}
// AddIgnoredCanonicalFields sets ignored canonical fields
func (l *LateInitializer) AddIgnoredCanonicalFields(cf string) {
if l.ignoredCanonicalFieldPaths == nil {
l.ignoredCanonicalFieldPaths = make([]string, 0)
}
l.ignoredCanonicalFieldPaths = append(l.ignoredCanonicalFieldPaths, cf)
}
// GetFieldPaths returns the fieldPaths map for Sensitive
func (s *Sensitive) GetFieldPaths() map[string]string {
return s.fieldPaths
}
// AddFieldPath adds the given tf path and xp path to the fieldPaths map.
func (s *Sensitive) AddFieldPath(tf, xp string) {
if s.fieldPaths == nil {
s.fieldPaths = make(map[string]string)
}
s.fieldPaths[tf] = xp
}
// OperationTimeouts allows configuring resource operation timeouts:
// https://www.terraform.io/language/resources/syntax#operation-timeouts
// Please note that, not all resources support configuring timeouts.
type OperationTimeouts struct {
Read time.Duration
Create time.Duration
Update time.Duration
Delete time.Duration
}
// NewInitializerFn returns the Initializer with a client.
type NewInitializerFn func(client client.Client) managed.Initializer
// TagInitializer returns a tagger to use default tag initializer.
var TagInitializer NewInitializerFn = func(client client.Client) managed.Initializer {
return NewTagger(client, "tags")
}
// Tagger implements the Initialize function to set external tags
type Tagger struct {
kube client.Client
fieldName string
}
// NewTagger returns a Tagger object.
func NewTagger(kube client.Client, fieldName string) *Tagger {
return &Tagger{kube: kube, fieldName: fieldName}
}
// Initialize is a custom initializer for setting external tags
func (t *Tagger) Initialize(ctx context.Context, mg xpresource.Managed) error {
paved, err := fieldpath.PaveObject(mg)
if err != nil {
return err
}
pavedByte, err := setExternalTagsWithPaved(xpresource.GetExternalTags(mg), paved, t.fieldName)
if err != nil {
return err
}
if err := json.Unmarshal(pavedByte, mg); err != nil {
return err
}
if err := t.kube.Update(ctx, mg); err != nil {
return err
}
return nil
}
func setExternalTagsWithPaved(externalTags map[string]string, paved *fieldpath.Paved, fieldName string) ([]byte, error) {
tags := map[string]*string{
xpresource.ExternalResourceTagKeyKind: pointer.String(externalTags[xpresource.ExternalResourceTagKeyKind]),
xpresource.ExternalResourceTagKeyName: pointer.String(externalTags[xpresource.ExternalResourceTagKeyName]),
xpresource.ExternalResourceTagKeyProvider: pointer.String(externalTags[xpresource.ExternalResourceTagKeyProvider]),
}
if err := paved.SetValue(fmt.Sprintf("spec.forProvider.%s", fieldName), tags); err != nil {
return nil, err
}
pavedByte, err := paved.MarshalJSON()
if err != nil {
return nil, err
}
return pavedByte, nil
}
// Resource is the set of information that you can override at different steps
// of the code generation pipeline.
type Resource struct {
// Name is the name of the resource type in Terraform,
// e.g. aws_rds_cluster.
Name string
// TerraformResource is the Terraform representation of the resource.
TerraformResource *schema.Resource
// ShortGroup is the short name of the API group of this CRD. The full
// CRD API group is calculated by adding the group suffix of the provider.
// For example, ShortGroup could be `ec2` where group suffix of the
// provider is `aws.crossplane.io` and in that case, the full group would
// be `ec2.aws.crossplane.io`
ShortGroup string
// Version is the version CRD will have.
Version string
// Kind is the kind of the CRD.
Kind string
// UseAsync should be enabled for resource whose creation and/or deletion
// takes more than 1 minute to complete such as Kubernetes clusters or
// databases.
UseAsync bool
InitializerFns []NewInitializerFn
// OperationTimeouts allows configuring resource operation timeouts.
OperationTimeouts OperationTimeouts
// ExternalName allows you to specify a custom ExternalName.
ExternalName ExternalName
// References keeps the configuration to build cross resource references
References References
// Sensitive keeps the configuration to handle sensitive information
Sensitive Sensitive
// LateInitializer configuration to control late-initialization behaviour
LateInitializer LateInitializer
}