Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use granular merge strategy for singleton lists #380

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions pkg/config/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ func NewProvider(schema []byte, prefix string, modulePath string, metadata []byt
p.Resources[name] = DefaultResource(name, terraformResource, terraformPluginFrameworkResource, providerMetadata.Resources[name], p.DefaultResourceOptions...)
p.Resources[name].useTerraformPluginSDKClient = isTerraformPluginSDK
p.Resources[name].useTerraformPluginFrameworkClient = isPluginFrameworkResource
p.injectServerSideApplyDefaults(p.Resources[name], terraformResource, "")
}
for i, refInjector := range p.refInjectors {
if err := refInjector.InjectReferences(p.Resources); err != nil {
Expand All @@ -360,6 +361,58 @@ func NewProvider(schema []byte, prefix string, modulePath string, metadata []byt
return p
}

func (p *Provider) injectServerSideApplyDefaults(cfg *Resource, res *schema.Resource, basePath string) { //nolint:gocyclo // Easier to follow the logic in a single function

for k, es := range res.Schema {

var fieldPath string
if basePath == "" {
fieldPath = k
} else {
fieldPath = fmt.Sprintf("%s.%s", basePath, k)
}

if r, ok := es.Elem.(*schema.Resource); ok {
if es.Type == schema.TypeList && es.MaxItems == 1 {
cfg.ServerSideApplyMergeStrategies[fieldPath] = MergeStrategy{
ListMergeStrategy: ListMergeStrategy{
MergeStrategy: ListTypeMap,
ListMapKeys: ListMapKeys{InjectedKey: InjectedKey{
Key: "ssamergekey",
DefaultValue: `"0"`,
}},
},
}
}
p.injectServerSideApplyDefaults(cfg, r, fieldPath)
} else {
switch es.Type { //nolint:exhaustive
case schema.TypeSet:
if el, ok := es.Elem.(*schema.Schema); ok {
switch el.Type { //nolint:exhaustive
case schema.TypeString, schema.TypeBool, schema.TypeInt, schema.TypeFloat:
cfg.ServerSideApplyMergeStrategies[fieldPath] = MergeStrategy{
ListMergeStrategy: ListMergeStrategy{
MergeStrategy: ListTypeSet,
},
}
}
}
case schema.TypeMap:
if el, ok := es.Elem.(*schema.Schema); ok {
switch el.Type { //nolint:exhaustive
case schema.TypeString, schema.TypeBool, schema.TypeInt, schema.TypeFloat:
// TODO(jono): I think this may be unnecessary since maps appear to default to granular?
cfg.ServerSideApplyMergeStrategies[fieldPath] = MergeStrategy{
MapMergeStrategy: MapTypeGranular,
}
}
}
}
}
}
}

// AddResourceConfigurator adds resource specific configurators.
func (p *Provider) AddResourceConfigurator(resource string, c ResourceConfiguratorFn) { //nolint:interfacer
// Note(turkenh): nolint reasoning - easier to provide a function without
Expand Down
1 change: 1 addition & 0 deletions pkg/types/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ func injectServerSideApplyListMergeKeys(cfg *config.Resource) error { //nolint:g
el.Schema[s.ListMergeStrategy.ListMapKeys.InjectedKey.Key] = &schema.Schema{
Type: schema.TypeString,
Required: true,
Computed: sch.Computed,
Description: descriptionInjectedKey,
}
if s.ListMergeStrategy.ListMapKeys.InjectedKey.DefaultValue != "" {
Expand Down
33 changes: 0 additions & 33 deletions pkg/types/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,42 +161,9 @@ func NewField(g *Builder, cfg *config.Resource, r *resource, sch *schema.Schema,
f.FieldType = fieldType
f.InitType = initType

AddServerSideApplyMarkers(f)
return f, errors.Wrapf(AddServerSideApplyMarkersFromConfig(f, cfg), "cannot add the server-side apply merge strategy markers for the field")
}

// AddServerSideApplyMarkers adds server-side apply comment markers to indicate
// that scalar maps and sets can be merged granularly, not replace atomically.
func AddServerSideApplyMarkers(f *Field) {
// for sensitive fields, we generate secret or secret key references
if f.Schema.Sensitive {
return
}

switch f.Schema.Type { //nolint:exhaustive
case schema.TypeMap:
// A map should always have an element of type Schema.
if es, ok := f.Schema.Elem.(*schema.Schema); ok {
switch es.Type { //nolint:exhaustive
// We assume scalar types can be granular maps.
case schema.TypeString, schema.TypeBool, schema.TypeInt, schema.TypeFloat:
f.Comment.ServerSideApplyOptions.MapType = ptr.To[config.MapType](config.MapTypeGranular)
}
}
case schema.TypeSet:
if es, ok := f.Schema.Elem.(*schema.Schema); ok {
switch es.Type { //nolint:exhaustive
// We assume scalar types can be granular sets.
case schema.TypeString, schema.TypeBool, schema.TypeInt, schema.TypeFloat:
f.Comment.ServerSideApplyOptions.ListType = ptr.To[config.ListType](config.ListTypeSet)
}
}
}
// TODO(negz): Can we reliably add SSA markers for lists of objects? Do we
// have cases where we're turning a Terraform map of maps into a list of
// objects with a well-known key that we could merge on?
}

func setInjectedField(fp, k string, f *Field, s config.MergeStrategy) bool {
if fp != fmt.Sprintf("%s.%s", k, s.ListMergeStrategy.ListMapKeys.InjectedKey.Key) {
return false
Expand Down