From 7ce200eee115b77d07d004d2e4e68746ef3641a1 Mon Sep 17 00:00:00 2001 From: Thomas Kappler Date: Tue, 24 Dec 2024 08:33:19 +0100 Subject: [PATCH 1/8] Detect conflicting API paths for the same resource and de-conflict them --- provider/pkg/gen/schema.go | 120 +++++++++++++++++++++++++++++++- provider/pkg/gen/schema_test.go | 22 ++++++ 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/provider/pkg/gen/schema.go b/provider/pkg/gen/schema.go index 220225d1efb2..53eaf16c5a46 100644 --- a/provider/pkg/gen/schema.go +++ b/provider/pkg/gen/schema.go @@ -32,6 +32,7 @@ import ( "github.com/pkg/errors" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi" + "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi/paths" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/resources" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/resources/customresources" "github.com/pulumi/pulumi/pkg/v3/codegen" @@ -291,6 +292,8 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning } slices.Sort(versions) + pathConflicts := map[string]map[string]struct{}{} + for _, sdkVersion := range versions { // Attempt to convert back to an API version for use elsewhere var apiVersion *openapi.ApiVersion @@ -301,6 +304,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning } apiVersion = &apiVersionConverted } + gen := packageGenerator{ pkg: &pkg, metadata: &metadata, @@ -314,6 +318,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning rootDir: rootDir, flattenedPropertyConflicts: flattenedPropertyConflicts, majorVersion: majorVersion, + pathConflicts: pathConflicts, } // Populate C#, Java, Python and Go module mapping. @@ -643,6 +648,8 @@ type packageGenerator struct { forceNewTypes []ForceNewType flattenedPropertyConflicts map[string]map[string]struct{} majorVersion int + // A tok -> path map to detect when the same resource tok is mapped to different API paths. + pathConflicts map[string]map[string]struct{} } func (g *packageGenerator) genResources(typeName string, resource *openapi.ResourceSpec, nestedResourceBodyRefs []string) error { @@ -775,16 +782,125 @@ func (g *packageGenerator) findResourceVariants(resource *openapi.ResourceSpec) return result, nil } +// dedupResourceNameByPath returns a modified resource name (`typeName`) if the resource is mapped to multiple API +// paths. For instance, the deprecated "single server" resources in `dbformysql` and `dbforpostgresql` are renamed +// to `SingleServerResource`. +// TODO,tkappler check each one if we can just get rid of an old API version instead of doing this. +func dedupResourceNameByPath(provider, typeName, canonPath string) string { + result := typeName + + prefix := func(prefix string) string { + if !strings.HasPrefix(typeName, prefix) { + return prefix + typeName + } + return typeName + } + + switch strings.ToLower(provider) { + case "cache": + if strings.Contains(canonPath, "/redis/") { + result = prefix("Redis") + } else if strings.Contains(canonPath, "/redisenterprise/") { + result = prefix("RedisEnterprise") + } + // $ rg --only-matching --no-filename --glob '!examples' 'providers/Microsoft.DBforMySQL/.+?/' azure-rest-api-specs/specification/ | sort | uniq + // providers/Microsoft.DBforMySQL/flexibleServers/ + // providers/Microsoft.DBforMySQL/servers/ + case "dbformysql": + if strings.Contains(canonPath, "/servers/") { + result = prefix("SingleServer") + } + // $ rg --only-matching --no-filename --glob '!examples' 'providers/Microsoft.DBforPostgreSQL/.+?/' azure-rest-api-specs/specification/ | sort | uniq + // providers/Microsoft.DBforPostgreSQL/flexibleServers/ + // providers/Microsoft.DBforPostgreSQL/serverGroupsv2/ + // providers/Microsoft.DBforPostgreSQL/servers/ + case "dbforpostgresql": + if strings.Contains(canonPath, "/servers/") { + result = prefix("SingleServer") + } else if strings.Contains(canonPath, "/servergroupsv2/") { + result = prefix("ServerGroup") + } + case "documentdb": + if strings.Contains(canonPath, "/mongoclusters/") { + prefix("MongoCluster") + } + case "hdinsight": + if strings.Contains(canonPath, "/clusterpools/") { + result = prefix("ClusterPool") + } + case "hybridcontainerservice": + if strings.Contains(canonPath, "/provisionedclusterinstances/") { + result = prefix("ClusterInstance") + } + case "labservices": + // /labaccounts is an old API that only occurs in 2018 but we support it in v2 + if strings.Contains(canonPath, "/labaccounts/") { + result = prefix("LabAccount") + } + case "migrate": + if strings.Contains(canonPath, "/assessmentprojects/") { + result = prefix("AssessmentProjects") + } + case "mobilenetwork": + if strings.Contains(canonPath, "/simgroups/") { + result = prefix("SimGroup") + } + case "netapp": + if strings.Contains(canonPath, "/backupvaults/") { + result = prefix("BackupVault") + } else if strings.Contains(canonPath, "/capacitypools/") { + result = prefix("CapacityPool") + } + } + + return result +} + +// checkForPathConflicts detects when the same resource type token is mapped to different API paths. +func (g *packageGenerator) checkForPathConflicts(typeName, canonPath string) error { + // Some resources have a /default path, e.g., azure-native:azurestackhci:GuestAgent has conflicting paths + // /subscriptions/{}/resourcegroups/{}/providers/microsoft.azurestackhci/virtualmachines/{}/guestagents/{}, + // /{}/providers/microsoft.azurestackhci/virtualmachineinstances/default/guestagents/default, + // also azure-native:hybridcontainerservice:HybridIdentityMetadatum + if strings.HasSuffix(canonPath, "/default") { + return nil + } + + tokWithoutVersion := generateTok(g.provider, typeName, "") + if paths, ok := g.pathConflicts[tokWithoutVersion]; ok { + paths[canonPath] = struct{}{} + if len(paths) > 1 { + // TODO,tkappler return error instead once we have all conflicts addressed. + log.Printf("Warning: resource %s has conflicting paths %s\n", tokWithoutVersion, strings.Join(codegen.SortedKeys(paths), ", ")) + } + } else { + g.pathConflicts[tokWithoutVersion] = map[string]struct{}{canonPath: {}} + } + + return nil +} + func (g *packageGenerator) genResourceVariant(apiSpec *openapi.ResourceSpec, resource *resourceVariant, nestedResourceBodyRefs []string, typeNameAliases ...string) error { module := g.moduleName() swagger := resource.Swagger path := resource.PathItem + canonPath := paths.NormalizePath(resource.Path) + + typeName := resource.typeName + if g.majorVersion > 3 { + typeName = dedupResourceNameByPath(g.provider, resource.typeName, canonPath) + } - resourceTok := fmt.Sprintf(`%s:%s:%s`, g.pkg.Name, module, resource.typeName) - if !g.versioning.ShouldInclude(g.provider, g.apiVersion, resource.typeName, resourceTok) { + resourceTok := generateTok(g.provider, typeName, g.sdkVersion) + if !g.versioning.ShouldInclude(g.provider, g.apiVersion, typeName, resourceTok) { return nil } + err := g.checkForPathConflicts(typeName, canonPath) + if err != nil { + return err + } + // Generate the resource. gen := moduleGenerator{ pkg: g.pkg, diff --git a/provider/pkg/gen/schema_test.go b/provider/pkg/gen/schema_test.go index 5fe1e6659e2f..01fef2208735 100644 --- a/provider/pkg/gen/schema_test.go +++ b/provider/pkg/gen/schema_test.go @@ -359,3 +359,25 @@ func TestGoModuleName(t *testing.T) { assert.Equal(t, "github.com/pulumi/pulumi-azure-native-sdk/network/v2", goModuleName("Network", "")) }) } + +func TestDedupResourceNameByPath(t *testing.T) { + t.Run("no change", func(t *testing.T) { + assert.Equal(t, "Resource", dedupResourceNameByPath("compute", "Resource", "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualmachines/{}")) + }) + + t.Run("dbformysql single server", func(t *testing.T) { + assert.Equal(t, "SingleServerResource", dedupResourceNameByPath("dbformysql", "Resource", "/subscriptions/{}/resourcegroups/{}/providers/Microsoft.DBforMySQL/servers/{}")) + }) + + t.Run("dbformysql flexible server", func(t *testing.T) { + assert.Equal(t, "Resource", dedupResourceNameByPath("dbformysql", "Resource", "/subscriptions/{}/resourcegroups/{}/providers/Microsoft.DBforMySQL/flexibleservers/{}")) + }) + + t.Run("dbforpostgresql single server", func(t *testing.T) { + assert.Equal(t, "SingleServerResource", dedupResourceNameByPath("dbforpostgresql", "Resource", "/subscriptions/{}/resourcegroups/{}/providers/Microsoft.DBforPostgreSQL/servers/{}")) + }) + + t.Run("dbforpostgresql flexible server", func(t *testing.T) { + assert.Equal(t, "Resource", dedupResourceNameByPath("dbforpostgresql", "Resource", "/subscriptions/{}/resourcegroups/{}/providers/Microsoft.DBforPostgreSQL/flexibleservers/{}")) + }) +} From 5714d516103abf33e110987acebbea128aad4bbb Mon Sep 17 00:00:00 2001 From: Thomas Kappler Date: Wed, 8 Jan 2025 09:20:17 +0100 Subject: [PATCH 2/8] Turn log messages into a proper report. Fail when we're on v3+. --- provider/pkg/gen/schema.go | 47 ++++++----- provider/pkg/versioning/build_schema.go | 18 ++-- reports/pathConflicts.json | 104 ++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 29 deletions(-) create mode 100644 reports/pathConflicts.json diff --git a/provider/pkg/gen/schema.go b/provider/pkg/gen/schema.go index 53eaf16c5a46..dc28a943c4b2 100644 --- a/provider/pkg/gen/schema.go +++ b/provider/pkg/gen/schema.go @@ -65,6 +65,7 @@ type GenerationResult struct { ForceNewTypes []ForceNewType TypeCaseConflicts CaseConflicts FlattenedPropertyConflicts map[string]map[string]struct{} + PathConflicts map[string]map[string]struct{} } // PulumiSchema will generate a Pulumi schema for the given Azure providers and resources map. @@ -283,6 +284,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning caseSensitiveTypes := newCaseSensitiveTokens() flattenedPropertyConflicts := map[string]map[string]struct{}{} exampleMap := make(map[string][]resources.AzureAPIExample) + resourcePaths := map[string]map[string]struct{}{} for _, providerName := range providers { versionMap := providerMap[providerName] @@ -292,8 +294,6 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning } slices.Sort(versions) - pathConflicts := map[string]map[string]struct{}{} - for _, sdkVersion := range versions { // Attempt to convert back to an API version for use elsewhere var apiVersion *openapi.ApiVersion @@ -318,7 +318,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning rootDir: rootDir, flattenedPropertyConflicts: flattenedPropertyConflicts, majorVersion: majorVersion, - pathConflicts: pathConflicts, + resourcePaths: resourcePaths, } // Populate C#, Java, Python and Go module mapping. @@ -408,6 +408,17 @@ version using infrastructure as code, which Pulumi then uses to drive the ARM AP "packages": javaPackages, }) + // When a resource maps to more than one API path, it's a conflict and we need to detect and report it. #2495 + pathConflicts := map[string]map[string]struct{}{} + for tok, paths := range resourcePaths { + if len(paths) > 1 { + pathConflicts[tok] = paths + } + } + if majorVersion >= 3 && len(pathConflicts) > 0 { + return nil, fmt.Errorf("path conflicts detected: %v", pathConflicts) + } + return &GenerationResult{ Schema: &pkg, Metadata: &metadata, @@ -415,6 +426,7 @@ version using infrastructure as code, which Pulumi then uses to drive the ARM AP ForceNewTypes: forceNewTypes, TypeCaseConflicts: caseSensitiveTypes.findCaseConflicts(), FlattenedPropertyConflicts: flattenedPropertyConflicts, + PathConflicts: pathConflicts, }, nil } @@ -648,8 +660,8 @@ type packageGenerator struct { forceNewTypes []ForceNewType flattenedPropertyConflicts map[string]map[string]struct{} majorVersion int - // A tok -> path map to detect when the same resource tok is mapped to different API paths. - pathConflicts map[string]map[string]struct{} + // A tok -> paths map to record API paths per resource and later detect conflicts. + resourcePaths map[string]map[string]struct{} } func (g *packageGenerator) genResources(typeName string, resource *openapi.ResourceSpec, nestedResourceBodyRefs []string) error { @@ -856,28 +868,22 @@ func dedupResourceNameByPath(provider, typeName, canonPath string) string { return result } -// checkForPathConflicts detects when the same resource type token is mapped to different API paths. -func (g *packageGenerator) checkForPathConflicts(typeName, canonPath string) error { +// recordPath keeps track of all API paths a resource is mapped to. +func (g *packageGenerator) recordPath(typeName, canonPath string) { // Some resources have a /default path, e.g., azure-native:azurestackhci:GuestAgent has conflicting paths // /subscriptions/{}/resourcegroups/{}/providers/microsoft.azurestackhci/virtualmachines/{}/guestagents/{}, // /{}/providers/microsoft.azurestackhci/virtualmachineinstances/default/guestagents/default, // also azure-native:hybridcontainerservice:HybridIdentityMetadatum if strings.HasSuffix(canonPath, "/default") { - return nil + return } tokWithoutVersion := generateTok(g.provider, typeName, "") - if paths, ok := g.pathConflicts[tokWithoutVersion]; ok { - paths[canonPath] = struct{}{} - if len(paths) > 1 { - // TODO,tkappler return error instead once we have all conflicts addressed. - log.Printf("Warning: resource %s has conflicting paths %s\n", tokWithoutVersion, strings.Join(codegen.SortedKeys(paths), ", ")) - } - } else { - g.pathConflicts[tokWithoutVersion] = map[string]struct{}{canonPath: {}} - } - return nil + if _, ok := g.resourcePaths[tokWithoutVersion]; !ok { + g.resourcePaths[tokWithoutVersion] = map[string]struct{}{} + } + g.resourcePaths[tokWithoutVersion][canonPath] = struct{}{} } func (g *packageGenerator) genResourceVariant(apiSpec *openapi.ResourceSpec, resource *resourceVariant, nestedResourceBodyRefs []string, typeNameAliases ...string) error { @@ -896,10 +902,7 @@ func (g *packageGenerator) genResourceVariant(apiSpec *openapi.ResourceSpec, res return nil } - err := g.checkForPathConflicts(typeName, canonPath) - if err != nil { - return err - } + g.recordPath(typeName, canonPath) // Generate the resource. gen := moduleGenerator{ diff --git a/provider/pkg/versioning/build_schema.go b/provider/pkg/versioning/build_schema.go index 18ce03033328..bccd6bffecce 100644 --- a/provider/pkg/versioning/build_schema.go +++ b/provider/pkg/versioning/build_schema.go @@ -33,6 +33,8 @@ type BuildSchemaArgs struct { type BuildSchemaReports struct { PathChangesResult + // A tok -> paths map to record types that have conflicting paths. + PathConflicts map[string]map[string]struct{} AllResourcesByVersion ProvidersVersionResources AllResourceVersionsByResource ProviderResourceVersions Pending openapi.ProviderVersionList @@ -49,19 +51,20 @@ type BuildSchemaReports struct { func (r BuildSchemaReports) WriteTo(outputDir string) ([]string, error) { return gen.EmitFiles(outputDir, gen.FileMap{ - "pathChanges.json": r.PathChangesResult, + "allEndpoints.json": r.AllEndpoints, "allResourcesByVersion.json": r.AllResourcesByVersion, "allResourceVersionsByResource.json": r.AllResourceVersionsByResource, - "pending.json": r.Pending, "curationViolations.json": r.CurationViolations, + "flattenedPropertyConflicts.json": r.FlattenedPropertyConflicts, + "forceNewTypes.json": r.ForceNewTypes, + "inactiveDefaultVersions.json": r.InactiveDefaultVersions, "namingDisambiguations.json": r.NamingDisambiguations, - "skippedPOSTEndpoints.json": r.SkippedPOSTEndpoints, + "pathChanges.json": r.PathChangesResult, + "pathConflicts.json": r.PathConflicts, + "pending.json": r.Pending, "providerNameErrors.json": r.ProviderNameErrors, - "forceNewTypes.json": r.ForceNewTypes, + "skippedPOSTEndpoints.json": r.SkippedPOSTEndpoints, "typeCaseConflicts.json": r.TypeCaseConflicts, - "flattenedPropertyConflicts.json": r.FlattenedPropertyConflicts, - "allEndpoints.json": r.AllEndpoints, - "inactiveDefaultVersions.json": r.InactiveDefaultVersions, }) } @@ -135,6 +138,7 @@ func BuildSchema(args BuildSchemaArgs) (*BuildSchemaResult, error) { buildSchemaReports.ForceNewTypes = generationResult.ForceNewTypes buildSchemaReports.TypeCaseConflicts = generationResult.TypeCaseConflicts buildSchemaReports.FlattenedPropertyConflicts = generationResult.FlattenedPropertyConflicts + buildSchemaReports.PathConflicts = generationResult.PathConflicts pkgSpec := generationResult.Schema metadata := generationResult.Metadata diff --git a/reports/pathConflicts.json b/reports/pathConflicts.json new file mode 100644 index 000000000000..a077126c4b35 --- /dev/null +++ b/reports/pathConflicts.json @@ -0,0 +1,104 @@ +{ + "azure-native:apimanagement:ApiPolicy": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policies/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policy": {} + }, + "azure-native:apimanagement:ProductPolicy": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policies/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policy": {} + }, + "azure-native:cache:AccessPolicyAssignment": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redis/{}/accesspolicyassignments/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redisenterprise/{}/databases/{}/accesspolicyassignments/{}": {} + }, + "azure-native:dbformysql:Configuration": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/configurations/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/configurations/{}": {} + }, + "azure-native:dbformysql:Database": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/databases/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/databases/{}": {} + }, + "azure-native:dbformysql:FirewallRule": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/firewallrules/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/firewallrules/{}": {} + }, + "azure-native:dbformysql:PrivateEndpointConnection": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/privateendpointconnections/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/privateendpointconnections/{}": {} + }, + "azure-native:dbformysql:Server": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}": {} + }, + "azure-native:dbforpostgresql:Configuration": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/configurations/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/configurations/{}": {} + }, + "azure-native:dbforpostgresql:Database": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/databases/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/databases/{}": {} + }, + "azure-native:dbforpostgresql:FirewallRule": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/firewallrules/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/firewallrules/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/firewallrules/{}": {} + }, + "azure-native:dbforpostgresql:PrivateEndpointConnection": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/privateendpointconnections/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/privateendpointconnections/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/privateendpointconnections/{}": {} + }, + "azure-native:dbforpostgresql:Server": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}": {} + }, + "azure-native:documentdb:PrivateEndpointConnection": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/databaseaccounts/{}/privateendpointconnections/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/mongoclusters/{}/privateendpointconnections/{}": {} + }, + "azure-native:hdinsight:Cluster": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusterpools/{}/clusters/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusters/{}": {} + }, + "azure-native:hybridcontainerservice:AgentPool": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hybridcontainerservice/provisionedclusters/{}/agentpools/{}": {}, + "/{}/providers/microsoft.hybridcontainerservice/provisionedclusterinstances/default/agentpools/{}": {} + }, + "azure-native:labservices:Lab": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}": {} + }, + "azure-native:labservices:User": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}/users/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}/users/{}": {} + }, + "azure-native:machinelearningservices:ConnectionRaiBlocklist": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": {} + }, + "azure-native:machinelearningservices:ConnectionRaiBlocklistItem": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": {} + }, + "azure-native:migrate:Assessment": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}/assessments/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}/assessments/{}": {} + }, + "azure-native:migrate:Group": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}": {} + }, + "azure-native:migrate:Project": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}": {} + }, + "azure-native:mobilenetwork:Sim": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/simgroups/{}/sims/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/sims/{}": {} + }, + "azure-native:netapp:Backup": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/backupvaults/{}/backups/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/capacitypools/{}/volumes/{}/backups/{}": {} + } +} \ No newline at end of file From 2a0185dba8342c040d7092755d674766bb2701a2 Mon Sep 17 00:00:00 2001 From: Thomas Kappler Date: Thu, 9 Jan 2025 09:36:09 +0100 Subject: [PATCH 3/8] Refactor to encapsulate logic more, type pathConflicts map more clearly --- provider/pkg/gen/schema.go | 79 ++++++--- provider/pkg/gen/schema_test.go | 49 +++++ provider/pkg/versioning/build_schema.go | 4 +- reports/pathConflicts.json | 226 +++++++++++++----------- 4 files changed, 232 insertions(+), 126 deletions(-) diff --git a/provider/pkg/gen/schema.go b/provider/pkg/gen/schema.go index dc28a943c4b2..0237dce1c160 100644 --- a/provider/pkg/gen/schema.go +++ b/provider/pkg/gen/schema.go @@ -65,7 +65,9 @@ type GenerationResult struct { ForceNewTypes []ForceNewType TypeCaseConflicts CaseConflicts FlattenedPropertyConflicts map[string]map[string]struct{} - PathConflicts map[string]map[string]struct{} + // A map of provider -> resource -> set of paths, to record resources that have conflicts where the same resource + // maps to more than one API path. + PathConflicts map[openapi.ProviderName]map[openapi.ResourceName]map[string]struct{} } // PulumiSchema will generate a Pulumi schema for the given Azure providers and resources map. @@ -284,7 +286,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning caseSensitiveTypes := newCaseSensitiveTokens() flattenedPropertyConflicts := map[string]map[string]struct{}{} exampleMap := make(map[string][]resources.AzureAPIExample) - resourcePaths := map[string]map[string]struct{}{} + resourcesPathTracker := newResourcesPathTracker() for _, providerName := range providers { versionMap := providerMap[providerName] @@ -294,6 +296,8 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning } slices.Sort(versions) + resourcePaths := map[openapi.ResourceName]map[string]struct{}{} + for _, sdkVersion := range versions { // Attempt to convert back to an API version for use elsewhere var apiVersion *openapi.ApiVersion @@ -318,7 +322,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning rootDir: rootDir, flattenedPropertyConflicts: flattenedPropertyConflicts, majorVersion: majorVersion, - resourcePaths: resourcePaths, + resourcePaths: map[openapi.ResourceName]string{}, } // Populate C#, Java, Python and Go module mapping. @@ -352,8 +356,17 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning // Populate invokes. gen.genInvokes(items.Invokes) + forceNewTypes = append(forceNewTypes, gen.forceNewTypes...) + gen.mergeResourcePathsInto(resourcePaths) } + + resourcesPathTracker.addPathConflictsForProvider(providerName, resourcePaths) + } + + // When a resource maps to more than one API path, it's a conflict and we need to detect and report it. #2495 + if majorVersion >= 3 && resourcesPathTracker.hasConflicts() { + return nil, fmt.Errorf("path conflicts detected: %v", resourcesPathTracker.pathConflicts) } err := genMixins(&pkg, &metadata) @@ -408,17 +421,6 @@ version using infrastructure as code, which Pulumi then uses to drive the ARM AP "packages": javaPackages, }) - // When a resource maps to more than one API path, it's a conflict and we need to detect and report it. #2495 - pathConflicts := map[string]map[string]struct{}{} - for tok, paths := range resourcePaths { - if len(paths) > 1 { - pathConflicts[tok] = paths - } - } - if majorVersion >= 3 && len(pathConflicts) > 0 { - return nil, fmt.Errorf("path conflicts detected: %v", pathConflicts) - } - return &GenerationResult{ Schema: &pkg, Metadata: &metadata, @@ -426,10 +428,35 @@ version using infrastructure as code, which Pulumi then uses to drive the ARM AP ForceNewTypes: forceNewTypes, TypeCaseConflicts: caseSensitiveTypes.findCaseConflicts(), FlattenedPropertyConflicts: flattenedPropertyConflicts, - PathConflicts: pathConflicts, + PathConflicts: resourcesPathTracker.pathConflicts, }, nil } +// resourcesPathConflicts tracks resource path conflicts by provider/module. Use newResourcesPathTracker to instantiate +type resourcesPathConflicts struct { + pathConflicts map[openapi.ProviderName]map[openapi.ResourceName]map[string]struct{} +} + +func newResourcesPathTracker() *resourcesPathConflicts { + return &resourcesPathConflicts{pathConflicts: map[openapi.ProviderName]map[openapi.ResourceName]map[string]struct{}{}} +} + +func (rpt *resourcesPathConflicts) addPathConflictsForProvider(providerName openapi.ProviderName, resourcePaths map[openapi.ResourceName]map[string]struct{}) { + providerPathConflicts := map[openapi.ResourceName]map[string]struct{}{} + for resource, paths := range resourcePaths { + if len(paths) > 1 { + providerPathConflicts[resource] = paths + } + } + if len(providerPathConflicts) > 0 { + rpt.pathConflicts[providerName] = providerPathConflicts + } +} + +func (rpt *resourcesPathConflicts) hasConflicts() bool { + return len(rpt.pathConflicts) > 0 +} + func (g *packageGenerator) genInvokes(invokes map[string]*openapi.ResourceSpec) { var invokeNames []string for invokeName := range invokes { @@ -660,8 +687,9 @@ type packageGenerator struct { forceNewTypes []ForceNewType flattenedPropertyConflicts map[string]map[string]struct{} majorVersion int - // A tok -> paths map to record API paths per resource and later detect conflicts. - resourcePaths map[string]map[string]struct{} + // A resource -> path map to record API paths per resource and later detect conflicts. + // packageGenerator is used for a single API version, so there won't be conflicting paths here. + resourcePaths map[openapi.ResourceName]string } func (g *packageGenerator) genResources(typeName string, resource *openapi.ResourceSpec, nestedResourceBodyRefs []string) error { @@ -868,8 +896,8 @@ func dedupResourceNameByPath(provider, typeName, canonPath string) string { return result } -// recordPath keeps track of all API paths a resource is mapped to. -func (g *packageGenerator) recordPath(typeName, canonPath string) { +// recordPath adds path to keep track of all API paths a resource is mapped to. +func (g *packageGenerator) recordPath(typeName openapi.ResourceName, canonPath string) { // Some resources have a /default path, e.g., azure-native:azurestackhci:GuestAgent has conflicting paths // /subscriptions/{}/resourcegroups/{}/providers/microsoft.azurestackhci/virtualmachines/{}/guestagents/{}, // /{}/providers/microsoft.azurestackhci/virtualmachineinstances/default/guestagents/default, @@ -878,12 +906,17 @@ func (g *packageGenerator) recordPath(typeName, canonPath string) { return } - tokWithoutVersion := generateTok(g.provider, typeName, "") + g.resourcePaths[typeName] = canonPath +} - if _, ok := g.resourcePaths[tokWithoutVersion]; !ok { - g.resourcePaths[tokWithoutVersion] = map[string]struct{}{} +// mergeResourcePathsInto merges this packageGenerator's resource paths into the given map. +func (g *packageGenerator) mergeResourcePathsInto(resourcePaths map[openapi.ResourceName]map[string]struct{}) { + for resource, path := range g.resourcePaths { + if _, ok := resourcePaths[resource]; !ok { + resourcePaths[resource] = map[string]struct{}{} + } + resourcePaths[resource][path] = struct{}{} } - g.resourcePaths[tokWithoutVersion][canonPath] = struct{}{} } func (g *packageGenerator) genResourceVariant(apiSpec *openapi.ResourceSpec, resource *resourceVariant, nestedResourceBodyRefs []string, typeNameAliases ...string) error { diff --git a/provider/pkg/gen/schema_test.go b/provider/pkg/gen/schema_test.go index 01fef2208735..41feb9c59515 100644 --- a/provider/pkg/gen/schema_test.go +++ b/provider/pkg/gen/schema_test.go @@ -381,3 +381,52 @@ func TestDedupResourceNameByPath(t *testing.T) { assert.Equal(t, "Resource", dedupResourceNameByPath("dbforpostgresql", "Resource", "/subscriptions/{}/resourcegroups/{}/providers/Microsoft.DBforPostgreSQL/flexibleservers/{}")) }) } + +func TestResourcePathTracker(t *testing.T) { + t.Run("no conflicts, one provider", func(t *testing.T) { + tracker := newResourcesPathTracker() + tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string]struct{}{ + "VirtualMachine": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": struct{}{}}, + }) + assert.False(t, tracker.hasConflicts()) + }) + + t.Run("conflicts, one provider", func(t *testing.T) { + tracker := newResourcesPathTracker() + tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string]struct{}{ + "VirtualMachine": { + "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": struct{}{}, + "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachinesFoo/{}": struct{}{}, + }, + }) + assert.True(t, tracker.hasConflicts()) + }) + + t.Run("no conflicts, multiple providers", func(t *testing.T) { + tracker := newResourcesPathTracker() + tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string]struct{}{ + "VirtualMachine": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": struct{}{}}, + }) + tracker.addPathConflictsForProvider("storage", map[openapi.ResourceName]map[string]struct{}{ + "StorageAccount": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Storage/storageAccounts/{}": struct{}{}}, + }) + assert.False(t, tracker.hasConflicts()) + }) + + t.Run("conflicts, multiple providers", func(t *testing.T) { + tracker := newResourcesPathTracker() + tracker.addPathConflictsForProvider("storage", map[openapi.ResourceName]map[string]struct{}{ + "StorageAccount": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Storage/storageAccounts/{}": struct{}{}}, + }) + tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string]struct{}{ + "VirtualMachine": { + "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": struct{}{}, + "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachinesFoo/{}": struct{}{}, + }, + }) + tracker.addPathConflictsForProvider("migrate", map[openapi.ResourceName]map[string]struct{}{ + "AssessmentProject": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Migrate/assessmentProjects/{}": struct{}{}}, + }) + assert.True(t, tracker.hasConflicts()) + }) +} diff --git a/provider/pkg/versioning/build_schema.go b/provider/pkg/versioning/build_schema.go index bccd6bffecce..70783967ed7c 100644 --- a/provider/pkg/versioning/build_schema.go +++ b/provider/pkg/versioning/build_schema.go @@ -33,8 +33,8 @@ type BuildSchemaArgs struct { type BuildSchemaReports struct { PathChangesResult - // A tok -> paths map to record types that have conflicting paths. - PathConflicts map[string]map[string]struct{} + // providerName -> resourceName -> set of paths, to record resources that have conflicting paths. + PathConflicts map[openapi.ProviderName]map[openapi.ResourceName]map[string]struct{} AllResourcesByVersion ProvidersVersionResources AllResourceVersionsByResource ProviderResourceVersions Pending openapi.ProviderVersionList diff --git a/reports/pathConflicts.json b/reports/pathConflicts.json index a077126c4b35..705bd158e367 100644 --- a/reports/pathConflicts.json +++ b/reports/pathConflicts.json @@ -1,104 +1,128 @@ { - "azure-native:apimanagement:ApiPolicy": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policies/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policy": {} - }, - "azure-native:apimanagement:ProductPolicy": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policies/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policy": {} - }, - "azure-native:cache:AccessPolicyAssignment": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redis/{}/accesspolicyassignments/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redisenterprise/{}/databases/{}/accesspolicyassignments/{}": {} - }, - "azure-native:dbformysql:Configuration": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/configurations/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/configurations/{}": {} - }, - "azure-native:dbformysql:Database": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/databases/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/databases/{}": {} - }, - "azure-native:dbformysql:FirewallRule": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/firewallrules/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/firewallrules/{}": {} - }, - "azure-native:dbformysql:PrivateEndpointConnection": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/privateendpointconnections/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/privateendpointconnections/{}": {} - }, - "azure-native:dbformysql:Server": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}": {} - }, - "azure-native:dbforpostgresql:Configuration": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/configurations/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/configurations/{}": {} - }, - "azure-native:dbforpostgresql:Database": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/databases/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/databases/{}": {} - }, - "azure-native:dbforpostgresql:FirewallRule": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/firewallrules/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/firewallrules/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/firewallrules/{}": {} - }, - "azure-native:dbforpostgresql:PrivateEndpointConnection": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/privateendpointconnections/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/privateendpointconnections/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/privateendpointconnections/{}": {} - }, - "azure-native:dbforpostgresql:Server": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}": {} - }, - "azure-native:documentdb:PrivateEndpointConnection": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/databaseaccounts/{}/privateendpointconnections/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/mongoclusters/{}/privateendpointconnections/{}": {} - }, - "azure-native:hdinsight:Cluster": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusterpools/{}/clusters/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusters/{}": {} - }, - "azure-native:hybridcontainerservice:AgentPool": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hybridcontainerservice/provisionedclusters/{}/agentpools/{}": {}, - "/{}/providers/microsoft.hybridcontainerservice/provisionedclusterinstances/default/agentpools/{}": {} - }, - "azure-native:labservices:Lab": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}": {} - }, - "azure-native:labservices:User": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}/users/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}/users/{}": {} - }, - "azure-native:machinelearningservices:ConnectionRaiBlocklist": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": {} - }, - "azure-native:machinelearningservices:ConnectionRaiBlocklistItem": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": {} - }, - "azure-native:migrate:Assessment": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}/assessments/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}/assessments/{}": {} - }, - "azure-native:migrate:Group": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}": {} - }, - "azure-native:migrate:Project": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}": {} - }, - "azure-native:mobilenetwork:Sim": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/simgroups/{}/sims/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/sims/{}": {} - }, - "azure-native:netapp:Backup": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/backupvaults/{}/backups/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/capacitypools/{}/volumes/{}/backups/{}": {} + "ApiManagement": { + "ApiPolicy": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policies/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policy": {} + }, + "ProductPolicy": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policies/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policy": {} + } + }, + "Cache": { + "AccessPolicyAssignment": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redis/{}/accesspolicyassignments/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redisenterprise/{}/databases/{}/accesspolicyassignments/{}": {} + } + }, + "DBforMySQL": { + "Configuration": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/configurations/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/configurations/{}": {} + }, + "Database": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/databases/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/databases/{}": {} + }, + "FirewallRule": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/firewallrules/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/firewallrules/{}": {} + }, + "PrivateEndpointConnection": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/privateendpointconnections/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/privateendpointconnections/{}": {} + }, + "Server": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}": {} + } + }, + "DBforPostgreSQL": { + "Configuration": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/configurations/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/configurations/{}": {} + }, + "Database": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/databases/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/databases/{}": {} + }, + "FirewallRule": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/firewallrules/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/firewallrules/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/firewallrules/{}": {} + }, + "PrivateEndpointConnection": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/privateendpointconnections/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/privateendpointconnections/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/privateendpointconnections/{}": {} + }, + "Server": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}": {} + } + }, + "DocumentDB": { + "PrivateEndpointConnection": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/databaseaccounts/{}/privateendpointconnections/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/mongoclusters/{}/privateendpointconnections/{}": {} + } + }, + "HDInsight": { + "Cluster": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusterpools/{}/clusters/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusters/{}": {} + } + }, + "HybridContainerService": { + "AgentPool": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hybridcontainerservice/provisionedclusters/{}/agentpools/{}": {}, + "/{}/providers/microsoft.hybridcontainerservice/provisionedclusterinstances/default/agentpools/{}": {} + } + }, + "LabServices": { + "Lab": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}": {} + }, + "User": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}/users/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}/users/{}": {} + } + }, + "MachineLearningServices": { + "ConnectionRaiBlocklist": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": {} + }, + "ConnectionRaiBlocklistItem": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": {} + } + }, + "Migrate": { + "Assessment": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}/assessments/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}/assessments/{}": {} + }, + "Group": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}": {} + }, + "Project": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}": {} + } + }, + "MobileNetwork": { + "Sim": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/simgroups/{}/sims/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/sims/{}": {} + } + }, + "NetApp": { + "Backup": { + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/backupvaults/{}/backups/{}": {}, + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/capacitypools/{}/volumes/{}/backups/{}": {} + } } } \ No newline at end of file From 1c70bf20297236576e31c8f5f3bc195e835b77a9 Mon Sep 17 00:00:00 2001 From: Thomas Kappler Date: Thu, 9 Jan 2025 09:39:52 +0100 Subject: [PATCH 4/8] Update error message for path conflicts detection --- provider/pkg/gen/schema.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/pkg/gen/schema.go b/provider/pkg/gen/schema.go index 0237dce1c160..3438c43f2d06 100644 --- a/provider/pkg/gen/schema.go +++ b/provider/pkg/gen/schema.go @@ -366,7 +366,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning // When a resource maps to more than one API path, it's a conflict and we need to detect and report it. #2495 if majorVersion >= 3 && resourcesPathTracker.hasConflicts() { - return nil, fmt.Errorf("path conflicts detected: %v", resourcesPathTracker.pathConflicts) + return nil, fmt.Errorf("path conflicts detected. You probably need to add a case to schema.go/dedupResourceNameByPath.\n%v", resourcesPathTracker.pathConflicts) } err := genMixins(&pkg, &metadata) From 3b05e30d6cea6f5c6089d910577796dafe6d2f22 Mon Sep 17 00:00:00 2001 From: Thomas Kappler Date: Fri, 10 Jan 2025 13:03:57 +0100 Subject: [PATCH 5/8] Track path conflicts more fully with API versions --- provider/pkg/gen/schema.go | 57 ++-- provider/pkg/gen/schema_test.go | 40 +-- provider/pkg/versioning/build_schema.go | 2 +- reports/pathConflicts.json | 351 ++++++++++++++++++++---- 4 files changed, 355 insertions(+), 95 deletions(-) diff --git a/provider/pkg/gen/schema.go b/provider/pkg/gen/schema.go index 3438c43f2d06..339718b3921c 100644 --- a/provider/pkg/gen/schema.go +++ b/provider/pkg/gen/schema.go @@ -67,7 +67,7 @@ type GenerationResult struct { FlattenedPropertyConflicts map[string]map[string]struct{} // A map of provider -> resource -> set of paths, to record resources that have conflicts where the same resource // maps to more than one API path. - PathConflicts map[openapi.ProviderName]map[openapi.ResourceName]map[string]struct{} + PathConflicts map[openapi.ProviderName]map[openapi.ResourceName]map[string][]openapi.ApiVersion } // PulumiSchema will generate a Pulumi schema for the given Azure providers and resources map. @@ -286,7 +286,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning caseSensitiveTypes := newCaseSensitiveTokens() flattenedPropertyConflicts := map[string]map[string]struct{}{} exampleMap := make(map[string][]resources.AzureAPIExample) - resourcesPathTracker := newResourcesPathTracker() + resourcesPathTracker := newResourcesPathConflictsTracker() for _, providerName := range providers { versionMap := providerMap[providerName] @@ -296,7 +296,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning } slices.Sort(versions) - resourcePaths := map[openapi.ResourceName]map[string]struct{}{} + resourcePaths := map[openapi.ResourceName]map[string][]openapi.ApiVersion{} for _, sdkVersion := range versions { // Attempt to convert back to an API version for use elsewhere @@ -322,7 +322,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning rootDir: rootDir, flattenedPropertyConflicts: flattenedPropertyConflicts, majorVersion: majorVersion, - resourcePaths: map[openapi.ResourceName]string{}, + resourcePaths: map[openapi.ResourceName]map[string]openapi.ApiVersion{}, } // Populate C#, Java, Python and Go module mapping. @@ -432,17 +432,17 @@ version using infrastructure as code, which Pulumi then uses to drive the ARM AP }, nil } -// resourcesPathConflicts tracks resource path conflicts by provider/module. Use newResourcesPathTracker to instantiate -type resourcesPathConflicts struct { - pathConflicts map[openapi.ProviderName]map[openapi.ResourceName]map[string]struct{} +// resourcesPathConflictsTracker tracks resource path conflicts by provider/module. Use newResourcesPathTracker to instantiate. +type resourcesPathConflictsTracker struct { + pathConflicts map[openapi.ProviderName]map[openapi.ResourceName]map[string][]openapi.ApiVersion } -func newResourcesPathTracker() *resourcesPathConflicts { - return &resourcesPathConflicts{pathConflicts: map[openapi.ProviderName]map[openapi.ResourceName]map[string]struct{}{}} +func newResourcesPathConflictsTracker() *resourcesPathConflictsTracker { + return &resourcesPathConflictsTracker{pathConflicts: map[openapi.ProviderName]map[openapi.ResourceName]map[string][]openapi.ApiVersion{}} } -func (rpt *resourcesPathConflicts) addPathConflictsForProvider(providerName openapi.ProviderName, resourcePaths map[openapi.ResourceName]map[string]struct{}) { - providerPathConflicts := map[openapi.ResourceName]map[string]struct{}{} +func (rpt *resourcesPathConflictsTracker) addPathConflictsForProvider(providerName openapi.ProviderName, resourcePaths map[openapi.ResourceName]map[string][]openapi.ApiVersion) { + providerPathConflicts := map[openapi.ResourceName]map[string][]openapi.ApiVersion{} for resource, paths := range resourcePaths { if len(paths) > 1 { providerPathConflicts[resource] = paths @@ -453,7 +453,7 @@ func (rpt *resourcesPathConflicts) addPathConflictsForProvider(providerName open } } -func (rpt *resourcesPathConflicts) hasConflicts() bool { +func (rpt *resourcesPathConflictsTracker) hasConflicts() bool { return len(rpt.pathConflicts) > 0 } @@ -687,9 +687,9 @@ type packageGenerator struct { forceNewTypes []ForceNewType flattenedPropertyConflicts map[string]map[string]struct{} majorVersion int - // A resource -> path map to record API paths per resource and later detect conflicts. - // packageGenerator is used for a single API version, so there won't be conflicting paths here. - resourcePaths map[openapi.ResourceName]string + // A resource -> path -> API version map to record API paths per resource and later detect conflicts. + // Each packageGenerator instance is only used for a single API version, so there won't be conflicting paths here. + resourcePaths map[openapi.ResourceName]map[string]openapi.ApiVersion } func (g *packageGenerator) genResources(typeName string, resource *openapi.ResourceSpec, nestedResourceBodyRefs []string) error { @@ -897,7 +897,7 @@ func dedupResourceNameByPath(provider, typeName, canonPath string) string { } // recordPath adds path to keep track of all API paths a resource is mapped to. -func (g *packageGenerator) recordPath(typeName openapi.ResourceName, canonPath string) { +func (g *packageGenerator) recordPath(typeName openapi.ResourceName, canonPath string, apiVersion openapi.ApiVersion) { // Some resources have a /default path, e.g., azure-native:azurestackhci:GuestAgent has conflicting paths // /subscriptions/{}/resourcegroups/{}/providers/microsoft.azurestackhci/virtualmachines/{}/guestagents/{}, // /{}/providers/microsoft.azurestackhci/virtualmachineinstances/default/guestagents/default, @@ -906,16 +906,25 @@ func (g *packageGenerator) recordPath(typeName openapi.ResourceName, canonPath s return } - g.resourcePaths[typeName] = canonPath + // We use the map here only as a tuple of (path, apiVersion), it will only have a single key. + g.resourcePaths[typeName] = map[string]openapi.ApiVersion{canonPath: apiVersion} } -// mergeResourcePathsInto merges this packageGenerator's resource paths into the given map. -func (g *packageGenerator) mergeResourcePathsInto(resourcePaths map[openapi.ResourceName]map[string]struct{}) { +// mergeResourcePathsInto merges this packageGenerator's resource paths into the given map. This happens for each API +// version, so that in the end `resourcePaths` contains all paths for each resource and API version. +func (g *packageGenerator) mergeResourcePathsInto(resourcePaths map[openapi.ResourceName]map[string][]openapi.ApiVersion) { for resource, path := range g.resourcePaths { if _, ok := resourcePaths[resource]; !ok { - resourcePaths[resource] = map[string]struct{}{} + resourcePaths[resource] = map[string][]openapi.ApiVersion{} + } + for path, apiVersion := range path { + if _, ok := resourcePaths[resource][path]; !ok { + resourcePaths[resource][path] = []openapi.ApiVersion{} + } + apiVersions := append(resourcePaths[resource][path], apiVersion) + slices.Sort(apiVersions) + resourcePaths[resource][path] = apiVersions } - resourcePaths[resource][path] = struct{}{} } } @@ -935,7 +944,11 @@ func (g *packageGenerator) genResourceVariant(apiSpec *openapi.ResourceSpec, res return nil } - g.recordPath(typeName, canonPath) + apiVersion := openapi.ApiVersion("default") + if g.apiVersion != nil { + apiVersion = *g.apiVersion + } + g.recordPath(typeName, canonPath, apiVersion) // Generate the resource. gen := moduleGenerator{ diff --git a/provider/pkg/gen/schema_test.go b/provider/pkg/gen/schema_test.go index 41feb9c59515..c05c2975dd11 100644 --- a/provider/pkg/gen/schema_test.go +++ b/provider/pkg/gen/schema_test.go @@ -384,48 +384,48 @@ func TestDedupResourceNameByPath(t *testing.T) { func TestResourcePathTracker(t *testing.T) { t.Run("no conflicts, one provider", func(t *testing.T) { - tracker := newResourcesPathTracker() - tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string]struct{}{ - "VirtualMachine": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": struct{}{}}, + tracker := newResourcesPathConflictsTracker() + tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string][]openapi.ApiVersion{ + "VirtualMachine": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": {openapi.ApiVersion("2022-02-22")}}, }) assert.False(t, tracker.hasConflicts()) }) t.Run("conflicts, one provider", func(t *testing.T) { - tracker := newResourcesPathTracker() - tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string]struct{}{ + tracker := newResourcesPathConflictsTracker() + tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string][]openapi.ApiVersion{ "VirtualMachine": { - "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": struct{}{}, - "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachinesFoo/{}": struct{}{}, + "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": {openapi.ApiVersion("2022-02-22")}, + "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachinesFoo/{}": {openapi.ApiVersion("2024-04-22")}, }, }) assert.True(t, tracker.hasConflicts()) }) t.Run("no conflicts, multiple providers", func(t *testing.T) { - tracker := newResourcesPathTracker() - tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string]struct{}{ - "VirtualMachine": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": struct{}{}}, + tracker := newResourcesPathConflictsTracker() + tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string][]openapi.ApiVersion{ + "VirtualMachine": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": {openapi.ApiVersion("2022-02-22")}}, }) - tracker.addPathConflictsForProvider("storage", map[openapi.ResourceName]map[string]struct{}{ - "StorageAccount": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Storage/storageAccounts/{}": struct{}{}}, + tracker.addPathConflictsForProvider("storage", map[openapi.ResourceName]map[string][]openapi.ApiVersion{ + "StorageAccount": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Storage/storageAccounts/{}": {openapi.ApiVersion("2022-02-22")}}, }) assert.False(t, tracker.hasConflicts()) }) t.Run("conflicts, multiple providers", func(t *testing.T) { - tracker := newResourcesPathTracker() - tracker.addPathConflictsForProvider("storage", map[openapi.ResourceName]map[string]struct{}{ - "StorageAccount": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Storage/storageAccounts/{}": struct{}{}}, + tracker := newResourcesPathConflictsTracker() + tracker.addPathConflictsForProvider("storage", map[openapi.ResourceName]map[string][]openapi.ApiVersion{ + "StorageAccount": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Storage/storageAccounts/{}": {openapi.ApiVersion("2022-02-22")}}, }) - tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string]struct{}{ + tracker.addPathConflictsForProvider("compute", map[openapi.ResourceName]map[string][]openapi.ApiVersion{ "VirtualMachine": { - "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": struct{}{}, - "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachinesFoo/{}": struct{}{}, + "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/{}": {openapi.ApiVersion("2022-02-22")}, + "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachinesFoo/{}": {openapi.ApiVersion("2024-04-22")}, }, }) - tracker.addPathConflictsForProvider("migrate", map[openapi.ResourceName]map[string]struct{}{ - "AssessmentProject": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Migrate/assessmentProjects/{}": struct{}{}}, + tracker.addPathConflictsForProvider("migrate", map[openapi.ResourceName]map[string][]openapi.ApiVersion{ + "AssessmentProject": {"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Migrate/assessmentProjects/{}": {openapi.ApiVersion("2022-02-22")}}, }) assert.True(t, tracker.hasConflicts()) }) diff --git a/provider/pkg/versioning/build_schema.go b/provider/pkg/versioning/build_schema.go index 70783967ed7c..f0fda62180c2 100644 --- a/provider/pkg/versioning/build_schema.go +++ b/provider/pkg/versioning/build_schema.go @@ -34,7 +34,7 @@ type BuildSchemaArgs struct { type BuildSchemaReports struct { PathChangesResult // providerName -> resourceName -> set of paths, to record resources that have conflicting paths. - PathConflicts map[openapi.ProviderName]map[openapi.ResourceName]map[string]struct{} + PathConflicts map[openapi.ProviderName]map[openapi.ResourceName]map[string][]openapi.ApiVersion AllResourcesByVersion ProvidersVersionResources AllResourceVersionsByResource ProviderResourceVersions Pending openapi.ProviderVersionList diff --git a/reports/pathConflicts.json b/reports/pathConflicts.json index 705bd158e367..4ea92133f6bf 100644 --- a/reports/pathConflicts.json +++ b/reports/pathConflicts.json @@ -1,128 +1,375 @@ { "ApiManagement": { "ApiPolicy": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policies/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policy": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policies/{}": [ + "2018-06-01-preview", + "2022-08-01", + "2022-09-01-preview", + "2023-03-01-preview", + "2023-05-01-preview", + "2023-09-01-preview", + "2024-05-01", + "2024-06-01-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/apis/{}/policy": [ + "2016-10-10" + ] }, "ProductPolicy": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policies/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policy": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policies/{}": [ + "2018-06-01-preview", + "2022-08-01", + "2022-09-01-preview", + "2023-03-01-preview", + "2023-05-01-preview", + "2023-09-01-preview", + "2024-05-01", + "2024-06-01-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.apimanagement/service/{}/products/{}/policy": [ + "2016-10-10" + ] } }, "Cache": { "AccessPolicyAssignment": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redis/{}/accesspolicyassignments/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redisenterprise/{}/databases/{}/accesspolicyassignments/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redis/{}/accesspolicyassignments/{}": [ + "2023-05-01-preview", + "2023-08-01", + "2024-03-01", + "2024-04-01-preview", + "2024-11-01", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.cache/redisenterprise/{}/databases/{}/accesspolicyassignments/{}": [ + "2024-09-01-preview" + ] } }, "DBforMySQL": { "Configuration": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/configurations/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/configurations/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/configurations/{}": [ + "2020-07-01-privatepreview", + "2022-01-01", + "2023-06-01-preview", + "2023-06-30", + "2023-12-30", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/configurations/{}": [ + "2017-12-01" + ] }, "Database": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/databases/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/databases/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/databases/{}": [ + "2022-01-01", + "2023-06-01-preview", + "2023-06-30", + "2023-12-30", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/databases/{}": [ + "2017-12-01" + ] }, "FirewallRule": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/firewallrules/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/firewallrules/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/firewallrules/{}": [ + "2022-01-01", + "2023-06-01-preview", + "2023-06-30", + "2023-12-30", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/firewallrules/{}": [ + "2017-12-01" + ] }, "PrivateEndpointConnection": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/privateendpointconnections/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/privateendpointconnections/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}/privateendpointconnections/{}": [ + "2022-09-30-preview", + "2023-06-30", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}/privateendpointconnections/{}": [ + "2018-06-01-privatepreview" + ] }, "Server": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/flexibleservers/{}": [ + "2020-07-01-preview", + "2020-07-01-privatepreview", + "2022-01-01", + "2022-09-30-preview", + "2023-06-01-preview", + "2023-06-30", + "2023-10-01-preview", + "2023-12-01-preview", + "2023-12-30", + "2024-02-01-preview", + "2024-06-01-preview", + "2024-10-01-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbformysql/servers/{}": [ + "2017-12-01", + "2018-06-01-privatepreview" + ] } }, "DBforPostgreSQL": { "Configuration": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/configurations/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/configurations/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/configurations/{}": [ + "2022-12-01", + "2023-03-01-preview", + "2023-06-01-preview", + "2023-12-01-preview", + "2024-03-01-preview", + "2024-08-01", + "2024-11-01-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/configurations/{}": [ + "2017-12-01" + ] }, "Database": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/databases/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/databases/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/databases/{}": [ + "2022-12-01", + "2023-03-01-preview", + "2023-06-01-preview", + "2023-12-01-preview", + "2024-03-01-preview", + "2024-08-01", + "2024-11-01-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/databases/{}": [ + "2017-12-01" + ] }, "FirewallRule": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/firewallrules/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/firewallrules/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/firewallrules/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/firewallrules/{}": [ + "2022-12-01", + "2023-03-01-preview", + "2023-06-01-preview", + "2023-12-01-preview", + "2024-03-01-preview", + "2024-08-01", + "2024-11-01-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/firewallrules/{}": [ + "2020-10-05-privatepreview", + "2022-11-08", + "2023-03-02-preview" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/firewallrules/{}": [ + "2017-12-01" + ] }, "PrivateEndpointConnection": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/privateendpointconnections/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/privateendpointconnections/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/privateendpointconnections/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}/privateendpointconnections/{}": [ + "2023-06-01-preview", + "2023-12-01-preview", + "2024-03-01-preview", + "2024-08-01", + "2024-11-01-preview" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servergroupsv2/{}/privateendpointconnections/{}": [ + "2022-11-08", + "2023-03-02-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}/privateendpointconnections/{}": [ + "2018-06-01-privatepreview" + ] }, "Server": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/flexibleservers/{}": [ + "2020-02-14-preview", + "2021-04-10-privatepreview", + "2021-06-15-privatepreview", + "2022-03-08-preview", + "2022-12-01", + "2023-03-01-preview", + "2023-06-01-preview", + "2023-12-01-preview", + "2024-03-01-preview", + "2024-08-01", + "2024-11-01-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.dbforpostgresql/servers/{}": [ + "2017-12-01", + "2017-12-01-preview" + ] } }, "DocumentDB": { "PrivateEndpointConnection": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/databaseaccounts/{}/privateendpointconnections/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/mongoclusters/{}/privateendpointconnections/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/databaseaccounts/{}/privateendpointconnections/{}": [ + "2023-04-15", + "2023-09-15", + "2023-09-15-preview", + "2023-11-15", + "2023-11-15-preview", + "2024-02-15-preview", + "2024-05-15", + "2024-05-15-preview", + "2024-08-15", + "2024-09-01-preview", + "2024-11-15", + "2024-12-01-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.documentdb/mongoclusters/{}/privateendpointconnections/{}": [ + "2024-03-01-preview", + "2024-06-01-preview", + "2024-07-01", + "2024-10-01-preview" + ] } }, "HDInsight": { "Cluster": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusterpools/{}/clusters/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusters/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusterpools/{}/clusters/{}": [ + "2023-06-01-preview", + "2023-11-01-preview", + "2024-05-01-preview" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hdinsight/clusters/{}": [ + "2021-06-01", + "2023-04-15-preview", + "2023-08-15-preview", + "2024-08-01-preview", + "default" + ] } }, "HybridContainerService": { "AgentPool": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hybridcontainerservice/provisionedclusters/{}/agentpools/{}": {}, - "/{}/providers/microsoft.hybridcontainerservice/provisionedclusterinstances/default/agentpools/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.hybridcontainerservice/provisionedclusters/{}/agentpools/{}": [ + "2022-05-01-preview", + "2022-09-01-preview", + "default" + ], + "/{}/providers/microsoft.hybridcontainerservice/provisionedclusterinstances/default/agentpools/{}": [ + "2023-11-15-preview", + "2024-01-01" + ] } }, "LabServices": { "Lab": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}": [ + "2018-10-15" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}": [ + "2022-08-01", + "2023-06-07", + "default" + ] }, "User": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}/users/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}/users/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labaccounts/{}/labs/{}/users/{}": [ + "2018-10-15" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.labservices/labs/{}/users/{}": [ + "2022-08-01", + "2023-06-07", + "default" + ] } }, "MachineLearningServices": { "ConnectionRaiBlocklist": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": [ + "2024-07-01-preview", + "2024-10-01-preview" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": [ + "2024-04-01-preview", + "default" + ] }, "ConnectionRaiBlocklistItem": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}": [ + "2024-04-01-preview", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.machinelearningservices/workspaces/{}/connections/{}/raiblocklists/{}/raiblocklistitems/{}": [ + "2024-07-01-preview", + "2024-10-01-preview" + ] } }, "Migrate": { "Assessment": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}/assessments/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}/assessments/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}/assessments/{}": [ + "2019-10-01", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}/assessments/{}": [ + "2018-02-02" + ] }, "Group": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}/groups/{}": [ + "2019-10-01", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}/groups/{}": [ + "2018-02-02" + ] }, "Project": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/assessmentprojects/{}": [ + "2019-10-01", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.migrate/projects/{}": [ + "2018-02-02" + ] } }, "MobileNetwork": { "Sim": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/simgroups/{}/sims/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/sims/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/simgroups/{}/sims/{}": [ + "2022-04-01-preview", + "2022-11-01", + "2023-06-01", + "2023-09-01", + "2024-02-01", + "2024-04-01", + "default" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.mobilenetwork/sims/{}": [ + "2022-03-01-preview" + ] } }, "NetApp": { "Backup": { - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/backupvaults/{}/backups/{}": {}, - "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/capacitypools/{}/volumes/{}/backups/{}": {} + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/backupvaults/{}/backups/{}": [ + "2022-11-01-preview", + "2023-05-01-preview", + "2023-07-01-preview", + "2023-11-01", + "2023-11-01-preview", + "2024-01-01", + "2024-03-01", + "2024-03-01-preview", + "2024-05-01", + "2024-05-01-preview", + "2024-07-01", + "2024-07-01-preview" + ], + "/subscriptions/{}/resourcegroups/{}/providers/microsoft.netapp/netappaccounts/{}/capacitypools/{}/volumes/{}/backups/{}": [ + "2022-11-01", + "default" + ] } } } \ No newline at end of file From db2ed455ab55b60dc8674efe9602a85d0398fee6 Mon Sep 17 00:00:00 2001 From: Thomas Kappler Date: Mon, 13 Jan 2025 17:59:10 +0100 Subject: [PATCH 6/8] Makefile: schema should also depend on MAJOR_VERSION-removed.json --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 915121f8fa58..b409b5875995 100644 --- a/Makefile +++ b/Makefile @@ -231,7 +231,7 @@ bin/$(CODEGEN): .make/prebuild .make/provider_mod_download provider/cmd/$(CODEGE # Writes schema-full.json and metadata-compact.json to bin/ # Also re-calculates files in versions/ at same time -bin/schema-full.json bin/metadata-compact.json &: bin/$(CODEGEN) $(SPECS) versions/az-provider-list.json versions/v${PREVIOUS_MAJOR_VERSION}-lock.json versions/v${MAJOR_VERSION}-config.yaml versions/v${MAJOR_VERSION}-spec.yaml versions/v${MAJOR_VERSION}-removed-resources.json versions/v${NEXT_MAJOR_VERSION}-removed-resources.json +bin/schema-full.json bin/metadata-compact.json &: bin/$(CODEGEN) $(SPECS) versions/az-provider-list.json versions/v${PREVIOUS_MAJOR_VERSION}-lock.json versions/v${MAJOR_VERSION}-config.yaml versions/v${MAJOR_VERSION}-spec.yaml versions/v${MAJOR_VERSION}-removed.json versions/v${MAJOR_VERSION}-removed-resources.json versions/v${NEXT_MAJOR_VERSION}-removed-resources.json bin/$(CODEGEN) schema $(PROVIDER_VERSION) # Docs schema - treat as phony becasuse it's committed so we always need to rebuild it. From 8537f69941c1a8e84162a5a83844ab49cfde01e4 Mon Sep 17 00:00:00 2001 From: Thomas Kappler Date: Mon, 13 Jan 2025 18:02:53 +0100 Subject: [PATCH 7/8] Only fail on path conflicts for v3 release builds --- provider/pkg/gen/gen_aliases_test.go | 5 +++-- provider/pkg/gen/gen_dashboard_test.go | 3 ++- provider/pkg/gen/gen_vnet_test.go | 3 ++- provider/pkg/gen/schema.go | 9 +++++---- provider/pkg/versioning/build_schema.go | 9 ++++----- provider/pkg/versioning/gen_bench_test.go | 3 ++- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/provider/pkg/gen/gen_aliases_test.go b/provider/pkg/gen/gen_aliases_test.go index f7ac8a06cc6c..c7e172cb67d3 100644 --- a/provider/pkg/gen/gen_aliases_test.go +++ b/provider/pkg/gen/gen_aliases_test.go @@ -5,6 +5,7 @@ import ( "path" "testing" + "github.com/blang/semver" "github.com/gkampitakis/go-snaps/snaps" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi" ) @@ -30,7 +31,7 @@ func TestAliasesGen(t *testing.T) { } t.Run("v2", func(t *testing.T) { - generationResult, err := PulumiSchema(rootDir, providers, versioningStub{}, 2) + generationResult, err := PulumiSchema(rootDir, providers, versioningStub{}, semver.MustParse("2.0.0")) if err != nil { t.Fatal(err) } @@ -40,7 +41,7 @@ func TestAliasesGen(t *testing.T) { }) t.Run("v3", func(t *testing.T) { - generationResult, err := PulumiSchema(rootDir, providers, versioningStub{}, 3) + generationResult, err := PulumiSchema(rootDir, providers, versioningStub{}, semver.MustParse("3.0.0")) if err != nil { t.Fatal(err) } diff --git a/provider/pkg/gen/gen_dashboard_test.go b/provider/pkg/gen/gen_dashboard_test.go index b9383a0b5a1b..29bd10217f42 100644 --- a/provider/pkg/gen/gen_dashboard_test.go +++ b/provider/pkg/gen/gen_dashboard_test.go @@ -5,6 +5,7 @@ import ( "path" "testing" + "github.com/blang/semver" "github.com/gkampitakis/go-snaps/snaps" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi" "github.com/stretchr/testify/assert" @@ -33,7 +34,7 @@ func TestPortalDashboardGen(t *testing.T) { "Dashboard": "2020-09-01-preview", }, }, openapi.DefaultVersionLock{}, nil, nil) - generationResult, err := PulumiSchema(rootDir, providers, versioningStub{}, 2) + generationResult, err := PulumiSchema(rootDir, providers, versioningStub{}, semver.MustParse("3.0.0")) if err != nil { t.Fatal(err) } diff --git a/provider/pkg/gen/gen_vnet_test.go b/provider/pkg/gen/gen_vnet_test.go index 33c0717e86b1..368a8f092ac0 100644 --- a/provider/pkg/gen/gen_vnet_test.go +++ b/provider/pkg/gen/gen_vnet_test.go @@ -5,6 +5,7 @@ import ( "path" "testing" + "github.com/blang/semver" "github.com/gkampitakis/go-snaps/snaps" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi" ) @@ -25,7 +26,7 @@ func TestVnetGen(t *testing.T) { if err != nil { t.Fatal(err) } - generationResult, err := PulumiSchema(rootDir, providers, versioningStub{}, 2) + generationResult, err := PulumiSchema(rootDir, providers, versioningStub{}, semver.MustParse("2.0.0")) if err != nil { t.Fatal(err) } diff --git a/provider/pkg/gen/schema.go b/provider/pkg/gen/schema.go index 339718b3921c..a27434fe1cb3 100644 --- a/provider/pkg/gen/schema.go +++ b/provider/pkg/gen/schema.go @@ -71,7 +71,7 @@ type GenerationResult struct { } // PulumiSchema will generate a Pulumi schema for the given Azure providers and resources map. -func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning Versioning, majorVersion int) (*GenerationResult, error) { +func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning Versioning, providerVersion semver.Version) (*GenerationResult, error) { pkg := pschema.PackageSpec{ Name: "azure-native", Description: "A native Pulumi package for creating and managing Azure resources.", @@ -321,7 +321,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning caseSensitiveTypes: caseSensitiveTypes, rootDir: rootDir, flattenedPropertyConflicts: flattenedPropertyConflicts, - majorVersion: majorVersion, + majorVersion: int(providerVersion.Major), resourcePaths: map[openapi.ResourceName]map[string]openapi.ApiVersion{}, } @@ -365,8 +365,9 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning } // When a resource maps to more than one API path, it's a conflict and we need to detect and report it. #2495 - if majorVersion >= 3 && resourcesPathTracker.hasConflicts() { - return nil, fmt.Errorf("path conflicts detected. You probably need to add a case to schema.go/dedupResourceNameByPath.\n%v", resourcesPathTracker.pathConflicts) + isReleaseBuild := len(providerVersion.Build) == 0 + if providerVersion.Major >= 3 && isReleaseBuild && resourcesPathTracker.hasConflicts() { + return nil, fmt.Errorf("path conflicts detected. You probably need to add a case to schema.go/dedupResourceNameByPath.\n%+v", resourcesPathTracker.pathConflicts) } err := genMixins(&pkg, &metadata) diff --git a/provider/pkg/versioning/build_schema.go b/provider/pkg/versioning/build_schema.go index f0fda62180c2..e956384562ff 100644 --- a/provider/pkg/versioning/build_schema.go +++ b/provider/pkg/versioning/build_schema.go @@ -3,9 +3,8 @@ package versioning import ( "path" "sort" - "strconv" - "strings" + "github.com/blang/semver" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/gen" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi/paths" @@ -86,7 +85,7 @@ func BuildSchema(args BuildSchemaArgs) (*BuildSchemaResult, error) { return nil, err } - majorVersion, err := strconv.ParseInt(strings.Split(args.Version, ".")[0], 10, 64) + providerVersion, err := semver.Parse(args.Version) if err != nil { return nil, err } @@ -95,7 +94,7 @@ func BuildSchema(args BuildSchemaArgs) (*BuildSchemaResult, error) { if args.OnlyExplicitVersions { versionMetadata = VersionMetadata{} } else { - versionMetadata, err = LoadVersionMetadata(args.RootDir, providers, int(majorVersion)) + versionMetadata, err = LoadVersionMetadata(args.RootDir, providers, int(providerVersion.Major)) if err != nil { return nil, err } @@ -124,7 +123,7 @@ func BuildSchema(args BuildSchemaArgs) (*BuildSchemaResult, error) { InactiveDefaultVersions: versionMetadata.InactiveDefaultVersions, } - generationResult, err := gen.PulumiSchema(args.RootDir, providers, versionMetadata, int(majorVersion)) + generationResult, err := gen.PulumiSchema(args.RootDir, providers, versionMetadata, providerVersion) if err != nil { return &BuildSchemaResult{ diff --git a/provider/pkg/versioning/gen_bench_test.go b/provider/pkg/versioning/gen_bench_test.go index 220902ee4f95..69857123d52c 100644 --- a/provider/pkg/versioning/gen_bench_test.go +++ b/provider/pkg/versioning/gen_bench_test.go @@ -4,6 +4,7 @@ import ( "path" "testing" + "github.com/blang/semver" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/gen" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi" ) @@ -35,5 +36,5 @@ func BenchmarkGen(b *testing.B) { specs = openapi.ApplyProvidersTransformations(specs, versionMetadata.Lock, nil, versionSources.RemovedVersions, nil) - gen.PulumiSchema(rootDir, specs, versionMetadata, 2) + gen.PulumiSchema(rootDir, specs, versionMetadata, semver.MustParse("2.0.0")) } From 7f21fb2e6ff600a8e8f0e0c806046f8fb3400798 Mon Sep 17 00:00:00 2001 From: Thomas Kappler Date: Mon, 13 Jan 2025 18:53:51 +0100 Subject: [PATCH 8/8] Remove old API versions that cause path conflicts --- versions/v3-removed.json | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/versions/v3-removed.json b/versions/v3-removed.json index e87a80e8f08f..44d39300a1a0 100644 --- a/versions/v3-removed.json +++ b/versions/v3-removed.json @@ -25,7 +25,8 @@ "2017-07-14" ], "ApiManagement": [ - "2016-07-07" + "2016-07-07", + "2016-10-10" ], "App": [], "AppComplianceAutomation": [], @@ -382,7 +383,9 @@ "Kusto": [ "2017-09-07-privatepreview" ], - "LabServices": [], + "LabServices": [ + "2018-10-15" + ], "LoadTestService": [], "Logic": [], "Logz": [], @@ -430,7 +433,8 @@ "MarketplaceOrdering": [], "Media": [], "Migrate": [ - "2017-11-11-preview" + "2017-11-11-preview", + "2018-02-02" ], "MixedReality": [ "2019-02-28-preview", @@ -479,7 +483,8 @@ "2018-02-01", "2018-03-01", "2018-03-01-preview", - "2018-04-01" + "2018-04-01", + "2021-05-01-preview" ], "NetworkCloud": [], "NetworkFunction": [], @@ -585,6 +590,9 @@ "2018-03-01-preview", "2018-10-01" ], + "Sim": [ + "2022-03-01-preview" + ], "SoftwarePlan": [], "Solutions": [ "2016-09-01-preview",