Skip to content

Commit

Permalink
Convert console logs into reports (#2728)
Browse files Browse the repository at this point in the history
Related to #2691

In preparation for improving unit testing, move all generation process console logging and warning into reports.

This has the added benefit of highlighting when warnings change during upgrades.

Stacked on top of #2727
  • Loading branch information
danielrbradley authored Sep 8, 2023
2 parents 4ede125 + 8b4d0dc commit a0b12ef
Show file tree
Hide file tree
Showing 21 changed files with 1,702 additions and 112 deletions.
21 changes: 16 additions & 5 deletions provider/cmd/pulumi-gen-azure-native/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,23 +82,34 @@ func main() {
}

if namespaces == "*" && apiVersions == "" {
if err = buildSchemaResult.Version.WriteTo("versions"); err != nil {
written, err := buildSchemaResult.Version.WriteTo("versions")
if err != nil {
panic(err)
}
for _, v := range written {
fmt.Printf("Emitted %s\n", v)
}
written, err = buildSchemaResult.Reports.WriteTo("reports")
if err != nil {
panic(err)
}
for _, v := range written {
fmt.Printf("Emitted %s\n", v)
}
} else {
fmt.Println("Note: skipping writing version metadata because DEBUG_CODEGEN_NAMESPACES or DEBUG_CODEGEN_APIVERSIONS is set.")
fmt.Println("Note: skipping writing version metadata and reports because DEBUG_CODEGEN_NAMESPACES or DEBUG_CODEGEN_APIVERSIONS is set.")
}
if codegenSchemaOutputPath == "" {
codegenSchemaOutputPath = path.Join("bin", "schema-full.json")
}
if err = emitSchema(buildSchemaResult.PackageSpec, version, codegenSchemaOutputPath, "main"); err != nil {
panic(err)
}
fmt.Printf("Emitted %q.\n", codegenSchemaOutputPath)
fmt.Printf("Emitted %s.\n", codegenSchemaOutputPath)

// We can't generate schema.json every time because it's slow and isn't reproducible.
// So we warn in case someone's expecting to see changes to schema.json after running this.
fmt.Println("Note: `provider/cmd/pulumi-resource-azure-native/schema.json` is generated by the `docs` target.")
fmt.Println("Note: provider/cmd/pulumi-resource-azure-native/schema.json is generated by the `docs` target.")

// Also, emit the resource metadata for the provider.
if codegenMetadataOutputPath == "" {
Expand All @@ -107,7 +118,7 @@ func main() {
if err = emitMetadata(&buildSchemaResult.Metadata, codegenMetadataOutputPath, "main"); err != nil {
panic(err)
}
fmt.Printf("Emitted %q.\n", codegenMetadataOutputPath)
fmt.Printf("Emitted %s.\n", codegenMetadataOutputPath)

case "docs":
buildSchemaArgs.ExcludeExplicitVersions = true
Expand Down
8 changes: 5 additions & 3 deletions provider/pkg/gen/emitFiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ type FileData = interface{}

type FileMap = map[FilePath]FileData

func EmitFiles(outDir string, files FileMap) error {
func EmitFiles(outDir string, files FileMap) ([]string, error) {
var written []string
for filename, data := range files {
outPath := path.Join(outDir, filename)
err := EmitFile(outPath, data)
if err != nil {
return err
return nil, err
}
written = append(written, outPath)
}
return nil
return written, nil
}

func EmitFile(outputPath string, data FileData) error {
Expand Down
21 changes: 17 additions & 4 deletions provider/pkg/gen/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,15 @@ type Versioning interface {
GetDeprecation(token string) (ResourceDeprecation, bool)
}

type GenerationResult struct {
Schema *pschema.PackageSpec
Metadata *resources.AzureAPIMetadata
Examples map[string][]resources.AzureAPIExample
}

// PulumiSchema will generate a Pulumi schema for the given Azure providers and resources map.
func PulumiSchema(providerMap openapi.AzureProviders, versioning Versioning) (*pschema.PackageSpec, *resources.AzureAPIMetadata, map[string][]resources.AzureAPIExample, error) {
func PulumiSchema(providerMap openapi.AzureProviders, versioning Versioning) (*GenerationResult, error) {
warnings := []string{}
pkg := pschema.PackageSpec{
Name: "azure-native",
Description: "A native Pulumi package for creating and managing Azure resources.",
Expand Down Expand Up @@ -247,7 +254,7 @@ func PulumiSchema(providerMap openapi.AzureProviders, versioning Versioning) (*p
resource := items.Resources[typeName]
err := gen.genResources(providerName, typeName, resource)
if err != nil {
return nil, nil, nil, err
return nil, err
}
}

Expand All @@ -262,12 +269,13 @@ func PulumiSchema(providerMap openapi.AzureProviders, versioning Versioning) (*p
invoke := items.Invokes[typeName]
gen.genPostFunctions(providerName, typeName, invoke.Path, invoke.PathItem, invoke.Swagger)
}
warnings = append(warnings, gen.warnings...)
}
}

err := genMixins(&pkg, &metadata)
if err != nil {
return nil, nil, nil, err
return nil, err
}

pkg.Language["go"] = rawMessage(map[string]interface{}{
Expand Down Expand Up @@ -314,7 +322,11 @@ version using infrastructure as code, which Pulumi then uses to drive the ARM AP
"packages": javaPackages,
})

return &pkg, &metadata, exampleMap, nil
return &GenerationResult{
Schema: &pkg,
Metadata: &metadata,
Examples: exampleMap,
}, nil
}

func genMixins(pkg *pschema.PackageSpec, metadata *resources.AzureAPIMetadata) error {
Expand Down Expand Up @@ -428,6 +440,7 @@ type packageGenerator struct {
examples map[string][]resources.AzureAPIExample
apiVersion string
versioning Versioning
warnings []string
}

func (g *packageGenerator) genResources(prov, typeName string, resource *openapi.ResourceSpec) error {
Expand Down
3 changes: 0 additions & 3 deletions provider/pkg/gen/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package gen

import (
"fmt"
"log"
"strings"

"github.com/go-openapi/spec"
Expand Down Expand Up @@ -102,13 +101,11 @@ Example of a relative ID: $self/frontEndConfigurations/my-frontend.`
// https://github.com/Azure/azure-rest-api-specs/issues/21431
// incompatible type "azure-native:network:SubResource" for resource "DnsForwardingRuleset" ("azure-native:network:DnsForwardingRuleset"): required properties do not match: only required in A: id
if tok == "azure-native:network/v20220701:SubResource" || tok == "azure-native:network:SubResource" {
log.Printf("Removing required 'id' from %s of %s", propertyName, tok)
props.requiredProperties.Delete("id")
props.requiredSpecs.Delete("id")
}
// incompatible type "azure-native:network:SecurityRule" for resource "LoadBalancer" ("azure-native:network:LoadBalancer"): required properties do not match: only required in A: priority
if tok == "azure-native:network:SecurityRule" {
log.Printf("Removing required 'priority' from %s of %s", propertyName, tok)
props.requiredProperties.Delete("priority")
props.requiredSpecs.Delete("priority")
}
Expand Down
69 changes: 51 additions & 18 deletions provider/pkg/openapi/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,16 @@ type InvokeName = string
type SdkVersion = string

// AzureProviders maps provider names (e.g. Compute) to versions in that providers and resources therein.
type AzureProviders = map[ProviderName]ProviderVersions
type AzureProviders map[ProviderName]ProviderVersions

// ProviderVersions maps API Versions (e.g. v20200801) to resources and invokes in that version.
type ProviderVersions = map[SdkVersion]VersionResources

type DiscoveryDiagnostics struct {
// Naming disambiguations
NamingDisambiguations []resources.NameDisambiguation
}

// VersionResources contains all resources and invokes in a given API version.
type VersionResources struct {
Resources map[ResourceName]*ResourceSpec
Expand Down Expand Up @@ -164,18 +169,23 @@ func buildDefaultVersion(versionMap ProviderVersions, defaultResourceVersions ma
// collected per Azure Provider and API Version - for all API versions.
// Use the namespace "*" to load all available namespaces, or a specific namespace to filter, e.g. "Compute".
// Use apiVersions with a wildcard to filter versions, e.g. "2022*preview", or leave it blank to use the default of "20*".
func ReadAzureProviders(specsDir, namespace, apiVersions string) (AzureProviders, error) {
func ReadAzureProviders(specsDir, namespace, apiVersions string) (AzureProviders, DiscoveryDiagnostics, error) {
diagnostics := DiscoveryDiagnostics{}
swaggerSpecLocations, err := swaggerLocations(specsDir, namespace, apiVersions)
if err != nil {
return nil, err
return nil, diagnostics, err
}

// Collect all versions for each path in the API across all Swagger files.
providers := AzureProviders{}
for _, location := range swaggerSpecLocations {
relLocation, err := filepath.Rel(specsDir, location)
if err != nil {
return nil, diagnostics, errors.Wrapf(err, "failed to get relative path for %q", location)
}
swagger, err := NewSpec(location)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse %q", location)
return nil, diagnostics, errors.Wrapf(err, "failed to parse %q", location)
}

orderedPaths := make([]string, 0, len(swagger.Paths.Paths))
Expand All @@ -184,10 +194,11 @@ func ReadAzureProviders(specsDir, namespace, apiVersions string) (AzureProviders
}
sort.Strings(orderedPaths)
for _, path := range orderedPaths {
addAPIPath(providers, location, path, swagger)
namingDisambiguations := providers.addAPIPath(specsDir, relLocation, path, swagger)
diagnostics.NamingDisambiguations = append(diagnostics.NamingDisambiguations, namingDisambiguations...)
}
}
return providers, nil
return providers, diagnostics, nil
}

func deprecateAll(resourceSpecs map[string]*ResourceSpec, version string) {
Expand Down Expand Up @@ -314,16 +325,16 @@ var excludeRegexes = []*regexp.Regexp{

// addAPIPath considers whether an API path contains resources and/or invokes and adds corresponding entries to the
// provider map. `providers` are mutated in-place.
func addAPIPath(providers AzureProviders, fileLocation, path string, swagger *Spec) {
func (providers AzureProviders) addAPIPath(specsDir, fileLocation, path string, swagger *Spec) []resources.NameDisambiguation {
for _, re := range excludeRegexes {
if re.MatchString(fileLocation) {
return
return nil
}
}

prov := resources.ResourceProvider(fileLocation, path)
prov := resources.ResourceProvider(filepath.Join(specsDir, fileLocation), path)
if prov == "" {
return
return nil
}

// Find (or create) the version map with this name.
Expand Down Expand Up @@ -355,7 +366,7 @@ func addAPIPath(providers AzureProviders, fileLocation, path string, swagger *Sp
}

pathItemList, hasList := swagger.Paths.Paths[path+"/list"]

var nameDisambiguations []resources.NameDisambiguation
// Add a resource entry.
if pathItem.Put != nil && !pathItem.Put.Deprecated {
hasDelete := pathItem.Delete != nil && !pathItem.Delete.Deprecated
Expand All @@ -368,7 +379,11 @@ func addAPIPath(providers AzureProviders, fileLocation, path string, swagger *Sp
panic(fmt.Sprintf("invalid defaultResourcesState '%s': non-empty collections aren't supported for deletable resources", path))
}

typeName := resources.ResourceName(pathItem.Get.ID, path)
typeName, disambiguation := resources.ResourceName(pathItem.Get.ID, path)
if disambiguation != nil {
disambiguation.FileLocation = fileLocation
nameDisambiguations = append(nameDisambiguations, *disambiguation)
}

if typeName != "" && (hasDelete || defaultState != nil) {
if _, ok := version.Resources[typeName]; ok && version.Resources[typeName].Path != path {
Expand All @@ -386,7 +401,11 @@ func addAPIPath(providers AzureProviders, fileLocation, path string, swagger *Sp
}
}
case pathItem.Head != nil && !pathItem.Head.Deprecated:
typeName := resources.ResourceName(pathItem.Head.ID, path)
typeName, disambiguation := resources.ResourceName(pathItem.Head.ID, path)
if disambiguation != nil {
disambiguation.FileLocation = fileLocation
nameDisambiguations = append(nameDisambiguations, *disambiguation)
}
if typeName != "" && hasDelete {
if _, ok := version.Resources[typeName]; ok && version.Resources[typeName].Path != path {
fmt.Printf("warning: duplicate resource %s/%s at paths:\n - %s\n - %s\n", apiVersion, typeName, path, version.Resources[typeName].Path)
Expand All @@ -399,11 +418,16 @@ func addAPIPath(providers AzureProviders, fileLocation, path string, swagger *Sp
}
case hasList:
var typeName string
var disambiguation *resources.NameDisambiguation
switch {
case pathItemList.Get != nil && !pathItemList.Get.Deprecated:
typeName = resources.ResourceName(pathItemList.Get.ID, path)
typeName, disambiguation = resources.ResourceName(pathItemList.Get.ID, path)
case pathItemList.Post != nil && !pathItemList.Post.Deprecated:
typeName = resources.ResourceName(pathItemList.Post.ID, path)
typeName, disambiguation = resources.ResourceName(pathItemList.Post.ID, path)
}
if disambiguation != nil {
disambiguation.FileLocation = fileLocation
nameDisambiguations = append(nameDisambiguations, *disambiguation)
}
if typeName != "" {
var defaultBody map[string]interface{}
Expand Down Expand Up @@ -434,7 +458,11 @@ func addAPIPath(providers AzureProviders, fileLocation, path string, swagger *Sp
// Add an entry for PATCH-based resources.
if pathItem.Patch != nil && !pathItem.Patch.Deprecated && pathItem.Get != nil && !pathItem.Get.Deprecated {
defaultState := defaults.GetDefaultResourceState(path)
typeName := resources.ResourceName(pathItem.Get.ID, path)
typeName, disambiguation := resources.ResourceName(pathItem.Get.ID, path)
if disambiguation != nil {
disambiguation.FileLocation = fileLocation
nameDisambiguations = append(nameDisambiguations, *disambiguation)
}
if typeName != "" && defaultState != nil {
if _, ok := version.Resources[typeName]; ok && version.Resources[typeName].Path != path {
fmt.Printf("warning: duplicate resource %s/%s at paths:\n - %s\n - %s\n", apiVersion, typeName, path, version.Resources[typeName].Path)
Expand Down Expand Up @@ -470,10 +498,14 @@ func addAPIPath(providers AzureProviders, fileLocation, path string, swagger *Sp
// - It's about a key, a token, or credentials.
prefix = "get"
default:
return
return nameDisambiguations
}

typeName := resources.ResourceName(pathItem.Post.ID, path)
typeName, disambiguation := resources.ResourceName(pathItem.Post.ID, path)
if disambiguation != nil {
disambiguation.FileLocation = fileLocation
nameDisambiguations = append(nameDisambiguations, *disambiguation)
}
if typeName != "" {
version.Invokes[prefix+typeName] = &ResourceSpec{
Path: path,
Expand All @@ -482,4 +514,5 @@ func addAPIPath(providers AzureProviders, fileLocation, path string, swagger *Sp
}
}
}
return nameDisambiguations
}
Loading

0 comments on commit a0b12ef

Please sign in to comment.