Skip to content

Commit debd8f7

Browse files
Allow opting out of property conversions if an error occurs (#5051)
* Add $conversionStrategy configuration * Respect renames when constructing compatibility types * Look up property conversion strategy from config * Use config to suppress errors * Add docs * Tidy azure-arm.yaml
1 parent 0919141 commit debd8f7

File tree

6 files changed

+88
-2
lines changed

6 files changed

+88
-2
lines changed

v2/azure-arm.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,18 @@ status:
10081008
# The name in the serialized ARM payload does NOT change. This only changes the name of the property
10091009
# in the ASO types. The shape serialized to ARM uses the original name.
10101010
#
1011+
# $conversionStrategy: <string>
1012+
# Specifies a custom conversion strategy to use when converting between API versions.
1013+
# Only valid on properties which are complex types (i.e. objects) for which attempted automatic
1014+
# conversion returns an error.
1015+
#
1016+
# Valid values are:
1017+
# - auto - use automatic conversion (default)
1018+
# - manual - use manually implemented conversion functions
1019+
#
1020+
# Note that manual implementation of the appropriate augment* interface(s) will be *required*
1021+
# to make the generated conversion tests pass.
1022+
#
10111023
objectModelConfiguration:
10121024
alertsmanagement:
10131025
2021-04-01:
@@ -2373,6 +2385,12 @@ objectModelConfiguration:
23732385
ManagedClusters_MaintenanceConfiguration:
23742386
$exportAs: MaintenanceConfiguration
23752387
$supportedFrom: v2.11.0
2388+
ScaleProfile:
2389+
Autoscale:
2390+
$conversionStrategy: manual
2391+
ScaleProfile_STATUS:
2392+
Autoscale:
2393+
$conversionStrategy: manual
23762394
UserAssignedIdentity:
23772395
ClientId:
23782396
$importConfigMapMode: optional

v2/tools/generator/internal/codegen/pipeline/repair_skipping_properties.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,12 @@ func (repairer *skippingPropertyRepairer) createRepairs(
332332
return repairer.createRepairs(reintroduced)
333333
}
334334

335-
// Construct the name of the compatibility type we need to create
335+
// Construct the name of the compatibility type we need to create, allowing for renames
336336
compatPkg := astmodel.MakeCompatPackageReference(lastMissing.DeclaringType().InternalPackageReference())
337337
compatType := tn.WithPackageReference(compatPkg)
338+
if newName, ok := repairer.objectModelConfiguration.TypeNameInNextVersion.Lookup(tn); ok {
339+
compatType = compatType.WithName(newName)
340+
}
338341

339342
result, err := repairer.createRepairs(reintroduced)
340343
if err != nil {

v2/tools/generator/internal/config/object_model_configuration.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type ObjectModelConfiguration struct {
4848
TypeNameInNextVersion typeAccess[string]
4949

5050
// Property access fields here (alphabetical, please)
51+
ConversionStrategy propertyAccess[ConversionStrategy]
5152
Description propertyAccess[string]
5253
ImportConfigMapMode propertyAccess[ImportConfigMapMode]
5354
IsSecret propertyAccess[bool]
@@ -106,6 +107,8 @@ func NewObjectModelConfiguration() *ObjectModelConfiguration {
106107
result, func(c *TypeConfiguration) *configurable[string] { return &c.NameInNextVersion })
107108

108109
// Initialize property access fields here (alphabetical, please)
110+
result.ConversionStrategy = makePropertyAccess[ConversionStrategy](
111+
result, func(c *PropertyConfiguration) *configurable[ConversionStrategy] { return &c.ConversionStrategy })
109112
result.Description = makePropertyAccess[string](
110113
result, func(c *PropertyConfiguration) *configurable[string] { return &c.Description })
111114
result.ImportConfigMapMode = makePropertyAccess[ImportConfigMapMode](

v2/tools/generator/internal/config/property_configuration.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
type PropertyConfiguration struct {
2424
name string
2525
// Configurable properties here (alphabetical, please)
26+
ConversionStrategy configurable[ConversionStrategy] // Specify the conversion strategy for this property
2627
Description configurable[string] // Specify a description override for this property
2728
ImportConfigMapMode configurable[ImportConfigMapMode] // The config map mode
2829
IsSecret configurable[bool] // Specify whether this property is a secret
@@ -52,8 +53,16 @@ const (
5253
// arm+compat allows us to fix up an ARM reference if we miss it before releasing a version of the resource.
5354
// See https://azure.github.io/azure-service-operator/design/adr-2025-01-arm-references/ for the gory details.
5455

56+
type ConversionStrategy string
57+
58+
const (
59+
ConversionStrategyAuto = ConversionStrategy("auto") // Automatic conversion (default)
60+
ConversionStrategyManual = ConversionStrategy("manual") // Manual conversion
61+
)
62+
5563
// Tags used in yaml files to specify configurable properties. Alphabetical please.
5664
const (
65+
conversionStrategyTag = "$conversionStrategy" // String specifying the conversion strategy (auto or manual)
5766
descriptionTag = "$description" // String overriding the properties default description
5867
exportAsConfigMapPropertyNameTag = "$exportAsConfigMapPropertyName" // String specifying the name of the property set to export this property as a config map.
5968
importConfigMapModeTag = "$importConfigMapMode" // string specifying the ImportConfigMapMode mode
@@ -69,6 +78,7 @@ func NewPropertyConfiguration(name string) *PropertyConfiguration {
6978
return &PropertyConfiguration{
7079
name: name,
7180
// Initialize configurable properties here (alphabetical, please)
81+
ConversionStrategy: makeConfigurable[ConversionStrategy](conversionStrategyTag, scope),
7282
Description: makeConfigurable[string](descriptionTag, scope),
7383
ImportConfigMapMode: makeConfigurable[ImportConfigMapMode](importConfigMapModeTag, scope),
7484
IsSecret: makeConfigurable[bool](isSecretTag, scope),
@@ -94,6 +104,20 @@ func (pc *PropertyConfiguration) UnmarshalYAML(value *yaml.Node) error {
94104
continue
95105
}
96106

107+
// $conversionStrategy: <string>
108+
if strings.EqualFold(lastID, conversionStrategyTag) && c.Kind == yaml.ScalarNode {
109+
switch strings.ToLower(c.Value) {
110+
case string(ConversionStrategyAuto):
111+
pc.ConversionStrategy.Set(ConversionStrategyAuto)
112+
case string(ConversionStrategyManual):
113+
pc.ConversionStrategy.Set(ConversionStrategyManual)
114+
default:
115+
return eris.Errorf("unknown %s value: %s.", conversionStrategyTag, c.Value)
116+
}
117+
118+
continue
119+
}
120+
97121
// $nameInNextVersion: <string>
98122
if strings.EqualFold(lastID, nameInNextVersionTag) && c.Kind == yaml.ScalarNode {
99123
pc.NameInNextVersion.Set(c.Value)

v2/tools/generator/internal/conversions/property_conversion_context.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,23 @@ func (c *PropertyConversionContext) FindNextType(name astmodel.InternalTypeName)
165165
return c.conversionGraph.FindNextType(name, c.definitions)
166166
}
167167

168+
// PropertyConversionStrategy returns true if the specified property has been configured to use manual conversion.
169+
func (c *PropertyConversionContext) PropertyConversionStrategy(
170+
container astmodel.InternalTypeName,
171+
property astmodel.PropertyName,
172+
) config.ConversionStrategy {
173+
if c.configuration == nil {
174+
return config.ConversionStrategyAuto
175+
}
176+
177+
strategy, ok := c.configuration.ConversionStrategy.Lookup(container, property)
178+
if !ok {
179+
return config.ConversionStrategyAuto
180+
}
181+
182+
return strategy
183+
}
184+
168185
// PathExists returns true if a path exists in the conversion graph starting from the specified type name and ending
169186
// at the specified type name. If no conversion graph is available, returns false.
170187
func (c *PropertyConversionContext) PathExists(

v2/tools/generator/internal/functions/property_assignment_function_builder.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/Azure/azure-service-operator/v2/internal/set"
1616
"github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel"
17+
"github.com/Azure/azure-service-operator/v2/tools/generator/internal/config"
1718
"github.com/Azure/azure-service-operator/v2/tools/generator/internal/conversions"
1819
)
1920

@@ -246,7 +247,27 @@ func (builder *PropertyAssignmentFunctionBuilder) createConversions(
246247
// Generate a conversion from one endpoint to another
247248
conv, err := builder.createConversion(sourceEndpoint, destinationEndpoint, conversionContext)
248249
if err != nil {
249-
// An error was returned, we abort creating conversions for this object
250+
// An error was returned, we can't create a conversion for this pair
251+
// We return the error *unless* we have an excluded property
252+
253+
dstName := builder.direction.SelectName(builder.receiverDefinition.Name(), builder.otherDefinition.Name())
254+
dstStrategy := conversionContext.PropertyConversionStrategy(
255+
dstName,
256+
astmodel.PropertyName(destinationEndpoint.Name()),
257+
)
258+
259+
srcName := builder.direction.SelectName(builder.otherDefinition.Name(), builder.receiverDefinition.Name())
260+
srcStrategy := conversionContext.PropertyConversionStrategy(
261+
srcName,
262+
astmodel.PropertyName(sourceEndpoint.Name()),
263+
)
264+
265+
if srcStrategy == config.ConversionStrategyManual || dstStrategy == config.ConversionStrategyManual {
266+
// One of the properties is marked as manual conversion, so we can skip this error
267+
return nil
268+
}
269+
270+
// Otherwise, return the conversion error
250271
return eris.Wrapf(
251272
err,
252273
"creating conversion to %s by %s",

0 commit comments

Comments
 (0)