diff --git a/.devcontainer/install-dependencies.sh b/.devcontainer/install-dependencies.sh index 49e9dee60ac..b28d63e4dbd 100755 --- a/.devcontainer/install-dependencies.sh +++ b/.devcontainer/install-dependencies.sh @@ -210,6 +210,10 @@ go-install gen-crd-api-reference-docs github.com/ahmetb/gen-crd-api-reference-do #doc# | setup-envtest | latest | https://book.kubebuilder.io/reference/envtest.html | go-install setup-envtest sigs.k8s.io/controller-runtime/tools/setup-envtest@latest +# Stricter GO formatting +#doc# | gofumpt | latest | https://pkg.go.dev/mvdan.cc/gofumpt | +go-install gofumpt mvdan.cc/gofumpt@latest + # Install golangci-lint #doc# | golangci-lint | 1.51.2 | https://github.com/golangci/golangci-lint | write-verbose "Checking for $TOOL_DEST/golangci-lint" diff --git a/Taskfile.yml b/Taskfile.yml index f254f50bc89..f9ed59d268b 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -70,7 +70,7 @@ tasks: - asoctl:quick-checks - crossplane:quick-checks cmds: - - task: format-code # Run after the deps to avoid racing with generated file creation + - task: format-code # Run after the deps to avoid racing with generated file creation ci: desc: Run all CI checks. @@ -93,7 +93,7 @@ tasks: desc: Ensure all code is formatted dir: v2 cmds: - - gofmt -l -s -w . + - gofumpt -l -s -w . build-docs-site: cmds: @@ -622,7 +622,7 @@ tasks: - if [ -d "{{.CONTROLLER_OUTPUT}}/crd/generated/patches" ]; then find "{{.CONTROLLER_OUTPUT}}/crd/generated/patches" -type f -delete; fi - cd v2/api && controller-gen {{.OBJECT_OPTIONS}} paths=./... - cd v2/api && controller-gen {{.CRD_OPTIONS}} {{.WEBHOOK_OPTIONS}} {{.RBAC_OPTIONS}} paths=./... - - cd v2/api && gofmt -l -s -w . # format all generated code + - cd v2/api && gofumpt -l -s -w . # format all generated code vars: OBJECT_OPTIONS: object:headerFile={{.HEADER_FILE}} CRD_OPTIONS: crd:crdVersions=v1,allowDangerousTypes=true output:crd:artifacts:config={{.CONTROLLER_OUTPUT}}/crd/generated/bases @@ -1037,7 +1037,7 @@ tasks: - if [ -d "{{.CROSSPLANE_OUTPUT}}/crd/patches" ]; then find "{{.CROSSPLANE_OUTPUT}}/crd/patches" -type f -delete; fi - cd apis && controller-gen {{.OBJECT_OPTIONS}} paths=./... - cd apis && controller-gen {{.CRD_OPTIONS}} {{.WEBHOOK_OPTIONS}} {{.RBAC_OPTIONS}} paths=./... - - cd apis && gofmt -l -s -w . # format all generated code + - cd apis && gofumpt -l -s -w . # format all generated code vars: OBJECT_OPTIONS: object:headerFile={{.HEADER_FILE}} CRD_OPTIONS: crd:crdVersions=v1,allowDangerousTypes=true output:crd:artifacts:config={{.CROSSPLANE_OUTPUT}}/crd/bases diff --git a/docs/hugo/content/contributing/aso-codegen-structure.svg b/docs/hugo/content/contributing/aso-codegen-structure.svg index 291604f0dff..4840544607c 100644 --- a/docs/hugo/content/contributing/aso-codegen-structure.svg +++ b/docs/hugo/content/contributing/aso-codegen-structure.svg @@ -1 +1 @@ -pkgpkginternalinternaltestcasestestcasestesttestreportingreportingjsonastjsonastfunctionsfunctionsconversionsconversionsconfigconfigcodegencodegenastmodelastmodelastbuilderastbuildertestdatatestdatatestdatatestdatatestdatatestdatatestdatatestdatapipelinepipelinetestdatatestdatastoragestoragetestdatatestdataArmResourceArmResourceAllOfAllOf.go.json.md.mod.yamleach dot sized by file size \ No newline at end of file +pkgpkginternalinternaltestcasestestcasestesttestreportingreportingjsonastjsonastfunctionsfunctionsconversionsconversionsconfigconfigcodegencodegenastmodelastmodelastbuilderastbuildertestdatatestdatatestdatatestdatatestdatatestdatatestdatatestdatapipelinepipelinetestdatatestdatastoragestoragetestdatatestdataArmResourceArmResourceAllOfAllOf.go.json.md.mod.yamleach dot sized by file size \ No newline at end of file diff --git a/docs/hugo/content/contributing/aso-v1-structure.svg b/docs/hugo/content/contributing/aso-v1-structure.svg index 55febe86bea..f3c82fe39a2 100644 --- a/docs/hugo/content/contributing/aso-v1-structure.svg +++ b/docs/hugo/content/contributing/aso-v1-structure.svg @@ -1 +1 @@ -scriptsscriptspkgpkgdevopsdevopscontrollerscontrollersconfigconfigchartschartsapiapisecretssecretsresourcemanagerresourcemanagerhelpershelperserrhelperrhelpsamplessamplesrbacrbacdefaultdefaultcrdcrdazure-service-operatorazure-service-operatorv1beta1v1beta1v1alpha2v1alpha2v1alpha1v1alpha1kubekubekeyvaultkeyvaultvnetvnetvmssvmssvmextvmextvmvmstoragesstoragesrediscachesrediscachespsqlpsqlpippipnicnicmysqlmysqlkeyvaultskeyvaultseventhubseventhubscosmosdbcosmosdbconfigconfigazuresqlazuresqlappinsightsappinsightsapimapimpatchespatchescrdscrdstemplatestemplatesredisredisvnetrulevnetruleserverserverpsqluserpsqluserdatabasedatabasevnetrulevnetruleserverservermysqlusermysqluserdatabasedatabasesqldatabasesqldatabaseaccountaccountazuresqldbazuresqldbapimgmtapimgmtgeneratedgenerated.gitignore.go.json.md.mod.mysql.sh.yaml.ymleach dot sized by file size \ No newline at end of file +scriptsscriptspkgpkgdevopsdevopscontrollerscontrollersconfigconfigchartschartsapiapisecretssecretsresourcemanagerresourcemanagerhelpershelperserrhelperrhelpsamplessamplesrbacrbacdefaultdefaultcrdcrdazure-service-operatorazure-service-operatorv1beta1v1beta1v1alpha2v1alpha2v1alpha1v1alpha1kubekubekeyvaultkeyvaultvnetvnetvmssvmssvmextvmextvmvmstoragesstoragesrediscachesrediscachespsqlpsqlpippipnicnicmysqlmysqlkeyvaultskeyvaultseventhubseventhubscosmosdbcosmosdbconfigconfigazuresqlazuresqlappinsightsappinsightsapimapimpatchespatchescrdscrdstemplatestemplatesredisredisvnetrulevnetruleserverserverpsqluserpsqluserdatabasedatabasevnetrulevnetruleserverservermysqlusermysqluserdatabasedatabasesqldatabasesqldatabaseaccountaccountazuresqldbazuresqldbapimgmtapimgmtgeneratedgenerated.gitignore.go.json.md.mod.mysql.sh.yaml.ymleach dot sized by file size \ No newline at end of file diff --git a/docs/hugo/content/contributing/aso-v2-structure.svg b/docs/hugo/content/contributing/aso-v2-structure.svg index 2e7179c7e6d..8130c6f69c0 100644 --- a/docs/hugo/content/contributing/aso-v2-structure.svg +++ b/docs/hugo/content/contributing/aso-v2-structure.svg @@ -1 +1 @@ -samplessamplespkgpkginternalinternalconfigconfigchartschartsapiapistoragestoragesqlsqlnetworknetworkgenruntimegenruntimecontrollerscontrollersutilutilwebwebsynapsesynapsestoragestoragesqlsqlservicebusservicebussearchsearchresourcesresourcesnetworknetworkmanagedidentitymanagedidentitykeyvaultkeyvaultinsightsinsightseventhubeventhubeventgrideventgriddocumentdbdocumentdbdevicesdevicesdbforpostgresqldbforpostgresqldbformysqldbformysqldbformariadbdbformariadbdatafactorydatafactorycontainerservicecontainerservicecomputecomputecdncdncachecachebatchbatchauthorizationauthorizationapimanagementapimanagementrecordingsrecordingsv1api20230101v1api20230101v1api20220901v1api20220901v1api20210401v1api20210401v1api20211101v1api20211101v1api20220701v1api20220701v1api20201101v1api20201101v1api20200601v1api20200601v1api20180501v1api20180501v1api20210515v1api20210515v1api20230201v1api20230201v1api20230501v1api20230501v1api20220801v1api20220801storagestoragestoragestorage.gitignore.go.md.mod.mysql.sql.txt.yamleach dot sized by file size \ No newline at end of file +samplessamplespkgpkginternalinternalconfigconfigchartschartsapiapistoragestoragesqlsqlnetworknetworkgenruntimegenruntimecontrollerscontrollersutilutiltestcommontestcommonwebwebsynapsesynapsestoragestoragesqlsqlservicebusservicebussearchsearchresourcesresourcesnetworknetworkmanagedidentitymanagedidentitykeyvaultkeyvaultinsightsinsightseventhubeventhubeventgrideventgriddocumentdbdocumentdbdevicesdevicesdbforpostgresqldbforpostgresqldbformysqldbformysqldbformariadbdbformariadbdatafactorydatafactorycontainerservicecontainerservicecomputecomputecdncdncachecachebatchbatchauthorizationauthorizationapimanagementapimanagementrecordingsrecordingsv1api20230101v1api20230101v1api20220901v1api20220901v1api20210401v1api20210401v1api20211101v1api20211101v1api20220701v1api20220701v1api20201101v1api20201101v1api20200601v1api20200601v1api20180501v1api20180501v1api20210515v1api20210515v1api20230201v1api20230201v1api20230501v1api20230501v1api20220801v1api20220801storagestoragestoragestorage.gitignore.go.md.mod.mysql.sql.txt.yamleach dot sized by file size \ No newline at end of file diff --git a/docs/hugo/content/contributing/asoctl-structure.svg b/docs/hugo/content/contributing/asoctl-structure.svg index e057790624b..4483e9fa502 100644 --- a/docs/hugo/content/contributing/asoctl-structure.svg +++ b/docs/hugo/content/contributing/asoctl-structure.svg @@ -1 +1 @@ -internalinternalcmdcmdimportingimportingcrdcrdimport_azure_r...import_azure_r...import_azure_r...root.goroot.goroot.golog.golog.golog.goclean-crds.goclean-crds.goclean-crds.gogo.sumgo.sumgo.sumgo.modgo.modgo.modimportable_arm_resource.goimportable_arm_resource.goimportable_arm_resource.goresource_importer.goresource_importer.goresource_importer.goimportable_arm...importable_arm...importable_arm...resource_impor...resource_impor...resource_impor...resource_impor...resource_impor...resource_impor...importable_re...importable_re...importable_re...find_resour...find_resour...find_resour...find_child...find_child...find_child...find_gro...find_gro...find_gro...import_s...import_s...import_s...cleaner_test.gocleaner_test.gocleaner_test.gocleaner.gocleaner.gocleaner.go.cmd.go.modeach dot sized by file size \ No newline at end of file +internalinternalcmdcmdimportingimportingcrdcrdimport_azure_r...import_azure_r...import_azure_r...root.goroot.goroot.golog.golog.golog.goclean-crds.goclean-crds.goclean-crds.gogo.sumgo.sumgo.sumgo.modgo.modgo.modimportable_arm_resource.goimportable_arm_resource.goimportable_arm_resource.goresource_importer.goresource_importer.goresource_importer.goimportable_arm...importable_arm...importable_arm...resource_impor...resource_impor...resource_impor...resource_impor...resource_impor...resource_impor...importable_res...importable_res...importable_res...find_resour...find_resour...find_resour...find_child...find_child...find_child...find_gro...find_gro...find_gro...import_s...import_s...import_s...cleaner_test.gocleaner_test.gocleaner_test.gocleaner.gocleaner.gocleaner.go.cmd.go.modeach dot sized by file size \ No newline at end of file diff --git a/docs/hugo/content/contributing/dependencies.md b/docs/hugo/content/contributing/dependencies.md index bb3a948c114..c0b83794d7c 100644 --- a/docs/hugo/content/contributing/dependencies.md +++ b/docs/hugo/content/contributing/dependencies.md @@ -2,18 +2,21 @@ title: Developer Dependencies linktitle: Dependencies --- -Development of Azure Service Operator depends on a number of development tools and libraries that need to be installed. +Development of Azure Service Operator depends on a number of development tools and libraries that need to be installed. -If you prefer to install those dependencies manually (instead of using the `.devcontainer/install-dependencies.sh` script), here is a list of what's required. +If you prefer to install those dependencies manually (instead of using the `.devcontainer/install-dependencies.sh` script), here is a list of what's required. | Dependency | Version | Reference | |:---------- |:-------:|:--------- | +| AZ | latest | https://docs.microsoft.com/en-us/cli/azure/install-azure-cli | | AZWI | v1.2.0 | https://github.com/Azure/azure-workload-identity | | BuildX | v0.11.2 | https://github.com/docker/buildx | | cmctl | latest | https://cert-manager.io/docs/reference/cmctl | | controller-gen | v0.13.0 | https://book.kubebuilder.io/reference/controller-gen | | conversion-gen | v0.28.0 | https://pkg.go.dev/k8s.io/code-generator/cmd/conversion-gen | | gen-crd-api-reference-docs | 11fe95cb | https://github.com/ahmetb/gen-crd-api-reference-docs | +| Go | 1.20 | https://golang.org/doc/install # +| gofumpt | latest | https://pkg.go.dev/mvdan.cc/gofumpt | | golangci-lint | 1.51.2 | https://github.com/golangci/golangci-lint | | Helm | v3.8.0 | https://helm.sh/ | | htmltest | latest | https://github.com/wjdp/htmltest (but see https://github.com/theunrepentantgeek/htmltest for our custom build ) diff --git a/docs/hugo/content/design/ADR-2024-02-Upstream-Deletion/_index.md b/docs/hugo/content/design/ADR-2024-02-Upstream-Deletion/_index.md new file mode 100644 index 00000000000..eb09e051295 --- /dev/null +++ b/docs/hugo/content/design/ADR-2024-02-Upstream-Deletion/_index.md @@ -0,0 +1,54 @@ +--- +title: 2024-02 Upstream Deletion +--- + +What do we do if/when a resource supported by ASO v2 is deleted upstream - removed from the Azure OpenAPI repository? + +## Context + +In July 2022, we encountered a problem when the preview versions of the `Microsoft.Auithorization` service were deleted from the [azure-resource-manager-schemas](https://github.com/Azure/azure-resource-manager-schemas) repository that ASO consumed at the time. This orphaned the `RoleAssignment` resource in ASO, which would have been a breaking change for our users. + +Since that time, ASO has migrated to depend soley on the [azure-rest-api-specs](https://github.com/Azure/azure-rest-api-specs) repo (see [PR#2323](https://github.com/Azure/azure-service-operator/pull/2323), merged December 2022) so the problem with `RoleAssignment` went away. + +However, we decided that this question still needed to be considered, as it may come up again in the future and we need to have a plan in place for how to handle it. + +Fortunately, the `azure-rest-api-specs` is very tightly controlled and is used as the source of truth for code generation of client APIs across multiple languages (including Python, Java, C# and Go) so it's vanishingly unlikely for a resource version to be removed without warning. + +### Scenario A: Deletion of preview version + +The most likely scenario is that an old preview version of a service is removed. Preview versions are only guaranteed to be supported for 90 days after creation, though in practice they are retained much longer. + +> When releasing a new preview, the service team may completely retire any previous preview versions after giving customers at least 90 days to upgrade their code. + -- [Microsoft Azure REST API Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md), retrieved 23 Feb 2024 + +In this situation, the resource provider will stop accepting the old version of the resource, requiring all users to update to a newer api version, including users of ASO. + +ASO can support migration by including a later version of the resource in a new release. + +If the new ASO release is available before the deprecation/deletion date, users can update their clusters to the new version, and then update their resources to the new version at any time. As long as they complete the update prior to deletion, no downtime will occur. + +If the new ASO release is not available until after the depredcation/deletion date, users will need to export/recreate their resources as a part of the upgrade process. + +In either case, the removal of the old version from ASO will be a breaking change that must be documented on the breaking release, and should be documented on earlier releases if possible. + +### Scenario: Deletion of GA version + +The deletion of a GA version of a resource is much rarer event, and requires a much longer notice and deprecation period. + +Either the resource provider will stop accepting the old version of the resource, or it will be entirely shut down. + +ASO should be aware of this change well in advance, and should be able to support migration by including a later version of the resource in a new release. We should also document the upcoming deprecation so that users know they need to migrate. + +### Scenario: Deletion of an entire resource + +Even rarer, this can happen when a service is retired or replaced. ASO should be aware of this change well in advance. Where possible, we should provide a migration path to the new service, and document the upcoming deprecation so that users know they need to migrate. + +## Decision + +Deletion inevitably means that attempts to use the API will fail because the backing resource provider will no longer accept the requests. + +This is a breaking change no matter what ASO does, even if we found a way to work around our current inability to regenerate the resource. + +ASO should work to be aware of upcoming deprecations, to document them where ASO users may be affected, and to provide migration paths to newer versions where possible. + +We may want to consider building upgrade tooling into `asoctl` to make migration smoother. diff --git a/docs/hugo/content/design/_index.md b/docs/hugo/content/design/_index.md index 3c1c67f7c96..0075f078b77 100644 --- a/docs/hugo/content/design/_index.md +++ b/docs/hugo/content/design/_index.md @@ -27,6 +27,10 @@ For background information, check out [this Cognitect blog entry](https://www.co ADR documents should be updated over time to keep them relevant, typically by updating the *Experience Report* section. +## 2024 + +[**Upstream Deletion**]({{< relref "ADR-2024-02-Upstream-Deletion" >}}) - when might deletion of an upstream resource occur and how will ASO handle it. + ## 2023 [**Adoption Policy**]({{< relref "ADR-2023-02-Adoption-Policy" >}}) - Understand the policy for adopting pre-existing Azure resources in Kubernetes through the Azure Service Operator, including the principles guiding resource adoption and the user's ability to control operator actions. @@ -67,7 +71,6 @@ ADR documents should be updated over time to keep them relevant, typically by up [**Reconciliation Extensions**]({{< relref "ADR-2022-12-Reconciliation-Extensions" >}}) - Understand the need for reconciliation extensions in Azure Service Operator to handle resources in transient states that preclude successful PUT operations, and the proposal for an extension point to modify the reconciliation flow accordingly. - ## 2021 [**API Version Recovery**]({{< relref "ADR-2021-06-API-Version-Recovery" >}}) - Identity the challenges of API version recovery in Azure Service Operator, discussing the importance of using the correct Azure API version for expected behavior, and the need to pivot back to the original version for generating the correct ARM payload. @@ -84,7 +87,6 @@ ADR documents should be updated over time to keep them relevant, typically by up [**Why Code Generation?**]({{< relref "ADR-2020-04-Code-Generation" >}}) - rationale and impact of adopting code generation in the Azure Service Operator, including the benefits of full feature coverage, reduced overhead for new resources, and consistent behavior across all resources. - ## Older [**Clarifying Object Structure**]({{< relref "clarifying-object-structure" >}}) - Dive into the challenges of differentiating Azure-specific properties from operator-specific properties in the object structure of resources, with a focus on potential solutions for Status and upcoming properties like SecretConfiguration and Credentials. @@ -97,8 +99,6 @@ ADR documents should be updated over time to keep them relevant, typically by up [**Managing dataplane secrets**]({{< relref "secrets" >}}) - managing dataplane secrets in Azure Service Operator, including the drawbacks of auto-generating secrets and the implications for secret rollover, control, security, resource adoption, and GitOps compatibility. - [**Reporting Resource Status**]({{< relref "resource-states" >}}) - a proposal for reporting resource status in Azure Service Operator, discussing the distinction between operator status and Azure resource state, and the current state and limitations of both handcrafted and auto-generated resources. [**Type References and Ownership**]({{< relref "type-references-and-ownership" >}}) - type references and ownership in Azure Service Operator, discussing goals such as idiomatic expression of Azure resource relationships, automatic ownership, garbage collection, and interaction with non-Kubernetes managed Azure resources. - diff --git a/v2/api/apimanagement/customizations/product_extensions.go b/v2/api/apimanagement/customizations/product_extensions.go index 10b6484c501..272cce54f60 100644 --- a/v2/api/apimanagement/customizations/product_extensions.go +++ b/v2/api/apimanagement/customizations/product_extensions.go @@ -28,8 +28,8 @@ func (extension *ProductExtension) Delete( resolver *resolver.Resolver, armClient *genericarmclient.GenericClient, obj genruntime.ARMMetaObject, - next extensions.DeleteFunc) (ctrl.Result, error) { - + next extensions.DeleteFunc, +) (ctrl.Result, error) { typedObj, ok := obj.(*storage.Product) if !ok { return ctrl.Result{}, errors.Errorf("cannot run on unknown resource type %T, expected *apiManagement.Product", obj) diff --git a/v2/api/apimanagement/customizations/subscription_extensions.go b/v2/api/apimanagement/customizations/subscription_extensions.go index d656cf9f95a..52a671959b2 100644 --- a/v2/api/apimanagement/customizations/subscription_extensions.go +++ b/v2/api/apimanagement/customizations/subscription_extensions.go @@ -31,7 +31,6 @@ func (ext *SubscriptionExtension) ExportKubernetesResources( armClient *genericarmclient.GenericClient, log logr.Logger, ) ([]client.Object, error) { - // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*apimanagement.Subscription) diff --git a/v2/api/appconfiguration/customizations/configuration_store_extensions.go b/v2/api/appconfiguration/customizations/configuration_store_extensions.go index 9b96a9df00e..aeda31980b9 100644 --- a/v2/api/appconfiguration/customizations/configuration_store_extensions.go +++ b/v2/api/appconfiguration/customizations/configuration_store_extensions.go @@ -29,8 +29,8 @@ func (ext *ConfigurationStoreExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*storage.ConfigurationStore) diff --git a/v2/api/cache/customizations/redis_extensions.go b/v2/api/cache/customizations/redis_extensions.go index b418b2dfac2..debf21305d5 100644 --- a/v2/api/cache/customizations/redis_extensions.go +++ b/v2/api/cache/customizations/redis_extensions.go @@ -31,8 +31,8 @@ func (ext *RedisExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*redis.Redis) diff --git a/v2/api/cache/customizations/redis_firewall_rule_extension_types.go.go b/v2/api/cache/customizations/redis_firewall_rule_extension_types.go.go index 10d3644cacf..c3414d71386 100644 --- a/v2/api/cache/customizations/redis_firewall_rule_extension_types.go.go +++ b/v2/api/cache/customizations/redis_firewall_rule_extension_types.go.go @@ -26,7 +26,8 @@ func (e *RedisFirewallRuleExtension) ClassifyError( cloudError *genericarmclient.CloudError, apiVersion string, log logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/cache/customizations/redis_linked_server_extension_types.go b/v2/api/cache/customizations/redis_linked_server_extension_types.go index 29097f6d4d3..2f9d71fec48 100644 --- a/v2/api/cache/customizations/redis_linked_server_extension_types.go +++ b/v2/api/cache/customizations/redis_linked_server_extension_types.go @@ -26,7 +26,8 @@ func (e *RedisLinkedServerExtension) ClassifyError( cloudError *genericarmclient.CloudError, apiVersion string, log logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/cache/customizations/redis_patch_schedule_extension_types.go b/v2/api/cache/customizations/redis_patch_schedule_extension_types.go index fefda4fcd54..3bddfed2caa 100644 --- a/v2/api/cache/customizations/redis_patch_schedule_extension_types.go +++ b/v2/api/cache/customizations/redis_patch_schedule_extension_types.go @@ -26,7 +26,8 @@ func (e *RedisPatchScheduleExtension) ClassifyError( cloudError *genericarmclient.CloudError, apiVersion string, log logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/compute/customizations/vm_extensions.go b/v2/api/compute/customizations/vm_extensions.go index b4c9fde117a..ece53d90f62 100644 --- a/v2/api/compute/customizations/vm_extensions.go +++ b/v2/api/compute/customizations/vm_extensions.go @@ -20,7 +20,8 @@ func (e *VirtualMachineExtension) ClassifyError( cloudError *genericarmclient.CloudError, apiVersion string, log logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/compute/customizations/vmss_extensions.go b/v2/api/compute/customizations/vmss_extensions.go index 58ff5580fc4..ee45c69a5fd 100644 --- a/v2/api/compute/customizations/vmss_extensions.go +++ b/v2/api/compute/customizations/vmss_extensions.go @@ -28,15 +28,18 @@ import ( var _ extensions.ErrorClassifier = &VirtualMachineScaleSetExtension{} -var rawChildCollectionPath = []string{"properties", "virtualMachineProfile", "extensionProfile", "extensions"} -var childCollectionPathARM = []string{"Properties", "VirtualMachineProfile", "ExtensionProfile", "Extensions"} +var ( + rawChildCollectionPath = []string{"properties", "virtualMachineProfile", "extensionProfile", "extensions"} + childCollectionPathARM = []string{"Properties", "VirtualMachineProfile", "ExtensionProfile", "Extensions"} +) // ClassifyError evaluates the provided error, returning whether it is fatal or can be retried. func (e *VirtualMachineScaleSetExtension) ClassifyError( cloudError *genericarmclient.CloudError, apiVersion string, log logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/containerservice/customizations/managed_cluster_extensions.go b/v2/api/containerservice/customizations/managed_cluster_extensions.go index 6715130d35b..3cb95756dc3 100644 --- a/v2/api/containerservice/customizations/managed_cluster_extensions.go +++ b/v2/api/containerservice/customizations/managed_cluster_extensions.go @@ -35,8 +35,8 @@ func (ext *ManagedClusterExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*containerservice.ManagedCluster) diff --git a/v2/api/dbformariadb/customizations/server_extensions.go b/v2/api/dbformariadb/customizations/server_extensions.go index d618c73293e..40340cc767d 100644 --- a/v2/api/dbformariadb/customizations/server_extensions.go +++ b/v2/api/dbformariadb/customizations/server_extensions.go @@ -28,8 +28,8 @@ func (ext *ServerExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current storage version. It will need to be updated // if the storage version changes. typedObj, ok := obj.(*mariadb.Server) diff --git a/v2/api/dbformysql/customizations/flexible_server_extensions.go b/v2/api/dbformysql/customizations/flexible_server_extensions.go index 2e15b2b55aa..591b67d6931 100644 --- a/v2/api/dbformysql/customizations/flexible_server_extensions.go +++ b/v2/api/dbformysql/customizations/flexible_server_extensions.go @@ -28,8 +28,8 @@ func (ext *FlexibleServerExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*mysql.FlexibleServer) diff --git a/v2/api/dbformysql/v1/user_types.go b/v2/api/dbformysql/v1/user_types.go index 14af72473c0..3279c509aec 100644 --- a/v2/api/dbformysql/v1/user_types.go +++ b/v2/api/dbformysql/v1/user_types.go @@ -360,7 +360,7 @@ type AADUserSpec struct { } type UserStatus struct { - //Conditions: The observed state of the resource + // Conditions: The observed state of the resource Conditions []conditions.Condition `json:"conditions,omitempty"` } diff --git a/v2/api/dbforpostgresql/customizations/flexible_server_extensions.go b/v2/api/dbforpostgresql/customizations/flexible_server_extensions.go index b412f9ab6d6..598a195c99b 100644 --- a/v2/api/dbforpostgresql/customizations/flexible_server_extensions.go +++ b/v2/api/dbforpostgresql/customizations/flexible_server_extensions.go @@ -34,8 +34,8 @@ func (ext *FlexibleServerExtension) ExportKubernetesResources( _ context.Context, obj genruntime.MetaObject, _ *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*postgresql.FlexibleServer) diff --git a/v2/api/dbforpostgresql/v1/user_types.go b/v2/api/dbforpostgresql/v1/user_types.go index f8d06ba4c0b..eb0fccd28aa 100644 --- a/v2/api/dbforpostgresql/v1/user_types.go +++ b/v2/api/dbforpostgresql/v1/user_types.go @@ -140,14 +140,14 @@ type UserList struct { } type UserSpec struct { - //AzureName: The name of the resource in Azure. This is often the same as the name of the resource in Kubernetes but it - //doesn't have to be. + // AzureName: The name of the resource in Azure. This is often the same as the name of the resource in Kubernetes but it + // doesn't have to be. AzureName string `json:"azureName,omitempty"` // +kubebuilder:validation:Required - //Owner: The owner of the resource. The owner controls where the resource goes when it is deployed. The owner also - //controls the resources lifecycle. When the owner is deleted the resource will also be deleted. Owner is expected to be a - //reference to a dbforpostgresql.azure.com/FlexibleServer resource + // Owner: The owner of the resource. The owner controls where the resource goes when it is deployed. The owner also + // controls the resources lifecycle. When the owner is deleted the resource will also be deleted. Owner is expected to be a + // reference to a dbforpostgresql.azure.com/FlexibleServer resource Owner *genruntime.KubernetesOwnerReference `group:"dbforpostgresql.azure.com" json:"owner,omitempty" kind:"FlexibleServer"` // The Azure Database for PostgreSQL server is created with the 3 default roles defined. @@ -210,7 +210,7 @@ type RoleOptionsSpec struct { } type UserStatus struct { - //Conditions: The observed state of the resource + // Conditions: The observed state of the resource Conditions []conditions.Condition `json:"conditions,omitempty"` } diff --git a/v2/api/devices/customizations/iot_hub_extension.go b/v2/api/devices/customizations/iot_hub_extension.go index b0488e8b002..a34a5440bae 100644 --- a/v2/api/devices/customizations/iot_hub_extension.go +++ b/v2/api/devices/customizations/iot_hub_extension.go @@ -30,8 +30,8 @@ func (ext *IotHubExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub devices version. It will need to be updated // if the hub devices version changes. typedObj, ok := obj.(*devices.IotHub) diff --git a/v2/api/documentdb/customizations/database_account_extensions.go b/v2/api/documentdb/customizations/database_account_extensions.go index c1c275822ec..6676b72af22 100644 --- a/v2/api/documentdb/customizations/database_account_extensions.go +++ b/v2/api/documentdb/customizations/database_account_extensions.go @@ -29,8 +29,8 @@ func (ext *DatabaseAccountExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*documentdb.DatabaseAccount) diff --git a/v2/api/documentdb/customizations/mongodb_database_extension.go b/v2/api/documentdb/customizations/mongodb_database_extension.go index 7ae54956a00..8b8c6752147 100644 --- a/v2/api/documentdb/customizations/mongodb_database_extension.go +++ b/v2/api/documentdb/customizations/mongodb_database_extension.go @@ -24,7 +24,8 @@ func (extension *MongodbDatabaseExtension) ClassifyError( cloudError *genericarmclient.CloudError, _ string, _ logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/documentdb/customizations/sql_database_extension_types.go b/v2/api/documentdb/customizations/sql_database_extension_types.go index 955661537c5..c3c332a7b2c 100644 --- a/v2/api/documentdb/customizations/sql_database_extension_types.go +++ b/v2/api/documentdb/customizations/sql_database_extension_types.go @@ -24,7 +24,8 @@ func (extension *SqlDatabaseExtension) ClassifyError( cloudError *genericarmclient.CloudError, _ string, _ logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/documentdb/customizations/sql_role_assignment_extension_types.go b/v2/api/documentdb/customizations/sql_role_assignment_extension_types.go index c1b5bac4a5b..9c1506263a4 100644 --- a/v2/api/documentdb/customizations/sql_role_assignment_extension_types.go +++ b/v2/api/documentdb/customizations/sql_role_assignment_extension_types.go @@ -26,7 +26,8 @@ func (extension *SqlRoleAssignmentExtension) ClassifyError( cloudError *genericarmclient.CloudError, _ string, _ logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/eventgrid/customizations/topic_extension.go b/v2/api/eventgrid/customizations/topic_extension.go index 1a25dc31a0d..2397791e2b1 100644 --- a/v2/api/eventgrid/customizations/topic_extension.go +++ b/v2/api/eventgrid/customizations/topic_extension.go @@ -28,8 +28,8 @@ func (ext *TopicExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*storage.Topic) diff --git a/v2/api/keyvault/customizations/vault_extensions.go b/v2/api/keyvault/customizations/vault_extensions.go index abd15cd2877..6a026c5f283 100644 --- a/v2/api/keyvault/customizations/vault_extensions.go +++ b/v2/api/keyvault/customizations/vault_extensions.go @@ -47,7 +47,6 @@ func (ex *VaultExtension) ModifyARMResource( resolver *resolver.Resolver, log logr.Logger, ) (genruntime.ARMResource, error) { - kv, ok := obj.(*keyvault.Vault) if !ok { return nil, errors.Errorf( diff --git a/v2/api/machinelearningservices/customizations/workspace_extension.go b/v2/api/machinelearningservices/customizations/workspace_extension.go index 2aa62b37cd8..e1ef1353aac 100644 --- a/v2/api/machinelearningservices/customizations/workspace_extension.go +++ b/v2/api/machinelearningservices/customizations/workspace_extension.go @@ -34,7 +34,6 @@ func (ext *WorkspaceExtension) ExportKubernetesResources( armClient *genericarmclient.GenericClient, log logr.Logger, ) ([]client.Object, error) { - // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*storage.Workspace) diff --git a/v2/api/network/customizations/load_balancer_extension.go b/v2/api/network/customizations/load_balancer_extension.go index 77ecbf11249..668c2690994 100644 --- a/v2/api/network/customizations/load_balancer_extension.go +++ b/v2/api/network/customizations/load_balancer_extension.go @@ -35,7 +35,6 @@ func (extension *LoadBalancerExtension) ModifyARMResource( resolver *resolver.Resolver, log logr.Logger, ) (genruntime.ARMResource, error) { - typedObj, ok := obj.(*network.LoadBalancer) if !ok { return nil, errors.Errorf("cannot run on unknown resource type %T, expected *network.LoadBalancer", obj) diff --git a/v2/api/network/customizations/private_endpoints_extensions.go b/v2/api/network/customizations/private_endpoints_extensions.go index f04de671d31..50121b7a00d 100644 --- a/v2/api/network/customizations/private_endpoints_extensions.go +++ b/v2/api/network/customizations/private_endpoints_extensions.go @@ -28,8 +28,8 @@ func (extension *PrivateEndpointExtension) PostReconcileCheck( _ *resolver.Resolver, _ *genericarmclient.GenericClient, _ logr.Logger, - _ extensions.PostReconcileCheckFunc) (extensions.PostReconcileCheckResult, error) { - + _ extensions.PostReconcileCheckFunc, +) (extensions.PostReconcileCheckResult, error) { endpoint, ok := obj.(*network.PrivateEndpoint) if !ok { return extensions.PostReconcileCheckResult{}, diff --git a/v2/api/search/customizations/search_service_extension.go b/v2/api/search/customizations/search_service_extension.go index d106f637966..9e5b679f551 100644 --- a/v2/api/search/customizations/search_service_extension.go +++ b/v2/api/search/customizations/search_service_extension.go @@ -30,8 +30,8 @@ func (ext *SearchServiceExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub devices version. It will need to be updated // if the hub devices version changes. typedObj, ok := obj.(*search.SearchService) diff --git a/v2/api/servicebus/customizations/namespace_extensions.go b/v2/api/servicebus/customizations/namespace_extensions.go index d213c60bb5e..f258dc625f5 100644 --- a/v2/api/servicebus/customizations/namespace_extensions.go +++ b/v2/api/servicebus/customizations/namespace_extensions.go @@ -32,8 +32,8 @@ func (ext *NamespaceExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. namespace, ok := obj.(*servicebus.Namespace) @@ -105,7 +105,6 @@ func namespaceSecretsSpecified(obj *servicebus.Namespace) bool { specSecrets.PrimaryConnectionString != nil || specSecrets.SecondaryKey != nil || specSecrets.SecondaryConnectionString != nil - } func namespaceSecretsToWrite( diff --git a/v2/api/servicebus/customizations/namespaces_topic_subscription_extension.go b/v2/api/servicebus/customizations/namespaces_topic_subscription_extension.go index 22e386c32a5..9f1be98eb16 100644 --- a/v2/api/servicebus/customizations/namespaces_topic_subscription_extension.go +++ b/v2/api/servicebus/customizations/namespaces_topic_subscription_extension.go @@ -26,7 +26,8 @@ func (e *NamespacesTopicsSubscriptionExtension) ClassifyError( cloudError *genericarmclient.CloudError, apiVersion string, log logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/signalrservice/customizations/signal_r_extention_authorization.go b/v2/api/signalrservice/customizations/signal_r_extention_authorization.go index 4f8fe1364c9..340c8844d7b 100644 --- a/v2/api/signalrservice/customizations/signal_r_extention_authorization.go +++ b/v2/api/signalrservice/customizations/signal_r_extention_authorization.go @@ -31,7 +31,8 @@ func (*SignalRExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { + log logr.Logger, +) ([]client.Object, error) { // Make sure we're working with the current hub version of the resource // This will need to be updated if the hub version changes typedObj, ok := obj.(*signalr.SignalR) @@ -73,7 +74,6 @@ func (*SignalRExtension) ExportKubernetesResources( } return secrets.SliceToClientObjectSlice(secretSlice), nil - } func secretsSpecified(obj *signalr.SignalR) bool { diff --git a/v2/api/sql/customizations/server_extension_types.go b/v2/api/sql/customizations/server_extension_types.go index f60bf687b83..e1f8fed4db6 100644 --- a/v2/api/sql/customizations/server_extension_types.go +++ b/v2/api/sql/customizations/server_extension_types.go @@ -24,7 +24,8 @@ func (e *ServerExtension) ClassifyError( cloudError *genericarmclient.CloudError, apiVersion string, log logr.Logger, - next extensions.ErrorClassifierFunc) (core.CloudErrorDetails, error) { + next extensions.ErrorClassifierFunc, +) (core.CloudErrorDetails, error) { details, err := next(cloudError) if err != nil { return core.CloudErrorDetails{}, err diff --git a/v2/api/sql/v1/user_types.go b/v2/api/sql/v1/user_types.go index 818ac4c1c16..e8ddc09f23e 100644 --- a/v2/api/sql/v1/user_types.go +++ b/v2/api/sql/v1/user_types.go @@ -273,7 +273,7 @@ type LocalUserSpec struct { } type UserStatus struct { - //Conditions: The observed state of the resource + // Conditions: The observed state of the resource Conditions []conditions.Condition `json:"conditions,omitempty"` } diff --git a/v2/api/storage/customizations/storage_account_extensions.go b/v2/api/storage/customizations/storage_account_extensions.go index 86265438ec4..16f0d558677 100644 --- a/v2/api/storage/customizations/storage_account_extensions.go +++ b/v2/api/storage/customizations/storage_account_extensions.go @@ -29,8 +29,8 @@ func (ext *StorageAccountExtension) ExportKubernetesResources( ctx context.Context, obj genruntime.MetaObject, armClient *genericarmclient.GenericClient, - log logr.Logger) ([]client.Object, error) { - + log logr.Logger, +) ([]client.Object, error) { // This has to be the current hub storage version. It will need to be updated // if the hub storage version changes. typedObj, ok := obj.(*storage.StorageAccount) diff --git a/v2/api/subscription/customizations/alias_extensions.go b/v2/api/subscription/customizations/alias_extensions.go index 1fe7b7894c6..b361d520ab8 100644 --- a/v2/api/subscription/customizations/alias_extensions.go +++ b/v2/api/subscription/customizations/alias_extensions.go @@ -27,8 +27,8 @@ func (extension *AliasExtension) Delete( resolver *resolver.Resolver, armClient *genericarmclient.GenericClient, obj genruntime.ARMMetaObject, - next extensions.DeleteFunc) (ctrl.Result, error) { - + next extensions.DeleteFunc, +) (ctrl.Result, error) { // First cancel the subscription, then delete the alias typedObj, ok := obj.(*storage.Alias) if !ok { diff --git a/v2/azure-arm.yaml b/v2/azure-arm.yaml index 9d368a8ca01..32192c813cc 100644 --- a/v2/azure-arm.yaml +++ b/v2/azure-arm.yaml @@ -1109,6 +1109,8 @@ objectModelConfiguration: $supportedFrom: v2.0.0-alpha.2 $defaultAzureName: false RoleAssignmentProperties: + DelegatedManagedIdentityResourceId: + $armReference: false # Actually, this *IS* a resource id, but we want to avoid breaking changes so we're fibbing here RoleDefinitionId: $armReference: true PrincipalId: @@ -1119,6 +1121,8 @@ objectModelConfiguration: $supportedFrom: v2.4.0 $defaultAzureName: false RoleAssignmentProperties: + DelegatedManagedIdentityResourceId: + $armReference: false # Actually, this *IS* a resource id, but we want to avoid breaking changes so we're fibbing here RoleDefinitionId: $armReference: true PrincipalId: @@ -1754,6 +1758,9 @@ objectModelConfiguration: FlexibleServers_FirewallRule: $exportAs: FlexibleServersFirewallRule $supportedFrom: v2.0.0-alpha.2 + ServerProperties: + SourceServerResourceId: + $armReference: false # Actually, this *IS* a resource id, but we want to avoid breaking changes so we're fibbing here 2022-01-01: FlexibleServers_Administrator: $exportAs: FlexibleServersAdministrator @@ -2180,6 +2187,12 @@ objectModelConfiguration: AksNetworkingConfiguration: SubnetId: $armReference: true + IdentityForCmk: + UserAssignedIdentity: + $armReference: false # Actually, this *IS* a resource id, but we want to avoid breaking changes so we're fibbing here + KeyVaultProperties: + KeyVaultArmId: + $armReference: false # Actually, this *IS* a resource id, but we want to avoid breaking changes so we're fibbing here Kubernetes: ResourceId: $armReference: true diff --git a/v2/cmd/asoctl/cmd/import_azure_resource.go b/v2/cmd/asoctl/cmd/import_azure_resource.go index efb29fbe06e..5364d5afdb9 100644 --- a/v2/cmd/asoctl/cmd/import_azure_resource.go +++ b/v2/cmd/asoctl/cmd/import_azure_resource.go @@ -52,10 +52,9 @@ func newImportAzureResourceCommand() *cobra.Command { // importAzureResource imports an ARM resource and writes the YAML to stdout or a file func importAzureResource(ctx context.Context, armIDs []string, options importAzureResourceOptions) error { - log, progress := CreateLoggerAndProgressBar() - //TODO: Support other Azure clouds + // TODO: Support other Azure clouds activeCloud := cloud.AzurePublic creds, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { diff --git a/v2/cmd/asoctl/go.mod b/v2/cmd/asoctl/go.mod index 89782299a2c..d1d7cd18933 100644 --- a/v2/cmd/asoctl/go.mod +++ b/v2/cmd/asoctl/go.mod @@ -7,7 +7,7 @@ replace github.com/Azure/azure-service-operator/v2 => ../../ replace github.com/Azure/azure-service-operator/v2/tools/generator => ../generator require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 github.com/Azure/azure-service-operator/v2 v2.5.0 github.com/go-logr/logr v1.4.1 @@ -93,7 +93,7 @@ require ( github.com/rivo/uniseg v0.4.4 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/crypto v0.19.0 // indirect - golang.org/x/net v0.20.0 // indirect + golang.org/x/net v0.21.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.17.0 // indirect diff --git a/v2/cmd/asoctl/go.sum b/v2/cmd/asoctl/go.sum index 36f47120e64..e09cee864b4 100644 --- a/v2/cmd/asoctl/go.sum +++ b/v2/cmd/asoctl/go.sum @@ -1,5 +1,7 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 h1:c4k2FIYIh4xtwqrQwV0Ct1v5+ehlNXj5NI/MWVsiTkQ= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2/go.mod h1:5FDJtLEO/GxwNgUxbwrY3LP0pEoThTQJtk2oysdXHxM= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 h1:n1DH8TPV4qqPTje2RcUBYwtrTWlabVp4n46+74X2pn4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0/go.mod h1:HDcZnuGbiyppErN6lB+idp4CKhjbc8gwjto6OPpyggM= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ= @@ -63,6 +65,7 @@ github.com/evanphx/json-patch/v5 v5.8.1 h1:iPEdwg0XayoS+E7Mth9JxwUtOgyVxnDTXHtKh github.com/evanphx/json-patch/v5 v5.8.1/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -80,6 +83,7 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= @@ -118,14 +122,18 @@ github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s= github.com/jackc/pgx/v5 v5.5.3/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= @@ -148,6 +156,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= @@ -178,6 +187,7 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -208,6 +218,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -261,6 +273,7 @@ google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7 google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/v2/cmd/asoctl/internal/crd/cleaner.go b/v2/cmd/asoctl/internal/crd/cleaner.go index f5c83e52c3c..1076a3db515 100644 --- a/v2/cmd/asoctl/internal/crd/cleaner.go +++ b/v2/cmd/asoctl/internal/crd/cleaner.go @@ -41,7 +41,8 @@ func NewCleaner( apiExtensionsClient apiextensionsclient.CustomResourceDefinitionInterface, client client.Client, dryRun bool, - log logr.Logger) *Cleaner { + log logr.Logger, +) *Cleaner { migrationBackoff := wait.Backoff{ Duration: 2 * time.Second, // wait 2s between attempts, this will help us in a state of conflict. Steps: 3, // 3 retry on error attempts per object @@ -156,8 +157,8 @@ func (c *Cleaner) Run(ctx context.Context) error { func (c *Cleaner) updateStorageVersions( ctx context.Context, crd apiextensions.CustomResourceDefinition, - newStoredVersions []string) error { - + newStoredVersions []string, +) error { if c.dryRun { c.log.Info( "Would update storedVersions", diff --git a/v2/cmd/asoctl/internal/importing/find_resources_by_scope.go b/v2/cmd/asoctl/internal/importing/find_resources_by_scope.go index 09a83c86928..3f983a91855 100644 --- a/v2/cmd/asoctl/internal/importing/find_resources_by_scope.go +++ b/v2/cmd/asoctl/internal/importing/find_resources_by_scope.go @@ -37,7 +37,6 @@ func IsExtensionType(typeName string) bool { if !ok { // No known extension resource types return false - } return s.Contains(typeName) diff --git a/v2/cmd/asoctl/internal/importing/importable_resource.go b/v2/cmd/asoctl/internal/importing/importable_resource.go index b5f11466b8e..c9dc7dcf2cd 100644 --- a/v2/cmd/asoctl/internal/importing/importable_resource.go +++ b/v2/cmd/asoctl/internal/importing/importable_resource.go @@ -7,6 +7,7 @@ package importing import ( "context" + "github.com/go-logr/logr" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" diff --git a/v2/cmd/asoctl/internal/importing/resource_importer.go b/v2/cmd/asoctl/internal/importing/resource_importer.go index f2bc609ea02..cd4e9c388cd 100644 --- a/v2/cmd/asoctl/internal/importing/resource_importer.go +++ b/v2/cmd/asoctl/internal/importing/resource_importer.go @@ -48,7 +48,8 @@ func NewResourceImporter( scheme *runtime.Scheme, client *genericarmclient.GenericClient, log logr.Logger, - progress *mpb.Progress) *ResourceImporter { + progress *mpb.Progress, +) *ResourceImporter { return &ResourceImporter{ scheme: scheme, client: client, @@ -79,7 +80,6 @@ func (ri *ResourceImporter) AddARMID(armID string) error { func (ri *ResourceImporter) Import( ctx context.Context, ) (*ResourceImportResult, error) { - workers := 1 candidates := make(chan ImportableResource) // candidates that need to be deduped pending := make(chan ImportableResource) // importers that are pending import @@ -144,7 +144,7 @@ func (ri *ResourceImporter) queueUniqueImporters( var queue []ImportableResource var current ImportableResource = nil - var running = true + running := true for running { // Dequeue from our internal buffer if needed if current == nil && len(queue) > 0 { diff --git a/v2/go.mod b/v2/go.mod index 4e76ae81aa8..f797b40562b 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -39,6 +39,8 @@ require ( golang.org/x/exp v0.0.0-20240119083558-1b970713d09a golang.org/x/sync v0.6.0 golang.org/x/time v0.5.0 + gopkg.in/dnaeon/go-vcr.v3 v3.1.2 + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.29.2 k8s.io/apiextensions-apiserver v0.29.2 k8s.io/apimachinery v0.29.2 @@ -98,7 +100,6 @@ require ( google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/component-base v0.29.2 // indirect k8s.io/kube-openapi v0.0.0-20240117194847-208609032b15 // indirect diff --git a/v2/go.sum b/v2/go.sum index 30c8a44c2fc..4c195c9d9b4 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -254,6 +254,8 @@ google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/dnaeon/go-vcr.v3 v3.1.2 h1:F1smfXBqQqwpVifDfUBQG6zzaGjzT+EnVZakrOdr5wA= +gopkg.in/dnaeon/go-vcr.v3 v3.1.2/go.mod h1:2IMOnnlx9I6u9x+YBsM3tAMx6AlOxnJ0pWxQAzZ79Ag= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -270,6 +272,8 @@ k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= +k8s.io/component-base v0.29.1 h1:MUimqJPCRnnHsskTTjKD+IC1EHBbRCVyi37IoFBrkYw= +k8s.io/component-base v0.29.1/go.mod h1:fP9GFjxYrLERq1GcWWZAE3bqbNcDKDytn2srWuHTtKc= k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8= k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= diff --git a/v2/internal/config/vars.go b/v2/internal/config/vars.go index 9617d22f4a0..285d56f740e 100644 --- a/v2/internal/config/vars.go +++ b/v2/internal/config/vars.go @@ -19,9 +19,11 @@ import ( // These are hardcoded because the init function that initializes them in azcore isn't in /cloud it's in /arm which // we don't import. -var DefaultEndpoint = "https://management.azure.com" -var DefaultAudience = "https://management.core.windows.net/" -var DefaultAADAuthorityHost = "https://login.microsoftonline.com/" +var ( + DefaultEndpoint = "https://management.azure.com" + DefaultAudience = "https://management.core.windows.net/" + DefaultAADAuthorityHost = "https://login.microsoftonline.com/" +) // NOTE: Changes to documentation or available values here should be documented in Helm values.yaml as well @@ -112,7 +114,6 @@ func (v Values) String() string { // Cloud returns the cloud the configuration is using func (v Values) Cloud() cloud.Configuration { - // Special handling if we've got all the defaults just return the official public cloud // configuration hasDefaultAzureAuthorityHost := v.AzureAuthorityHost == "" || v.AzureAuthorityHost == DefaultAADAuthorityHost diff --git a/v2/internal/controllers/containerservice_managedcluster_crud_v1api20210501_test.go b/v2/internal/controllers/containerservice_managedcluster_crud_v1api20210501_test.go index bd1012a8c20..086f2a6e454 100644 --- a/v2/internal/controllers/containerservice_managedcluster_crud_v1api20210501_test.go +++ b/v2/internal/controllers/containerservice_managedcluster_crud_v1api20210501_test.go @@ -24,7 +24,7 @@ func Test_AKS_ManagedCluster_20210501_CRUD(t *testing.T) { rg := tc.CreateTestResourceGroupAndWait() region := to.Ptr("westus3") // TODO: the default test region of westus2 doesn't allow ds2_v2 at the moment - //region := tc.AzureRegion + // region := tc.AzureRegion adminUsername := "adminUser" sshPublicKey, err := tc.GenerateSSHKey(2048) diff --git a/v2/internal/controllers/containerservice_managedcluster_crud_v1api20230202preview_test.go b/v2/internal/controllers/containerservice_managedcluster_crud_v1api20230202preview_test.go index 1d3ce9f0abb..72d13db3eb5 100644 --- a/v2/internal/controllers/containerservice_managedcluster_crud_v1api20230202preview_test.go +++ b/v2/internal/controllers/containerservice_managedcluster_crud_v1api20230202preview_test.go @@ -90,7 +90,7 @@ func Test_AKS_ManagedCluster_20230202Preview_CRUD(t *testing.T) { func NewManagedCluster20230202preview(tc *testcommon.KubePerTestContext, rg *v1api20200601.ResourceGroup, adminUsername string, sshPublicKey *string) *aks.ManagedCluster { region := to.Ptr("westus3") // TODO: the default test region of westus2 doesn't allow ds2_v2 at the moment - //region := tc.AzureRegion + // region := tc.AzureRegion cluster := &aks.ManagedCluster{ ObjectMeta: tc.MakeObjectMeta("mc"), @@ -199,7 +199,6 @@ func AKS_ManagedCluster_TrustedAccessRoleBinding_20230102Preview_CRUD( resourceGroup *v1api20200601.ResourceGroup, cluster *aks.ManagedCluster, ) { - // Create a storage account and key vault to use for the workspace sa := newStorageAccount(tc, resourceGroup) tc.CreateResourceAndWait(sa) diff --git a/v2/internal/controllers/containerservice_managedcluster_crud_v1api20231001_test.go b/v2/internal/controllers/containerservice_managedcluster_crud_v1api20231001_test.go index afca4008304..1af8452d4ea 100644 --- a/v2/internal/controllers/containerservice_managedcluster_crud_v1api20231001_test.go +++ b/v2/internal/controllers/containerservice_managedcluster_crud_v1api20231001_test.go @@ -78,7 +78,7 @@ func Test_AKS_ManagedCluster_20231001_CRUD(t *testing.T) { func NewManagedCluster20231001(tc *testcommon.KubePerTestContext, rg *v1api20200601.ResourceGroup, adminUsername string, sshPublicKey *string) *aks.ManagedCluster { region := to.Ptr("westus3") // TODO: the default test region of westus2 doesn't allow ds2_v2 at the moment - //region := tc.AzureRegion + // region := tc.AzureRegion cluster := &aks.ManagedCluster{ ObjectMeta: tc.MakeObjectMeta("mc"), diff --git a/v2/internal/controllers/containerservice_managedcluster_crud_v1api20231102preview_test.go b/v2/internal/controllers/containerservice_managedcluster_crud_v1api20231102preview_test.go index e25370de220..a9a8021af85 100644 --- a/v2/internal/controllers/containerservice_managedcluster_crud_v1api20231102preview_test.go +++ b/v2/internal/controllers/containerservice_managedcluster_crud_v1api20231102preview_test.go @@ -78,7 +78,7 @@ func Test_AKS_ManagedCluster_20231102preview_CRUD(t *testing.T) { func NewManagedCluster20231102preview(tc *testcommon.KubePerTestContext, rg *v1api20200601.ResourceGroup, adminUsername string, sshPublicKey *string) *aks.ManagedCluster { region := to.Ptr("westus3") // TODO: the default test region of westus2 doesn't allow ds2_v2 at the moment - //region := tc.AzureRegion + // region := tc.AzureRegion cluster := &aks.ManagedCluster{ ObjectMeta: tc.MakeObjectMeta("mc"), diff --git a/v2/internal/controllers/controller_resources.go b/v2/internal/controllers/controller_resources.go index d19b2c2b469..16523d60656 100644 --- a/v2/internal/controllers/controller_resources.go +++ b/v2/internal/controllers/controller_resources.go @@ -50,8 +50,8 @@ func GetKnownStorageTypes( credentialProvider identity.CredentialProvider, kubeClient kubeclient.Client, positiveConditions *conditions.PositiveConditionBuilder, - options generic.Options) ([]*registration.StorageType, error) { - + options generic.Options, +) ([]*registration.StorageType, error) { resourceResolver := resolver.NewResolver(kubeClient) knownStorageTypes, err := getGeneratedStorageTypes(mgr, armConnectionFactory, kubeClient, resourceResolver, positiveConditions, options) if err != nil { @@ -152,7 +152,8 @@ func getGeneratedStorageTypes( kubeClient kubeclient.Client, resourceResolver *resolver.Resolver, positiveConditions *conditions.PositiveConditionBuilder, - options generic.Options) ([]*registration.StorageType, error) { + options generic.Options, +) ([]*registration.StorageType, error) { knownStorageTypes := getKnownStorageTypes() err := resourceResolver.IndexStorageTypes(mgr.GetScheme(), knownStorageTypes) @@ -195,7 +196,8 @@ func augmentWithARMReconciler( positiveConditions *conditions.PositiveConditionBuilder, options generic.Options, extension genruntime.ResourceExtension, - t *registration.StorageType) { + t *registration.StorageType, +) { t.Reconciler = arm.NewAzureDeploymentReconciler( armConnectionFactory, kubeClient, @@ -206,7 +208,6 @@ func augmentWithARMReconciler( } func augmentWithPredicate(t *registration.StorageType) { - t.Predicate = makeStandardPredicate() } @@ -266,7 +267,6 @@ func CreateScheme() *runtime.Scheme { // GetResourceExtensions returns a map between resource and resource extension func GetResourceExtensions(scheme *runtime.Scheme) (map[schema.GroupVersionKind]genruntime.ResourceExtension, error) { - extensionMapping := make(map[schema.GroupVersionKind]genruntime.ResourceExtension) for _, extension := range getResourceExtensions() { diff --git a/v2/internal/controllers/crd_apimanagement_20220801_test.go b/v2/internal/controllers/crd_apimanagement_20220801_test.go index fd9b3ae8a43..dbc2711f809 100644 --- a/v2/internal/controllers/crd_apimanagement_20220801_test.go +++ b/v2/internal/controllers/crd_apimanagement_20220801_test.go @@ -170,7 +170,7 @@ func APIM_Subscription_CRUD(tc *testcommon.KubePerTestContext, service client.Ob // There should be no secrets at this point secretList := &v1.SecretList{} tc.ListResources(secretList, client.InNamespace(tc.Namespace)) - tc.Expect(secretList.Items).To(HaveLen(1)) //for secret created for authorization provider resource + tc.Expect(secretList.Items).To(HaveLen(1)) // for secret created for authorization provider resource // Run sub-tests on subscription in sequence tc.RunSubtests( @@ -257,7 +257,6 @@ func APIM_Policy_CRUD(tc *testcommon.KubePerTestContext, service client.Object) } func APIM_PolicyFragment_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { - // Add a simple Policy Fragment policyFragment := apim.PolicyFragment{ ObjectMeta: tc.MakeObjectMetaWithName(tc.Namer.GenerateName("policyfragment")), @@ -278,7 +277,6 @@ func APIM_PolicyFragment_CRUD(tc *testcommon.KubePerTestContext, service client. } func APIM_Product_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { - productName := tc.Namer.GenerateName("cust1") // Now add a product product := apim.Product{ @@ -307,7 +305,6 @@ func APIM_Product_CRUD(tc *testcommon.KubePerTestContext, service client.Object) } func APIM_Product_Policy_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { - productName := tc.Namer.GenerateName("product1") // Now add a product product := apim.Product{ @@ -347,7 +344,6 @@ func APIM_Product_Policy_CRUD(tc *testcommon.KubePerTestContext, service client. } func APIM_Product_Api_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { - productName := tc.Namer.GenerateName("product2") product := apim.Product{ ObjectMeta: tc.MakeObjectMetaWithName(productName), @@ -404,7 +400,8 @@ func APIM_Product_Api_CRUD(tc *testcommon.KubePerTestContext, service client.Obj }, Protocols: []apim.ApiCreateOrUpdateProperties_Protocols{ - apim.ApiCreateOrUpdateProperties_Protocols_Https}, + apim.ApiCreateOrUpdateProperties_Protocols_Https, + }, TermsOfServiceUrl: to.Ptr("https://www.bing.com/tos"), Type: to.Ptr(apim.ApiCreateOrUpdateProperties_Type_Http), @@ -435,7 +432,6 @@ func APIM_Product_Api_CRUD(tc *testcommon.KubePerTestContext, service client.Obj } func APIM_Api_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { - versionSet := apim.ApiVersionSet{ ObjectMeta: tc.MakeObjectMetaWithName(tc.Namer.GenerateName("vs")), Spec: apim.Service_ApiVersionSet_Spec{ @@ -475,7 +471,8 @@ func APIM_Api_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { }, Protocols: []apim.ApiCreateOrUpdateProperties_Protocols{ - apim.ApiCreateOrUpdateProperties_Protocols_Https}, + apim.ApiCreateOrUpdateProperties_Protocols_Https, + }, TermsOfServiceUrl: to.Ptr("https://www.bing.com/tos"), Type: to.Ptr(apim.ApiCreateOrUpdateProperties_Type_Http), diff --git a/v2/internal/controllers/crd_apimanagement_20230501preview_test.go b/v2/internal/controllers/crd_apimanagement_20230501preview_test.go index 360ba5d37b7..e7c87f14890 100644 --- a/v2/internal/controllers/crd_apimanagement_20230501preview_test.go +++ b/v2/internal/controllers/crd_apimanagement_20230501preview_test.go @@ -160,7 +160,7 @@ func APIM_Subscription20230501preview_CRUD(tc *testcommon.KubePerTestContext, se // There should be no secrets at this point secretList := &v1.SecretList{} tc.ListResources(secretList, client.InNamespace(tc.Namespace)) - tc.Expect(secretList.Items).To(HaveLen(1)) //for secret created for authorization provider resource + tc.Expect(secretList.Items).To(HaveLen(1)) // for secret created for authorization provider resource // Run sub-tests on subscription in sequence tc.RunSubtests( @@ -268,7 +268,6 @@ func APIM_PolicyFragment20230501preview_CRUD(tc *testcommon.KubePerTestContext, // Currently not called as we need to find a way to delete the subscription func APIM_Product20230501preview_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { - productName := tc.Namer.GenerateName("cust1") // Now add a product product := apim.Product{ @@ -297,7 +296,6 @@ func APIM_Product20230501preview_CRUD(tc *testcommon.KubePerTestContext, service } func APIM_Product_Policy20230501preview_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { - productName := tc.Namer.GenerateName("product1") // Now add a product product := apim.Product{ @@ -337,7 +335,6 @@ func APIM_Product_Policy20230501preview_CRUD(tc *testcommon.KubePerTestContext, } func APIM_Product_Api20230501preview_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { - productName := tc.Namer.GenerateName("product2") product := apim.Product{ ObjectMeta: tc.MakeObjectMetaWithName(productName), @@ -394,7 +391,8 @@ func APIM_Product_Api20230501preview_CRUD(tc *testcommon.KubePerTestContext, ser }, Protocols: []apim.ApiCreateOrUpdateProperties_Protocols{ - apim.ApiCreateOrUpdateProperties_Protocols_Https}, + apim.ApiCreateOrUpdateProperties_Protocols_Https, + }, TermsOfServiceUrl: to.Ptr("https://www.bing.com/tos"), Type: to.Ptr(apim.ApiCreateOrUpdateProperties_Type_Http), @@ -425,7 +423,6 @@ func APIM_Product_Api20230501preview_CRUD(tc *testcommon.KubePerTestContext, ser } func APIM_Api20230501preview_CRUD(tc *testcommon.KubePerTestContext, service client.Object) { - versionSet := apim.ApiVersionSet{ ObjectMeta: tc.MakeObjectMetaWithName(tc.Namer.GenerateName("vs")), Spec: apim.Service_ApiVersionSet_Spec{ @@ -465,7 +462,8 @@ func APIM_Api20230501preview_CRUD(tc *testcommon.KubePerTestContext, service cli }, Protocols: []apim.ApiCreateOrUpdateProperties_Protocols{ - apim.ApiCreateOrUpdateProperties_Protocols_Https}, + apim.ApiCreateOrUpdateProperties_Protocols_Https, + }, TermsOfServiceUrl: to.Ptr("https://www.bing.com/tos"), Type: to.Ptr(apim.ApiCreateOrUpdateProperties_Type_Http), diff --git a/v2/internal/controllers/crd_appconfiguration_configstore_test.go b/v2/internal/controllers/crd_appconfiguration_configstore_test.go index 33dad78bb24..018369d8fe5 100644 --- a/v2/internal/controllers/crd_appconfiguration_configstore_test.go +++ b/v2/internal/controllers/crd_appconfiguration_configstore_test.go @@ -76,7 +76,6 @@ func ConfigStore_WriteSecrets(tc *testcommon.KubePerTestContext, cs *appconfig.C old := cs.DeepCopy() csKeysSecret := "cskeyssecret" cs.Spec.OperatorSpec = &appconfig.ConfigurationStoreOperatorSpec{ - Secrets: &appconfig.ConfigurationStoreOperatorSecrets{ PrimaryConnectionString: &genruntime.SecretDestination{Name: csKeysSecret, Key: "primaryConnectionString"}, SecondaryConnectionString: &genruntime.SecretDestination{Name: csKeysSecret, Key: "secondaryConnectionString"}, diff --git a/v2/internal/controllers/crd_compute_vmss_20201201_test.go b/v2/internal/controllers/crd_compute_vmss_20201201_test.go index db3144bb0c1..651d6d2011e 100644 --- a/v2/internal/controllers/crd_compute_vmss_20201201_test.go +++ b/v2/internal/controllers/crd_compute_vmss_20201201_test.go @@ -313,5 +313,4 @@ func VMSS_Extension_20201201_CRUD(tc *testcommon.KubePerTestContext, vmss *compu tc.Expect(err).ToNot(HaveOccurred()) tc.Expect(retryAfter).To(BeZero()) tc.Expect(exists).To(BeFalse()) - } diff --git a/v2/internal/controllers/crd_devices_iothub_test.go b/v2/internal/controllers/crd_devices_iothub_test.go index 05164da26a7..e1c74820274 100644 --- a/v2/internal/controllers/crd_devices_iothub_test.go +++ b/v2/internal/controllers/crd_devices_iothub_test.go @@ -61,7 +61,6 @@ func Test_Devices_IotHub_CRUD(t *testing.T) { tc.Expect(err).ToNot(HaveOccurred()) tc.Expect(retryAfter).To(BeZero()) tc.Expect(exists).To(BeFalse()) - } func IotHub_WriteSecrets(tc *testcommon.KubePerTestContext, iotHub *devices.IotHub) { @@ -95,5 +94,4 @@ func IotHub_WriteSecrets(tc *testcommon.KubePerTestContext, iotHub *devices.IotH "registryReadWriteSecondaryKey", "servicePrimaryKey", "serviceSecondaryKey") - } diff --git a/v2/internal/controllers/crd_insights_metricalert_test.go b/v2/internal/controllers/crd_insights_metricalert_test.go index c7c6ef09334..4cabc41fb69 100644 --- a/v2/internal/controllers/crd_insights_metricalert_test.go +++ b/v2/internal/controllers/crd_insights_metricalert_test.go @@ -78,5 +78,4 @@ func Test_Insights_MetricAlert_CRUD(t *testing.T) { tc.Expect(err).ToNot(HaveOccurred()) tc.Expect(retryAfter).To(BeZero()) tc.Expect(exists).To(BeFalse()) - } diff --git a/v2/internal/controllers/crd_insights_scheduledqueryrule_test.go b/v2/internal/controllers/crd_insights_scheduledqueryrule_test.go index b46803ec81e..16bb484d63d 100644 --- a/v2/internal/controllers/crd_insights_scheduledqueryrule_test.go +++ b/v2/internal/controllers/crd_insights_scheduledqueryrule_test.go @@ -75,5 +75,4 @@ func Test_Insights_ScheduledQueryRule_CRUD(t *testing.T) { tc.Expect(err).ToNot(HaveOccurred()) tc.Expect(retryAfter).To(BeZero()) tc.Expect(exists).To(BeFalse()) - } diff --git a/v2/internal/controllers/crd_machinelearningservices_test.go b/v2/internal/controllers/crd_machinelearningservices_test.go index 576954f371b..62168987077 100644 --- a/v2/internal/controllers/crd_machinelearningservices_test.go +++ b/v2/internal/controllers/crd_machinelearningservices_test.go @@ -130,7 +130,6 @@ func WorkspaceConnection_CRUD(tc *testcommon.KubePerTestContext, workspaces *mac } func WorkspaceCompute_CRUD(tc *testcommon.KubePerTestContext, owner *genruntime.KnownResourceReference, rg *resources.ResourceGroup) { - vnet := newVMVirtualNetwork(tc, testcommon.AsOwner(rg)) tc.CreateResourceAndWait(vnet) @@ -158,7 +157,6 @@ func WorkspaceCompute_CRUD(tc *testcommon.KubePerTestContext, owner *genruntime. tc.DeleteResourceAndWait(vm) tc.DeleteResourceAndWait(networkInterface) tc.DeleteResourceAndWait(vnet) - } func newWorkspacesCompute(tc *testcommon.KubePerTestContext, owner *genruntime.KnownResourceReference, vm *v1api20201201.VirtualMachine, secret genruntime.SecretReference) *machinelearningservices.WorkspacesCompute { @@ -198,7 +196,6 @@ func newWorkspacesCompute(tc *testcommon.KubePerTestContext, owner *genruntime.K } func newVMNetworkInterfaceWithPublicIP(tc *testcommon.KubePerTestContext, owner *genruntime.KnownResourceReference, subnet *network.VirtualNetworksSubnet, publicIP *network.PublicIPAddress, nsg *network.NetworkSecurityGroup) *network.NetworkInterface { - dynamic := network.IPAllocationMethod_Dynamic return &network.NetworkInterface{ ObjectMeta: tc.MakeObjectMeta("nic"), @@ -233,7 +230,6 @@ func newNetworkSecurityGroup(tc *testcommon.KubePerTestContext, owner *genruntim Owner: owner, }, } - } func newNetworkSecurityGroupRule(tc *testcommon.KubePerTestContext, owner *genruntime.KnownResourceReference) *network.NetworkSecurityGroupsSecurityRule { diff --git a/v2/internal/controllers/crd_managedidentity_userassignedidentity_test.go b/v2/internal/controllers/crd_managedidentity_userassignedidentity_test.go index 65180112a5f..997fd759bae 100644 --- a/v2/internal/controllers/crd_managedidentity_userassignedidentity_test.go +++ b/v2/internal/controllers/crd_managedidentity_userassignedidentity_test.go @@ -69,7 +69,6 @@ func Test_ManagedIdentity_UserAssignedIdentity_CRUD(t *testing.T) { } func FederatedIdentityCredentials_CRUD(tc *testcommon.KubePerTestContext, umi *managedidentity2018.UserAssignedIdentity) { - fic := &managedidentity2022.FederatedIdentityCredential{ ObjectMeta: tc.MakeObjectMeta("fic"), Spec: managedidentity2022.UserAssignedIdentities_FederatedIdentityCredential_Spec{ diff --git a/v2/internal/controllers/crd_mariadb_server_test.go b/v2/internal/controllers/crd_mariadb_server_test.go index b8e429070a0..c0c677b0b7c 100644 --- a/v2/internal/controllers/crd_mariadb_server_test.go +++ b/v2/internal/controllers/crd_mariadb_server_test.go @@ -101,7 +101,8 @@ func Test_MariaDB_Server_CRUD(t *testing.T) { func createPasswordSecret( name string, key string, - tc *testcommon.KubePerTestContext) genruntime.SecretReference { + tc *testcommon.KubePerTestContext, +) genruntime.SecretReference { password := tc.Namer.GeneratePasswordOfLength(40) secret := &v1.Secret{ diff --git a/v2/internal/controllers/crd_networking_dnszone_test.go b/v2/internal/controllers/crd_networking_dnszone_test.go index e88d19a95d6..c0bf7f98989 100644 --- a/v2/internal/controllers/crd_networking_dnszone_test.go +++ b/v2/internal/controllers/crd_networking_dnszone_test.go @@ -43,7 +43,7 @@ func Test_Networking_DnsZone_CRUD(t *testing.T) { tc.DeleteResourceAndWait(zone) - //Ensure that the resource was really deleted in Azure + // Ensure that the resource was really deleted in Azure exists, retryAfter, err := tc.AzureClient.CheckExistenceWithGetByID(tc.Ctx, armId, string(network.APIVersion_Value)) tc.Expect(err).ToNot(HaveOccurred()) tc.Expect(retryAfter).To(BeZero()) diff --git a/v2/internal/controllers/crd_networking_loadbalancer_test.go b/v2/internal/controllers/crd_networking_loadbalancer_test.go index 7d47b02a4ce..f5349f663f7 100644 --- a/v2/internal/controllers/crd_networking_loadbalancer_test.go +++ b/v2/internal/controllers/crd_networking_loadbalancer_test.go @@ -118,7 +118,6 @@ func Test_Networking_LoadBalancer_CRUD(t *testing.T) { } func LoadBalancer_InboundNatRule_CRUD(tc *testcommon.KubePerTestContext, lb *network.LoadBalancer, frontendIPConfigurationARMID string) { - natRule := &network.LoadBalancersInboundNatRule{ ObjectMeta: tc.MakeObjectMeta("rule"), Spec: network.LoadBalancers_InboundNatRule_Spec{ @@ -145,7 +144,6 @@ func LoadBalancer_InboundNatRule_CRUD(tc *testcommon.KubePerTestContext, lb *net tc.Expect(natRule.Status.FrontendPort).To(BeEquivalentTo(&port)) tc.DeleteResourceAndWait(natRule) - } // TODO: This is still really awkward diff --git a/v2/internal/controllers/crd_networking_natgateway_test.go b/v2/internal/controllers/crd_networking_natgateway_test.go index 9f1cfaac600..a190305da2f 100644 --- a/v2/internal/controllers/crd_networking_natgateway_test.go +++ b/v2/internal/controllers/crd_networking_natgateway_test.go @@ -60,5 +60,4 @@ func Test_Networking_NatGateway_CRUD(t *testing.T) { tc.Expect(err).ToNot(HaveOccurred()) tc.Expect(retryAfter).To(BeZero()) tc.Expect(exists).To(BeFalse()) - } diff --git a/v2/internal/controllers/crd_networking_privatednszone_test.go b/v2/internal/controllers/crd_networking_privatednszone_test.go index 2b40859f740..0c4e64a03cc 100644 --- a/v2/internal/controllers/crd_networking_privatednszone_test.go +++ b/v2/internal/controllers/crd_networking_privatednszone_test.go @@ -51,7 +51,7 @@ func Test_Networking_PrivateDnsZone_CRUD(t *testing.T) { tc.DeleteResourceAndWait(zone) - //Ensure that the resource was really deleted in Azure + // Ensure that the resource was really deleted in Azure armId, hasID := genruntime.GetResourceID(zone) tc.Expect(hasID).To(BeTrue()) diff --git a/v2/internal/controllers/crd_networking_publicipprefix_test.go b/v2/internal/controllers/crd_networking_publicipprefix_test.go index aa42fc64648..9574edc5cdc 100644 --- a/v2/internal/controllers/crd_networking_publicipprefix_test.go +++ b/v2/internal/controllers/crd_networking_publicipprefix_test.go @@ -54,5 +54,4 @@ func Test_Networking_PublicIPPrefix_CRUD(t *testing.T) { tc.Expect(err).ToNot(HaveOccurred()) tc.Expect(retryAfter).To(BeZero()) tc.Expect(exists).To(BeFalse()) - } diff --git a/v2/internal/controllers/crd_networking_trafficmanagerprofile_test.go b/v2/internal/controllers/crd_networking_trafficmanagerprofile_test.go index c644eadb2dd..be9475ffef1 100644 --- a/v2/internal/controllers/crd_networking_trafficmanagerprofile_test.go +++ b/v2/internal/controllers/crd_networking_trafficmanagerprofile_test.go @@ -43,7 +43,8 @@ func Test_Networking_TrafficManagerProfile(t *testing.T) { Name: configName, Key: configKey, }, - }}, + }, + }, Owner: testcommon.AsOwner(rg), TrafficRoutingMethod: to.Ptr(network.ProfileProperties_TrafficRoutingMethod_Performance), }, @@ -90,7 +91,6 @@ func Test_Networking_TrafficManagerProfile(t *testing.T) { tc.Expect(err).ToNot(HaveOccurred()) tc.Expect(retryAfter).To(BeZero()) tc.Expect(exists).To(BeFalse()) - } func Networking_TrafficManagerProfiles_NestedEndpoint(tc *testcommon.KubePerTestContext, rg *resources.ResourceGroup, tmp *network.TrafficManagerProfile) { diff --git a/v2/internal/controllers/crd_networking_virtualnetworkgateway_test.go b/v2/internal/controllers/crd_networking_virtualnetworkgateway_test.go index ed842f7e546..33798bb4ab4 100644 --- a/v2/internal/controllers/crd_networking_virtualnetworkgateway_test.go +++ b/v2/internal/controllers/crd_networking_virtualnetworkgateway_test.go @@ -40,7 +40,6 @@ func Test_Networking_VirtualNetworkGateway_CRUD(t *testing.T) { tc.CreateResourceAndWait(gateway) tc.DeleteResourceAndWait(gateway) - } func newVnetGateway(tc *testcommon.KubePerTestContext, publicIPAddress *network.PublicIPAddress, subnet *network.VirtualNetworksSubnet, rg *resources.ResourceGroup) *network.VirtualNetworkGateway { diff --git a/v2/internal/controllers/crd_sql_server_test.go b/v2/internal/controllers/crd_sql_server_test.go index 05fb3446aa7..f73e6486dfc 100644 --- a/v2/internal/controllers/crd_sql_server_test.go +++ b/v2/internal/controllers/crd_sql_server_test.go @@ -150,7 +150,7 @@ func SQL_Server_ConnectionPolicy_CRUD(tc *testcommon.KubePerTestContext, server tc.Expect(policy.Status.Id).ToNot(BeNil()) // TODO: Delete is not allowed for this resource - //tc.DeleteResourceAndWait(policy) + // tc.DeleteResourceAndWait(policy) } func SQL_Server_AdvancedThreatProtection_CRUD(tc *testcommon.KubePerTestContext, server *sql.Server) { @@ -210,12 +210,12 @@ func SQL_Server_VulnerabilityAssessments_CRUD(tc *testcommon.KubePerTestContext, tc.DeleteResourceAndWait(vulnerabilityAssessment) // TODO: It seems like delete of this resource isn't actually honored by the service - it's accepted but doesn't remove the resource - //exists, _, err := tc.AzureClient.HeadByID( + // exists, _, err := tc.AzureClient.HeadByID( // tc.Ctx, // armId, // string(sql.APIVersion_Value)) - //tc.Expect(err).ToNot(HaveOccurred()) - //tc.Expect(exists).To(BeFalse()) + // tc.Expect(err).ToNot(HaveOccurred()) + // tc.Expect(exists).To(BeFalse()) } func SQL_Server_FirewallRules_CRUD(tc *testcommon.KubePerTestContext, server *sql.Server) { @@ -525,12 +525,12 @@ func SQL_Database_VulnerabilityAssessment_CRUD(tc *testcommon.KubePerTestContext tc.DeleteResourceAndWait(vulnerabilityAssessment) // TODO: It seems like delete of this resource isn't actually honored by the service - it's accepted but doesn't remove the resource - //exists, _, err := tc.AzureClient.HeadByID( + // exists, _, err := tc.AzureClient.HeadByID( // tc.Ctx, // armId, // string(sql.APIVersion_Value)) - //tc.Expect(err).ToNot(HaveOccurred()) - //tc.Expect(exists).To(BeFalse()) + // tc.Expect(err).ToNot(HaveOccurred()) + // tc.Expect(exists).To(BeFalse()) } func SQL_Database_AuditingSetting_CRUD(tc *testcommon.KubePerTestContext, db *sql.ServersDatabase, storageDetails vulnStorageAccountDetails) { diff --git a/v2/internal/controllers/crd_subscription_alias_test.go b/v2/internal/controllers/crd_subscription_alias_test.go index 001d01c57f3..7a7d13ecba0 100644 --- a/v2/internal/controllers/crd_subscription_alias_test.go +++ b/v2/internal/controllers/crd_subscription_alias_test.go @@ -12,7 +12,7 @@ import ( authorization "github.com/Azure/azure-service-operator/v2/api/authorization/v1api20200801preview" subscription "github.com/Azure/azure-service-operator/v2/api/subscription/v1api20211001" - "github.com/Azure/azure-service-operator/v2/internal/testcommon" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/creds" "github.com/Azure/azure-service-operator/v2/internal/util/to" "github.com/Azure/azure-service-operator/v2/pkg/genruntime" ) @@ -32,7 +32,7 @@ func Test_SubscriptionAndAlias_CRUD(t *testing.T) { tc := globalTestContext.ForTest(t) if tc.AzureBillingInvoiceID == "" { - t.Fatalf("%q enviornment variable must be set", testcommon.TestBillingIDVar) + t.Fatalf("%q enviornment variable must be set", creds.TestBillingIDVar) } // TODO: Once ManagedIdentity is registered in our sub, we can use this instead of the hardcoded identity below @@ -74,7 +74,7 @@ func Test_SubscriptionAndAlias_CRUD(t *testing.T) { ObjectMeta: tc.MakeObjectMetaWithName(roleAssignmentGUID.String()), Spec: authorization.RoleAssignment_Spec{ Owner: tc.AsExtensionOwner(sub), - //PrincipalId: mi.Status.PrincipalId, + // PrincipalId: mi.Status.PrincipalId, PrincipalId: to.Ptr("1605884e-16c3-4fc0-bf09-4220deecef02"), // 1605884e-16c3-4fc0-bf09-4220deecef02 == my user in PPE RoleDefinitionReference: &genruntime.ResourceReference{ ARMID: "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", // This is owner @@ -90,5 +90,4 @@ func Test_SubscriptionAndAlias_CRUD(t *testing.T) { tc.Expect(err).ToNot(HaveOccurred()) tc.Expect(retryAfter).To(BeZero()) tc.Expect(exists).To(BeFalse()) - } diff --git a/v2/internal/controllers/crd_synapse_test.go b/v2/internal/controllers/crd_synapse_test.go index 5a6fccbd775..6a24bf45e60 100644 --- a/v2/internal/controllers/crd_synapse_test.go +++ b/v2/internal/controllers/crd_synapse_test.go @@ -60,7 +60,9 @@ func Test_Workspace_BigDataPool(t *testing.T) { }, Location: to.Ptr("eastus2"), Owner: testcommon.AsOwner(rg), - Tags: map[string]string{"cheese": "blue"}, + Tags: map[string]string{ + "cheese": "blue", + }, }, } @@ -68,6 +70,7 @@ func Test_Workspace_BigDataPool(t *testing.T) { tc.Expect(ws.Status.Id).ToNot(BeNil()) wsArmId := *ws.Status.Id + // Perform a simple patch old := ws.DeepCopy() ws.Spec.Tags["cheese"] = "époisses" diff --git a/v2/internal/controllers/crd_web_serverfarm_test.go b/v2/internal/controllers/crd_web_serverfarm_test.go index f7d10a243c0..ff19e329828 100644 --- a/v2/internal/controllers/crd_web_serverfarm_test.go +++ b/v2/internal/controllers/crd_web_serverfarm_test.go @@ -44,7 +44,6 @@ func Test_Web_ServerFarm_CRUD(t *testing.T) { string(v1api20220301.APIVersion_Value)) tc.Expect(err).ToNot(gomega.HaveOccurred()) tc.Expect(exists).To(gomega.BeFalse()) - } func newServerFarm(tc *testcommon.KubePerTestContext, rg *resources.ResourceGroup, location string) *v1api20220301.ServerFarm { diff --git a/v2/internal/controllers/dbforpostgresql_flexibleserver_crud_v1api20210601_test.go b/v2/internal/controllers/dbforpostgresql_flexibleserver_crud_v1api20210601_test.go index 477dd623177..3c7c8ff25d7 100644 --- a/v2/internal/controllers/dbforpostgresql_flexibleserver_crud_v1api20210601_test.go +++ b/v2/internal/controllers/dbforpostgresql_flexibleserver_crud_v1api20210601_test.go @@ -29,7 +29,7 @@ func Test_DBForPostgreSQL_FlexibleServer_20210601_CRUD(t *testing.T) { ctx := context.Background() tc := globalTestContext.ForTest(t) - //location := tc.AzureRegion Capacity crunch in West US 2 makes this not work when live + // location := tc.AzureRegion Capacity crunch in West US 2 makes this not work when live location := "eastus" rg := tc.CreateTestResourceGroupAndWait() diff --git a/v2/internal/controllers/dbforpostgresql_flexibleserver_crud_v1api20221201_test.go b/v2/internal/controllers/dbforpostgresql_flexibleserver_crud_v1api20221201_test.go index 06ccdde0b5c..119ab32ea43 100644 --- a/v2/internal/controllers/dbforpostgresql_flexibleserver_crud_v1api20221201_test.go +++ b/v2/internal/controllers/dbforpostgresql_flexibleserver_crud_v1api20221201_test.go @@ -29,7 +29,7 @@ func Test_DBForPostgreSQL_FlexibleServer_20221201_CRUD(t *testing.T) { ctx := context.Background() tc := globalTestContext.ForTest(t) - //location := tc.AzureRegion Capacity crunch in West US 2 makes this not work when live + // location := tc.AzureRegion Capacity crunch in West US 2 makes this not work when live location := "eastus" rg := tc.CreateTestResourceGroupAndWait() diff --git a/v2/internal/controllers/owner_arm_id_negative_test.go b/v2/internal/controllers/owner_arm_id_negative_test.go index 53edc690d6d..d0c128516d5 100644 --- a/v2/internal/controllers/owner_arm_id_negative_test.go +++ b/v2/internal/controllers/owner_arm_id_negative_test.go @@ -12,6 +12,7 @@ import ( . "github.com/onsi/gomega" "github.com/Azure/azure-service-operator/v2/internal/testcommon" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/creds" "github.com/Azure/azure-service-operator/v2/pkg/common/annotations" ) @@ -76,7 +77,7 @@ func Test_OwnerIsARMIDFromDifferentSubscription_ResourceFails(t *testing.T) { uuid, err := tc.Namer.GenerateUUID() tc.Expect(err).ToNot(HaveOccurred()) - scopedCredentialSecret := testcommon.NewScopedServicePrincipalSecret( + scopedCredentialSecret := creds.NewScopedServicePrincipalSecret( uuid.String(), tc.AzureTenant, cfg.ClientID, diff --git a/v2/internal/controllers/recordings/TestOperatorMode_Watchers.yaml b/v2/internal/controllers/recordings/TestOperatorMode_Watchers.yaml index e69de29bb2d..2797c38e00e 100644 --- a/v2/internal/controllers/recordings/TestOperatorMode_Watchers.yaml +++ b/v2/internal/controllers/recordings/TestOperatorMode_Watchers.yaml @@ -0,0 +1,3 @@ +--- +version: 2 +interactions: [] diff --git a/v2/internal/controllers/recordings/TestOperatorMode_Webhooks.yaml b/v2/internal/controllers/recordings/TestOperatorMode_Webhooks.yaml index e69de29bb2d..2797c38e00e 100644 --- a/v2/internal/controllers/recordings/TestOperatorMode_Webhooks.yaml +++ b/v2/internal/controllers/recordings/TestOperatorMode_Webhooks.yaml @@ -0,0 +1,3 @@ +--- +version: 2 +interactions: [] diff --git a/v2/internal/controllers/recordings/Test_SameObjectHasTwoSecretsWritingToSameDestination_RejectedByWebhook.yaml b/v2/internal/controllers/recordings/Test_SameObjectHasTwoSecretsWritingToSameDestination_RejectedByWebhook.yaml index e69de29bb2d..2797c38e00e 100644 --- a/v2/internal/controllers/recordings/Test_SameObjectHasTwoSecretsWritingToSameDestination_RejectedByWebhook.yaml +++ b/v2/internal/controllers/recordings/Test_SameObjectHasTwoSecretsWritingToSameDestination_RejectedByWebhook.yaml @@ -0,0 +1,3 @@ +--- +version: 2 +interactions: [] diff --git a/v2/internal/controllers/recordings/Test_Samples_CreationAndDeletion/Test_Dbforpostgresql_v1api_CreationAndDeletion.yaml b/v2/internal/controllers/recordings/Test_Samples_CreationAndDeletion/Test_Dbforpostgresql_v1api_CreationAndDeletion.yaml index e69de29bb2d..2797c38e00e 100644 --- a/v2/internal/controllers/recordings/Test_Samples_CreationAndDeletion/Test_Dbforpostgresql_v1api_CreationAndDeletion.yaml +++ b/v2/internal/controllers/recordings/Test_Samples_CreationAndDeletion/Test_Dbforpostgresql_v1api_CreationAndDeletion.yaml @@ -0,0 +1,3 @@ +--- +version: 2 +interactions: [] diff --git a/v2/internal/controllers/recordings/Test_Samples_CreationAndDeletion/Test_V1api_v1api_CreationAndDeletion.yaml b/v2/internal/controllers/recordings/Test_Samples_CreationAndDeletion/Test_V1api_v1api_CreationAndDeletion.yaml index e69de29bb2d..2797c38e00e 100644 --- a/v2/internal/controllers/recordings/Test_Samples_CreationAndDeletion/Test_V1api_v1api_CreationAndDeletion.yaml +++ b/v2/internal/controllers/recordings/Test_Samples_CreationAndDeletion/Test_V1api_v1api_CreationAndDeletion.yaml @@ -0,0 +1,3 @@ +--- +version: 2 +interactions: [] diff --git a/v2/internal/controllers/samples_test.go b/v2/internal/controllers/samples_test.go index 43d53d6749a..83f68088899 100644 --- a/v2/internal/controllers/samples_test.go +++ b/v2/internal/controllers/samples_test.go @@ -62,7 +62,6 @@ func Test_Samples_CreationAndDeletion(t *testing.T) { _ = filepath.WalkDir(samplesPath, func(filePath string, info os.DirEntry, err error) error { - if info.IsDir() && !testcommon.IsFolderExcluded(filePath, skipTests) { basePath := filepath.Base(filePath) // proceed only if the base path is the matching versions. @@ -79,11 +78,9 @@ func Test_Samples_CreationAndDeletion(t *testing.T) { } return err }) - } func runGroupTest(tc *testcommon.KubePerTestContext, groupVersionPath string) { - rg := tc.NewTestResourceGroup() useRandomName := !testcommon.IsFolderExcluded(groupVersionPath, randomNameExclusions) samples, err := testcommon.NewSamplesTester( diff --git a/v2/internal/controllers/suite_test.go b/v2/internal/controllers/suite_test.go index d186ae4af44..d56749760e8 100644 --- a/v2/internal/controllers/suite_test.go +++ b/v2/internal/controllers/suite_test.go @@ -22,8 +22,10 @@ const ( DefaultResourceTimeout = 10 * time.Minute ) -var globalTestContext testcommon.KubeGlobalContext -var isLive = flag.Bool("live", false, "Enable to skip tests in live mode") +var ( + globalTestContext testcommon.KubeGlobalContext + isLive = flag.Bool("live", false, "Enable to skip tests in live mode") +) func setup() error { options := getOptions() diff --git a/v2/internal/crdmanagement/helpers.go b/v2/internal/crdmanagement/helpers.go index 5ccee8dfeb8..50c163caec5 100644 --- a/v2/internal/crdmanagement/helpers.go +++ b/v2/internal/crdmanagement/helpers.go @@ -24,8 +24,8 @@ func GetNonReadyCRDs( cfg config.Values, crdManager *Manager, goalCRDs []apiextensions.CustomResourceDefinition, - existingCRDs []apiextensions.CustomResourceDefinition) map[string]apiextensions.CustomResourceDefinition { - + existingCRDs []apiextensions.CustomResourceDefinition, +) map[string]apiextensions.CustomResourceDefinition { equalityCheck := SpecEqual // If we're not the webhooks install, we're in multitenant mode and we expect that the CRD webhook points to a different // namespace than ours. We don't actually know what the right namespace is though so we can't verify it - we just have to trust it's right. diff --git a/v2/internal/crdmanagement/manager.go b/v2/internal/crdmanagement/manager.go index cfd3b6bf7b7..e643dda48a0 100644 --- a/v2/internal/crdmanagement/manager.go +++ b/v2/internal/crdmanagement/manager.go @@ -29,10 +29,12 @@ import ( // ServiceOperatorVersionLabelOld is the label the CRDs have on them containing the ASO version. This value must match the value // injected by config/crd/labels.yaml -const ServiceOperatorVersionLabelOld = "serviceoperator.azure.com/version" -const ServiceOperatorVersionLabel = "app.kubernetes.io/version" -const ServiceOperatorAppLabel = "app.kubernetes.io/name" -const ServiceOperatorAppValue = "azure-service-operator" +const ( + ServiceOperatorVersionLabelOld = "serviceoperator.azure.com/version" + ServiceOperatorVersionLabel = "app.kubernetes.io/version" + ServiceOperatorAppLabel = "app.kubernetes.io/name" + ServiceOperatorAppValue = "azure-service-operator" +) const CRDLocation = "crds" @@ -101,7 +103,6 @@ func (m *Manager) FindMatchingCRDs( goal []apiextensions.CustomResourceDefinition, comparators ...func(a apiextensions.CustomResourceDefinition, b apiextensions.CustomResourceDefinition) bool, ) map[string]apiextensions.CustomResourceDefinition { - matching := make(map[string]apiextensions.CustomResourceDefinition) // Build a map so lookup is faster @@ -145,7 +146,6 @@ func (m *Manager) FindNonMatchingCRDs( goal []apiextensions.CustomResourceDefinition, comparators ...func(a apiextensions.CustomResourceDefinition, b apiextensions.CustomResourceDefinition) bool, ) map[string]apiextensions.CustomResourceDefinition { - // Just invert the comparators and call FindMatchingCRDs invertedComparators := make([]func(a apiextensions.CustomResourceDefinition, b apiextensions.CustomResourceDefinition) bool, 0, len(comparators)) for _, c := range comparators { @@ -167,7 +167,6 @@ func (m *Manager) DetermineCRDsToInstallOrUpgrade( existingCRDs []apiextensions.CustomResourceDefinition, patterns string, ) ([]*CRDInstallationInstruction, error) { - m.logger.V(Info).Info("Goal CRDs", "count", len(goalCRDs)) m.logger.V(Info).Info("Existing CRDs", "count", len(existingCRDs)) diff --git a/v2/internal/crdmanagement/manager_test.go b/v2/internal/crdmanagement/manager_test.go index 7696ed03268..ed518afbed2 100644 --- a/v2/internal/crdmanagement/manager_test.go +++ b/v2/internal/crdmanagement/manager_test.go @@ -41,7 +41,7 @@ func Test_LoadCRDs(t *testing.T) { g.Expect(err).ToNot(HaveOccurred()) crdPath := filepath.Join(dir, "crd.yaml") - g.Expect(os.WriteFile(crdPath, bytes, 0600)).To(Succeed()) + g.Expect(os.WriteFile(crdPath, bytes, 0o600)).To(Succeed()) crdManager := crdmanagement.NewManager(logger, nil) @@ -80,7 +80,7 @@ func Test_LoadCRDs_FixesNamespace(t *testing.T) { g.Expect(err).ToNot(HaveOccurred()) crdPath := filepath.Join(dir, "crd.yaml") - g.Expect(os.WriteFile(crdPath, bytes, 0600)).To(Succeed()) + g.Expect(os.WriteFile(crdPath, bytes, 0o600)).To(Succeed()) crdManager := crdmanagement.NewManager(logger, nil) diff --git a/v2/internal/duration/duration.go b/v2/internal/duration/duration.go index 97c6bf0c16e..eebfd4baac8 100644 --- a/v2/internal/duration/duration.go +++ b/v2/internal/duration/duration.go @@ -21,9 +21,7 @@ type ( } ) -var ( - durationRegex = regexp.MustCompile(`P([\d\.]+Y)?([\d\.]+M)?([\d\.]+D)?T?([\d\.]+H)?([\d\.]+M)?([\d\.]+?S)?`) -) +var durationRegex = regexp.MustCompile(`P([\d\.]+Y)?([\d\.]+M)?([\d\.]+D)?T?([\d\.]+H)?([\d\.]+M)?([\d\.]+?S)?`) /* Duration marshalling diff --git a/v2/internal/genericarmclient/default_http_client.go b/v2/internal/genericarmclient/default_http_client.go index cc0a748af93..b59d2716d5e 100644 --- a/v2/internal/genericarmclient/default_http_client.go +++ b/v2/internal/genericarmclient/default_http_client.go @@ -38,5 +38,4 @@ func init() { defaultHttpClient = &http.Client{ Transport: httpTransport, } - } diff --git a/v2/internal/genericarmclient/generic_client.go b/v2/internal/genericarmclient/generic_client.go index 06c5ed67c40..4ad61f51e5d 100644 --- a/v2/internal/genericarmclient/generic_client.go +++ b/v2/internal/genericarmclient/generic_client.go @@ -24,8 +24,10 @@ import ( "github.com/Azure/azure-service-operator/v2/internal/version" ) -const CreatePollerID = "GenericClient.CreateOrUpdateByID" -const DeletePollerID = "GenericClient.DeleteByID" +const ( + CreatePollerID = "GenericClient.CreateOrUpdateByID" + DeletePollerID = "GenericClient.DeleteByID" +) // NOTE: All of these methods (and types) were adapted from // https://github.com/Azure/azure-sdk-for-go/blob/sdk/resources/armresources/v0.3.0/sdk/resources/armresources/zz_generated_resources_client.go @@ -115,7 +117,6 @@ func NewGenericClient( creds: creds, opts: opts, }, nil - } // Creds returns the credentials used by this client @@ -134,7 +135,8 @@ func (client *GenericClient) BeginCreateOrUpdateByID( ctx context.Context, resourceID string, apiVersion string, - resource interface{}) (*PollerResponse[GenericResource], error) { + resource interface{}, +) (*PollerResponse[GenericResource], error) { // The linter doesn't realize that the response is closed in the course of // the autorest.NewPoller call below. Suppressing it as it is a false positive. // nolint:bodyclose @@ -160,8 +162,8 @@ func (client *GenericClient) createOrUpdateByID( ctx context.Context, resourceID string, apiVersion string, - resource interface{}) (*http.Response, error) { - + resource interface{}, +) (*http.Response, error) { req, err := client.createOrUpdateByIDCreateRequest(ctx, resourceID, apiVersion, resource) if err != nil { return nil, err @@ -184,8 +186,8 @@ func (client *GenericClient) createOrUpdateByIDCreateRequest( ctx context.Context, resourceID string, apiVersion string, - resource interface{}) (*policy.Request, error) { - + resource interface{}, +) (*policy.Request, error) { if resourceID == "" { return nil, errors.New("parameter resourceID cannot be empty") } diff --git a/v2/internal/genericarmclient/generic_client_test.go b/v2/internal/genericarmclient/generic_client_test.go index c81dd2752c6..8151c103dfe 100644 --- a/v2/internal/genericarmclient/generic_client_test.go +++ b/v2/internal/genericarmclient/generic_client_test.go @@ -23,6 +23,7 @@ import ( "github.com/Azure/azure-service-operator/v2/internal/genericarmclient" asometrics "github.com/Azure/azure-service-operator/v2/internal/metrics" "github.com/Azure/azure-service-operator/v2/internal/testcommon" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/creds" "github.com/Azure/azure-service-operator/v2/internal/util/to" "github.com/Azure/azure-service-operator/v2/pkg/genruntime" ) @@ -198,7 +199,7 @@ func Test_NewResourceGroup_SubscriptionNotRegisteredError(t *testing.T) { HttpClient: server.Client(), Metrics: metrics, } - client, err := genericarmclient.NewGenericClient(cfg, testcommon.MockTokenCredential{}, options) + client, err := genericarmclient.NewGenericClient(cfg, creds.MockTokenCredential{}, options) g.Expect(err).ToNot(HaveOccurred()) resourceURI := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Fake/fakeResource/fake", subscriptionId, "myrg") diff --git a/v2/internal/identity/client_certificate_credential.go b/v2/internal/identity/client_certificate_credential.go index 051dfdb069f..fc4a33cad4b 100644 --- a/v2/internal/identity/client_certificate_credential.go +++ b/v2/internal/identity/client_certificate_credential.go @@ -21,5 +21,4 @@ func NewClientCertificateCredential(tenantID, clientID string, clientCertificate return nil, err } return cred, nil - } diff --git a/v2/internal/identity/credential_provider.go b/v2/internal/identity/credential_provider.go index aabf3c751cf..1346efb9a4b 100644 --- a/v2/internal/identity/credential_provider.go +++ b/v2/internal/identity/credential_provider.go @@ -262,7 +262,6 @@ func (c *credentialProvider) newCredentialFromSecret(secret *v1.Secret) (*Creden authMode, err := authModeOrDefault(string(value)) if err != nil { return nil, errors.Wrap(err, errors.Errorf("invalid identity auth mode for %q encountered", nsName).Error()) - } if authMode == config.PodIdentityAuthMode { @@ -270,7 +269,6 @@ func (c *credentialProvider) newCredentialFromSecret(secret *v1.Secret) (*Creden ClientOptions: azcore.ClientOptions{}, ID: azidentity.ClientID(clientID), }) - if err != nil { return nil, errors.Wrap(err, errors.Errorf("invalid Managed Identity for %q encountered", nsName).Error()) } diff --git a/v2/internal/identity/credential_provider_test.go b/v2/internal/identity/credential_provider_test.go index bc5b37c95fe..ccec4c86269 100644 --- a/v2/internal/identity/credential_provider_test.go +++ b/v2/internal/identity/credential_provider_test.go @@ -25,9 +25,11 @@ import ( "github.com/Azure/azure-service-operator/v2/pkg/common/config" ) -const testPodNamespace = "azureserviceoperator-system-test" -const testSubscriptionID = "00000011-1111-0011-1100-110000000000" // Arbitrary GUID that isn't all 0s -const fakeID = "00000000-0000-0000-0000-000000000000" +const ( + testPodNamespace = "azureserviceoperator-system-test" + testSubscriptionID = "00000011-1111-0011-1100-110000000000" // Arbitrary GUID that isn't all 0s + fakeID = "00000000-0000-0000-0000-000000000000" +) type testCredentialProviderResources struct { Provider CredentialProvider diff --git a/v2/internal/metrics/arm_client_metrics.go b/v2/internal/metrics/arm_client_metrics.go index 4d941dbbe1f..408f53f6962 100644 --- a/v2/internal/metrics/arm_client_metrics.go +++ b/v2/internal/metrics/arm_client_metrics.go @@ -22,7 +22,6 @@ type ARMClientMetrics struct { var _ Metrics = &ARMClientMetrics{} func NewARMClientMetrics() *ARMClientMetrics { - azureSuccessfulRequestsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "azure_successful_requests_total", Help: "Total number of successful requests to azure", diff --git a/v2/internal/reconcilers/arm/arm_client_cache.go b/v2/internal/reconcilers/arm/arm_client_cache.go index 963494d43a8..2ce13760a30 100644 --- a/v2/internal/reconcilers/arm/arm_client_cache.go +++ b/v2/internal/reconcilers/arm/arm_client_cache.go @@ -36,8 +36,8 @@ func NewARMClientCache( kubeClient kubeclient.Client, configuration cloud.Configuration, httpClient *http.Client, - armMetrics *metrics.ARMClientMetrics) *ARMClientCache { - + armMetrics *metrics.ARMClientMetrics, +) *ARMClientCache { return &ARMClientCache{ lock: sync.Mutex{}, clients: make(map[string]*armClient), diff --git a/v2/internal/reconcilers/arm/arm_client_cache_test.go b/v2/internal/reconcilers/arm/arm_client_cache_test.go index fe8f1ffb9d4..e840e29fb1e 100644 --- a/v2/internal/reconcilers/arm/arm_client_cache_test.go +++ b/v2/internal/reconcilers/arm/arm_client_cache_test.go @@ -31,9 +31,11 @@ import ( "github.com/Azure/azure-service-operator/v2/pkg/genruntime/core" ) -const testPodNamespace = "azureserviceoperator-system-test" -const testSubscriptionID = "00000011-1111-0011-1100-110000000000" // Arbitrary GUID that isn't all 0s -const fakeID = "00000000-0000-0000-0000-000000000000" +const ( + testPodNamespace = "azureserviceoperator-system-test" + testSubscriptionID = "00000011-1111-0011-1100-110000000000" // Arbitrary GUID that isn't all 0s + fakeID = "00000000-0000-0000-0000-000000000000" +) func NewFakeKubeClient(s *runtime.Scheme) kubeclient.Client { fakeClient := fake.NewClientBuilder().WithScheme(s).Build() diff --git a/v2/internal/reconcilers/arm/azure_generic_arm_reconciler.go b/v2/internal/reconcilers/arm/azure_generic_arm_reconciler.go index ecd939cef05..c8ec86601e3 100644 --- a/v2/internal/reconcilers/arm/azure_generic_arm_reconciler.go +++ b/v2/internal/reconcilers/arm/azure_generic_arm_reconciler.go @@ -64,8 +64,8 @@ func NewAzureDeploymentReconciler( resourceResolver *resolver.Resolver, positiveConditions *conditions.PositiveConditionBuilder, cfg config.Values, - extension genruntime.ResourceExtension) *AzureDeploymentReconciler { - + extension genruntime.ResourceExtension, +) *AzureDeploymentReconciler { return &AzureDeploymentReconciler{ ARMConnectionFactory: armConnectionFactory, KubeClient: kubeClient, diff --git a/v2/internal/reconcilers/arm/azure_generic_arm_reconciler_instance.go b/v2/internal/reconcilers/arm/azure_generic_arm_reconciler_instance.go index d74dc4bb665..86230ba2b48 100644 --- a/v2/internal/reconcilers/arm/azure_generic_arm_reconciler_instance.go +++ b/v2/internal/reconcilers/arm/azure_generic_arm_reconciler_instance.go @@ -771,7 +771,8 @@ func ConvertToARMResourceImpl( metaObject genruntime.ARMMetaObject, scheme *runtime.Scheme, resolver *resolver.Resolver, - subscriptionID string) (genruntime.ARMResource, error) { + subscriptionID string, +) (genruntime.ARMResource, error) { spec, err := genruntime.GetVersionedSpec(metaObject, scheme) if err != nil { return nil, errors.Wrapf(err, "unable to get spec from %s", metaObject.GetObjectKind().GroupVersionKind()) @@ -821,8 +822,8 @@ func (r *azureDeploymentReconcilerInstance) deleteResource( log logr.Logger, resolver *resolver.Resolver, armClient *genericarmclient.GenericClient, - obj genruntime.ARMMetaObject) (ctrl.Result, error) { - + obj genruntime.ARMMetaObject, +) (ctrl.Result, error) { // If we have no resourceID to begin with, the Azure resource was never created resourceID := genruntime.GetResourceIDOrDefault(obj) if resourceID == "" { diff --git a/v2/internal/reconcilers/azuresql/azuresql_user_reconciler.go b/v2/internal/reconcilers/azuresql/azuresql_user_reconciler.go index 7ecdfe7fc21..ff6f1e1d2fc 100644 --- a/v2/internal/reconcilers/azuresql/azuresql_user_reconciler.go +++ b/v2/internal/reconcilers/azuresql/azuresql_user_reconciler.go @@ -50,8 +50,8 @@ func NewAzureSQLUserReconciler( resourceResolver *resolver.Resolver, positiveConditions *conditions.PositiveConditionBuilder, credentialProvider identity.CredentialProvider, - cfg config.Values) *AzureSQLUserReconciler { - + cfg config.Values, +) *AzureSQLUserReconciler { return &AzureSQLUserReconciler{ ResourceResolver: resourceResolver, CredentialProvider: credentialProvider, diff --git a/v2/internal/reconcilers/generic/register.go b/v2/internal/reconcilers/generic/register.go index 41b66219436..ad6ba481e2f 100644 --- a/v2/internal/reconcilers/generic/register.go +++ b/v2/internal/reconcilers/generic/register.go @@ -65,8 +65,8 @@ func RegisterAll( kubeClient kubeclient.Client, positiveConditions *conditions.PositiveConditionBuilder, objs []*registration.StorageType, - options Options) error { - + options Options, +) error { // pre-register any indexes we need for _, obj := range objs { for _, indexer := range obj.Indexes { @@ -95,8 +95,8 @@ func register( kubeClient kubeclient.Client, positiveConditions *conditions.PositiveConditionBuilder, info *registration.StorageType, - options Options) error { - + options Options, +) error { // Use the provided GVK to construct a new runtime object of the desired concrete type. gvk, err := apiutil.GVKForObject(info.Obj, mgr.GetScheme()) if err != nil { diff --git a/v2/internal/reconcilers/mysql/mysql_user_reconciler.go b/v2/internal/reconcilers/mysql/mysql_user_reconciler.go index 4c530f28523..220a25ce941 100644 --- a/v2/internal/reconcilers/mysql/mysql_user_reconciler.go +++ b/v2/internal/reconcilers/mysql/mysql_user_reconciler.go @@ -9,7 +9,7 @@ import ( "context" "github.com/go-logr/logr" - _ "github.com/go-sql-driver/mysql" //mysql driver + _ "github.com/go-sql-driver/mysql" // mysql driver "github.com/pkg/errors" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" @@ -40,8 +40,8 @@ func NewMySQLUserReconciler( resourceResolver *resolver.Resolver, positiveConditions *conditions.PositiveConditionBuilder, credentialProvider identity.CredentialProvider, - cfg config.Values) *MySQLUserReconciler { - + cfg config.Values, +) *MySQLUserReconciler { return &MySQLUserReconciler{ ResourceResolver: resourceResolver, CredentialProvider: credentialProvider, diff --git a/v2/internal/reconcilers/postgresql/postgresql_user_reconciler.go b/v2/internal/reconcilers/postgresql/postgresql_user_reconciler.go index a777b33348b..e3bd4fc7ee7 100644 --- a/v2/internal/reconcilers/postgresql/postgresql_user_reconciler.go +++ b/v2/internal/reconcilers/postgresql/postgresql_user_reconciler.go @@ -40,8 +40,8 @@ func NewPostgreSQLUserReconciler( kubeClient kubeclient.Client, resourceResolver *resolver.Resolver, positiveConditions *conditions.PositiveConditionBuilder, - cfg config.Values) *PostgreSQLUserReconciler { - + cfg config.Values, +) *PostgreSQLUserReconciler { return &PostgreSQLUserReconciler{ ResourceResolver: resourceResolver, Config: cfg, diff --git a/v2/internal/reflecthelpers/reflect_visitor.go b/v2/internal/reflecthelpers/reflect_visitor.go index 5df1f0aeeca..8471fde01bb 100644 --- a/v2/internal/reflecthelpers/reflect_visitor.go +++ b/v2/internal/reflecthelpers/reflect_visitor.go @@ -116,7 +116,6 @@ func IdentityVisitPtr(this *ReflectVisitor, it reflect.Value, ctx interface{}) e // IdentityVisitSlice is the identity visit function for slices. It visits each element of the slice. func IdentityVisitSlice(this *ReflectVisitor, it reflect.Value, ctx interface{}) error { - for i := 0; i < it.Len(); i++ { err := this.visit(it.Index(i), ctx) if err != nil { @@ -129,7 +128,6 @@ func IdentityVisitSlice(this *ReflectVisitor, it reflect.Value, ctx interface{}) // IdentityVisitMap is the identity visit function for maps. It visits each key and value in the map. func IdentityVisitMap(this *ReflectVisitor, it reflect.Value, ctx interface{}) error { - for _, key := range it.MapKeys() { err := this.visit(key, ctx) diff --git a/v2/internal/resolver/resource_hierarchy.go b/v2/internal/resolver/resource_hierarchy.go index 5edbea32f7b..5baac2d134c 100644 --- a/v2/internal/resolver/resource_hierarchy.go +++ b/v2/internal/resolver/resource_hierarchy.go @@ -28,8 +28,10 @@ const ( ) // If we wanted to type-assert we'd have to solve some circular dependency problems... for now this is ok. -const ResourceGroupKind = "ResourceGroup" -const ResourceGroupGroup = "resources.azure.com" +const ( + ResourceGroupKind = "ResourceGroup" + ResourceGroupGroup = "resources.azure.com" +) type ResourceHierarchy []genruntime.ARMMetaObject diff --git a/v2/internal/set/set_test.go b/v2/internal/set/set_test.go index eb535c12923..5111b4f4910 100644 --- a/v2/internal/set/set_test.go +++ b/v2/internal/set/set_test.go @@ -6,8 +6,9 @@ package set import ( - . "github.com/onsi/gomega" "testing" + + . "github.com/onsi/gomega" ) func TestSet_WhenConstructedWithItems_HasExpectedSize(t *testing.T) { diff --git a/v2/internal/testcommon/be_deleted_matcher.go b/v2/internal/testcommon/be_deleted_matcher.go index 40d54b6b8e7..2f303453e6b 100644 --- a/v2/internal/testcommon/be_deleted_matcher.go +++ b/v2/internal/testcommon/be_deleted_matcher.go @@ -34,7 +34,6 @@ type BeDeletedMatcher struct { var _ types.GomegaMatcher = &BeDeletedMatcher{} func (m *BeDeletedMatcher) Match(actual interface{}) (bool, error) { - if actual == nil { return false, nil } diff --git a/v2/internal/testcommon/creds/constants.go b/v2/internal/testcommon/creds/constants.go new file mode 100644 index 00000000000..45f2ea12602 --- /dev/null +++ b/v2/internal/testcommon/creds/constants.go @@ -0,0 +1,8 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package creds + +const DummyBillingId = "/providers/Microsoft.Billing/billingAccounts/00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000_2019-05-31/billingProfiles/0000-0000-000-000/invoiceSections/0000-0000-000-000" diff --git a/v2/internal/testcommon/creds.go b/v2/internal/testcommon/creds/creds.go similarity index 72% rename from v2/internal/testcommon/creds.go rename to v2/internal/testcommon/creds/creds.go index 988e0a1208a..07da7c8907b 100644 --- a/v2/internal/testcommon/creds.go +++ b/v2/internal/testcommon/creds/creds.go @@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation. Licensed under the MIT license. */ -package testcommon +package creds import ( "os" @@ -19,19 +19,20 @@ import ( // this is shared between tests because // instantiating it requires HTTP calls -var cachedCreds azcore.TokenCredential -var cachedIds AzureIDs +var ( + cachedCreds azcore.TokenCredential + cachedIds AzureIDs +) const TestBillingIDVar = "TEST_BILLING_ID" type AzureIDs struct { - subscriptionID string - tenantID string - billingInvoiceID string + SubscriptionID string + TenantID string + BillingInvoiceID string } -func getCreds() (azcore.TokenCredential, AzureIDs, error) { - +func GetCreds() (azcore.TokenCredential, AzureIDs, error) { if cachedCreds != nil { return cachedCreds, cachedIds, nil } @@ -56,9 +57,9 @@ func getCreds() (azcore.TokenCredential, AzureIDs, error) { billingInvoiceId := os.Getenv(TestBillingIDVar) ids := AzureIDs{ - subscriptionID: subscriptionID, - tenantID: tenantID, - billingInvoiceID: billingInvoiceId, + SubscriptionID: subscriptionID, + TenantID: tenantID, + BillingInvoiceID: billingInvoiceId, } cachedCreds = creds @@ -66,6 +67,7 @@ func getCreds() (azcore.TokenCredential, AzureIDs, error) { return creds, ids, nil } +// newScopedCredentialSecret is the internal factory used to create credential secrets func newScopedCredentialSecret(subscriptionID, tenantID, name, namespace string) *v1.Secret { secretData := make(map[string][]byte) @@ -81,7 +83,14 @@ func newScopedCredentialSecret(subscriptionID, tenantID, name, namespace string) } } -func NewScopedServicePrincipalSecret(subscriptionID, tenantID, clientID, clientSecret, name, namespace string) *v1.Secret { +func NewScopedServicePrincipalSecret( + subscriptionID string, + tenantID string, + clientID string, + clientSecret string, + name string, + namespace string, +) *v1.Secret { secret := newScopedCredentialSecret(subscriptionID, tenantID, name, namespace) secret.Data[config.AzureClientID] = []byte(clientID) @@ -90,7 +99,13 @@ func NewScopedServicePrincipalSecret(subscriptionID, tenantID, clientID, clientS return secret } -func NewScopedManagedIdentitySecret(subscriptionID, tenantID, clientID, name, namespace string) *v1.Secret { +func NewScopedManagedIdentitySecret( + subscriptionID string, + tenantID string, + clientID string, + name string, + namespace string, +) *v1.Secret { secret := newScopedCredentialSecret(subscriptionID, tenantID, name, namespace) secret.Data[config.AzureClientID] = []byte(clientID) @@ -98,7 +113,14 @@ func NewScopedManagedIdentitySecret(subscriptionID, tenantID, clientID, name, na return secret } -func NewScopedServicePrincipalCertificateSecret(subscriptionID, tenantID, clientID, clientCert, name, namespace string) *v1.Secret { +func NewScopedServicePrincipalCertificateSecret( + subscriptionID string, + tenantID string, + clientID string, + clientCert string, + name string, + namespace string, +) *v1.Secret { secret := newScopedCredentialSecret(subscriptionID, tenantID, name, namespace) secret.Data[config.AzureClientID] = []byte(clientID) diff --git a/v2/internal/testcommon/mock_token_cred.go b/v2/internal/testcommon/creds/mock_token_cred.go similarity index 98% rename from v2/internal/testcommon/mock_token_cred.go rename to v2/internal/testcommon/creds/mock_token_cred.go index fffd08d120c..6e3774edd3e 100644 --- a/v2/internal/testcommon/mock_token_cred.go +++ b/v2/internal/testcommon/creds/mock_token_cred.go @@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation. Licensed under the MIT license. */ -package testcommon +package creds import ( "context" diff --git a/v2/internal/testcommon/kube_per_test_context.go b/v2/internal/testcommon/kube_per_test_context.go index 8d86d056369..f558ce10aa2 100644 --- a/v2/internal/testcommon/kube_per_test_context.go +++ b/v2/internal/testcommon/kube_per_test_context.go @@ -17,7 +17,6 @@ import ( "testing" "time" - "github.com/dnaeon/go-vcr/recorder" "github.com/onsi/gomega" "github.com/onsi/gomega/format" "github.com/pkg/errors" @@ -305,7 +304,7 @@ var OperationTimeoutReplaying = 2 * time.Minute var OperationTimeoutRecording = 30 * time.Minute func (tc *KubePerTestContext) DefaultOperationTimeout() time.Duration { - if tc.AzureClientRecorder.Mode() == recorder.ModeReplaying { + if tc.AzureClientRecorder.IsReplaying() { return OperationTimeoutReplaying } @@ -325,7 +324,7 @@ var PollingIntervalRecording = 5 * time.Second // PollingInterval returns the polling interval to use for Gomega Eventually func (tc *KubePerTestContext) PollingInterval() time.Duration { - if tc.AzureClientRecorder.Mode() == recorder.ModeReplaying { + if tc.AzureClientRecorder.IsReplaying() { return PollingIntervalReplaying } @@ -472,8 +471,8 @@ func (tc *KubePerTestContext) CreateResourcesAndWait(objs ...client.Object) { func (tc *KubePerTestContext) CreateResourceAndWaitForState( obj client.Object, status metav1.ConditionStatus, - severity conditions.ConditionSeverity) { - + severity conditions.ConditionSeverity, +) { tc.T.Helper() tc.CreateResource(obj) tc.Eventually(obj).Should(tc.Match.BeInState(status, severity, 0)) @@ -508,7 +507,8 @@ func (tc *KubePerTestContext) PatchResourceAndWaitForState( old client.Object, new client.Object, status metav1.ConditionStatus, - severity conditions.ConditionSeverity) { + severity conditions.ConditionSeverity, +) { gen := old.GetGeneration() tc.T.Helper() @@ -806,7 +806,6 @@ func (tc *KubePerTestContext) ExportAsSampleNamed(resource client.Object, name s } func (tc *KubePerTestContext) cleanSample(resource any) { - if kr, ok := resource.(genruntime.KubernetesResource); ok { // Remove Status emptyStatus := kr.NewEmptyStatus() @@ -842,7 +841,7 @@ func (tc *KubePerTestContext) exportAsYAML(resource runtime.Object, filePath str return errors.Wrapf(err, "couldn't create directory path to %s", filePath) } - file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) + file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600) if err != nil { return errors.Wrapf(err, "failed to open file %s", filePath) } diff --git a/v2/internal/testcommon/kube_per_test_context_test.go b/v2/internal/testcommon/kube_per_test_context_test.go index 05c2dccfefb..3b6d2f69db0 100644 --- a/v2/internal/testcommon/kube_per_test_context_test.go +++ b/v2/internal/testcommon/kube_per_test_context_test.go @@ -6,8 +6,9 @@ Licensed under the MIT license. package testcommon import ( - . "github.com/onsi/gomega" "testing" + + . "github.com/onsi/gomega" ) func TestSanitiseSample(t *testing.T) { diff --git a/v2/internal/testcommon/kube_test_context.go b/v2/internal/testcommon/kube_test_context.go index c559840b8f8..b8fc5220396 100644 --- a/v2/internal/testcommon/kube_test_context.go +++ b/v2/internal/testcommon/kube_test_context.go @@ -30,8 +30,8 @@ func NewKubeContext( useEnvTest bool, recordReplay bool, region string, - nameConfig *ResourceNameConfig) (KubeGlobalContext, error) { - + nameConfig *ResourceNameConfig, +) (KubeGlobalContext, error) { var err error var cbtc BaseTestContextFactory var cleanup func() = func() {} diff --git a/v2/internal/testcommon/kube_test_context_envtest.go b/v2/internal/testcommon/kube_test_context_envtest.go index e964308647e..8969d4fa2e9 100644 --- a/v2/internal/testcommon/kube_test_context_envtest.go +++ b/v2/internal/testcommon/kube_test_context_envtest.go @@ -14,14 +14,11 @@ import ( "os/exec" "path/filepath" "runtime" - "sigs.k8s.io/controller-runtime/pkg/metrics/server" - "sigs.k8s.io/controller-runtime/pkg/webhook" "strings" "sync" "time" "github.com/benbjohnson/clock" - "github.com/dnaeon/go-vcr/recorder" "github.com/go-logr/logr" "github.com/pkg/errors" "golang.org/x/sync/semaphore" @@ -34,7 +31,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/envtest" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/webhook" "github.com/Azure/azure-service-operator/v2/internal/config" "github.com/Azure/azure-service-operator/v2/internal/controllers" @@ -89,6 +88,11 @@ func createSharedEnvTest(cfg testConfig, namespaceResources *namespaceResources) Scheme: scheme, } + // TODO: Switch to klogr.New() below the below if we want controller-runtime logs in the tests. + // By default we've disabled controller runtime logs because they're very verbose and usually not useful. + // ctrl.SetLogger(klogr.New()) + ctrl.SetLogger(logr.New(ctrllog.NullLogSink{})) + log.Println("Starting envtest") kubeConfig, err := environment.Start() if err != nil { @@ -155,7 +159,7 @@ func createSharedEnvTest(cfg testConfig, namespaceResources *namespaceResources) // TODO: Uncomment the below if we want controller-runtime logs in the tests. // By default we've disabled controller runtime logs because they're very verbose and usually not useful. - //ctrl.SetLogger(klogr.New()) + // ctrl.SetLogger(klogr.New()) ctrl.SetLogger(logr.New(ctrllog.NullLogSink{})) loggerFactory := func(obj metav1.Object) logr.Logger { @@ -469,11 +473,9 @@ func createEnvtestContext() (BaseTestContextFactory, context.CancelFunc) { } create := func(perTestContext PerTestContext, cfg config.Values) (*KubeBaseTestContext, error) { - - replaying := perTestContext.AzureClientRecorder.Mode() == recorder.ModeReplaying testCfg := testConfig{ Values: cfg, - Replaying: replaying, + Replaying: perTestContext.AzureClientRecorder.IsReplaying(), CountsTowardsLimit: perTestContext.CountsTowardsParallelLimits, } envtest, err := envTests.getEnvTestForConfig(perTestContext.Ctx, testCfg, perTestContext.logger) diff --git a/v2/internal/testcommon/matchers/be_deleted_in_azure_matcher.go b/v2/internal/testcommon/matchers/be_deleted_in_azure_matcher.go index 2deccc8f743..145ea5178ca 100644 --- a/v2/internal/testcommon/matchers/be_deleted_in_azure_matcher.go +++ b/v2/internal/testcommon/matchers/be_deleted_in_azure_matcher.go @@ -40,7 +40,6 @@ type BeDeletedInAzureMatcher struct { var _ types.GomegaMatcher = &BeDeletedInAzureMatcher{} func (m *BeDeletedInAzureMatcher) Match(actual interface{}) (bool, error) { - if actual == nil { return false, nil } diff --git a/v2/internal/testcommon/samples_tester.go b/v2/internal/testcommon/samples_tester.go index aa53dc709ae..9ee61fa3cb0 100644 --- a/v2/internal/testcommon/samples_tester.go +++ b/v2/internal/testcommon/samples_tester.go @@ -125,7 +125,6 @@ func (t *SamplesTester) LoadSamples() (*SampleObject, error) { err := filepath.Walk(t.groupVersionPath, func(filePath string, info os.FileInfo, err error) error { - if !info.IsDir() && !IsSampleExcluded(filePath, exclusions) { sample, err := t.getObjectFromFile(filePath) if err != nil { @@ -148,7 +147,6 @@ func (t *SamplesTester) LoadSamples() (*SampleObject, error) { } return nil }) - if err != nil { return nil, err } @@ -263,7 +261,6 @@ func setOwnersName(sample genruntime.ARMMetaObject, ownerName string) genruntime func IsFolderExcluded(path string, exclusions []string) bool { for _, exclusion := range exclusions { - if strings.Contains(path, "/"+exclusion+"/") { return true } @@ -304,7 +301,6 @@ func (t *SamplesTester) updateFieldsForTest(obj genruntime.ARMMetaObject) error // visitStruct checks and sets the SubscriptionID and ResourceGroup name for ARM references to current values func (t *SamplesTester) visitStruct(this *reflecthelpers.ReflectVisitor, it reflect.Value, ctx any) error { - // Configure any ResourceReference we find if it.Type() == reflect.TypeOf(genruntime.ResourceReference{}) { return t.visitResourceReference(this, it, ctx) diff --git a/v2/internal/testcommon/test_context.go b/v2/internal/testcommon/test_context.go index b466c4f961b..e55c85713d9 100644 --- a/v2/internal/testcommon/test_context.go +++ b/v2/internal/testcommon/test_context.go @@ -6,24 +6,17 @@ Licensed under the MIT license. package testcommon import ( - "bytes" "context" "crypto/rand" "crypto/rsa" "crypto/sha256" "encoding/hex" - "io" "net/http" - "os" "regexp" "strings" "testing" - "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/dnaeon/go-vcr/cassette" - "github.com/dnaeon/go-vcr/recorder" "github.com/go-logr/logr" - "github.com/google/uuid" "github.com/pkg/errors" "golang.org/x/crypto/ssh" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -33,6 +26,7 @@ import ( "github.com/Azure/azure-service-operator/v2/internal/config" "github.com/Azure/azure-service-operator/v2/internal/genericarmclient" "github.com/Azure/azure-service-operator/v2/internal/metrics" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" ) // Use WestUS2 as some things (such as VM quota) are hard to get in West US. @@ -48,7 +42,7 @@ type PerTestContext struct { TestContext T *testing.T logger logr.Logger - AzureClientRecorder *recorder.Recorder + AzureClientRecorder vcr.Interface AzureClient *genericarmclient.GenericClient AzureSubscription string AzureTenant string @@ -81,12 +75,11 @@ const ResourcePrefix = "asotest" // either a real cluster or a kind cluster. const LiveResourcePrefix = "asolivetest" -const DummyBillingId = "/providers/Microsoft.Billing/billingAccounts/00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000_2019-05-31/billingProfiles/0000-0000-000-000/invoiceSections/0000-0000-000-000" - func NewTestContext( region string, recordReplay bool, - nameConfig *ResourceNameConfig) TestContext { + nameConfig *ResourceNameConfig, +) TestContext { return TestContext{ AzureRegion: ®ion, RecordReplay: recordReplay, @@ -98,35 +91,34 @@ func (tc TestContext) ForTest(t *testing.T, cfg config.Values) (PerTestContext, logger := NewTestLogger(t) cassetteName := "recordings/" + t.Name() - details, err := createRecorder(cassetteName, cfg, tc.RecordReplay) + details, err := createTestRecorder(cassetteName, cfg, tc.RecordReplay, logger) if err != nil { return PerTestContext{}, errors.Wrapf(err, "creating recorder") } + // Use the recorder-specific CFG, which will force URLs and AADAuthorityHost (among other things) to default // values so that the recordings look the same regardless of which cloud you ran them in - cfg = details.cfg + cfg = details.Cfg() // To Go SDK client reuses HTTP clients among instances by default. We add handlers to the HTTP client based on // the specific test in question, which means that clients cannot be reused. // We explicitly create a new http.Client so that the recording from one test doesn't // get used for all other parallel tests. - httpClient := &http.Client{ - Transport: addCountHeader(translateErrors(details.recorder, cassetteName, t)), - } + httpClient := details.CreateClient(t) var globalARMClient *genericarmclient.GenericClient options := &genericarmclient.GenericClientOptions{ Metrics: metrics.NewARMClientMetrics(), HttpClient: httpClient, } - globalARMClient, err = genericarmclient.NewGenericClient(cfg.Cloud(), details.creds, options) + globalARMClient, err = genericarmclient.NewGenericClient(cfg.Cloud(), details.Creds(), options) if err != nil { return PerTestContext{}, errors.Wrapf(err, "failed to create generic ARM client") } t.Cleanup(func() { if !t.Failed() { - err := details.recorder.Stop() + err := details.Stop() if err != nil { // cleanup function should not error-out logger.Error(err, "unable to stop ARM client recorder") @@ -139,7 +131,7 @@ func (tc TestContext) ForTest(t *testing.T, cfg config.Values) (PerTestContext, // requests. Create an empty cassette to record that // so subsequent replay tests can run without needing // credentials. - err = ensureCassetteFileExists(cassetteName) + err = vcr.EnsureCassetteFileExists(cassetteName) if err != nil { logger.Error(err, "ensuring cassette file exists") t.Fail() @@ -159,11 +151,11 @@ func (tc TestContext) ForTest(t *testing.T, cfg config.Values) (PerTestContext, Namer: namer, NoSpaceNamer: namer.WithSeparator(""), AzureClient: globalARMClient, - AzureSubscription: details.ids.subscriptionID, - AzureTenant: details.ids.tenantID, - AzureBillingInvoiceID: details.ids.billingInvoiceID, + AzureSubscription: details.IDs().SubscriptionID, + AzureTenant: details.IDs().TenantID, + AzureBillingInvoiceID: details.IDs().BillingInvoiceID, AzureMatch: NewARMMatcher(globalARMClient), - AzureClientRecorder: details.recorder, + AzureClientRecorder: details, HttpClient: httpClient, TestName: t.Name(), Namespace: createTestNamespaceName(t), @@ -190,264 +182,6 @@ func createTestNamespaceName(t *testing.T) string { return result + "-" + disambig } -func ensureCassetteFileExists(cassetteName string) error { - filename := cassetteName + ".yaml" - _, err := os.Stat(filename) - if err == nil { - return nil - } - if !os.IsNotExist(err) { - return err - } - f, err := os.OpenFile(filename, os.O_RDONLY|os.O_CREATE, 0644) - if err != nil { - return errors.Wrapf(err, "creating empty cassette %q", filename) - } - if err := f.Close(); err != nil { - return errors.Wrapf(err, "failed to close empty cassette %q", filename) - } - return nil -} - -type recorderDetails struct { - creds azcore.TokenCredential - ids AzureIDs - recorder *recorder.Recorder - cfg config.Values -} - -func createRecorder(cassetteName string, cfg config.Values, recordReplay bool) (recorderDetails, error) { - var err error - var r *recorder.Recorder - if recordReplay { - r, err = recorder.New(cassetteName) - } else { - r, err = recorder.NewAsMode(cassetteName, recorder.ModeDisabled, nil) - } - - if err != nil { - return recorderDetails{}, errors.Wrapf(err, "creating recorder") - } - - var creds azcore.TokenCredential - var azureIDs AzureIDs - if r.Mode() == recorder.ModeRecording || - r.Mode() == recorder.ModeDisabled { - // if we are recording, we need auth - creds, azureIDs, err = getCreds() - if err != nil { - return recorderDetails{}, err - } - } else { - // if we are replaying, we won't need auth - // and we use a dummy subscription ID/tenant ID - creds = MockTokenCredential{} - azureIDs.tenantID = uuid.Nil.String() - azureIDs.subscriptionID = uuid.Nil.String() - azureIDs.billingInvoiceID = DummyBillingId - // Force these values to be the default - cfg.ResourceManagerEndpoint = config.DefaultEndpoint - cfg.ResourceManagerAudience = config.DefaultAudience - cfg.AzureAuthorityHost = config.DefaultAADAuthorityHost - } - - // check body as well as URL/Method (copied from go-vcr documentation) - r.SetMatcher(func(r *http.Request, i cassette.Request) bool { - if !cassette.DefaultMatcher(r, i) { - return false - } - - // verify custom request count header (see counting_roundtripper.go) - if r.Header.Get(COUNT_HEADER) != i.Headers.Get(COUNT_HEADER) { - return false - } - - if r.Body == nil { - return i.Body == "" - } - - var b bytes.Buffer - if _, err := b.ReadFrom(r.Body); err != nil { - panic(err) - } - - r.Body = io.NopCloser(&b) - return b.String() == "" || hideRecordingData(b.String()) == i.Body - }) - - r.AddSaveFilter(func(i *cassette.Interaction) error { - // rewrite all request/response fields to hide the real subscription ID - // this is *not* a security measure but intended to make the tests updateable from - // any subscription, so a contributor can update the tests against their own sub. - hide := func(s string, id string, replacement string) string { - return strings.ReplaceAll(s, id, replacement) - } - - // Note that this changes the cassette in-place so there's no return needed - hideCassetteString := func(cas *cassette.Interaction, id string, replacement string) { - i.Request.Body = strings.ReplaceAll(cas.Request.Body, id, replacement) - i.Response.Body = strings.ReplaceAll(cas.Response.Body, id, replacement) - i.Request.URL = strings.ReplaceAll(cas.Request.URL, id, replacement) - } - - // Hide the subscription ID - hideCassetteString(i, azureIDs.subscriptionID, uuid.Nil.String()) - // Hide the tenant ID - hideCassetteString(i, azureIDs.tenantID, uuid.Nil.String()) - // Hide the billing ID - if azureIDs.billingInvoiceID != "" { - hideCassetteString(i, azureIDs.billingInvoiceID, DummyBillingId) - } - - // Hiding other sensitive fields - i.Request.Body = hideRecordingData(i.Request.Body) - i.Response.Body = hideRecordingData(i.Response.Body) - i.Request.URL = hideURLData(i.Request.URL) - - for _, values := range i.Request.Headers { - for i := range values { - values[i] = hide(values[i], azureIDs.subscriptionID, uuid.Nil.String()) - values[i] = hide(values[i], azureIDs.tenantID, uuid.Nil.String()) - if azureIDs.billingInvoiceID != "" { - values[i] = hide(values[i], azureIDs.billingInvoiceID, DummyBillingId) - } - } - } - - for key, values := range i.Response.Headers { - for i := range values { - values[i] = hide(values[i], azureIDs.subscriptionID, uuid.Nil.String()) - values[i] = hide(values[i], azureIDs.tenantID, uuid.Nil.String()) - if azureIDs.billingInvoiceID != "" { - values[i] = hide(values[i], azureIDs.billingInvoiceID, DummyBillingId) - } - } - // Hide the base request URL in the AzureOperation and Location headers - if key == genericarmclient.AsyncOperationHeader || key == genericarmclient.LocationHeader { - for i := range values { - values[i] = hideBaseRequestURL(values[i]) - } - } - } - - for _, header := range requestHeadersToRemove { - delete(i.Request.Headers, header) - } - - for _, header := range responseHeadersToRemove { - delete(i.Response.Headers, header) - } - - return nil - }) - - return recorderDetails{ - creds: creds, - ids: azureIDs, - recorder: r, - cfg: cfg, - }, nil -} - -var requestHeadersToRemove = []string{ - // remove all Authorization headers from stored requests - "Authorization", - - // Not needed, adds to diff churn: - "User-Agent", -} - -var responseHeadersToRemove = []string{ - // Request IDs - "X-Ms-Arm-Service-Request-Id", - "X-Ms-Correlation-Request-Id", - "X-Ms-Request-Id", - "X-Ms-Routing-Request-Id", - "X-Ms-Client-Request-Id", - "Client-Request-Id", - - // Quota limits - "X-Ms-Ratelimit-Remaining-Subscription-Deletes", - "X-Ms-Ratelimit-Remaining-Subscription-Reads", - "X-Ms-Ratelimit-Remaining-Subscription-Writes", - - // Not needed, adds to diff churn - "Date", -} - -var ( - dateMatcher = regexp.MustCompile(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?Z`) - sshKeyMatcher = regexp.MustCompile("ssh-rsa [0-9a-zA-Z+/=]+") - passwordMatcher = regexp.MustCompile("\"pass[^\"]*?pass\"") - - // keyMatcher matches any valid base64 value with at least 10 sets of 4 bytes of data that ends in = or ==. - // Both storage account keys and Redis account keys are longer than that and end in = or ==. Note that technically - // base64 values need not end in == or =, but allowing for that in the match will flag tons of false positives as - // any text (including long URLs) have strings of characters that meet this requirement. There are other base64 values - // in the payloads (such as operationResults URLs for polling async operations for some services) that seem to use - // very long base64 strings as well. - keyMatcher = regexp.MustCompile("(?:[A-Za-z0-9+/]{4}){10,}(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)") - - // kubeConfigMatcher specifically matches base64 data returned by the AKS get keys API - kubeConfigMatcher = regexp.MustCompile(`"value": "[a-zA-Z0-9+/]+={0,2}"`) - - // baseURLMatcher matches the base part of a URL - baseURLMatcher = regexp.MustCompile(`^https://[^/]+/`) - - // customKeyMatcher is used to match 'key' or 'Key' followed by the base64 patterns without '=' padding. - customKeyMatcher = regexp.MustCompile(`"([a-z]+)?[K-k]ey":"[a-zA-Z0-9+/]+"`) - customKeyReplacer = regexp.MustCompile(`"(?:[A-Za-z0-9+/]{4}){10,}(?:[A-Za-z0-9+/]{4}|[A-Za-z0-9+/])"`) -) - -// hideDates replaces all ISO8601 datetimes with a fixed value -// this lets us match requests that may contain time-sensitive information (timestamps, etc) -func hideDates(s string) string { - return dateMatcher.ReplaceAllLiteralString(s, "2001-02-03T04:05:06Z") // this should be recognizable/parseable as a fake date -} - -// hideSSHKeys hides anything that looks like SSH keys -func hideSSHKeys(s string) string { - return sshKeyMatcher.ReplaceAllLiteralString(s, "ssh-rsa {KEY}") -} - -// hidePasswords hides anything that looks like a generated password -func hidePasswords(s string) string { - return passwordMatcher.ReplaceAllLiteralString(s, "\"{PASSWORD}\"") -} - -func hideKeys(s string) string { - return keyMatcher.ReplaceAllLiteralString(s, "{KEY}") -} - -func hideKubeConfigs(s string) string { - return kubeConfigMatcher.ReplaceAllLiteralString(s, `"value": "IA=="`) // Have to replace with valid base64 data, so replace with " " -} - -func hideBaseRequestURL(s string) string { - return baseURLMatcher.ReplaceAllLiteralString(s, `https://management.azure.com/`) -} - -func hideCustomKeys(s string) string { - return customKeyMatcher.ReplaceAllStringFunc(s, func(matched string) string { - return customKeyReplacer.ReplaceAllString(matched, `"{KEY}"`) - }) -} - -func hideRecordingData(s string) string { - result := hideDates(s) - result = hideSSHKeys(result) - result = hidePasswords(result) - result = hideKubeConfigs(result) - result = hideKeys(result) - result = hideCustomKeys(result) - - return result -} - -func hideURLData(s string) string { - return hideBaseRequestURL(s) -} - func (tc PerTestContext) NewTestResourceGroup() *resources.ResourceGroup { return &resources.ResourceGroup{ ObjectMeta: metav1.ObjectMeta{ diff --git a/v2/internal/testcommon/test_recorder.go b/v2/internal/testcommon/test_recorder.go new file mode 100644 index 00000000000..dd5587b70c0 --- /dev/null +++ b/v2/internal/testcommon/test_recorder.go @@ -0,0 +1,41 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package testcommon + +import ( + "github.com/go-logr/logr" + "github.com/pkg/errors" + + "github.com/Azure/azure-service-operator/v2/internal/config" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" + v1 "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr/v1" + v3 "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr/v3" +) + +// createTestRecorder returns an instance of testRecorder to allow recording and playback of HTTP requests. +func createTestRecorder( + cassetteName string, + cfg config.Values, + recordReplay bool, + log logr.Logger, +) (vcr.Interface, error) { + if !recordReplay { + // We're not using VCR, so just pass through the requests + return vcr.NewTestPassthroughRecorder(cfg) + } + + // If a cassette file exists in the old format, use the old player + v1Exists, err := v1.CassetteFileExists(cassetteName) + if err != nil { + return nil, errors.Wrapf(err, "checking existence of cassette %s", cassetteName) + } + + if v1Exists { + return v1.NewTestPlayer(cassetteName, cfg) + } + + return v3.NewTestRecorder(cassetteName, cfg, log) +} diff --git a/v2/internal/testcommon/vcr/cassette.go b/v2/internal/testcommon/vcr/cassette.go new file mode 100644 index 00000000000..ddc898988f6 --- /dev/null +++ b/v2/internal/testcommon/vcr/cassette.go @@ -0,0 +1,54 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package vcr + +import ( + "os" + + "github.com/pkg/errors" +) + +func EnsureCassetteFileExists(cassetteName string) error { + exists, err := CassetteFileExists(cassetteName) + if err != nil { + return err + } + + if exists { + return nil + } + + filename := CassetteFileName(cassetteName) + + f, err := os.OpenFile(filename, os.O_RDONLY|os.O_CREATE, 0o644) + if err != nil { + return errors.Wrapf(err, "creating empty cassette %q", filename) + } + if err := f.Close(); err != nil { + return errors.Wrapf(err, "failed to close empty cassette %q", filename) + } + + return nil +} + +func CassetteFileExists(cassetteName string) (bool, error) { + filename := CassetteFileName(cassetteName) + + _, err := os.Stat(filename) + if err == nil { + return true, nil + } + + if os.IsNotExist(err) { + return false, nil + } + + return false, err +} + +func CassetteFileName(cassetteName string) string { + return cassetteName + ".yaml" +} diff --git a/v2/internal/testcommon/counting_roundtripper.go b/v2/internal/testcommon/vcr/counting_roundtripper.go similarity index 92% rename from v2/internal/testcommon/counting_roundtripper.go rename to v2/internal/testcommon/vcr/counting_roundtripper.go index 6204e4ca4c6..4231b46e3bb 100644 --- a/v2/internal/testcommon/counting_roundtripper.go +++ b/v2/internal/testcommon/vcr/counting_roundtripper.go @@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation. Licensed under the MIT license. */ -package testcommon +package vcr import ( "fmt" @@ -24,7 +24,7 @@ type requestCounter struct { counts map[string]uint32 } -func addCountHeader(inner http.RoundTripper) *requestCounter { +func AddCountHeader(inner http.RoundTripper) *requestCounter { return &requestCounter{ inner: inner, counts: make(map[string]uint32), diff --git a/v2/internal/testcommon/vcr/fake_roundtripper.go b/v2/internal/testcommon/vcr/fake_roundtripper.go new file mode 100644 index 00000000000..a00a439f37f --- /dev/null +++ b/v2/internal/testcommon/vcr/fake_roundtripper.go @@ -0,0 +1,58 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package vcr + +import ( + "net/http" + + "gopkg.in/dnaeon/go-vcr.v3/cassette" +) + +// FakeRoundTripper is a fake implementation of http.RoundTripper used in testing. +type FakeRoundTripper struct { + responses map[string][]fakeRoundTripResponse +} + +type fakeRoundTripResponse struct { + response *http.Response + err error +} + +var _ http.RoundTripper = &FakeRoundTripper{} + +func NewFakeRoundTripper() *FakeRoundTripper { + return &FakeRoundTripper{ + responses: make(map[string][]fakeRoundTripResponse), + } +} + +// RoundTrip implements http.RoundTripper. +func (fake *FakeRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { + key := request.URL.String() + if available, ok := fake.responses[key]; ok { + if len(available) > 0 { + response := available[0] + fake.responses[key] = available[1:] + return response.response, response.err + } + + return nil, cassette.ErrInteractionNotFound + } + + return nil, cassette.ErrInteractionNotFound +} + +// AddResponse adds a response to the fake round tripper. +func (fake *FakeRoundTripper) AddResponse(request *http.Request, response *http.Response) { + key := request.URL.String() + fake.responses[key] = append(fake.responses[key], fakeRoundTripResponse{response: response}) +} + +// AddError adds an error to the fake round tripper. +func (fake *FakeRoundTripper) AddError(request *http.Request, err error) { + key := request.URL.String() + fake.responses[key] = append(fake.responses[key], fakeRoundTripResponse{err: err}) +} diff --git a/v2/internal/testcommon/vcr/recorder.go b/v2/internal/testcommon/vcr/recorder.go new file mode 100644 index 00000000000..d6548a178f3 --- /dev/null +++ b/v2/internal/testcommon/vcr/recorder.go @@ -0,0 +1,38 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package vcr + +import ( + "net/http" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-service-operator/v2/internal/config" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/creds" +) + +// Interface is a lightweight interface that allows us to swap out implementations of Go-VCR +// as required. +type Interface interface { + // Cfg returns the available configuration for the test + Cfg() config.Values + + // Creds returns Azure credentials when running for real + Creds() azcore.TokenCredential + + // IDs returns the available Azure resource IDs for the test + IDs() creds.AzureIDs + + // Stop recording + Stop() error + + // IsReplaying returns true if we're replaying a recorded test, false if we're recording a new test + IsReplaying() bool + + // CreateClient creates an HTTP client configured to record or replay HTTP requests. + // t is a reference to the test currently executing. + CreateClient(t *testing.T) *http.Client +} diff --git a/v2/internal/testcommon/vcr/redact.go b/v2/internal/testcommon/vcr/redact.go new file mode 100644 index 00000000000..9e4950223a2 --- /dev/null +++ b/v2/internal/testcommon/vcr/redact.go @@ -0,0 +1,126 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package vcr + +import ( + "net/http" + "regexp" +) + +// requestHeadersToRemove is the list of request headers to remove when recording or replaying. +var requestHeadersToRemove = []string{ + // remove all Authorization headers from stored requests + "Authorization", + + // Not needed, adds to diff churn: + "User-Agent", +} + +func RedactRequestHeaders(headers http.Header) { + for _, header := range requestHeadersToRemove { + delete(headers, header) + } +} + +// responseHeadersToRemove is the list of response headers to remove when recording or replaying. +var responseHeadersToRemove = []string{ + // Request IDs + "X-Ms-Arm-Service-Request-Id", + "X-Ms-Correlation-Request-Id", + "X-Ms-Request-Id", + "X-Ms-Routing-Request-Id", + "X-Ms-Client-Request-Id", + "Client-Request-Id", + + // Quota limits + "X-Ms-Ratelimit-Remaining-Subscription-Deletes", + "X-Ms-Ratelimit-Remaining-Subscription-Reads", + "X-Ms-Ratelimit-Remaining-Subscription-Writes", + + // Not needed, adds to diff churn + "Date", +} + +func RedactResponseHeaders(headers http.Header) { + for _, header := range responseHeadersToRemove { + delete(headers, header) + } +} + +func HideRecordingData(s string) string { + result := hideDates(s) + result = hideSSHKeys(result) + result = hidePasswords(result) + result = hideKubeConfigs(result) + result = hideKeys(result) + result = hideCustomKeys(result) + + return result +} + +var dateMatcher = regexp.MustCompile(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?Z`) + +// hideDates replaces all ISO8601 datetimes with a fixed value +// this lets us match requests that may contain time-sensitive information (timestamps, etc) +func hideDates(s string) string { + return dateMatcher.ReplaceAllLiteralString(s, "2001-02-03T04:05:06Z") // this should be recognizable/parseable as a fake date +} + +var sshKeyMatcher = regexp.MustCompile("ssh-rsa [0-9a-zA-Z+/=]+") + +// hideSSHKeys hides anything that looks like SSH keys +func hideSSHKeys(s string) string { + return sshKeyMatcher.ReplaceAllLiteralString(s, "ssh-rsa {KEY}") +} + +var passwordMatcher = regexp.MustCompile("\"pass[^\"]*?pass\"") + +// hidePasswords hides anything that looks like a generated password +func hidePasswords(s string) string { + return passwordMatcher.ReplaceAllLiteralString(s, "\"{PASSWORD}\"") +} + +// kubeConfigMatcher specifically matches base64 data returned by the AKS get keys API +var kubeConfigMatcher = regexp.MustCompile(`"value": "[a-zA-Z0-9+/]+={0,2}"`) + +func hideKubeConfigs(s string) string { + return kubeConfigMatcher.ReplaceAllLiteralString(s, `"value": "IA=="`) // Have to replace with valid base64 data, so replace with " " +} + +// keyMatcher matches any valid base64 value with at least 10 sets of 4 bytes of data that ends in = or ==. +// Both storage account keys and Redis account keys are longer than that and end in = or ==. Note that technically +// base64 values need not end in == or =, but allowing for that in the match will flag tons of false positives as +// any text (including long URLs) have strings of characters that meet this requirement. There are other base64 values +// in the payloads (such as operationResults URLs for polling async operations for some services) that seem to use +// very long base64 strings as well. +var keyMatcher = regexp.MustCompile("(?:[A-Za-z0-9+/]{4}){10,}(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)") + +func hideKeys(s string) string { + return keyMatcher.ReplaceAllLiteralString(s, "{KEY}") +} + +var ( + // customKeyMatcher is used to match 'key' or 'Key' followed by the base64 patterns without '=' padding. + customKeyMatcher = regexp.MustCompile(`"([a-z]+)?[K-k]ey":"[a-zA-Z0-9+/]+"`) + customKeyReplacer = regexp.MustCompile(`"(?:[A-Za-z0-9+/]{4}){10,}(?:[A-Za-z0-9+/]{4}|[A-Za-z0-9+/])"`) +) + +func hideCustomKeys(s string) string { + return customKeyMatcher.ReplaceAllStringFunc(s, func(matched string) string { + return customKeyReplacer.ReplaceAllString(matched, `"{KEY}"`) + }) +} + +func HideURLData(s string) string { + return HideBaseRequestURL(s) +} + +// baseURLMatcher matches the base part of a URL +var baseURLMatcher = regexp.MustCompile(`^https://[^/]+/`) + +func HideBaseRequestURL(s string) string { + return baseURLMatcher.ReplaceAllLiteralString(s, `https://management.azure.com/`) +} diff --git a/v2/internal/testcommon/vcr/test_passthrough_recorder.go b/v2/internal/testcommon/vcr/test_passthrough_recorder.go new file mode 100644 index 00000000000..8ac9ddea4a6 --- /dev/null +++ b/v2/internal/testcommon/vcr/test_passthrough_recorder.go @@ -0,0 +1,70 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package vcr + +import ( + "net/http" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-service-operator/v2/internal/config" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/creds" +) + +// testPassthroughRecorder is an implementation of testRecorder that does not record or replay HTTP requests, +// but which instead just passes them through to a real HTTP endpoint. +type testPassthroughRecorder struct { + cfg config.Values + creds azcore.TokenCredential + ids creds.AzureIDs +} + +var _ Interface = &testPassthroughRecorder{} + +// NewTestPassthroughRecorder returns an instance of testRecorder that does not record or replay HTTP requests, +func NewTestPassthroughRecorder(cfg config.Values) (Interface, error) { + creds, azureIDs, err := creds.GetCreds() + if err != nil { + return nil, err + } + + return &testPassthroughRecorder{ + cfg: cfg, + creds: creds, + ids: azureIDs, + }, nil +} + +// Cfg implements testRecorder. +func (r *testPassthroughRecorder) Cfg() config.Values { + return r.cfg +} + +// CreateClient implements testRecorder. +func (*testPassthroughRecorder) CreateClient(t *testing.T) *http.Client { + return http.DefaultClient +} + +// Creds implements testRecorder. +func (r *testPassthroughRecorder) Creds() azcore.TokenCredential { + return r.creds +} + +// IDs implements testRecorder. +func (r *testPassthroughRecorder) IDs() creds.AzureIDs { + return r.ids +} + +// IsReplaying implements testRecorder. +func (*testPassthroughRecorder) IsReplaying() bool { + return false +} + +// Stop implements testRecorder. +func (*testPassthroughRecorder) Stop() error { + // Nothing to do + return nil +} diff --git a/v2/internal/testcommon/vcr/v1/README.md b/v2/internal/testcommon/vcr/v1/README.md new file mode 100644 index 00000000000..09dbe7c376a --- /dev/null +++ b/v2/internal/testcommon/vcr/v1/README.md @@ -0,0 +1,5 @@ +# Legacy support for go-vcr v1 + +To avoid having to re-record all our tests in one go, this package provides legacy support for go-vcr v1 recordings. + +Once we have migrated all recordings to the new format (as required by go-vcr v3), this package will be removed. diff --git a/v2/internal/testcommon/vcr/v1/cassette.go b/v2/internal/testcommon/vcr/v1/cassette.go new file mode 100644 index 00000000000..0957f6bf620 --- /dev/null +++ b/v2/internal/testcommon/vcr/v1/cassette.go @@ -0,0 +1,47 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + "os" + + "github.com/pkg/errors" + "gopkg.in/yaml.v2" + + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" +) + +// CassetteFileExists returns true if a cassette file exists AND it contains a go-vcr V1 recording +func CassetteFileExists(cassetteName string) (bool, error) { + exists, err := vcr.CassetteFileExists(cassetteName) + if err != nil { + return false, errors.Wrapf(err, "checking whether v1 cassette exists") + } + if !exists { + return false, nil + } + + filename := vcr.CassetteFileName(cassetteName) + var content struct { + Version int `json:"version"` + } + + file, err := os.Open(filename) + if err != nil { + return false, errors.Wrapf(err, "opening cassette file %q", filename) + } + + defer file.Close() + + decoder := yaml.NewDecoder(file) + + err = decoder.Decode(&content) + if err != nil { + return false, errors.Wrapf(err, "parsing cassette file %q", filename) + } + + return content.Version == 1, nil +} diff --git a/v2/internal/testcommon/vcr/v1/error_translating_roundtripper.go b/v2/internal/testcommon/vcr/v1/error_translating_roundtripper.go new file mode 100644 index 00000000000..3882a020a98 --- /dev/null +++ b/v2/internal/testcommon/vcr/v1/error_translating_roundtripper.go @@ -0,0 +1,134 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + "fmt" + "io" + "net/http" + "strings" + "testing" + + cassettev1 "github.com/dnaeon/go-vcr/cassette" + "github.com/google/go-cmp/cmp" + "github.com/pkg/errors" + + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" + "github.com/Azure/azure-service-operator/v2/pkg/genruntime/conditions" +) + +// translateErrors wraps a go-vcr v1 recorder to handle any "Requested interaction not found" +// and log better information about what the expected request was. +// +// By default the error will be returned to the controller which might ignore/retry it +// and not log any useful information. So instead here we find the recorded request with +// the body that most closely matches what was sent and report the "expected" body. +// +// Ideally we would panic on this error but we don't have a good way to deal with the following +// problem at the moment: +// - during record the controller does GET (404), PUT, … GET (OK) +// - during playback the controller does GET (which now returns OK), DELETE, PUT, … +// and fails due to a missing DELETE recording +func translateErrors( + r http.RoundTripper, + cassetteName string, + t *testing.T, +) http.RoundTripper { + return errorTranslation{ + recorder: r, + cassetteName: cassetteName, + t: t, + } +} + +type errorTranslation struct { + recorder http.RoundTripper + cassetteName string + cassette *cassettev1.Cassette + t *testing.T +} + +func (w errorTranslation) ensureCassette() *cassettev1.Cassette { + if w.cassette == nil { + cassette, err := cassettev1.Load(w.cassetteName) + if err != nil { + panic(fmt.Sprintf("unable to load cassette %q", w.cassetteName)) + } + + w.cassette = cassette + } + + return w.cassette +} + +func (w errorTranslation) RoundTrip(req *http.Request) (*http.Response, error) { + resp, originalErr := w.recorder.RoundTrip(req) + // sorry, go-vcr doesn't expose the error type or message + if originalErr == nil || !strings.Contains(originalErr.Error(), "interaction not found") { + return resp, originalErr + } + + sentBodyString := "" + if req.Body != nil { + bodyBytes, bodyErr := io.ReadAll(req.Body) + if bodyErr != nil { + // see invocation of SetMatcher in the createRecorder, which does this + panic("io.ReadAll(req.Body) failed, this should always succeed because req.Body has been replaced by a buffer") + } + + // Apply the same body filtering that we do in recordings so that the diffs don't show things + // that we've just removed + sentBodyString = vcr.HideRecordingData(string(bodyBytes)) + } + + // find all request bodies for the specified method/URL combination + matchingBodies := w.findMatchingBodies(req) + + if len(matchingBodies) == 0 { + return nil, conditions.NewReadyConditionImpactingError( + errors.Errorf("cannot find go-vcr recording for request from test %q (cassette: %q) (no responses recorded for this method/URL): %s %s (attempt: %s)\n\n", + w.t.Name(), + w.cassetteName, + req.Method, + req.URL.String(), + req.Header.Get(vcr.COUNT_HEADER)), + conditions.ConditionSeverityError, + conditions.ReasonReconciliationFailedPermanently) + } + + // locate the request body with the shortest diff from the sent body + shortestDiff := "" + for i, bodyString := range matchingBodies { + diff := cmp.Diff(bodyString, sentBodyString) + if i == 0 || len(diff) < len(shortestDiff) { + shortestDiff = diff + } + } + + return nil, conditions.NewReadyConditionImpactingError( + errors.Errorf("cannot find go-vcr recording for request from test %q (cassette: %q) (body mismatch): %s %s\nShortest body diff: %s\n\n", + w.t.Name(), + w.cassetteName, + req.Method, + req.URL.String(), + shortestDiff), + conditions.ConditionSeverityError, + conditions.ReasonReconciliationFailedPermanently) +} + +// finds bodies for interactions where request method, URL, and vcr.COUNT_HEADER match +func (w errorTranslation) findMatchingBodies(r *http.Request) []string { + urlString := r.URL.String() + var result []string + for _, interaction := range w.ensureCassette().Interactions { + if urlString == interaction.URL && r.Method == interaction.Request.Method && + r.Header.Get(vcr.COUNT_HEADER) == interaction.Request.Headers.Get(vcr.COUNT_HEADER) { + result = append(result, interaction.Request.Body) + } + } + + return result +} diff --git a/v2/internal/testcommon/vcr/v1/recordings/TestReplayer_WhenRecordingExists_ReturnsResult.yaml b/v2/internal/testcommon/vcr/v1/recordings/TestReplayer_WhenRecordingExists_ReturnsResult.yaml new file mode 100644 index 00000000000..d5974861ec2 --- /dev/null +++ b/v2/internal/testcommon/vcr/v1/recordings/TestReplayer_WhenRecordingExists_ReturnsResult.yaml @@ -0,0 +1,361 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Test-Request-Attempt: + - "0" + url: https://www.bing.com + method: GET + response: + body: "Bing

Trending + on Bing

" + headers: + Alt-Svc: + - h3=":443"; ma=93600 + Cache-Control: + - private + Content-Type: + - text/html; charset=utf-8 + Date: + - Mon, 29 Jan 2024 21:31:26 GMT + P3p: + - CP="NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND" + Set-Cookie: + - MUID=08AD274FC21A6D9A2047335AC3376CE2; domain=.bing.com; expires=Sat, 22-Feb-2025 + 21:31:26 GMT; path=/; secure; SameSite=None + - MUIDB=08AD274FC21A6D9A2047335AC3376CE2; expires=Sat, 22-Feb-2025 21:31:26 + GMT; path=/; HttpOnly + - _EDGE_S=F=1&SID=3CEC1A4B7B6862D4071E0E5E7A4563FF; domain=.bing.com; path=/; + HttpOnly + - _EDGE_V=1; domain=.bing.com; expires=Sat, 22-Feb-2025 21:31:26 GMT; path=/; + HttpOnly + - SRCHD=AF=NOFORM; domain=.bing.com; expires=Thu, 29-Jan-2026 21:31:26 GMT; + path=/ + - SRCHUID=V=2&GUID=4386BDCF6279490E967DD22ED8795DF8&dmnchg=1; domain=.bing.com; + expires=Thu, 29-Jan-2026 21:31:26 GMT; path=/ + - SRCHUSR=DOB=20240129; domain=.bing.com; expires=Thu, 29-Jan-2026 21:31:26 + GMT; path=/ + - SRCHHPGUSR=SRCHLANG=en&IG=E74C6BD23B3247DBB915E11412298CBE; domain=.bing.com; + expires=Thu, 29-Jan-2026 21:31:26 GMT; path=/ + - _SS=SID=3CEC1A4B7B6862D4071E0E5E7A4563FF; domain=.bing.com; path=/ + - ULC=; domain=.bing.com; expires=Sun, 28-Jan-2024 21:31:26 GMT; path=/ + - _HPVN=CS=eyJQbiI6eyJDbiI6MSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiUCJ9LCJTYyI6eyJDbiI6MSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiSCJ9LCJReiI6eyJDbiI6MSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiVCJ9LCJBcCI6dHJ1ZSwiTXV0ZSI6dHJ1ZSwiTGFkIjoiMjAyNC0wMS0yOVQwMDowMDowMFoiLCJJb3RkIjowLCJHd2IiOjAsIlRucyI6MCwiRGZ0IjpudWxsLCJNdnMiOjAsIkZsdCI6MCwiSW1wIjoxLCJUb2JuIjowfQ==; + domain=.bing.com; expires=Thu, 29-Jan-2026 21:31:26 GMT; path=/ + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Useragentreductionoptout: + - A7kgTC5xdZ2WIVGZEfb1hUoNuvjzOZX3VIV/BA6C18kQOOF50Q0D3oWoAm49k3BQImkujKILc7JmPysWk3CSjwUAAACMeyJvcmlnaW4iOiJodHRwczovL3d3dy5iaW5nLmNvbTo0NDMiLCJmZWF0dXJlIjoiU2VuZEZ1bGxVc2VyQWdlbnRBZnRlclJlZHVjdGlvbiIsImV4cGlyeSI6MTY4NDg4NjM5OSwiaXNTdWJkb21haW4iOnRydWUsImlzVGhpcmRQYXJ0eSI6dHJ1ZX0= + Vary: + - Accept-Encoding + X-Cdn-Traceid: + - 0.54e6ca17.1706563886.1f2c7a48 + X-Eventid: + - 65b8192e0a61460a981d54c8cb13bcce + status: 200 OK + code: 200 + duration: "" diff --git a/v2/internal/testcommon/vcr/v1/test_replayer.go b/v2/internal/testcommon/vcr/v1/test_replayer.go new file mode 100644 index 00000000000..2f91b6c4436 --- /dev/null +++ b/v2/internal/testcommon/vcr/v1/test_replayer.go @@ -0,0 +1,138 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + "bytes" + "io" + "net/http" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/dnaeon/go-vcr/cassette" + "github.com/dnaeon/go-vcr/recorder" + "github.com/google/uuid" + "github.com/pkg/errors" + + "github.com/Azure/azure-service-operator/v2/internal/config" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/creds" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" +) + +// player is an implementation of testRecorder using go-vcr v1 that can only play back +// test recordings, not record them. +type player struct { + cassetteName string + creds azcore.TokenCredential + ids creds.AzureIDs + recorder *recorder.Recorder + cfg config.Values +} + +// Verify we implement testRecorder +var _ vcr.Interface = &player{} + +// NewTestPlayer creates a TestRecorder that can be used to replay test recorded with go-vcr v1. +// cassetteName is the name of the cassette file to replay. +// cfg is the configuration to use when replaying the test. +func NewTestPlayer( + cassetteName string, + cfg config.Values, +) (vcr.Interface, error) { + cassetteExists, err := vcr.CassetteFileExists(cassetteName) + if err != nil { + return nil, errors.Wrapf(err, "checking for cassette file") + } + if !cassetteExists { + return nil, errors.Errorf("cassette %s does not exist", cassetteName) + } + + r, err := recorder.NewAsMode(cassetteName, recorder.ModeReplaying, nil) + if err != nil { + return nil, errors.Wrapf(err, "creating player") + } + + var credentials azcore.TokenCredential + var azureIDs creds.AzureIDs + + // if We are replaying, we won't need auth + // and we use a dummy subscription ID/tenant ID + credentials = creds.MockTokenCredential{} + azureIDs.TenantID = uuid.Nil.String() + azureIDs.SubscriptionID = uuid.Nil.String() + azureIDs.BillingInvoiceID = creds.DummyBillingId + + // Force these values to be the default + cfg.ResourceManagerEndpoint = config.DefaultEndpoint + cfg.ResourceManagerAudience = config.DefaultAudience + cfg.AzureAuthorityHost = config.DefaultAADAuthorityHost + + // check body as well as URL/Method (copied from go-vcr documentation) + r.SetMatcher(func(r *http.Request, i cassette.Request) bool { + if !cassette.DefaultMatcher(r, i) { + return false + } + + // verify custom request count header (see counting_roundtripper.go) + if r.Header.Get(vcr.COUNT_HEADER) != i.Headers.Get(vcr.COUNT_HEADER) { + return false + } + + if r.Body == nil { + return i.Body == "" + } + + var b bytes.Buffer + if _, err := b.ReadFrom(r.Body); err != nil { + panic(err) + } + + r.Body = io.NopCloser(&b) + return b.String() == "" || vcr.HideRecordingData(b.String()) == i.Body + }) + + return &player{ + cassetteName: cassetteName, + creds: credentials, + ids: azureIDs, + recorder: r, + cfg: cfg, + }, nil +} + +// Cfg returns the available configuration for the test +func (r *player) Cfg() config.Values { + return r.cfg +} + +// Creds returns Azure credentials when running for real +func (r *player) Creds() azcore.TokenCredential { + return r.creds +} + +// IDs returns the available Azure resource IDs for the test +func (r *player) IDs() creds.AzureIDs { + return r.ids +} + +// Stop recording +func (r *player) Stop() error { + return r.recorder.Stop() +} + +// IsReplaying returns true if we're replaying a recorded test, false if we're recording a new test +func (r *player) IsReplaying() bool { + return r.recorder.Mode() == recorder.ModeReplaying +} + +// CreateClient creates an HTTP client configured to record or replay HTTP requests. +// t is a reference to the test currently executing. +// TODO: Remove the reference to t to reduce coupling +func (r *player) CreateClient(t *testing.T) *http.Client { + return &http.Client{ + Transport: vcr.AddCountHeader(translateErrors(r.recorder, r.cassetteName, t)), + } +} diff --git a/v2/internal/testcommon/vcr/v1/test_replayer_test.go b/v2/internal/testcommon/vcr/v1/test_replayer_test.go new file mode 100644 index 00000000000..f3a4ae48754 --- /dev/null +++ b/v2/internal/testcommon/vcr/v1/test_replayer_test.go @@ -0,0 +1,43 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + "context" + "io" + "net/http" + "testing" + + . "github.com/onsi/gomega" + + "github.com/Azure/azure-service-operator/v2/internal/config" +) + +func TestReplayer_WhenRecordingExists_ReturnsResult(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + + cfg := config.Values{} + cassetteName := "recordings/" + t.Name() + replayer, err := NewTestPlayer(cassetteName, cfg) + g.Expect(err).To(BeNil()) + + url := "https://www.bing.com" + client := replayer.CreateClient(t) + + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil) + g.Expect(err).To(BeNil()) + + resp, err := client.Do(req) + g.Expect(err).To(BeNil()) + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + g.Expect(err).To(BeNil()) + g.Expect(body).NotTo(HaveLen(0)) + + g.Expect(replayer.Stop()).To(Succeed()) +} diff --git a/v2/internal/testcommon/error_translating_roundtripper.go b/v2/internal/testcommon/vcr/v3/error_translating_roundtripper.go similarity index 87% rename from v2/internal/testcommon/error_translating_roundtripper.go rename to v2/internal/testcommon/vcr/v3/error_translating_roundtripper.go index a4206962dba..6f3016ba7a0 100644 --- a/v2/internal/testcommon/error_translating_roundtripper.go +++ b/v2/internal/testcommon/vcr/v3/error_translating_roundtripper.go @@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation. Licensed under the MIT license. */ -package testcommon +package v3 import ( "fmt" @@ -12,11 +12,11 @@ import ( "strings" "testing" - "github.com/dnaeon/go-vcr/cassette" - "github.com/dnaeon/go-vcr/recorder" "github.com/google/go-cmp/cmp" "github.com/pkg/errors" + "gopkg.in/dnaeon/go-vcr.v3/cassette" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" "github.com/Azure/azure-service-operator/v2/pkg/genruntime/conditions" ) @@ -32,12 +32,12 @@ import ( // - during record the controller does GET (404), PUT, … GET (OK) // - during playback the controller does GET (which now returns OK), DELETE, PUT, … // and fails due to a missing DELETE recording -func translateErrors(r *recorder.Recorder, cassetteName string, t *testing.T) http.RoundTripper { +func translateErrors(r http.RoundTripper, cassetteName string, t *testing.T) http.RoundTripper { return errorTranslation{r, cassetteName, nil, t} } type errorTranslation struct { - recorder *recorder.Recorder + recorder http.RoundTripper cassetteName string cassette *cassette.Cassette @@ -74,7 +74,7 @@ func (w errorTranslation) RoundTrip(req *http.Request) (*http.Response, error) { // Apply the same body filtering that we do in recordings so that the diffs don't show things // that we've just removed - sentBodyString = hideRecordingData(string(bodyBytes)) + sentBodyString = vcr.HideRecordingData(string(bodyBytes)) } // find all request bodies for the specified method/URL combination @@ -87,7 +87,7 @@ func (w errorTranslation) RoundTrip(req *http.Request) (*http.Response, error) { w.cassetteName, req.Method, req.URL.String(), - req.Header.Get(COUNT_HEADER)), + req.Header.Get(vcr.COUNT_HEADER)), conditions.ConditionSeverityError, conditions.ReasonReconciliationFailedPermanently) } @@ -117,8 +117,8 @@ func (w errorTranslation) findMatchingBodies(r *http.Request) []string { urlString := r.URL.String() var result []string for _, interaction := range w.ensureCassette().Interactions { - if urlString == interaction.URL && r.Method == interaction.Request.Method && - r.Header.Get(COUNT_HEADER) == interaction.Request.Headers.Get(COUNT_HEADER) { + if urlString == interaction.Request.RequestURI && r.Method == interaction.Request.Method && + r.Header.Get(vcr.COUNT_HEADER) == interaction.Request.Headers.Get(vcr.COUNT_HEADER) { result = append(result, interaction.Request.Body) } } diff --git a/v2/internal/testcommon/vcr/v3/recordings/TestRecorder_WhenRecordingAndRecordingExists_DoesPlayback.yaml b/v2/internal/testcommon/vcr/v3/recordings/TestRecorder_WhenRecordingAndRecordingExists_DoesPlayback.yaml new file mode 100644 index 00000000000..b3dd8926693 --- /dev/null +++ b/v2/internal/testcommon/vcr/v3/recordings/TestRecorder_WhenRecordingAndRecordingExists_DoesPlayback.yaml @@ -0,0 +1,64 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: www.bing.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Test-Request-Attempt: + - "0" + url: https://www.bing.com + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: -1 + uncompressed: true + body: "Bing
" + headers: + Alt-Svc: + - h3=":443"; ma=93600 + Cache-Control: + - private + Content-Type: + - text/html; charset=utf-8 + P3p: + - CP="NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND" + Set-Cookie: + - MUID=0466C7A7010A6EDC2019D3BF00636FBB; domain=.bing.com; expires=Tue, 25-Feb-2025 03:49:38 GMT; path=/; secure; SameSite=None + - MUIDB=0466C7A7010A6EDC2019D3BF00636FBB; expires=Tue, 25-Feb-2025 03:49:38 GMT; path=/; HttpOnly + - _EDGE_S=F=1&SID=1CBBB52565B261C3353AA13D64DB60EE; domain=.bing.com; path=/; HttpOnly + - _EDGE_V=1; domain=.bing.com; expires=Tue, 25-Feb-2025 03:49:38 GMT; path=/; HttpOnly + - SRCHD=AF=NOFORM; domain=.bing.com; expires=Sun, 01-Feb-2026 03:49:38 GMT; path=/ + - SRCHUID=V=2&GUID=EDE3B6F1E8F8406086C2CB8E15FDC2A1&dmnchg=1; domain=.bing.com; expires=Sun, 01-Feb-2026 03:49:38 GMT; path=/ + - SRCHUSR=DOB=20240201; domain=.bing.com; expires=Sun, 01-Feb-2026 03:49:38 GMT; path=/ + - SRCHHPGUSR=SRCHLANG=en&IG=EB26FB3DD1B844F48D4B7A7F74D40C87; domain=.bing.com; expires=Sun, 01-Feb-2026 03:49:38 GMT; path=/ + - _SS=SID=1CBBB52565B261C3353AA13D64DB60EE; domain=.bing.com; path=/ + - ULC=; domain=.bing.com; expires=Wed, 31-Jan-2024 03:49:38 GMT; path=/ + - _HPVN=CS=eyJQbiI6eyJDbiI6MSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiUCJ9LCJTYyI6eyJDbiI6MSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiSCJ9LCJReiI6eyJDbiI6MSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiVCJ9LCJBcCI6dHJ1ZSwiTXV0ZSI6dHJ1ZSwiTGFkIjoiMjAyNC0wMi0wMVQwMDowMDowMFoiLCJJb3RkIjowLCJHd2IiOjAsIlRucyI6MCwiRGZ0IjpudWxsLCJNdnMiOjAsIkZsdCI6MCwiSW1wIjoxLCJUb2JuIjowfQ==; domain=.bing.com; expires=Sun, 01-Feb-2026 03:49:38 GMT; path=/ + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Useragentreductionoptout: + - A7kgTC5xdZ2WIVGZEfb1hUoNuvjzOZX3VIV/BA6C18kQOOF50Q0D3oWoAm49k3BQImkujKILc7JmPysWk3CSjwUAAACMeyJvcmlnaW4iOiJodHRwczovL3d3dy5iaW5nLmNvbTo0NDMiLCJmZWF0dXJlIjoiU2VuZEZ1bGxVc2VyQWdlbnRBZnRlclJlZHVjdGlvbiIsImV4cGlyeSI6MTY4NDg4NjM5OSwiaXNTdWJkb21haW4iOnRydWUsImlzVGhpcmRQYXJ0eSI6dHJ1ZX0= + Vary: + - Accept-Encoding + X-Cdn-Traceid: + - 0.d2f0d317.1706759378.4d4f688 + X-Eventid: + - 65bb14d2e9c347c29d205a69ade84c64 + status: 200 OK + code: 200 + duration: 277.448705ms diff --git a/v2/internal/testcommon/vcr/v3/replay_roundtripper.go b/v2/internal/testcommon/vcr/v3/replay_roundtripper.go new file mode 100644 index 00000000000..944ca731e77 --- /dev/null +++ b/v2/internal/testcommon/vcr/v3/replay_roundtripper.go @@ -0,0 +1,153 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v3 + +import ( + "bytes" + "crypto/sha256" + "errors" + "fmt" + "io" + "net/http" + "sync" + + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" + "github.com/go-logr/logr" + "gopkg.in/dnaeon/go-vcr.v3/cassette" +) + +// replayRoundTripper wraps an inner round tripper and replays requests in order to improve the resilience of ASO tests. +// +// PUT requests are cached by hash of the (sanitised) PUT body and may be replayed ONCE if an extra PUT occurs. +// +// GET requests are cached by target URL, and may be replayed multiple times. +// +// This combination should allow additional reconciles - an extra PUT gets returned the same long running operation as +// the original, which a GET then shows is complete. +type replayRoundTripper struct { + inner http.RoundTripper + gets map[string]*http.Response + puts map[string]*http.Response + log logr.Logger + padlock sync.Mutex +} + +var _ http.RoundTripper = &replayRoundTripper{} + +// newReplayRoundTripper creates a new replayRoundTripper that will replay selected requests to improve test resilience. +func NewReplayRoundTripper( + inner http.RoundTripper, + log logr.Logger, +) http.RoundTripper { + return &replayRoundTripper{ + inner: inner, + gets: make(map[string]*http.Response), + puts: make(map[string]*http.Response), + log: log, + } +} + +// RoundTrip implements http.RoundTripper. +func (replayer *replayRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { + if request.Method == http.MethodGet { + return replayer.roundTripGet(request) + } + + if request.Method == http.MethodPut { + return replayer.roundTripPut(request) + } + + // For other kinds of request, just pass through to the inner round tripper. + return replayer.inner.RoundTrip(request) +} + +func (replayer *replayRoundTripper) roundTripGet(request *http.Request) (*http.Response, error) { + // First use our inner round tripper to get the response. + response, err := replayer.inner.RoundTrip(request) + if err != nil { + // We have an error - return it, unless it's from go-vcr + if !errors.Is(err, cassette.ErrInteractionNotFound) { + return response, err + } + + replayer.padlock.Lock() + defer replayer.padlock.Unlock() + + // We didn't find an interaction, see if we have a cached response to return + if cachedResponse, ok := replayer.gets[request.URL.String()]; ok { + replayer.log.Info("Replaying GET request", "url", request.URL.String()) + return cachedResponse, nil + } + + // No cached response, return the original response and error + return response, err + } + + replayer.padlock.Lock() + defer replayer.padlock.Unlock() + + // We have a response, cache it and return it + replayer.gets[request.URL.String()] = response + return response, nil +} + +func (replayer *replayRoundTripper) roundTripPut(request *http.Request) (*http.Response, error) { + // Calculate a hash of the request body to use as a cache key + // We need this whether we are updating our cache or replaying + hash := replayer.hashOfBody(request) + + response, err := replayer.inner.RoundTrip(request) + if err != nil { + // We have an error - return it, unless it's from go-vcr + if !errors.Is(err, cassette.ErrInteractionNotFound) { + return response, err + } + + replayer.padlock.Lock() + defer replayer.padlock.Unlock() + + // We didn't find an interaction, see if we have a cached response to return + if cachedResponse, ok := replayer.puts[hash]; ok { + // Remove it from the cache to ensure we only replay it once + delete(replayer.puts, hash) + replayer.log.Info("Replaying PUT request", "url", request.URL.String(), "hash", hash) + return cachedResponse, nil + } + + // No cached response, return the original response and error + return response, err + } + + replayer.padlock.Lock() + defer replayer.padlock.Unlock() + + // We have a response, cache it and return it + replayer.puts[hash] = response + return response, nil +} + +// hashOfBody calculates a hash of the body of a request, for use as a cache key. +// The body is santised before calculating the hash to ensure that the same request body always results in the same hash. +func (replayer *replayRoundTripper) hashOfBody(request *http.Request) string { + // Read all the content of the request body + var body bytes.Buffer + _, err := body.ReadFrom(request.Body) + if err != nil { + // Should never fail + panic(fmt.Sprintf("reading request.Body failed: %s", err)) + } + + // Apply the same body filtering that we do in recordings so that the hash is consistent + bodyString := vcr.HideRecordingData(string(body.Bytes())) + + // Calculate a hash based on body string + hash := sha256.Sum256([]byte(bodyString)) + + // Reset the body so it can be read again + request.Body = io.NopCloser(&body) + + return fmt.Sprintf("%x", hash) +} diff --git a/v2/internal/testcommon/vcr/v3/replay_roundtripper_test.go b/v2/internal/testcommon/vcr/v3/replay_roundtripper_test.go new file mode 100644 index 00000000000..5efa4fae2b7 --- /dev/null +++ b/v2/internal/testcommon/vcr/v3/replay_roundtripper_test.go @@ -0,0 +1,204 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v3 + +import ( + "bytes" + "io" + "net/http" + "net/url" + "strings" + "testing" + + . "github.com/onsi/gomega" + + "github.com/go-logr/logr" + + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" +) + +func TestReplayRoundTripperRoundTrip_GivenSingleGET_ReturnsMultipleTimes(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + + // Arrange + req := &http.Request{ + URL: &url.URL{Path: "/foo"}, + Method: http.MethodGet, + Body: io.NopCloser(strings.NewReader("GET body goes here")), + } + + resp := &http.Response{ + StatusCode: 200, + } + + fake := vcr.NewFakeRoundTripper() + fake.AddResponse(req, resp) + + // Act + replayer := NewReplayRoundTripper(fake, logr.Discard()) + + // Assert - first request works + //nolint:bodyclose // there's no actual body in this response to close + resp, err := replayer.RoundTrip(req) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp.StatusCode).To(Equal(200)) + + // Assert - second request works by replaying the first + //nolint:bodyclose // there's no actual body in this response to close + resp, err = replayer.RoundTrip(req) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp.StatusCode).To(Equal(200)) + + // Assert - third request works by replaying the first + //nolint:bodyclose // there's no actual body in this response to close + resp, err = replayer.RoundTrip(req) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp.StatusCode).To(Equal(200)) +} + +func TestReplayRoundTripperRoundTrip_GivenSinglePut_ReturnsOnceExtra(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + + // Arrange + req := &http.Request{ + URL: &url.URL{Path: "/foo"}, + Method: http.MethodPut, + Body: io.NopCloser(strings.NewReader("PUT body goes here")), + } + + resp := &http.Response{ + StatusCode: 200, + } + + fake := vcr.NewFakeRoundTripper() + fake.AddResponse(req, resp) + + // Act + replayer := NewReplayRoundTripper(fake, logr.Discard()) + + // Assert - first request works + //nolint:bodyclose // there's no actual body in this response to close + resp, err := replayer.RoundTrip(req) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp.StatusCode).To(Equal(200)) + + // Assert - second request works by replaying the first + //nolint:bodyclose // there's no actual body in this response to close + resp, err = replayer.RoundTrip(req) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp.StatusCode).To(Equal(200)) + + // Assert - third request fails because we've had our one replay + //nolint:bodyclose // there's no actual body in this response to close + _, err = replayer.RoundTrip(req) + g.Expect(err).To(HaveOccurred()) +} + +func TestReplayRoundTripperRoundTrip_GivenMultiplePUTsToSameURL_ReturnsExpectedBodies(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + + // Arrange + alphaRequest := &http.Request{ + URL: &url.URL{Path: "/foo"}, + Method: http.MethodPut, + Body: io.NopCloser(strings.NewReader("PUT body Alpha goes here")), + } + + alphaResponse := &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader("PUT response Alpha goes here")), + } + + betaRequest := &http.Request{ + URL: &url.URL{Path: "/foo"}, + Method: http.MethodPut, + Body: io.NopCloser(strings.NewReader("PUT body Beta goes here")), + } + + betaResponse := &http.Response{ + StatusCode: 203, + Body: io.NopCloser(strings.NewReader("PUT response Beta goes here")), + } + + gammaRequest := &http.Request{ + URL: &url.URL{Path: "/foo"}, + Method: http.MethodPut, + Body: io.NopCloser(strings.NewReader("PUT body Gamma goes here")), + } + + gammaResponse := &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader("PUT response Gamma goes here")), + } + + fake := vcr.NewFakeRoundTripper() + fake.AddResponse(alphaRequest, alphaResponse) + fake.AddResponse(betaRequest, betaResponse) + fake.AddResponse(gammaRequest, gammaResponse) + + // Act + replayer := NewReplayRoundTripper(fake, logr.Discard()) + + // Assert - first alpha request works + //nolint:bodyclose // there's no actual body in this response to close + actual, err := replayer.RoundTrip(alphaRequest) + g.Expect(err).ToNot(HaveOccurred()) + assertResponse(t, actual, 200, "PUT response Alpha goes here") + + // Assert - first beta request works + //nolint:bodyclose // there's no actual body in this response to close + actual, err = replayer.RoundTrip(betaRequest) + g.Expect(err).ToNot(HaveOccurred()) + assertResponse(t, actual, 203, "PUT response Beta goes here") + + // Assert - first gamma request works + //nolint:bodyclose // there's no actual body in this response to close + actual, err = replayer.RoundTrip(gammaRequest) + g.Expect(err).ToNot(HaveOccurred()) + assertResponse(t, actual, 200, "PUT response Gamma goes here") + + // Assert - second alpha request works by replaying the first + //nolint:bodyclose // there's no actual body in this response to close + actual, err = replayer.RoundTrip(alphaRequest) + g.Expect(err).ToNot(HaveOccurred()) + assertResponse(t, actual, 200, "PUT response Alpha goes here") + + // Assert - second beta request works by replaying the first + //nolint:bodyclose // there's no actual body in this response to close + actual, err = replayer.RoundTrip(betaRequest) + g.Expect(err).ToNot(HaveOccurred()) + assertResponse(t, actual, 203, "PUT response Beta goes here") + + // Assert - second gamma request works by replaying the first + //nolint:bodyclose // there's no actual body in this response to close + actual, err = replayer.RoundTrip(gammaRequest) + g.Expect(err).ToNot(HaveOccurred()) + assertResponse(t, actual, 200, "PUT response Gamma goes here") +} + +func assertResponse( + t *testing.T, + response *http.Response, + expectedStatus int, + expectedBodyContent string, +) { + t.Helper() + g := NewGomegaWithT(t) + + g.Expect(response.StatusCode).To(Equal(expectedStatus)) + + var body bytes.Buffer + _, err := body.ReadFrom(response.Body) + g.Expect(err).ToNot(HaveOccurred()) + + g.Expect(string(body.String())).To(ContainSubstring(expectedBodyContent)) + + // Reset the body so it can be read again + response.Body = io.NopCloser(&body) +} diff --git a/v2/internal/testcommon/vcr/v3/test_recorder.go b/v2/internal/testcommon/vcr/v3/test_recorder.go new file mode 100644 index 00000000000..edfca8ae029 --- /dev/null +++ b/v2/internal/testcommon/vcr/v3/test_recorder.go @@ -0,0 +1,230 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v3 + +import ( + "bytes" + "io" + "net/http" + "strings" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/go-logr/logr" + "github.com/google/uuid" + "github.com/pkg/errors" + "gopkg.in/dnaeon/go-vcr.v3/cassette" + "gopkg.in/dnaeon/go-vcr.v3/recorder" + + "github.com/Azure/azure-service-operator/v2/internal/config" + "github.com/Azure/azure-service-operator/v2/internal/genericarmclient" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/creds" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" +) + +// recorderDetails is an implementation of testRecorder using go-vcr v3. +type recorderDetails struct { + cassetteName string + creds azcore.TokenCredential + ids creds.AzureIDs + recorder *recorder.Recorder + cfg config.Values + log logr.Logger +} + +var nilGuid = uuid.Nil.String() + +func NewTestRecorder( + cassetteName string, + cfg config.Values, + log logr.Logger, +) (vcr.Interface, error) { + opts := &recorder.Options{ + CassetteName: cassetteName, + } + + cassetteExists, err := vcr.CassetteFileExists(cassetteName) + if err != nil { + return nil, errors.Wrapf(err, "checking existence of cassette %s", cassetteName) + } + + // Work out whether we are recording or replaying + if cassetteExists { + opts.Mode = recorder.ModeReplayOnly + } else { + opts.Mode = recorder.ModeRecordOnly + } + + r, err := recorder.NewWithOptions(opts) + if err != nil { + return nil, errors.Wrapf(err, "creating recorder") + } + + var credentials azcore.TokenCredential + var azureIDs creds.AzureIDs + if r.Mode() == recorder.ModeRecordOnly { + // if we are recording, we need auth + credentials, azureIDs, err = creds.GetCreds() + if err != nil { + return nil, err + } + } else { + // if we are replaying, we won't need auth + // and we use a dummy subscription ID/tenant ID + credentials = creds.MockTokenCredential{} + azureIDs.TenantID = nilGuid + azureIDs.SubscriptionID = nilGuid + azureIDs.BillingInvoiceID = creds.DummyBillingId + + // Force these values to be the default + cfg.ResourceManagerEndpoint = config.DefaultEndpoint + cfg.ResourceManagerAudience = config.DefaultAudience + cfg.AzureAuthorityHost = config.DefaultAADAuthorityHost + } + + // check body as well as URL/Method (copied from go-vcr documentation) + r.SetMatcher(func(r *http.Request, i cassette.Request) bool { + if !cassette.DefaultMatcher(r, i) { + return false + } + + // verify custom request count header (see counting_roundtripper.go) + if r.Header.Get(vcr.COUNT_HEADER) != i.Headers.Get(vcr.COUNT_HEADER) { + return false + } + + if r.Body == nil { + return i.Body == "" + } + + var b bytes.Buffer + if _, err := b.ReadFrom(r.Body); err != nil { + panic(err) + } + + r.Body = io.NopCloser(&b) + return b.String() == "" || vcr.HideRecordingData(b.String()) == i.Body + }) + + r.AddHook(redactRecording(azureIDs), recorder.BeforeSaveHook) + + return &recorderDetails{ + cassetteName: cassetteName, + creds: credentials, + ids: azureIDs, + recorder: r, + cfg: cfg, + log: log, + }, nil +} + +// redactRecording is a BeforeSaveHook that rewrites data in the cassette +// This incldues hiding the SubscriptionID, TenantID, and BillingInvoiceID, but is not +// a a security measure but intended to make the tests updateable from +// any subscription, so a contributor can update the tests against their own sub. +func redactRecording( + azureIDs creds.AzureIDs, +) recorder.HookFunc { + hide := func(s string, id string, replacement string) string { + return strings.ReplaceAll(s, id, replacement) + } + + return func(i *cassette.Interaction) error { + // Note that this changes the cassette in-place so there's no return needed + hideCassetteString := func(cas *cassette.Interaction, id string, replacement string) { + i.Request.Body = hide(cas.Request.Body, id, replacement) + i.Response.Body = hide(cas.Response.Body, id, replacement) + i.Request.URL = hide(cas.Request.URL, id, replacement) + } + + // Hide the subscription ID + hideCassetteString(i, azureIDs.SubscriptionID, nilGuid) + + // Hide the tenant ID + hideCassetteString(i, azureIDs.TenantID, nilGuid) + + // Hide the billing ID + if azureIDs.BillingInvoiceID != "" { + hideCassetteString(i, azureIDs.BillingInvoiceID, creds.DummyBillingId) + } + + // Hiding other sensitive fields + i.Request.Body = vcr.HideRecordingData(i.Request.Body) + i.Response.Body = vcr.HideRecordingData(i.Response.Body) + i.Request.URL = vcr.HideURLData(i.Request.URL) + + // Hide sensitive request headers + for _, values := range i.Request.Headers { + for i := range values { + values[i] = hide(values[i], azureIDs.SubscriptionID, nilGuid) + values[i] = hide(values[i], azureIDs.TenantID, nilGuid) + if azureIDs.BillingInvoiceID != "" { + values[i] = hide(values[i], azureIDs.BillingInvoiceID, creds.DummyBillingId) + } + } + } + + // Hide sensitive response headers + for key, values := range i.Response.Headers { + for i := range values { + values[i] = hide(values[i], azureIDs.SubscriptionID, nilGuid) + values[i] = hide(values[i], azureIDs.TenantID, nilGuid) + if azureIDs.BillingInvoiceID != "" { + values[i] = hide(values[i], azureIDs.BillingInvoiceID, creds.DummyBillingId) + } + } + + // Hide the base request URL in the AzureOperation and Location headers + if key == genericarmclient.AsyncOperationHeader || key == genericarmclient.LocationHeader { + for i := range values { + values[i] = vcr.HideBaseRequestURL(values[i]) + } + } + } + + vcr.RedactRequestHeaders(i.Request.Headers) + vcr.RedactResponseHeaders(i.Response.Headers) + + return nil + } +} + +// Cfg returns the available configuration for the test +func (r *recorderDetails) Cfg() config.Values { + return r.cfg +} + +// Creds returns Azure credentials when running for real +func (r *recorderDetails) Creds() azcore.TokenCredential { + return r.creds +} + +// IDs returns the available Azure resource IDs for the test +func (r *recorderDetails) IDs() creds.AzureIDs { + return r.ids +} + +// Stop recording +func (r *recorderDetails) Stop() error { + return r.recorder.Stop() +} + +// IsReplaying returns true if we're replaying a recorded test, false if we're recording a new test +func (r *recorderDetails) IsReplaying() bool { + return r.recorder.Mode() == recorder.ModeReplayOnly +} + +// CreateClient creates an HTTP client configured to record or replay HTTP requests. +// t is a reference to the test currently executing. +func (r *recorderDetails) CreateClient(t *testing.T) *http.Client { + withReplay := NewReplayRoundTripper(r.recorder, r.log) + withErrorTranslation := translateErrors(withReplay, r.cassetteName, t) + withCountHeader := vcr.AddCountHeader(withErrorTranslation) + + return &http.Client{ + Transport: withCountHeader, + } +} diff --git a/v2/internal/testcommon/vcr/v3/test_recorder_test.go b/v2/internal/testcommon/vcr/v3/test_recorder_test.go new file mode 100644 index 00000000000..59c821c0268 --- /dev/null +++ b/v2/internal/testcommon/vcr/v3/test_recorder_test.go @@ -0,0 +1,127 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v3 + +import ( + "context" + "io" + "net/http" + "os" + "testing" + + "github.com/go-logr/logr" + . "github.com/onsi/gomega" + + "github.com/Azure/azure-service-operator/v2/internal/config" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/vcr" +) + +//nolint:paralleltest +func TestRecorder_WhenRecordingAndRecordingDoesNotExist_MakesRecording(t *testing.T) { + // NB: Can't run tests using Setenv() in parallel + t.Setenv("AZURE_SUBSCRIPTION_ID", "00000000-0000-0000-0000-000000000000") + t.Setenv("AZURE_TENANT_ID", "00000000-0000-0000-0000-000000000000") + + g := NewGomegaWithT(t) + + cfg := config.Values{} + cassetteName := "recordings/" + t.Name() + + // Test prerequisite: The recording must not already exist + // We delete the recording at the end of the test, so it shouldn't ever be committed + // But if something goes awry, and we don't clean up, we need to flag the presence of + // this file as a failure. + // We're noisy about it (instead of just deleting the file proactively) in order to + // ensure the dev knows the file shouldn't be committed. + exists, err := vcr.CassetteFileExists(cassetteName) + g.Expect(err).To(BeNil()) + g.Expect(exists).To(BeFalse()) + + // Ensure we clean up the cassette file at the end of the test + cassetteFile := vcr.CassetteFileName(cassetteName) + defer func() { + g.Expect(os.Remove(cassetteFile)).To(Succeed()) + }() + + // Create our TestRecorder and ensure it's recording + recorder, err := NewTestRecorder(cassetteName, cfg, logr.Discard()) + g.Expect(err).To(BeNil()) + g.Expect(recorder.IsReplaying()).To(BeFalse()) + + url := "https://www.bing.com" + client := recorder.CreateClient(t) + + // Make sure we can get a response from the internet + //nolint:noctx + resp, err := client.Get(url) + g.Expect(err).To(BeNil()) + defer resp.Body.Close() + + // Ensure the body is not empty + body, err := io.ReadAll(resp.Body) + g.Expect(err).To(BeNil()) + g.Expect(body).NotTo(HaveLen(0)) + + // Stop the recorder + err = recorder.Stop() + g.Expect(err).To(BeNil()) + + // Verify we created a recording + exists, err = vcr.CassetteFileExists(cassetteName) + g.Expect(err).To(BeNil()) + g.Expect(exists).To(BeTrue()) +} + +func TestRecorder_WhenRecordingAndRecordingExists_DoesPlayback(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + + // + // Rerecording this test can be a challenge, due to + // (a) a prerequisite check ensuring the cassette exists + // (b) a similar check ensuring we're in replay mode, and + // (c) the requirement for selected environment variables to be present. + // + // The easiest way to rerecord this test is to: + // (i) Delete the cassette file + // (ii) Comment out the prerequisite checks + // (iii) Define the required environment variables + // (iv) go test -run TestRecorderV3_WhenRecordingAndRecordingExists_DoesPlayback + // + + cfg := config.Values{} + cassetteName := "recordings/" + t.Name() + + // Test prerequisite: The recording must already exist + exists, err := vcr.CassetteFileExists(cassetteName) + g.Expect(err).To(BeNil()) + g.Expect(exists).To(BeTrue()) + + // Create our TestRecorder and ensure it's recording + recorder, err := NewTestRecorder(cassetteName, cfg, logr.Discard()) + g.Expect(err).To(BeNil()) + g.Expect(recorder.IsReplaying()).To(BeTrue()) + + url := "https://www.bing.com" + client := recorder.CreateClient(t) + + // Make sure we can get a response from the internet + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil) + g.Expect(err).To(BeNil()) + + resp, err := client.Do(req) + g.Expect(err).To(BeNil()) + defer resp.Body.Close() + + // Ensure the body is not empty + body, err := io.ReadAll(resp.Body) + g.Expect(err).To(BeNil()) + g.Expect(body).NotTo(HaveLen(0)) + + // Stop the recorder + err = recorder.Stop() + g.Expect(err).To(BeNil()) +} diff --git a/v2/internal/testcommon/verify.go b/v2/internal/testcommon/verify.go index fffba9d1467..b412b65ec7d 100644 --- a/v2/internal/testcommon/verify.go +++ b/v2/internal/testcommon/verify.go @@ -34,8 +34,8 @@ func (e *Verify) HasState( obj client.Object, desiredState metav1.ConditionStatus, desiredSeverity conditions.ConditionSeverity, - oldGeneration int64) (bool, error) { - + oldGeneration int64, +) (bool, error) { key := client.ObjectKeyFromObject(obj) // In order to ensure that "old state" is cleared out from obj, we need to: diff --git a/v2/internal/testcommon/wait.go b/v2/internal/testcommon/wait.go index 5a67f592648..00ff7b481b6 100644 --- a/v2/internal/testcommon/wait.go +++ b/v2/internal/testcommon/wait.go @@ -41,8 +41,8 @@ func WaitFor(ctx context.Context, timeout time.Duration, check func(context.Cont func SetupTeardownTestMain( m *testing.M, setup func() error, - teardown func() error) int { - + teardown func() error, +) int { // safety check before calling testing.Short() if !flag.CommandLine.Parsed() { flag.Parse() diff --git a/v2/internal/util/azuresql/azuresql.go b/v2/internal/util/azuresql/azuresql.go index 04620089669..965a35f27de 100644 --- a/v2/internal/util/azuresql/azuresql.go +++ b/v2/internal/util/azuresql/azuresql.go @@ -122,7 +122,6 @@ ELSE // username can be either the actual AAD username (for real AAD users), the group name for groups, or // the managed identity name (app) for managed identities. func CreateOrUpdateAADUser(ctx context.Context, db *sql.DB, username string) error { - //"CREATE USER [Azure_AD_user_name] FROM EXTERNAL PROVIDER;" //"CREATE USER [bob@contoso.com] FROM EXTERNAL PROVIDER;" //"CREATE USER [alice@fabrikam.onmicrosoft.com] FROM EXTERNAL PROVIDER;" diff --git a/v2/internal/util/azuresql/role.go b/v2/internal/util/azuresql/role.go index e78132c3b4c..7971c6ee8cb 100644 --- a/v2/internal/util/azuresql/role.go +++ b/v2/internal/util/azuresql/role.go @@ -147,7 +147,6 @@ func alterRoles(ctx context.Context, db *sql.DB, user string, roles set.Set[stri return errors.Wrap(err, "problem found with role") } _, err := builder.WriteString(fmt.Sprintf("ALTER ROLE %s %s MEMBER %s;\n", role, string(mode), user)) - if err != nil { return errors.Wrapf(err, "failed to build T-SQL ALTER ROLE %s statement", mode) } diff --git a/v2/internal/util/azuresql/role_test.go b/v2/internal/util/azuresql/role_test.go index 93da6ae174a..1064cf32e45 100644 --- a/v2/internal/util/azuresql/role_test.go +++ b/v2/internal/util/azuresql/role_test.go @@ -52,7 +52,6 @@ func TestDiffCurrentAndExpectedSQLRoles(t *testing.T) { expectedRoleDeletes: set.Set[string]{"db_datawriter": {}, "db_accessadmin": {}, "db_ddladmin": {}}, }, { - name: "Expected has many roles more than current", currentRoles: set.Set[string]{"db_datareader": {}, "db_securityadmin": {}}, expectedRoles: set.Set[string]{"db_datareader": {}, "db_datawriter": {}, "db_accessadmin": {}, "db_securityadmin": {}, "db_ddladmin": {}}, diff --git a/v2/internal/util/lockedrand/rand.go b/v2/internal/util/lockedrand/rand.go index 0b0e31588ca..21fbb12ec8e 100644 --- a/v2/internal/util/lockedrand/rand.go +++ b/v2/internal/util/lockedrand/rand.go @@ -15,8 +15,10 @@ type LockedSource struct { src rand.Source64 } -var _ rand.Source = &LockedSource{} -var _ rand.Source64 = &LockedSource{} +var ( + _ rand.Source = &LockedSource{} + _ rand.Source64 = &LockedSource{} +) func (l *LockedSource) Int63() int64 { l.lock.Lock() diff --git a/v2/internal/util/mysql/mysql.go b/v2/internal/util/mysql/mysql.go index fc08c855e01..5b5182d95b4 100644 --- a/v2/internal/util/mysql/mysql.go +++ b/v2/internal/util/mysql/mysql.go @@ -12,7 +12,7 @@ import ( "time" "github.com/go-sql-driver/mysql" - _ "github.com/go-sql-driver/mysql" //mysql drive link + _ "github.com/go-sql-driver/mysql" // mysql drive link "github.com/pkg/errors" ) diff --git a/v2/internal/util/postgresql/postgresql.go b/v2/internal/util/postgresql/postgresql.go index bf224dc4d61..13d15689d55 100644 --- a/v2/internal/util/postgresql/postgresql.go +++ b/v2/internal/util/postgresql/postgresql.go @@ -9,7 +9,7 @@ import ( "fmt" "strings" - _ "github.com/jackc/pgx/v5/stdlib" //the pgx lib + _ "github.com/jackc/pgx/v5/stdlib" // the pgx lib "github.com/pkg/errors" ) @@ -26,7 +26,6 @@ const DefaultMaintanenceDatabase = "postgres" // ConnectToDB connects to the PostgreSQL db using the given credentials func ConnectToDB(ctx context.Context, fullservername string, database string, port int, user string, password string) (*sql.DB, error) { - connString := fmt.Sprintf("host=%s user=%s password=%s port=%d dbname=%s sslmode=require connect_timeout=30", fullservername, user, password, port, database) db, err := sql.Open(PDriverName, connString) @@ -44,7 +43,7 @@ func ConnectToDB(ctx context.Context, fullservername string, database string, po func CreateUser(ctx context.Context, db *sql.DB, username string, password string) (*SQLUser, error) { // make an effort to prevent sql injection - //TODO find better solution to check user and password for SQL Injection + // TODO find better solution to check user and password for SQL Injection if err := FindBadChars(username); err != nil { return nil, errors.Wrap(err, "problem found with username") } @@ -60,7 +59,7 @@ func CreateUser(ctx context.Context, db *sql.DB, username string, password strin func UpdateUser(ctx context.Context, db *sql.DB, user SQLUser, password string) error { // make an effort to prevent sql injection - //TODO find better solution to check password for SQL Injection + // TODO find better solution to check password for SQL Injection if err := FindBadChars(password); err != nil { return errors.Wrap(err, "problem found with password") } diff --git a/v2/internal/util/postgresql/role_option.go b/v2/internal/util/postgresql/role_option.go index 22ab86644a8..c9ab969ed8c 100644 --- a/v2/internal/util/postgresql/role_option.go +++ b/v2/internal/util/postgresql/role_option.go @@ -25,7 +25,6 @@ type SQLRoleOptionDelta struct { // because they are only settable with an existing a superuser // Azure Flexible server does not offer superuser access for customers type RoleOptions struct { - // WITH LOGIN or NOLOGIN Login bool @@ -95,7 +94,7 @@ func GetUserRoleOptions(ctx context.Context, db *sql.DB, user SQLUser) (*RoleOpt if err != nil { return nil, errors.Wrapf(err, "extracting RoleOption field") } - //No error handling required here, as sql returns already defined constants + // No error handling required here, as sql returns already defined constants } if rows.Err() != nil { return nil, errors.Wrap(rows.Err(), "iterating RoleOptions") diff --git a/v2/internal/util/postgresql/role_option_test.go b/v2/internal/util/postgresql/role_option_test.go index 598ecdef59c..c391d8435cc 100644 --- a/v2/internal/util/postgresql/role_option_test.go +++ b/v2/internal/util/postgresql/role_option_test.go @@ -21,22 +21,25 @@ func TestDiffCurrentAndExpectedSQLRoleOptions(t *testing.T) { currentRoleOptions RoleOptions expectedRoleOptions RoleOptions expectedChangedRoleOptions set.Set[RoleOption] - }{{ - name: "Current and expected equal", - currentRoleOptions: RoleOptions{Login: true, CreateRole: false, CreateDb: false, Replication: false}, - expectedRoleOptions: RoleOptions{Login: true}, - expectedChangedRoleOptions: set.Make[RoleOption](), - }, { - name: "Expected has single option more than current", - currentRoleOptions: RoleOptions{Login: true, CreateRole: false, CreateDb: false, Replication: false}, - expectedRoleOptions: RoleOptions{Login: true, CreateDb: true}, - expectedChangedRoleOptions: set.Set[RoleOption]{CreateDb: {}}, - }, { - name: "Expected all new values are set", - currentRoleOptions: RoleOptions{Login: true, CreateRole: false, CreateDb: false, Replication: false}, - expectedRoleOptions: RoleOptions{Login: false, CreateRole: true, CreateDb: true, Replication: true}, - expectedChangedRoleOptions: set.Set[RoleOption]{NoLogin: {}, CreateRole: {}, CreateDb: {}, Replication: {}}, - }, + }{ + { + name: "Current and expected equal", + currentRoleOptions: RoleOptions{Login: true, CreateRole: false, CreateDb: false, Replication: false}, + expectedRoleOptions: RoleOptions{Login: true}, + expectedChangedRoleOptions: set.Make[RoleOption](), + }, + { + name: "Expected has single option more than current", + currentRoleOptions: RoleOptions{Login: true, CreateRole: false, CreateDb: false, Replication: false}, + expectedRoleOptions: RoleOptions{Login: true, CreateDb: true}, + expectedChangedRoleOptions: set.Set[RoleOption]{CreateDb: {}}, + }, + { + name: "Expected all new values are set", + currentRoleOptions: RoleOptions{Login: true, CreateRole: false, CreateDb: false, Replication: false}, + expectedRoleOptions: RoleOptions{Login: false, CreateRole: true, CreateDb: true, Replication: true}, + expectedChangedRoleOptions: set.Set[RoleOption]{NoLogin: {}, CreateRole: {}, CreateDb: {}, Replication: {}}, + }, { name: "Expected all new values are set (non defaults)", currentRoleOptions: RoleOptions{Login: false, CreateRole: true, CreateDb: true, Replication: true}, @@ -54,7 +57,8 @@ func TestDiffCurrentAndExpectedSQLRoleOptions(t *testing.T) { currentRoleOptions: RoleOptions{}, expectedRoleOptions: RoleOptions{Login: true}, expectedChangedRoleOptions: set.Set[RoleOption]{Login: {}}, - }, { + }, + { name: "Test CreateRole changed", currentRoleOptions: RoleOptions{}, expectedRoleOptions: RoleOptions{CreateRole: true}, diff --git a/v2/internal/util/postgresql/roles.go b/v2/internal/util/postgresql/roles.go index 7d0fae514c4..dccd3ef0b03 100644 --- a/v2/internal/util/postgresql/roles.go +++ b/v2/internal/util/postgresql/roles.go @@ -47,7 +47,6 @@ func DiffCurrentAndExpectedSQLRoles(currentRoles set.Set[string], expectedRoles // GetUserServerRoles gets the server-level roles the user has as a set. func GetUserServerRoles(ctx context.Context, db *sql.DB, user SQLUser) (set.Set[string], error) { - rows, err := db.QueryContext( ctx, "SELECT c.rolname as role_name FROM pg_roles a INNER JOIN pg_auth_members b on a.oid = b.member INNER JOIN pg_roles c ON b.roleid = c.oid WHERE a.rolname = $1", diff --git a/v2/internal/version/version.go b/v2/internal/version/version.go index 98bdbaba627..2218aadbbc5 100644 --- a/v2/internal/version/version.go +++ b/v2/internal/version/version.go @@ -14,10 +14,8 @@ import ( "github.com/spf13/cobra" ) -var ( - // This is populated from the build (see Taskfile.yml) - BuildVersion string = "" -) +// This is populated from the build (see Taskfile.yml) +var BuildVersion string = "" // NewCommand creates a new reusable cobra command to display the current version of the tool func NewCommand() (*cobra.Command, error) { diff --git a/v2/pkg/common/config/scoped_credentials.go b/v2/pkg/common/config/scoped_credentials.go index 54acd3819c0..dae24997fe4 100644 --- a/v2/pkg/common/config/scoped_credentials.go +++ b/v2/pkg/common/config/scoped_credentials.go @@ -10,6 +10,6 @@ const ( WorkloadIdentityAuthMode AuthModeOption = "workloadidentity" // AuthMode enum is used to determine if we're using Pod Identity or Workload Identity - //authentication for namespace and per-resource scoped credentials + // authentication for namespace and per-resource scoped credentials AuthMode = "AUTH_MODE" ) diff --git a/v2/pkg/genruntime/conditions/ready_condition_builder.go b/v2/pkg/genruntime/conditions/ready_condition_builder.go index b2f33e85298..ff7f7e00a8b 100644 --- a/v2/pkg/genruntime/conditions/ready_condition_builder.go +++ b/v2/pkg/genruntime/conditions/ready_condition_builder.go @@ -23,22 +23,28 @@ type Reason struct { var ReasonSubscriptionMismatch = Reason{Name: "SubscriptionMismatch", RetryClassification: RetryFast} // Precondition reasons -var ReasonSecretNotFound = Reason{Name: "SecretNotFound", RetryClassification: RetryFast} -var ReasonConfigMapNotFound = Reason{Name: "ConfigMapNotFound", RetryClassification: RetryFast} -var ReasonReferenceNotFound = Reason{Name: "ReferenceNotFound", RetryClassification: RetryFast} -var ReasonWaitingForOwner = Reason{Name: "WaitingForOwner", RetryClassification: RetryFast} +var ( + ReasonSecretNotFound = Reason{Name: "SecretNotFound", RetryClassification: RetryFast} + ReasonConfigMapNotFound = Reason{Name: "ConfigMapNotFound", RetryClassification: RetryFast} + ReasonReferenceNotFound = Reason{Name: "ReferenceNotFound", RetryClassification: RetryFast} + ReasonWaitingForOwner = Reason{Name: "WaitingForOwner", RetryClassification: RetryFast} +) // Post-ARM PUT reasons -var ReasonAzureResourceNotFound = Reason{Name: "AzureResourceNotFound", RetryClassification: RetrySlow} -var ReasonAdditionalKubernetesObjWriteFailure = Reason{Name: "FailedWritingAdditionalKubernetesObjects", RetryClassification: RetrySlow} +var ( + ReasonAzureResourceNotFound = Reason{Name: "AzureResourceNotFound", RetryClassification: RetrySlow} + ReasonAdditionalKubernetesObjWriteFailure = Reason{Name: "FailedWritingAdditionalKubernetesObjects", RetryClassification: RetrySlow} +) // Other reasons -var ReasonReconciling = Reason{Name: "Reconciling", RetryClassification: RetryFast} -var ReasonDeleting = Reason{Name: "Deleting", RetryClassification: RetryFast} -var ReasonReconciliationFailedPermanently = Reason{Name: "ReconciliationFailedPermanently", RetryClassification: RetryNone} -var ReasonReconcileBlocked = Reason{Name: "ReconciliationBlocked", RetryClassification: RetrySlow} -var ReasonReconcilePostponed = Reason{Name: "ReconciliationPostponed", RetryClassification: RetrySlow} -var ReasonPostReconcileFailure = Reason{Name: "PostReconciliationFailure", RetryClassification: RetrySlow} +var ( + ReasonReconciling = Reason{Name: "Reconciling", RetryClassification: RetryFast} + ReasonDeleting = Reason{Name: "Deleting", RetryClassification: RetryFast} + ReasonReconciliationFailedPermanently = Reason{Name: "ReconciliationFailedPermanently", RetryClassification: RetryNone} + ReasonReconcileBlocked = Reason{Name: "ReconciliationBlocked", RetryClassification: RetrySlow} + ReasonReconcilePostponed = Reason{Name: "ReconciliationPostponed", RetryClassification: RetrySlow} + ReasonPostReconcileFailure = Reason{Name: "PostReconciliationFailure", RetryClassification: RetrySlow} +) // ReasonFailed is a catch-all error code for when we don't have a more specific error classification var ReasonFailed = Reason{Name: "Failed", RetryClassification: RetrySlow} diff --git a/v2/pkg/genruntime/core/errors.go b/v2/pkg/genruntime/core/errors.go index d0ff8e27796..698e9c640dd 100644 --- a/v2/pkg/genruntime/core/errors.go +++ b/v2/pkg/genruntime/core/errors.go @@ -92,8 +92,10 @@ func NewReferenceNotFoundError(name types.NamespacedName, cause error) *Referenc } } -var _ error = &ReferenceNotFound{} -var _ causer = &ReferenceNotFound{} +var ( + _ error = &ReferenceNotFound{} + _ causer = &ReferenceNotFound{} +) func (e *ReferenceNotFound) Error() string { return fmt.Sprintf("%s does not exist (%s)", e.NamespacedName, e.cause) @@ -128,8 +130,10 @@ func NewSecretNotFoundError(name types.NamespacedName, cause error) *SecretNotFo } } -var _ error = &SecretNotFound{} -var _ causer = &SecretNotFound{} +var ( + _ error = &SecretNotFound{} + _ causer = &SecretNotFound{} +) func (e *SecretNotFound) Error() string { return fmt.Sprintf("%s does not exist (%s)", e.NamespacedName, e.cause) @@ -164,8 +168,10 @@ func NewConfigMapNotFoundError(name types.NamespacedName, cause error) *ConfigMa } } -var _ error = &ConfigMapNotFound{} -var _ causer = &ConfigMapNotFound{} +var ( + _ error = &ConfigMapNotFound{} + _ causer = &ConfigMapNotFound{} +) func (e *ConfigMapNotFound) Error() string { return fmt.Sprintf("%s does not exist (%s)", e.NamespacedName, e.cause) @@ -207,8 +213,10 @@ func NewSubscriptionMismatchError(expectedSub string, actualSub string) *Subscri } } -var _ error = &SubscriptionMismatch{} -var _ causer = &SubscriptionMismatch{} +var ( + _ error = &SubscriptionMismatch{} + _ causer = &SubscriptionMismatch{} +) func (e *SubscriptionMismatch) Error() string { return e.inner.Error() diff --git a/v2/pkg/genruntime/extensions/arm_resource_modifier.go b/v2/pkg/genruntime/extensions/arm_resource_modifier.go index ed99512268a..089bb4ec09e 100644 --- a/v2/pkg/genruntime/extensions/arm_resource_modifier.go +++ b/v2/pkg/genruntime/extensions/arm_resource_modifier.go @@ -42,8 +42,8 @@ func CreateARMResourceModifier( armClient *genericarmclient.GenericClient, kubeClient kubeclient.Client, resolver *resolver.Resolver, - log logr.Logger) ARMResourceModifierFunc { - + log logr.Logger, +) ARMResourceModifierFunc { impl, ok := host.(ARMResourceModifier) if !ok { return func(ctx context.Context, obj genruntime.ARMMetaObject, armObj genruntime.ARMResource) (genruntime.ARMResource, error) { diff --git a/v2/pkg/genruntime/extensions/claimer.go b/v2/pkg/genruntime/extensions/claimer.go index fc6e5c3dd67..390e8d07b35 100644 --- a/v2/pkg/genruntime/extensions/claimer.go +++ b/v2/pkg/genruntime/extensions/claimer.go @@ -27,8 +27,8 @@ type ClaimFunc = func(ctx context.Context, log logr.Logger, obj genruntime.ARMOw // the provided default ClaimFunc is run by default. func CreateClaimer( host genruntime.ResourceExtension, - next ClaimFunc) ClaimFunc { - + next ClaimFunc, +) ClaimFunc { impl, ok := host.(Claimer) if !ok { return next diff --git a/v2/pkg/genruntime/extensions/deleter.go b/v2/pkg/genruntime/extensions/deleter.go index bbbf2d75550..e77fbaf12c7 100644 --- a/v2/pkg/genruntime/extensions/deleter.go +++ b/v2/pkg/genruntime/extensions/deleter.go @@ -41,8 +41,8 @@ type DeleteFunc = func( // the provided default DeleteFunc is run by default. func CreateDeleter( host genruntime.ResourceExtension, - next DeleteFunc) DeleteFunc { - + next DeleteFunc, +) DeleteFunc { impl, ok := host.(Deleter) if !ok { return next diff --git a/v2/pkg/genruntime/extensions/kubernetes_exporter.go b/v2/pkg/genruntime/extensions/kubernetes_exporter.go index a756f335fe0..74ab09c362a 100644 --- a/v2/pkg/genruntime/extensions/kubernetes_exporter.go +++ b/v2/pkg/genruntime/extensions/kubernetes_exporter.go @@ -28,8 +28,8 @@ func CreateKubernetesExporter( ctx context.Context, host genruntime.ResourceExtension, armClient *genericarmclient.GenericClient, - log logr.Logger) KubernetesExportFunc { - + log logr.Logger, +) KubernetesExportFunc { impl, ok := host.(genruntime.KubernetesExporter) if !ok { return func(obj genruntime.MetaObject) ([]client.Object, error) { diff --git a/v2/pkg/genruntime/extensions/postreconciliation_checker.go b/v2/pkg/genruntime/extensions/postreconciliation_checker.go index e6bf91580a4..ae090db7df3 100644 --- a/v2/pkg/genruntime/extensions/postreconciliation_checker.go +++ b/v2/pkg/genruntime/extensions/postreconciliation_checker.go @@ -7,6 +7,7 @@ package extensions import ( "context" + "github.com/Azure/azure-service-operator/v2/internal/resolver" "github.com/go-logr/logr" diff --git a/v2/pkg/genruntime/extensions/prereconciliation_checker.go b/v2/pkg/genruntime/extensions/prereconciliation_checker.go index 73d6f649c0a..977f1550798 100644 --- a/v2/pkg/genruntime/extensions/prereconciliation_checker.go +++ b/v2/pkg/genruntime/extensions/prereconciliation_checker.go @@ -7,6 +7,7 @@ package extensions import ( "context" + "github.com/Azure/azure-service-operator/v2/internal/genericarmclient" . "github.com/Azure/azure-service-operator/v2/internal/logging" "github.com/Azure/azure-service-operator/v2/internal/resolver" diff --git a/v2/pkg/genruntime/extensions/successful_resource_modifier.go b/v2/pkg/genruntime/extensions/successful_resource_modifier.go index a21514cec2d..42661d15be2 100644 --- a/v2/pkg/genruntime/extensions/successful_resource_modifier.go +++ b/v2/pkg/genruntime/extensions/successful_resource_modifier.go @@ -26,8 +26,8 @@ type SuccessFunc = func(obj genruntime.ARMMetaObject) error // If the resource did not implement SuccessfulCreationHandler a default handler that does nothing is returned. func CreateSuccessfulCreationHandler( host genruntime.ResourceExtension, - log logr.Logger) SuccessFunc { - + log logr.Logger, +) SuccessFunc { impl, ok := host.(SuccessfulCreationHandler) if !ok { return func(obj genruntime.ARMMetaObject) error { diff --git a/v2/pkg/genruntime/kubernetes_resource.go b/v2/pkg/genruntime/kubernetes_resource.go index 782a01f396a..a354798b726 100644 --- a/v2/pkg/genruntime/kubernetes_resource.go +++ b/v2/pkg/genruntime/kubernetes_resource.go @@ -19,7 +19,6 @@ type ARMOwned interface { } type SupportedResourceOperations interface { - // GetSupportedOperations gets the set of supported resource operations GetSupportedOperations() []ResourceOperation } diff --git a/v2/pkg/genruntime/resource_reference_test.go b/v2/pkg/genruntime/resource_reference_test.go index 5a21f7e30f0..ba9b4696046 100644 --- a/v2/pkg/genruntime/resource_reference_test.go +++ b/v2/pkg/genruntime/resource_reference_test.go @@ -13,11 +13,13 @@ import ( "github.com/Azure/azure-service-operator/v2/pkg/genruntime" ) -var validARMIDRef = genruntime.ResourceReference{ARMID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg/providers/microsoft.compute/VirtualMachine/myvm"} -var validKubRef = genruntime.ResourceReference{Group: "microsoft.resources.azure.com", Kind: "ResourceGroup", Name: "myrg"} -var invalidRefBothSpecified = genruntime.ResourceReference{Group: "microsoft.resources.azure.com", Kind: "ResourceGroup", Name: "myrg", ARMID: "oops"} -var invalidRefNeitherSpecified = genruntime.ResourceReference{} -var invalidRefIncompleteKubReference = genruntime.ResourceReference{Group: "microsoft.resources.azure.com", Name: "myrg"} +var ( + validARMIDRef = genruntime.ResourceReference{ARMID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg/providers/microsoft.compute/VirtualMachine/myvm"} + validKubRef = genruntime.ResourceReference{Group: "microsoft.resources.azure.com", Kind: "ResourceGroup", Name: "myrg"} + invalidRefBothSpecified = genruntime.ResourceReference{Group: "microsoft.resources.azure.com", Kind: "ResourceGroup", Name: "myrg", ARMID: "oops"} + invalidRefNeitherSpecified = genruntime.ResourceReference{} + invalidRefIncompleteKubReference = genruntime.ResourceReference{Group: "microsoft.resources.azure.com", Name: "myrg"} +) func Test_ResourceReference_Validate(t *testing.T) { t.Parallel() diff --git a/v2/pkg/genruntime/test/recordings/Test_ApplyObjAndEnsureOwner.yaml b/v2/pkg/genruntime/test/recordings/Test_ApplyObjAndEnsureOwner.yaml index e69de29bb2d..2797c38e00e 100644 --- a/v2/pkg/genruntime/test/recordings/Test_ApplyObjAndEnsureOwner.yaml +++ b/v2/pkg/genruntime/test/recordings/Test_ApplyObjAndEnsureOwner.yaml @@ -0,0 +1,3 @@ +--- +version: 2 +interactions: [] diff --git a/v2/pkg/tests/convertible_spec_test.go b/v2/pkg/tests/convertible_spec_test.go index e4576f9bfb2..16c3ef0884b 100644 --- a/v2/pkg/tests/convertible_spec_test.go +++ b/v2/pkg/tests/convertible_spec_test.go @@ -30,5 +30,5 @@ func TestGetVersionedSpec_WorksWhenNoPivotNeeded(t *testing.T) { g.Expect(rsrc).NotTo(BeNil()) } -//TODO (@unrepentantgeek): once we have multiple versions of a resource, we should test that the pivot works too -//func TestGetVersionedSpec_WorksWhenPivotNeeded(t *testing.T) { +// TODO (@unrepentantgeek): once we have multiple versions of a resource, we should test that the pivot works too +// func TestGetVersionedSpec_WorksWhenPivotNeeded(t *testing.T) { diff --git a/v2/pkg/tests/convertible_status_test.go b/v2/pkg/tests/convertible_status_test.go index a19cfc226f5..73656bd251b 100644 --- a/v2/pkg/tests/convertible_status_test.go +++ b/v2/pkg/tests/convertible_status_test.go @@ -30,9 +30,8 @@ func TestGetVersionedStatus_WorksWhenNoPivotNeeded(t *testing.T) { g.Expect(rsrc).NotTo(BeNil()) } -//TODO: once we have multiple versions of a resource, we should test that the pivot works too -//func TestGetVersionedStatus_WorksWhenPivotNeeded(t *testing.T) { - +// TODO: once we have multiple versions of a resource, we should test that the pivot works too +// func TestGetVersionedStatus_WorksWhenPivotNeeded(t *testing.T) { func TestNewEmptyVersionedStatus_WorksWhenNoPivotNeeded(t *testing.T) { t.Parallel() g := NewGomegaWithT(t) @@ -48,5 +47,5 @@ func TestNewEmptyVersionedStatus_WorksWhenNoPivotNeeded(t *testing.T) { g.Expect(rsrc).NotTo(BeNil()) } -//TODO: once we have multiple versions of a resource, we should test that the pivot works too -//func TestNewEmptyVersionedStatus_WorksWhenPivotNeeded(t *testing.T) { +// TODO: once we have multiple versions of a resource, we should test that the pivot works too +// func TestNewEmptyVersionedStatus_WorksWhenPivotNeeded(t *testing.T) { diff --git a/v2/pkg/tests/kubernetes_resource_test.go b/v2/pkg/tests/kubernetes_resource_test.go index 551f007963b..e3df3eb2bd3 100644 --- a/v2/pkg/tests/kubernetes_resource_test.go +++ b/v2/pkg/tests/kubernetes_resource_test.go @@ -30,5 +30,5 @@ func TestNewEmptyVersionedResource_WorksWhenNoPivotNeeded(t *testing.T) { g.Expect(rsrc).NotTo(BeNil()) } -//TODO: once we have multiple versions of a resource, we should test that the pivot works too -//func TestNewEmptyVersionedResource_WorksWhenPivotNeeded(t *testing.T) { +// TODO: once we have multiple versions of a resource, we should test that the pivot works too +// func TestNewEmptyVersionedResource_WorksWhenPivotNeeded(t *testing.T) { diff --git a/v2/test/mysql_aad_test.go b/v2/test/mysql_aad_test.go index 4126b0f4a88..67a5cd49eab 100644 --- a/v2/test/mysql_aad_test.go +++ b/v2/test/mysql_aad_test.go @@ -10,21 +10,23 @@ import ( "os" "testing" - _ "github.com/go-sql-driver/mysql" //sql drive link + _ "github.com/go-sql-driver/mysql" // sql drive link . "github.com/onsi/gomega" "github.com/pkg/errors" + "github.com/Azure/azure-service-operator/v2/internal/set" + "github.com/Azure/azure-service-operator/v2/internal/testcommon" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/creds" + "github.com/Azure/azure-service-operator/v2/internal/util/to" + "github.com/Azure/azure-service-operator/v2/pkg/common/annotations" + "github.com/Azure/azure-service-operator/v2/pkg/genruntime" + mysqlv1 "github.com/Azure/azure-service-operator/v2/api/dbformysql/v1" mysql "github.com/Azure/azure-service-operator/v2/api/dbformysql/v1api20210501" mysql20220101 "github.com/Azure/azure-service-operator/v2/api/dbformysql/v1api20220101" managedidentity "github.com/Azure/azure-service-operator/v2/api/managedidentity/v1api20181130" resources "github.com/Azure/azure-service-operator/v2/api/resources/v1api20200601" - "github.com/Azure/azure-service-operator/v2/internal/set" - "github.com/Azure/azure-service-operator/v2/internal/testcommon" mysqlutil "github.com/Azure/azure-service-operator/v2/internal/util/mysql" - "github.com/Azure/azure-service-operator/v2/internal/util/to" - "github.com/Azure/azure-service-operator/v2/pkg/common/annotations" - "github.com/Azure/azure-service-operator/v2/pkg/genruntime" ) const ( @@ -133,8 +135,8 @@ func MySQL_AADUser_CRUD( server *mysql.FlexibleServer, rg *resources.ResourceGroup, admin *mysql20220101.FlexibleServersAdministrator, - standardAdminPassword string) { - + standardAdminPassword string, +) { configMapName := "my-configmap" clientIDKey := "clientId" tenantIDKey := "tenantId" @@ -210,7 +212,7 @@ func MySQL_AADUser_CRUD( tc.Expect(serverPrivs).To(Equal(set.Make[string](user.Spec.Privileges...))) // Update the user once again, this time to remove privs and also use a resource scoped identity - secret := testcommon.NewScopedManagedIdentitySecret(tc.AzureSubscription, tc.AzureTenant, to.Value(admin.Spec.Sid), "credential", tc.Namespace) + secret := creds.NewScopedManagedIdentitySecret(tc.AzureSubscription, tc.AzureTenant, to.Value(admin.Spec.Sid), "credential", tc.Namespace) tc.CreateResource(secret) old = user.DeepCopy() @@ -260,8 +262,8 @@ func MySQL_LocalUser_AADAdmin_CRUD( tc *testcommon.KubePerTestContext, server *mysql.FlexibleServer, admin *mysql20220101.FlexibleServersAdministrator, - standardAdminPassword string) { - + standardAdminPassword string, +) { passwordKey := "password" password := tc.Namer.GeneratePassword() userSecret := newSecret(tc, passwordKey, password) @@ -335,8 +337,8 @@ func MySQL_AADUserAndGroup_CRUD( tc *testcommon.KubePerTestContext, server *mysql.FlexibleServer, admin *mysql20220101.FlexibleServersAdministrator, - standardAdminPassword string) { - + standardAdminPassword string, +) { // Note: when logging in to the DB you still log in with the actual username not the alias. // Alias is just for management of it in SQL. alias := "myaliaseduser" diff --git a/v2/test/mysql_test.go b/v2/test/mysql_test.go index bf12fbed4b4..897e16c5402 100644 --- a/v2/test/mysql_test.go +++ b/v2/test/mysql_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - _ "github.com/go-sql-driver/mysql" //sql drive link + _ "github.com/go-sql-driver/mysql" // sql drive link . "github.com/onsi/gomega" v1 "k8s.io/api/core/v1" diff --git a/v2/test/postgresql_test.go b/v2/test/postgresql_test.go index a2d8c08a653..118ae73dc37 100644 --- a/v2/test/postgresql_test.go +++ b/v2/test/postgresql_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - _ "github.com/jackc/pgx/v5/stdlib" //the pgx lib + _ "github.com/jackc/pgx/v5/stdlib" // the pgx lib . "github.com/onsi/gomega" v1 "k8s.io/api/core/v1" diff --git a/v2/test/single_operator_multitenant_test.go b/v2/test/single_operator_multitenant_test.go index 2b16034f8ce..7c8842a0ce4 100644 --- a/v2/test/single_operator_multitenant_test.go +++ b/v2/test/single_operator_multitenant_test.go @@ -21,6 +21,7 @@ import ( storage "github.com/Azure/azure-service-operator/v2/api/storage/v1api20210401" "github.com/Azure/azure-service-operator/v2/internal/identity" "github.com/Azure/azure-service-operator/v2/internal/testcommon" + "github.com/Azure/azure-service-operator/v2/internal/testcommon/creds" "github.com/Azure/azure-service-operator/v2/pkg/common/annotations" "github.com/Azure/azure-service-operator/v2/pkg/genruntime" "github.com/Azure/azure-service-operator/v2/pkg/genruntime/conditions" @@ -166,7 +167,7 @@ func newClientSecretCredential(subscriptionID, tenantID, name, namespace string) return nil, errors.Errorf("required environment variable %q was not supplied", AzureClientIDMultitenantVar) } - return testcommon.NewScopedServicePrincipalSecret(subscriptionID, tenantID, clientID, clientSecret, name, namespace), nil + return creds.NewScopedServicePrincipalSecret(subscriptionID, tenantID, clientID, clientSecret, name, namespace), nil } func newClientCertificateCredential(subscriptionID, tenantID, name, namespace string) (*v1.Secret, error) { @@ -180,7 +181,7 @@ func newClientCertificateCredential(subscriptionID, tenantID, name, namespace st return nil, errors.Errorf("required environment variable %q was not supplied", AzureClientIDMultitenantCertAuthVar) } - return testcommon.NewScopedServicePrincipalCertificateSecret(subscriptionID, tenantID, clientID, clientCert, name, namespace), nil + return creds.NewScopedServicePrincipalCertificateSecret(subscriptionID, tenantID, clientID, clientCert, name, namespace), nil } func newStorageAccount(tc *testcommon.KubePerTestContext, rg *resources.ResourceGroup) *storage.StorageAccount { diff --git a/v2/tools/generator/gen_types.go b/v2/tools/generator/gen_types.go index cde0dc135b2..14200a8a698 100644 --- a/v2/tools/generator/gen_types.go +++ b/v2/tools/generator/gen_types.go @@ -60,7 +60,6 @@ func NewGenTypesCommand() (*cobra.Command, error) { } err = cg.Generate(ctx, log) - if err != nil { err = errors.Wrap(err, "error generating code") return err diff --git a/v2/tools/generator/internal/armconversion/convert_to_arm_function_builder.go b/v2/tools/generator/internal/armconversion/convert_to_arm_function_builder.go index 49be34761cc..498e8429381 100644 --- a/v2/tools/generator/internal/armconversion/convert_to_arm_function_builder.go +++ b/v2/tools/generator/internal/armconversion/convert_to_arm_function_builder.go @@ -142,7 +142,6 @@ func (builder *convertToARMBuilder) namePropertyHandler( toProp *astmodel.PropertyDefinition, _ *astmodel.ObjectType, ) (propertyConversionHandlerResult, error) { - if toProp.PropertyName() != "Name" || builder.typeKind != TypeKindSpec { return notHandled, nil } @@ -163,7 +162,6 @@ func (builder *convertToARMBuilder) operatorSpecPropertyHandler( toProp *astmodel.PropertyDefinition, _ *astmodel.ObjectType, ) (propertyConversionHandlerResult, error) { - if toProp.PropertyName() != astmodel.OperatorSpecProperty || builder.typeKind != TypeKindSpec { return notHandled, nil } @@ -176,13 +174,12 @@ func (builder *convertToARMBuilder) configMapReferencePropertyHandler( toProp *astmodel.PropertyDefinition, fromType *astmodel.ObjectType, ) (propertyConversionHandlerResult, error) { - // This is just an optimization to avoid scanning excess properties collections _, isString := astmodel.AsPrimitiveType(toProp.PropertyType()) // TODO: Do we support slices or maps? Skipped for now - //isSliceString := astmodel.TypeEquals(toProp.PropertyType(), astmodel.NewArrayType(astmodel.StringType)) - //isMapString := astmodel.TypeEquals(toProp.PropertyType(), astmodel.NewMapType(astmodel.StringType, astmodel.StringType)) + // isSliceString := astmodel.TypeEquals(toProp.PropertyType(), astmodel.NewArrayType(astmodel.StringType)) + // isMapString := astmodel.TypeEquals(toProp.PropertyType(), astmodel.NewMapType(astmodel.StringType, astmodel.StringType)) if !isString { return notHandled, nil @@ -275,7 +272,6 @@ func (builder *convertToARMBuilder) userAssignedIdentitiesPropertyHandler( toProp *astmodel.PropertyDefinition, fromType *astmodel.ObjectType, ) (propertyConversionHandlerResult, error) { - if _, ok := astmodel.IsUserAssignedIdentityProperty(toProp); !ok { return notHandled, nil } @@ -322,7 +318,6 @@ func (builder *convertToARMBuilder) referencePropertyHandler( toProp *astmodel.PropertyDefinition, fromType *astmodel.ObjectType, ) (propertyConversionHandlerResult, error) { - // This is just an optimization to avoid scanning excess properties collections isString := astmodel.TypeEquals(toProp.PropertyType(), astmodel.StringType) isOptionalString := astmodel.TypeEquals(toProp.PropertyType(), astmodel.OptionalStringType) @@ -388,7 +383,6 @@ func (builder *convertToARMBuilder) flattenedPropertyHandler( toProp *astmodel.PropertyDefinition, fromType *astmodel.ObjectType, ) (propertyConversionHandlerResult, error) { - toPropName := toProp.PropertyName() // collect any fromProps that were flattened from the to-prop @@ -510,8 +504,8 @@ func (builder *convertToARMBuilder) flattenedPropertyHandler( func (builder *convertToARMBuilder) buildToPropInitializer( fromProps []*astmodel.PropertyDefinition, toPropTypeName astmodel.TypeName, - toPropName astmodel.PropertyName) dst.Stmt { - + toPropName astmodel.PropertyName, +) dst.Stmt { // build (x != nil, y != nil, …) conditions := make([]dst.Expr, 0, len(fromProps)) for _, prop := range fromProps { @@ -540,7 +534,6 @@ func (builder *convertToARMBuilder) propertiesByNameHandler( toProp *astmodel.PropertyDefinition, fromType *astmodel.ObjectType, ) (propertyConversionHandlerResult, error) { - fromProp, found := fromType.Property(toProp.PropertyName()) if !found { // No direct match by name, look for renames diff --git a/v2/tools/generator/internal/astbuilder/calls.go b/v2/tools/generator/internal/astbuilder/calls.go index d5c870e8930..2649853f691 100644 --- a/v2/tools/generator/internal/astbuilder/calls.go +++ b/v2/tools/generator/internal/astbuilder/calls.go @@ -6,8 +6,9 @@ package astbuilder import ( - "github.com/dave/dst" "go/token" + + "github.com/dave/dst" ) // CallFunc creates an expression to call a function with specified arguments diff --git a/v2/tools/generator/internal/astbuilder/comments_test.go b/v2/tools/generator/internal/astbuilder/comments_test.go index f120e3b4fb7..5a0b89022b3 100644 --- a/v2/tools/generator/internal/astbuilder/comments_test.go +++ b/v2/tools/generator/internal/astbuilder/comments_test.go @@ -45,7 +45,8 @@ func TestDocumentationCommentFormatting(t *testing.T) { []string{ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ", "eiusmod tempor incididunt ut labore et dolore magna aliqua.", - }}, + }, + }, } for _, c := range cases { diff --git a/v2/tools/generator/internal/astbuilder/func_details.go b/v2/tools/generator/internal/astbuilder/func_details.go index 513f565e4ea..3735b34bc65 100644 --- a/v2/tools/generator/internal/astbuilder/func_details.go +++ b/v2/tools/generator/internal/astbuilder/func_details.go @@ -25,7 +25,6 @@ type FuncDetails struct { // NewTestFuncDetails returns a FuncDetails for a test method // Tests require a particular signature, so this makes it simpler to create test functions func NewTestFuncDetails(testingPackage string, testName string, body ...dst.Stmt) *FuncDetails { - // Ensure the method name starts with `Test` as required var name string if strings.HasPrefix(testName, "Test") { diff --git a/v2/tools/generator/internal/astmodel/code_generation_context.go b/v2/tools/generator/internal/astmodel/code_generation_context.go index e90ea067841..209d1022792 100644 --- a/v2/tools/generator/internal/astmodel/code_generation_context.go +++ b/v2/tools/generator/internal/astmodel/code_generation_context.go @@ -29,8 +29,8 @@ var _ ReadonlyTypeDefinitions = &CodeGenerationContext{} func NewCodeGenerationContext( currentPackage InternalPackageReference, packageImports *PackageImportSet, - generatedPackages map[InternalPackageReference]*PackageDefinition) *CodeGenerationContext { - + generatedPackages map[InternalPackageReference]*PackageDefinition, +) *CodeGenerationContext { imports := NewPackageImportSet() imports.Merge(packageImports) @@ -38,7 +38,8 @@ func NewCodeGenerationContext( currentPackage: currentPackage, packageImports: imports, generatedPackages: generatedPackages, - usedImports: NewPackageImportSet()} + usedImports: NewPackageImportSet(), + } } // CurrentPackage returns the current package being generated @@ -102,7 +103,6 @@ func (ctx *CodeGenerationContext) GetGeneratedPackage(reference InternalPackageR // GetDefinition looks up a particular type definition in a package available in this context func (ctx *CodeGenerationContext) GetDefinition(name InternalTypeName) (TypeDefinition, error) { pkg, err := ctx.GetGeneratedPackage(name.InternalPackageReference()) - if err != nil { return TypeDefinition{}, err } diff --git a/v2/tools/generator/internal/astmodel/conversion_function_builder.go b/v2/tools/generator/internal/astmodel/conversion_function_builder.go index fd5aa00a2bf..998a2b77f35 100644 --- a/v2/tools/generator/internal/astmodel/conversion_function_builder.go +++ b/v2/tools/generator/internal/astmodel/conversion_function_builder.go @@ -530,7 +530,6 @@ func IdentityAssignPrimitiveType( params.AssignmentHandlerOrDefault()( params.GetDestination(), params.GetSource())), nil - } // AssignToOptional assigns address of source to destination. @@ -554,7 +553,6 @@ func AssignToOptional( params.AssignmentHandlerOrDefault()( params.GetDestination(), astbuilder.AddrOf(params.GetSource()))), nil - } // a more complex conversion is needed diff --git a/v2/tools/generator/internal/astmodel/enum_type.go b/v2/tools/generator/internal/astmodel/enum_type.go index 9918bc9c7ad..886d26df1a1 100644 --- a/v2/tools/generator/internal/astmodel/enum_type.go +++ b/v2/tools/generator/internal/astmodel/enum_type.go @@ -47,7 +47,8 @@ func NewEnumType(baseType *PrimitiveType, options ...EnumValue) *EnumType { return &EnumType{ baseType: baseType, options: options, - emitValidation: true} + emitValidation: true, + } } // WithoutValidation returns a copy of this enum, without associated Kubebuilder annotations. @@ -90,8 +91,8 @@ func (enum *EnumType) createBaseDeclaration( codeGenerationContext *CodeGenerationContext, name TypeName, description []string, - validations []KubeBuilderValidation) dst.Decl { - + validations []KubeBuilderValidation, +) dst.Decl { typeSpecification := &dst.TypeSpec{ Name: dst.NewIdent(name.Name()), Type: enum.baseType.AsType(codeGenerationContext), diff --git a/v2/tools/generator/internal/astmodel/external_package_reference.go b/v2/tools/generator/internal/astmodel/external_package_reference.go index 93cffcb4886..3936832ac4e 100644 --- a/v2/tools/generator/internal/astmodel/external_package_reference.go +++ b/v2/tools/generator/internal/astmodel/external_package_reference.go @@ -16,8 +16,10 @@ type ExternalPackageReference struct { packagePath string } -var _ PackageReference = ExternalPackageReference{} -var _ fmt.Stringer = ExternalPackageReference{} +var ( + _ PackageReference = ExternalPackageReference{} + _ fmt.Stringer = ExternalPackageReference{} +) // MakeExternalPackageReference creates a new package reference from a path func MakeExternalPackageReference(packagePath string) ExternalPackageReference { diff --git a/v2/tools/generator/internal/astmodel/file_definition.go b/v2/tools/generator/internal/astmodel/file_definition.go index e55ae4c3a65..80b1a9798ff 100644 --- a/v2/tools/generator/internal/astmodel/file_definition.go +++ b/v2/tools/generator/internal/astmodel/file_definition.go @@ -36,7 +36,6 @@ func NewFileDefinition( definitions []TypeDefinition, generatedPackages map[InternalPackageReference]*PackageDefinition, ) *FileDefinition { - // Topological sort of the definitions, putting them in order of reference ranks := calcRanks(definitions) sort.Slice(definitions, func(i, j int) bool { @@ -139,7 +138,6 @@ func assignRanks(definers []TypeDefinition, ranks map[TypeName]int) []TypeDefini // generateImports products the definitive set of imports for use in this file func (file *FileDefinition) generateImports() *PackageImportSet { - allReferences := NewPackageReferenceSet() for _, s := range file.definitions { allReferences.Merge(s.RequiredPackageReferences()) diff --git a/v2/tools/generator/internal/astmodel/generation_consts.go b/v2/tools/generator/internal/astmodel/generation_consts.go index cc3e30ee2bb..3862d9ce5b6 100644 --- a/v2/tools/generator/internal/astmodel/generation_consts.go +++ b/v2/tools/generator/internal/astmodel/generation_consts.go @@ -5,12 +5,10 @@ package astmodel -var ( - CodeGenerationComments []string = []string{ - // Note that this format is actually an official specification in go: https://github.com/golang/go/issues/13560 - "// Code generated by azure-service-operator-codegen. DO NOT EDIT.", - } -) +var CodeGenerationComments []string = []string{ + // Note that this format is actually an official specification in go: https://github.com/golang/go/issues/13560 + "// Code generated by azure-service-operator-codegen. DO NOT EDIT.", +} const ( // CodeGeneratedFileSuffix is used to identify generated files (note there is no file extension here) diff --git a/v2/tools/generator/internal/astmodel/go_source_file.go b/v2/tools/generator/internal/astmodel/go_source_file.go index 995e699a99f..b4762ddc0dc 100644 --- a/v2/tools/generator/internal/astmodel/go_source_file.go +++ b/v2/tools/generator/internal/astmodel/go_source_file.go @@ -46,7 +46,6 @@ func (w *GoSourceFileWriter) SaveToWriter(destination io.Writer) error { // SaveToFile writes the given FileAst to the specified file path func (w *GoSourceFileWriter) SaveToFile(filePath string) error { - file, err := os.Create(filePath) if err != nil { return err diff --git a/v2/tools/generator/internal/astmodel/identifier_factory.go b/v2/tools/generator/internal/astmodel/identifier_factory.go index b08b4275fc7..3452fc5b111 100644 --- a/v2/tools/generator/internal/astmodel/identifier_factory.go +++ b/v2/tools/generator/internal/astmodel/identifier_factory.go @@ -131,7 +131,6 @@ func (factory *identifierFactory) createIdentifierImpl(name string, visibility V } func (factory *identifierFactory) createIdentifierUncached(name string, visibility Visibility, reservedWords reservedWordConsideration) string { - // Trim any leading or trailing underscores before proceeding. name = strings.Trim(name, "_") diff --git a/v2/tools/generator/internal/astmodel/interface_implementation_injector.go b/v2/tools/generator/internal/astmodel/interface_implementation_injector.go index 64e9f212efd..7c7d346ba68 100644 --- a/v2/tools/generator/internal/astmodel/interface_implementation_injector.go +++ b/v2/tools/generator/internal/astmodel/interface_implementation_injector.go @@ -25,7 +25,8 @@ func NewInterfaceImplementationInjector() *InterfaceImplementationInjector { // Inject modifies the passed type definition by injecting the passed interface implementation func (injector *InterfaceImplementationInjector) Inject( - def TypeDefinition, implementations ...*InterfaceImplementation) (TypeDefinition, error) { + def TypeDefinition, implementations ...*InterfaceImplementation, +) (TypeDefinition, error) { result := def for _, impl := range implementations { @@ -42,7 +43,8 @@ func (injector *InterfaceImplementationInjector) Inject( // injectInterfaceImplementationIntoObject takes the interface implementationprovided as a context and includes it on // the provided object type func (_ *InterfaceImplementationInjector) injectInterfaceImplementationIntoObject( - _ *TypeVisitor[any], ot *ObjectType, ctx interface{}) (Type, error) { + _ *TypeVisitor[any], ot *ObjectType, ctx interface{}, +) (Type, error) { impl := ctx.(*InterfaceImplementation) return ot.WithInterface(impl), nil } @@ -50,7 +52,8 @@ func (_ *InterfaceImplementationInjector) injectInterfaceImplementationIntoObjec // injectInterfaceImplementationIntoResource takes the interface implementation provided as a context and includes it on // the provided resource type func (_ *InterfaceImplementationInjector) injectInterfaceImplementationIntoResource( - _ *TypeVisitor[any], rt *ResourceType, ctx interface{}) (Type, error) { + _ *TypeVisitor[any], rt *ResourceType, ctx interface{}, +) (Type, error) { impl := ctx.(*InterfaceImplementation) return rt.WithInterface(impl), nil } diff --git a/v2/tools/generator/internal/astmodel/interface_implementer.go b/v2/tools/generator/internal/astmodel/interface_implementer.go index d880a94059f..72a5b1c293f 100644 --- a/v2/tools/generator/internal/astmodel/interface_implementer.go +++ b/v2/tools/generator/internal/astmodel/interface_implementer.go @@ -7,9 +7,10 @@ package astmodel import ( "go/token" - kerrors "k8s.io/apimachinery/pkg/util/errors" "sort" + kerrors "k8s.io/apimachinery/pkg/util/errors" + "github.com/dave/dst" "golang.org/x/exp/maps" diff --git a/v2/tools/generator/internal/astmodel/interface_injector.go b/v2/tools/generator/internal/astmodel/interface_injector.go index 618bbc7cd1d..780bedd29ec 100644 --- a/v2/tools/generator/internal/astmodel/interface_injector.go +++ b/v2/tools/generator/internal/astmodel/interface_injector.go @@ -35,7 +35,8 @@ func (i *InterfaceInjector) Inject(def TypeDefinition, implementation *Interface // injectFunctionIntoObject takes the function provided as a context and includes it on the // provided object type func (i *InterfaceInjector) injectInterfaceIntoObject( - _ *TypeVisitor[any], ot *ObjectType, ctx any) (Type, error) { + _ *TypeVisitor[any], ot *ObjectType, ctx any, +) (Type, error) { implementation := ctx.(*InterfaceImplementation) return ot.WithInterface(implementation), nil } @@ -43,7 +44,8 @@ func (i *InterfaceInjector) injectInterfaceIntoObject( // injectFunctionIntoResource takes the function provided as a context and includes it on the // provided resource type func (i *InterfaceInjector) injectInterfaceIntoResource( - _ *TypeVisitor[any], rt *ResourceType, ctx any) (Type, error) { + _ *TypeVisitor[any], rt *ResourceType, ctx any, +) (Type, error) { fn := ctx.(*InterfaceImplementation) return rt.WithInterface(fn), nil } diff --git a/v2/tools/generator/internal/astmodel/interface_type.go b/v2/tools/generator/internal/astmodel/interface_type.go index bc0056c2a3c..f22283b404c 100644 --- a/v2/tools/generator/internal/astmodel/interface_type.go +++ b/v2/tools/generator/internal/astmodel/interface_type.go @@ -6,12 +6,14 @@ package astmodel import ( - "github.com/pkg/errors" "go/token" - kerrors "k8s.io/apimachinery/pkg/util/errors" "sort" "strings" + "github.com/pkg/errors" + + kerrors "k8s.io/apimachinery/pkg/util/errors" + "github.com/dave/dst" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astbuilder" diff --git a/v2/tools/generator/internal/astmodel/kubebuilder_validations.go b/v2/tools/generator/internal/astmodel/kubebuilder_validations.go index 34641f3d309..f44185c6db3 100644 --- a/v2/tools/generator/internal/astmodel/kubebuilder_validations.go +++ b/v2/tools/generator/internal/astmodel/kubebuilder_validations.go @@ -153,7 +153,7 @@ func MakeMaximumValidation(value *big.Rat) KubeBuilderValidation { if value.IsInt() { return KubeBuilderValidation{MaximumValidationName, value.RatString()} } else { - //TODO (@bearps) Restore this check when we modify AsDeclarations() to allow error returns + // TODO (@bearps) Restore this check when we modify AsDeclarations() to allow error returns if floatValue, ok := value.Float64(); ok { return KubeBuilderValidation{MaximumValidationName, floatValue} } @@ -167,7 +167,7 @@ func MaxMinimumValidation(value *big.Rat) KubeBuilderValidation { if value.IsInt() { return KubeBuilderValidation{MinimumValidationName, value.RatString()} } else { - //TODO (@bearps) Restore this check when we modify AsDeclarations() to allow error returns + // TODO (@bearps) Restore this check when we modify AsDeclarations() to allow error returns if floatValue, ok := value.Float64(); ok { return KubeBuilderValidation{MinimumValidationName, floatValue} } @@ -189,7 +189,7 @@ func MakeMultipleOfValidation(value *big.Rat) KubeBuilderValidation { if value.IsInt() { return KubeBuilderValidation{MultipleOfValidationName, value.RatString()} } else { - //TODO (@bearps) Restore this check when we modify AsDeclarations() to allow error returns + // TODO (@bearps) Restore this check when we modify AsDeclarations() to allow error returns if floatValue, ok := value.Float64(); ok { return KubeBuilderValidation{MultipleOfValidationName, floatValue} } diff --git a/v2/tools/generator/internal/astmodel/object_type.go b/v2/tools/generator/internal/astmodel/object_type.go index c5e772616b6..597580bbdde 100644 --- a/v2/tools/generator/internal/astmodel/object_type.go +++ b/v2/tools/generator/internal/astmodel/object_type.go @@ -7,10 +7,11 @@ package astmodel import ( "go/token" - kerrors "k8s.io/apimachinery/pkg/util/errors" "sort" "strings" + kerrors "k8s.io/apimachinery/pkg/util/errors" + "github.com/dave/dst" "github.com/pkg/errors" "golang.org/x/exp/slices" @@ -31,8 +32,10 @@ type ObjectType struct { } // for want of a better place for this to live… -var AdditionalPropertiesPropertyName = PropertyName("AdditionalProperties") -var AdditionalPropertiesJsonName = "additionalProperties" +var ( + AdditionalPropertiesPropertyName = PropertyName("AdditionalProperties") + AdditionalPropertiesJsonName = "additionalProperties" +) // EmptyObjectType is an empty object var EmptyObjectType = NewObjectType() diff --git a/v2/tools/generator/internal/astmodel/package_import.go b/v2/tools/generator/internal/astmodel/package_import.go index ad05f01e0f5..a843a5d9a2c 100644 --- a/v2/tools/generator/internal/astmodel/package_import.go +++ b/v2/tools/generator/internal/astmodel/package_import.go @@ -7,6 +7,7 @@ package astmodel import ( "fmt" + "github.com/dave/dst" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astbuilder" diff --git a/v2/tools/generator/internal/astmodel/package_reference.go b/v2/tools/generator/internal/astmodel/package_reference.go index 80f9275839c..89f81f421e7 100644 --- a/v2/tools/generator/internal/astmodel/package_reference.go +++ b/v2/tools/generator/internal/astmodel/package_reference.go @@ -6,9 +6,10 @@ package astmodel import ( - "golang.org/x/exp/slices" "strings" "unicode" + + "golang.org/x/exp/slices" ) const ( diff --git a/v2/tools/generator/internal/astmodel/property_injector.go b/v2/tools/generator/internal/astmodel/property_injector.go index dca4744f51f..32361e6b86f 100644 --- a/v2/tools/generator/internal/astmodel/property_injector.go +++ b/v2/tools/generator/internal/astmodel/property_injector.go @@ -36,7 +36,8 @@ func (pi *PropertyInjector) Inject(def TypeDefinition, prop *PropertyDefinition) // injectPropertyIntoObject takes the property provided as a context and includes it on the provided object type func (pi *PropertyInjector) injectPropertyIntoObject( - _ *TypeVisitor[*PropertyDefinition], ot *ObjectType, prop *PropertyDefinition) (Type, error) { + _ *TypeVisitor[*PropertyDefinition], ot *ObjectType, prop *PropertyDefinition, +) (Type, error) { // Ensure that we don't already have a property with the same name if _, ok := ot.Property(prop.PropertyName()); ok { return nil, errors.Errorf("already has property named %q", prop.PropertyName()) diff --git a/v2/tools/generator/internal/astmodel/resource_reference.go b/v2/tools/generator/internal/astmodel/resource_reference.go index 65c1143eb1a..712b7cc46cd 100644 --- a/v2/tools/generator/internal/astmodel/resource_reference.go +++ b/v2/tools/generator/internal/astmodel/resource_reference.go @@ -5,8 +5,10 @@ package astmodel -var arrayResourceReferenceType = NewArrayType(ResourceReferenceType) -var mapResourceReferenceType = NewMapType(StringType, ResourceReferenceType) +var ( + arrayResourceReferenceType = NewArrayType(ResourceReferenceType) + mapResourceReferenceType = NewMapType(StringType, ResourceReferenceType) +) func IsTypeResourceReference(t Type) bool { isResourceReference := TypeEquals(t, ResourceReferenceType) diff --git a/v2/tools/generator/internal/astmodel/sub_package_reference.go b/v2/tools/generator/internal/astmodel/sub_package_reference.go index 9e8bd112b21..c61ac7f3a68 100644 --- a/v2/tools/generator/internal/astmodel/sub_package_reference.go +++ b/v2/tools/generator/internal/astmodel/sub_package_reference.go @@ -15,9 +15,11 @@ type SubPackageReference struct { name string } -var _ PackageReference = SubPackageReference{} -var _ InternalPackageReference = SubPackageReference{} -var _ DerivedPackageReference = SubPackageReference{} +var ( + _ PackageReference = SubPackageReference{} + _ InternalPackageReference = SubPackageReference{} + _ DerivedPackageReference = SubPackageReference{} +) var _ fmt.Stringer = SubPackageReference{} diff --git a/v2/tools/generator/internal/astmodel/test_case_injector.go b/v2/tools/generator/internal/astmodel/test_case_injector.go index d48ba7017c1..40c582e2977 100644 --- a/v2/tools/generator/internal/astmodel/test_case_injector.go +++ b/v2/tools/generator/internal/astmodel/test_case_injector.go @@ -41,7 +41,8 @@ func (fi *TestCaseInjector) Inject(def TypeDefinition, cases ...TestCase) (TypeD // injectTestCaseIntoObject takes the function provided as a context and includes it on the // provided object type func (_ *TestCaseInjector) injectTestCaseIntoObject( - _ *TypeVisitor[any], ot *ObjectType, ctx any) (Type, error) { + _ *TypeVisitor[any], ot *ObjectType, ctx any, +) (Type, error) { fn := ctx.(TestCase) return ot.WithTestCase(fn), nil } @@ -49,7 +50,8 @@ func (_ *TestCaseInjector) injectTestCaseIntoObject( // injectTestCaseIntoResource takes the function provided as a context and includes it on the // provided resource type func (_ *TestCaseInjector) injectTestCaseIntoResource( - _ *TypeVisitor[any], rt *ResourceType, ctx any) (Type, error) { + _ *TypeVisitor[any], rt *ResourceType, ctx any, +) (Type, error) { fn := ctx.(TestCase) return rt.WithTestCase(fn), nil } diff --git a/v2/tools/generator/internal/astmodel/type_association_test.go b/v2/tools/generator/internal/astmodel/type_association_test.go index 02270869e9b..47e10863e1c 100644 --- a/v2/tools/generator/internal/astmodel/type_association_test.go +++ b/v2/tools/generator/internal/astmodel/type_association_test.go @@ -11,8 +11,10 @@ import ( . "github.com/onsi/gomega" ) -var t1 = MakeInternalTypeName(makeTestLocalPackageReference("group", "2020-01-01"), "t1") -var t2 = MakeInternalTypeName(makeTestLocalPackageReference("group", "2020-01-01"), "t2") +var ( + t1 = MakeInternalTypeName(makeTestLocalPackageReference("group", "2020-01-01"), "t1") + t2 = MakeInternalTypeName(makeTestLocalPackageReference("group", "2020-01-01"), "t2") +) func TestEmptyTypeAssociation_AreEqual(t *testing.T) { t.Parallel() diff --git a/v2/tools/generator/internal/astmodel/type_definition_set_test.go b/v2/tools/generator/internal/astmodel/type_definition_set_test.go index 76fa721fefc..5f55a2801e3 100644 --- a/v2/tools/generator/internal/astmodel/type_definition_set_test.go +++ b/v2/tools/generator/internal/astmodel/type_definition_set_test.go @@ -396,7 +396,8 @@ func createTestResource( // createTestSpec makes a spec for testing func createTestSpec( name string, - properties ...*PropertyDefinition) TypeDefinition { + properties ...*PropertyDefinition, +) TypeDefinition { specName := MakeInternalTypeName(pkg, name+SpecSuffix) return MakeTypeDefinition( specName, @@ -406,7 +407,8 @@ func createTestSpec( // createTestStatus makes a status for testing func createTestStatus( name string, - properties ...*PropertyDefinition) TypeDefinition { + properties ...*PropertyDefinition, +) TypeDefinition { statusName := MakeInternalTypeName(pkg, name+StatusSuffix) return MakeTypeDefinition( statusName, diff --git a/v2/tools/generator/internal/astmodel/type_definition_test.go b/v2/tools/generator/internal/astmodel/type_definition_test.go index 27e86532d88..d5d7d84f7e6 100644 --- a/v2/tools/generator/internal/astmodel/type_definition_test.go +++ b/v2/tools/generator/internal/astmodel/type_definition_test.go @@ -109,7 +109,6 @@ func TestApplyObjectTransformation_GivenObjectAndTransformation_AppliesTransform prop, ok := ot.Property("FullName") g.Expect(ok).To(BeTrue()) g.Expect(prop).NotTo(BeNil()) - } func TestApplyObjectTransformation_GivenObjectAndTransformationReturningError_ReturnsError(t *testing.T) { diff --git a/v2/tools/generator/internal/astmodel/type_flag.go b/v2/tools/generator/internal/astmodel/type_flag.go index 7bb3eb970a4..e360d46b83b 100644 --- a/v2/tools/generator/internal/astmodel/type_flag.go +++ b/v2/tools/generator/internal/astmodel/type_flag.go @@ -30,7 +30,6 @@ func (f TypeFlag) ApplyTo(t Type) *FlaggedType { // RemoveFrom applies the tag to the provided type func (f TypeFlag) RemoveFrom(t Type) (Type, error) { - removeFlag := func(it *FlaggedType) Type { return it.WithoutFlag(f) } diff --git a/v2/tools/generator/internal/astmodel/type_visitor.go b/v2/tools/generator/internal/astmodel/type_visitor.go index acdd2a3cca0..8fa2055f446 100644 --- a/v2/tools/generator/internal/astmodel/type_visitor.go +++ b/v2/tools/generator/internal/astmodel/type_visitor.go @@ -416,7 +416,6 @@ func IdentityVisitOfResourceType[C any](this *TypeVisitor[C], it *ResourceType, } func IdentityVisitOfOneOfType[C any](this *TypeVisitor[C], it *OneOfType, ctx C) (Type, error) { - result := it.WithoutAnyPropertyObjects() propertyObjects := it.PropertyObjects() @@ -444,7 +443,6 @@ func IdentityVisitOfOneOfType[C any](this *TypeVisitor[C], it *OneOfType, ctx C) newTypes = append(newTypes, newType) return nil }) - if err != nil { return nil, err } diff --git a/v2/tools/generator/internal/codegen/debug_reporter.go b/v2/tools/generator/internal/codegen/debug_reporter.go index f4da424927c..9630fc65322 100644 --- a/v2/tools/generator/internal/codegen/debug_reporter.go +++ b/v2/tools/generator/internal/codegen/debug_reporter.go @@ -6,9 +6,10 @@ package codegen import ( - "github.com/pkg/errors" "strconv" + "github.com/pkg/errors" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/codegen/pipeline" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/reporting" diff --git a/v2/tools/generator/internal/codegen/pipeline/add_cross_resource_references.go b/v2/tools/generator/internal/codegen/pipeline/add_cross_resource_references.go index cab0fa95e46..d2c78b96647 100644 --- a/v2/tools/generator/internal/codegen/pipeline/add_cross_resource_references.go +++ b/v2/tools/generator/internal/codegen/pipeline/add_cross_resource_references.go @@ -20,7 +20,6 @@ import ( // TransformCrossResourceReferencesStageID is the unique identifier for this pipeline stage const TransformCrossResourceReferencesStageID = "transformCrossResourceReferences" -var armIDDescriptionRegex = regexp.MustCompile("(?i)(.*/subscriptions/.*?/resourceGroups/.*|ARM ID|Resource ID|resourceId)") var idRegex = regexp.MustCompile("^(.*)I[d|D]s?$") // TransformCrossResourceReferences replaces cross resource references with genruntime.ResourceReference. @@ -206,8 +205,8 @@ func MakeARMIDToResourceReferenceTypeVisitor(idFactory astmodel.IdentifierFactor func makeResourceReferenceProperty( typeName astmodel.InternalTypeName, idFactory astmodel.IdentifierFactory, - existing *astmodel.PropertyDefinition) *astmodel.PropertyDefinition { - + existing *astmodel.PropertyDefinition, +) *astmodel.PropertyDefinition { _, isSlice := astmodel.AsArrayType(existing.PropertyType()) _, isMap := astmodel.AsMapType(existing.PropertyType()) var referencePropertyName string diff --git a/v2/tools/generator/internal/codegen/pipeline/add_cross_resource_references_test.go b/v2/tools/generator/internal/codegen/pipeline/add_cross_resource_references_test.go index fd95d1f866a..9ec50b0d156 100644 --- a/v2/tools/generator/internal/codegen/pipeline/add_cross_resource_references_test.go +++ b/v2/tools/generator/internal/codegen/pipeline/add_cross_resource_references_test.go @@ -17,6 +17,7 @@ import ( var IDsArrayProperty = astmodel.NewPropertyDefinition("Ids", "ids", astmodel.NewArrayType(astmodel.ARMIDType)). WithDescription("This is the important line here. The final type should be []genruntime.ResourceReference") + var IDsMapProperty = astmodel.NewPropertyDefinition("Ids", "ids", astmodel.NewMapType(astmodel.StringType, astmodel.ARMIDType)). WithDescription("This is the important line here. The final type should be map[string]genruntime.ResourceReference") diff --git a/v2/tools/generator/internal/codegen/pipeline/add_operator_spec.go b/v2/tools/generator/internal/codegen/pipeline/add_operator_spec.go index e45339af7ca..5c4b58a8b22 100644 --- a/v2/tools/generator/internal/codegen/pipeline/add_operator_spec.go +++ b/v2/tools/generator/internal/codegen/pipeline/add_operator_spec.go @@ -63,8 +63,8 @@ func createOperatorSpecIfNeeded( defs astmodel.TypeDefinitionSet, configuration *config.Configuration, idFactory astmodel.IdentifierFactory, - resource astmodel.TypeDefinition) (astmodel.TypeDefinitionSet, ExportedProperties, error) { - + resource astmodel.TypeDefinition, +) (astmodel.TypeDefinitionSet, ExportedProperties, error) { resolved, err := defs.ResolveResourceSpecAndStatus(resource) if err != nil { return nil, nil, errors.Wrapf(err, "resolving resource spec and status for %s", resource.Name()) @@ -140,7 +140,6 @@ func newConfigMapTypeWalker( defs astmodel.TypeDefinitionSet, paths map[string]string, ) *configMapTypeWalker { - result := &configMapTypeWalker{ configuredProperties: paths, exportedProperties: make(ExportedProperties), @@ -245,8 +244,8 @@ func makeJSONPathFromProps(props []*astmodel.PropertyDefinition) string { func getConfigMapProperties( defs astmodel.TypeDefinitionSet, configuration *config.Configuration, - resource astmodel.TypeDefinition) ([]string, ExportedProperties, error) { - + resource astmodel.TypeDefinition, +) ([]string, ExportedProperties, error) { configMapPaths, configMapPathsOk := configuration.ObjectModelConfiguration.GeneratedConfigs.Lookup(resource.Name()) additionalConfigMaps, additionalConfigMapsOk := configuration.ObjectModelConfiguration.ManualConfigs.Lookup(resource.Name()) @@ -288,7 +287,8 @@ type operatorSpecBuilder struct { func newOperatorSpecBuilder( configuration *config.Configuration, idFactory astmodel.IdentifierFactory, - resource astmodel.TypeDefinition) *operatorSpecBuilder { + resource astmodel.TypeDefinition, +) *operatorSpecBuilder { result := &operatorSpecBuilder{ idFactory: idFactory, configuration: configuration, @@ -349,8 +349,8 @@ func (b *operatorSpecBuilder) newConfigMapProperty(configMapTypeName astmodel.Ty } func (b *operatorSpecBuilder) addSecretsToOperatorSpec( - azureGeneratedSecrets []string) { - + azureGeneratedSecrets []string, +) { if len(azureGeneratedSecrets) == 0 { return // Nothing to do } @@ -392,8 +392,8 @@ func (b *operatorSpecBuilder) addSecretsToOperatorSpec( } func (b *operatorSpecBuilder) addConfigs( - exportedConfigs []string) { - + exportedConfigs []string, +) { if len(exportedConfigs) == 0 { return // Nothing to do } diff --git a/v2/tools/generator/internal/codegen/pipeline/add_simple_cross_resource_references.go b/v2/tools/generator/internal/codegen/pipeline/add_simple_cross_resource_references.go index aa175d57bf2..054bd51413d 100644 --- a/v2/tools/generator/internal/codegen/pipeline/add_simple_cross_resource_references.go +++ b/v2/tools/generator/internal/codegen/pipeline/add_simple_cross_resource_references.go @@ -20,7 +20,6 @@ func TransformCrossResourceReferencesToString() *Stage { TransformCrossResourceReferencesToStringStageID, "Replace cross-resource references with string", func(ctx context.Context, state *State) (*State, error) { - updatedDefs, err := stripARMIDPrimitiveTypes(state.Definitions()) if err != nil { return nil, errors.Wrap(err, "failed to strip ARM ID primitive types") diff --git a/v2/tools/generator/internal/codegen/pipeline/add_status_conditions.go b/v2/tools/generator/internal/codegen/pipeline/add_status_conditions.go index 923eaed34cc..fe73bc6b375 100644 --- a/v2/tools/generator/internal/codegen/pipeline/add_status_conditions.go +++ b/v2/tools/generator/internal/codegen/pipeline/add_status_conditions.go @@ -67,8 +67,8 @@ func AddStatusConditions(idFactory astmodel.IdentifierFactory) *Stage { // SetConditions() methods, implementing the genruntime.Conditioner interface. func NewConditionerInterfaceImpl( idFactory astmodel.IdentifierFactory, - resource *astmodel.ResourceType) (*astmodel.InterfaceImplementation, error) { - + resource *astmodel.ResourceType, +) (*astmodel.InterfaceImplementation, error) { getConditions := functions.NewResourceFunction( "Get"+astmodel.ConditionsProperty, resource, diff --git a/v2/tools/generator/internal/codegen/pipeline/apply_cross_resource_references_from_config.go b/v2/tools/generator/internal/codegen/pipeline/apply_cross_resource_references_from_config.go index 9aa6c86894b..fe7d8bb4f1e 100644 --- a/v2/tools/generator/internal/codegen/pipeline/apply_cross_resource_references_from_config.go +++ b/v2/tools/generator/internal/codegen/pipeline/apply_cross_resource_references_from_config.go @@ -7,9 +7,11 @@ package pipeline import ( "context" + "regexp" "github.com/go-logr/logr" "github.com/pkg/errors" + kerrors "k8s.io/apimachinery/pkg/util/errors" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" @@ -174,22 +176,40 @@ func MakeARMIDPropertyTypeVisitor( return visitor } +var ( + armIDNameRegex = regexp.MustCompile("(?i)(^Id$|ResourceID|ARMID)") + armIDDescriptionRegex = regexp.MustCompile("(?i)(.*/subscriptions/.*?/resourceGroups/.*|ARMID|ARM ID|Resource ID|resourceId)") +) + // DoesPropertyLookLikeARMReference uses a simple heuristic to determine if a property looks like it might be an ARM reference. // This can be used for logging/reporting purposes to discover references which we missed. func DoesPropertyLookLikeARMReference(prop *astmodel.PropertyDefinition) bool { // The property must be a string, optional string, list of strings, or map[string]string - isString := astmodel.TypeEquals(prop.PropertyType(), astmodel.StringType) - isOptionalString := astmodel.TypeEquals(prop.PropertyType(), astmodel.OptionalStringType) - isStringSlice := astmodel.TypeEquals(prop.PropertyType(), astmodel.NewArrayType(astmodel.StringType)) - isStringMap := astmodel.TypeEquals(prop.PropertyType(), astmodel.MapOfStringStringType) + mightBeReference := false + if pt, ok := astmodel.AsPrimitiveType(prop.PropertyType()); ok { + // Might be a reference if we have a primitive type that's a string + mightBeReference = pt == astmodel.StringType + } - if !isString && !isOptionalString && !isStringSlice && !isStringMap { - return false + if at, ok := astmodel.AsArrayType(prop.PropertyType()); ok { + // Might be references if we have an array of strings + elementType, elementTypeIsPrimitive := astmodel.AsPrimitiveType(at.Element()) + mightBeReference = elementTypeIsPrimitive && + elementType == astmodel.StringType } + if mt, ok := astmodel.AsMapType(prop.PropertyType()); ok { + // Might be references if we have a map of strings to strings + keyType, keyTypeIsPrimitive := astmodel.AsPrimitiveType(mt.KeyType()) + valueType, valueTypeIsPrimitive := astmodel.AsPrimitiveType(mt.ValueType()) + mightBeReference = keyTypeIsPrimitive && valueTypeIsPrimitive && + keyType == astmodel.StringType && valueType == astmodel.StringType + } + + hasMatchingName := armIDNameRegex.MatchString(prop.PropertyName().String()) hasMatchingDescription := armIDDescriptionRegex.MatchString(prop.Description()) - namedID := prop.HasName("Id") - if hasMatchingDescription || namedID { + + if mightBeReference && (hasMatchingName || hasMatchingDescription) { return true } diff --git a/v2/tools/generator/internal/codegen/pipeline/apply_defaulter_and_validator_interfaces.go b/v2/tools/generator/internal/codegen/pipeline/apply_defaulter_and_validator_interfaces.go index 157165c8377..5c61a61be93 100644 --- a/v2/tools/generator/internal/codegen/pipeline/apply_defaulter_and_validator_interfaces.go +++ b/v2/tools/generator/internal/codegen/pipeline/apply_defaulter_and_validator_interfaces.go @@ -94,8 +94,8 @@ func getDefaults( func getValidations( resourceDef astmodel.TypeDefinition, idFactory astmodel.IdentifierFactory, - defs astmodel.TypeDefinitionSet) (map[functions.ValidationKind][]*functions.ResourceFunction, error) { - + defs astmodel.TypeDefinitionSet, +) (map[functions.ValidationKind][]*functions.ResourceFunction, error) { resource, ok := resourceDef.Type().(*astmodel.ResourceType) if !ok { return nil, errors.Errorf("resource %s did not have type of kind *astmodel.ResourceType, instead %T", resourceDef.Name(), resourceDef.Type()) diff --git a/v2/tools/generator/internal/codegen/pipeline/apply_type_rewrites.go b/v2/tools/generator/internal/codegen/pipeline/apply_type_rewrites.go index 87ab86dba8d..816e43eacfc 100644 --- a/v2/tools/generator/internal/codegen/pipeline/apply_type_rewrites.go +++ b/v2/tools/generator/internal/codegen/pipeline/apply_type_rewrites.go @@ -7,6 +7,7 @@ package pipeline import ( "context" + "github.com/go-logr/logr" "github.com/pkg/errors" diff --git a/v2/tools/generator/internal/codegen/pipeline/assemble_oneof_test.go b/v2/tools/generator/internal/codegen/pipeline/assemble_oneof_test.go index 1a199d10ead..4eafa82916c 100644 --- a/v2/tools/generator/internal/codegen/pipeline/assemble_oneof_test.go +++ b/v2/tools/generator/internal/codegen/pipeline/assemble_oneof_test.go @@ -6,9 +6,10 @@ package pipeline import ( + "testing" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/test" - "testing" . "github.com/onsi/gomega" ) diff --git a/v2/tools/generator/internal/codegen/pipeline/convert_allof_and_oneof_to_objects.go b/v2/tools/generator/internal/codegen/pipeline/convert_allof_and_oneof_to_objects.go index 6575d21b15e..d598f1ddb1c 100644 --- a/v2/tools/generator/internal/codegen/pipeline/convert_allof_and_oneof_to_objects.go +++ b/v2/tools/generator/internal/codegen/pipeline/convert_allof_and_oneof_to_objects.go @@ -34,7 +34,6 @@ func ConvertAllOfAndOneOfToObjects(idFactory astmodel.IdentifierFactory) *Stage "allof-anyof-objects", "Convert allOf and oneOf to object types", func(ctx context.Context, state *State) (*State, error) { - baseSynthesizer := newSynthesizer(state.Definitions(), idFactory) newDefs := make(astmodel.TypeDefinitionSet) @@ -414,7 +413,6 @@ func (s synthesizer) getOneOfName(t astmodel.Type, propIndex int) (propertyNames func (s synthesizer) oneOfToObject( oneOf *astmodel.OneOfType, ) (astmodel.Type, error) { - if oneOf.HasDiscriminatorValue() { propertyObjects := oneOf.PropertyObjects() types := make([]astmodel.Type, 0, len(propertyObjects)) diff --git a/v2/tools/generator/internal/codegen/pipeline/convert_allof_and_oneof_to_objects_test.go b/v2/tools/generator/internal/codegen/pipeline/convert_allof_and_oneof_to_objects_test.go index 6129abe6385..77d570037da 100644 --- a/v2/tools/generator/internal/codegen/pipeline/convert_allof_and_oneof_to_objects_test.go +++ b/v2/tools/generator/internal/codegen/pipeline/convert_allof_and_oneof_to_objects_test.go @@ -7,9 +7,10 @@ package pipeline import ( "context" - "github.com/Azure/azure-service-operator/v2/tools/generator/internal/test" "testing" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/test" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" . "github.com/onsi/gomega" diff --git a/v2/tools/generator/internal/codegen/pipeline/create_arm_types.go b/v2/tools/generator/internal/codegen/pipeline/create_arm_types.go index 5561ffe6471..6cfd34f8e62 100644 --- a/v2/tools/generator/internal/codegen/pipeline/create_arm_types.go +++ b/v2/tools/generator/internal/codegen/pipeline/create_arm_types.go @@ -80,7 +80,8 @@ func newARMTypeCreator( definitions astmodel.TypeDefinitionSet, configuration *config.ObjectModelConfiguration, idFactory astmodel.IdentifierFactory, - log logr.Logger) *armTypeCreator { + log logr.Logger, +) *armTypeCreator { result := &armTypeCreator{ definitions: definitions, idFactory: idFactory, diff --git a/v2/tools/generator/internal/codegen/pipeline/create_conversion_graph.go b/v2/tools/generator/internal/codegen/pipeline/create_conversion_graph.go index cda31ecb217..152ef1ffbca 100644 --- a/v2/tools/generator/internal/codegen/pipeline/create_conversion_graph.go +++ b/v2/tools/generator/internal/codegen/pipeline/create_conversion_graph.go @@ -69,7 +69,7 @@ func exportConversionGraph(settings *DebugSettings, index int, state *State) err // Create our output folder outputFolder := settings.CreateFileName(fmt.Sprintf("conversion-graph-%d", index)) - err := os.Mkdir(outputFolder, 0700) + err := os.Mkdir(outputFolder, 0o700) if err != nil { return errors.Wrapf(err, "creating output folder for conversion graph diagnostic") } diff --git a/v2/tools/generator/internal/codegen/pipeline/create_resource_extension_types.go b/v2/tools/generator/internal/codegen/pipeline/create_resource_extension_types.go index 4e3304f1e45..82ab61137a4 100644 --- a/v2/tools/generator/internal/codegen/pipeline/create_resource_extension_types.go +++ b/v2/tools/generator/internal/codegen/pipeline/create_resource_extension_types.go @@ -22,7 +22,6 @@ func CreateResourceExtensions(localPath string, idFactory astmodel.IdentifierFac CreateResourceExtensionsStageID, "Create Resource Extensions for each resource type", func(ctx context.Context, state *State) (*State, error) { - // Map of the new extension types, to all the resource types names on which the extension applies to extendedResourceTypesMapping := make(map[astmodel.InternalTypeName][]astmodel.InternalTypeName) extendedResourceDefs := make(astmodel.TypeDefinitionSet) diff --git a/v2/tools/generator/internal/codegen/pipeline/create_resource_extension_types_test.go b/v2/tools/generator/internal/codegen/pipeline/create_resource_extension_types_test.go index 871cd795fce..79ee9aed150 100644 --- a/v2/tools/generator/internal/codegen/pipeline/create_resource_extension_types_test.go +++ b/v2/tools/generator/internal/codegen/pipeline/create_resource_extension_types_test.go @@ -57,11 +57,9 @@ func TestGolden_ResoureExtension_MoreThanOneVersion(t *testing.T) { } func getResourceExtensionTestData(pkg astmodel.LocalPackageReference, resourceName string) (astmodel.TypeDefinition, astmodel.TypeDefinition, astmodel.TypeDefinition) { - spec := test.CreateSpec(pkg, resourceName, test.FullNameProperty, test.FamilyNameProperty, test.KnownAsProperty) status := test.CreateStatus(pkg, resourceName) resource := test.CreateResource(pkg, resourceName, spec, status) return resource, spec, status - } diff --git a/v2/tools/generator/internal/codegen/pipeline/crossplane_add_at_provider.go b/v2/tools/generator/internal/codegen/pipeline/crossplane_add_at_provider.go index 2953e837fec..3ab206afe9b 100644 --- a/v2/tools/generator/internal/codegen/pipeline/crossplane_add_at_provider.go +++ b/v2/tools/generator/internal/codegen/pipeline/crossplane_add_at_provider.go @@ -51,8 +51,8 @@ func AddCrossplaneAtProvider(idFactory astmodel.IdentifierFactory) *Stage { func nestStatusIntoAtProvider( idFactory astmodel.IdentifierFactory, definitions astmodel.TypeDefinitionSet, - typeDef astmodel.TypeDefinition) ([]astmodel.TypeDefinition, error) { - + typeDef astmodel.TypeDefinition, +) ([]astmodel.TypeDefinition, error) { resource, ok := astmodel.AsResourceType(typeDef.Type()) if !ok { return nil, errors.Errorf("provided typeDef was not a resourceType, instead %T", typeDef.Type()) diff --git a/v2/tools/generator/internal/codegen/pipeline/crossplane_add_for_provider.go b/v2/tools/generator/internal/codegen/pipeline/crossplane_add_for_provider.go index 37e1bb2faaa..055c23796e9 100644 --- a/v2/tools/generator/internal/codegen/pipeline/crossplane_add_for_provider.go +++ b/v2/tools/generator/internal/codegen/pipeline/crossplane_add_for_provider.go @@ -46,8 +46,8 @@ func AddCrossplaneForProvider(idFactory astmodel.IdentifierFactory) *Stage { func nestSpecIntoForProvider( idFactory astmodel.IdentifierFactory, definitions astmodel.TypeDefinitionSet, - typeDef astmodel.TypeDefinition) ([]astmodel.TypeDefinition, error) { - + typeDef astmodel.TypeDefinition, +) ([]astmodel.TypeDefinition, error) { resource, ok := astmodel.AsResourceType(typeDef.Type()) if !ok { return nil, errors.Errorf("provided typeDef was not a resourceType, instead %T", typeDef.Type()) @@ -78,8 +78,8 @@ func nestType( definitions astmodel.TypeDefinitionSet, outerTypeName astmodel.InternalTypeName, nestedTypeName string, - nestedPropertyName string) ([]astmodel.TypeDefinition, error) { - + nestedPropertyName string, +) ([]astmodel.TypeDefinition, error) { outerType, ok := definitions[outerTypeName] if !ok { return nil, errors.Errorf("couldn't find type %q", outerTypeName) diff --git a/v2/tools/generator/internal/codegen/pipeline/debug_settings.go b/v2/tools/generator/internal/codegen/pipeline/debug_settings.go index 0f63d71892b..2adc5bf519e 100644 --- a/v2/tools/generator/internal/codegen/pipeline/debug_settings.go +++ b/v2/tools/generator/internal/codegen/pipeline/debug_settings.go @@ -6,11 +6,12 @@ package pipeline import ( - "github.com/Azure/azure-service-operator/v2/internal/util/match" - "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "path/filepath" "regexp" "strings" + + "github.com/Azure/azure-service-operator/v2/internal/util/match" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" ) type DebugSettings struct { diff --git a/v2/tools/generator/internal/codegen/pipeline/determine_resource_ownership.go b/v2/tools/generator/internal/codegen/pipeline/determine_resource_ownership.go index 016e9c1d979..b47fde65aab 100644 --- a/v2/tools/generator/internal/codegen/pipeline/determine_resource_ownership.go +++ b/v2/tools/generator/internal/codegen/pipeline/determine_resource_ownership.go @@ -12,6 +12,7 @@ import ( "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/config" + kerrors "k8s.io/apimachinery/pkg/util/errors" "github.com/pkg/errors" ) @@ -23,33 +24,74 @@ const DetermineResourceOwnershipStageId = "determineResourceOwnership" func DetermineResourceOwnership( configuration *config.Configuration, ) *Stage { - return NewLegacyStage( + return NewStage( DetermineResourceOwnershipStageId, "Determine ARM resource relationships", - func(ctx context.Context, definitions astmodel.TypeDefinitionSet) (astmodel.TypeDefinitionSet, error) { - return determineOwnership(definitions, configuration) + func(ctx context.Context, state *State) (*State, error) { + determiner := newOwnershipStage(configuration, state.Definitions()) + defs, err := determiner.assignOwners() + if err != nil { + return nil, errors.Wrapf(err, "failed to determine resource ownership") + } + + return state.WithOverlaidDefinitions(defs), nil }) } -func determineOwnership( - definitions astmodel.TypeDefinitionSet, +type ownershipStage struct { + configuration *config.Configuration + definitions astmodel.TypeDefinitionSet + resourcesByParentURI map[string][]astmodel.InternalTypeName +} + +func newOwnershipStage( configuration *config.Configuration, -) (astmodel.TypeDefinitionSet, error) { + definitions astmodel.TypeDefinitionSet, +) *ownershipStage { + result := &ownershipStage{ + configuration: configuration, + definitions: definitions, + resourcesByParentURI: make(map[string][]astmodel.InternalTypeName), + } + + result.indexByParent() + return result +} + +func (o *ownershipStage) indexByParent() { + resources := astmodel.FindResourceDefinitions(o.definitions) + + // Index all resources by canonical URL of their parent + for _, def := range resources { + rt, _ := astmodel.AsResourceType(def.Type()) + canonical := o.canonicalizeURI(rt.ARMURI()) + parent := o.uriOfParentResource(canonical) + if parent != "" { + o.resourcesByParentURI[parent] = append(o.resourcesByParentURI[parent], def.Name()) + } + } +} + +func (o *ownershipStage) assignOwners() (astmodel.TypeDefinitionSet, error) { updatedDefs := make(astmodel.TypeDefinitionSet) + resources := astmodel.FindResourceDefinitions(o.definitions) - resources := astmodel.FindResourceDefinitions(definitions) + // Loop through and associate children with parents, if found + var errs []error for _, def := range resources { - resolved, err := definitions.ResolveResourceSpecAndStatus(def) + resolved, err := o.definitions.ResolveResourceSpecAndStatus(def) if err != nil { return nil, errors.Wrapf(err, "unable to find resource %s spec and status", def.Name()) } - rt := def.Type().(*astmodel.ResourceType) - childResourceTypeNames := findChildren(rt, def.Name(), resources) + childResourceTypeNames := o.findChildren(def) - err = updateChildResourceDefinitionsWithOwner(definitions, childResourceTypeNames, def.Name(), updatedDefs) + err = o.updateChildResourceDefinitionsWithOwner(childResourceTypeNames, def.Name(), updatedDefs) if err != nil { - return nil, err + errs = append( + errs, + errors.Wrapf(err, "failed to update ownership for resource %s", def.Name())) + continue } // Remove the resources property from the owning resource spec @@ -58,75 +100,50 @@ func determineOwnership( updatedDefs[resolved.SpecDef.Name()] = newDef } - setDefaultOwner(configuration, definitions, updatedDefs) + if len(errs) > 0 { + return nil, errors.Wrapf( + kerrors.NewAggregate(errs), + "failed to update ownership for some resources") + } + + o.setDefaultOwner(updatedDefs) - return definitions.OverlayWith(updatedDefs), nil + return updatedDefs, nil } -var urlParamRegex = regexp.MustCompile("\\{.*?}") +var urlParamRegex = regexp.MustCompile(`\{.*?}`) -func findChildren( - rt *astmodel.ResourceType, - resourceName astmodel.InternalTypeName, - others astmodel.TypeDefinitionSet, +func (o *ownershipStage) findChildren( + def astmodel.TypeDefinition, ) []astmodel.InternalTypeName { - // append "/" to the ARM URI so that if this is (e.g.): - // /resource/name - // it doesn't match as a prefix of: - // /resource/namedThing - // but it is a prefix of: - // /resource/name/subresource/subname - myPrefix := rt.ARMURI() + "/" - myPrefix = canonicalizeURI(myPrefix) - - var result []astmodel.InternalTypeName - for otherName, otherDef := range others { - other, ok := astmodel.AsResourceType(otherDef.Type()) - if !ok { - continue - } - if rt == other { - continue // don’t self-own - } - - // TODO: If it ever arises that we have a resource whose owner doesn't exist in the same API - // TODO: version this might be an issue. - // Ownership transcends APIVersion, but in order for things like $exportAs to work, it's best if - // ownership for each resource points to the owner in the same package. This ensures that standard tools - // like renamingVisitor work. - if !otherDef.Name().PackageReference().Equals(resourceName.PackageReference()) { - continue // Don't own if in a different package - } - - otherURI := canonicalizeURI(other.ARMURI()) - - // Compare case-insensitive in case specs have different URL casings in their Swagger - if strings.HasPrefix(strings.ToLower(otherURI), strings.ToLower(myPrefix)) { - // now, accept it only if it contains two '/' exactly: - // so that the string is of the form: - // {prefix}/resourceType/resourceName - // and not a grandchild resource: - // {prefix}/resourceType/resourceName/anotherResourceType/anotherResourceName - withoutPrefix := otherURI[len(myPrefix)-1:] - if strings.Count(withoutPrefix, "/") == 2 { - result = append(result, otherName) - } - - } + rt, ok := astmodel.AsResourceType(def.Type()) + if !ok { + return nil } - return result + resourceURI := o.canonicalizeURI(rt.ARMURI()) + return o.resourcesByParentURI[resourceURI] } -func canonicalizeURI(uri string) string { +func (*ownershipStage) canonicalizeURI(uri string) string { // Replace all {.*}'s with {}, in case different URIs use different names for the same // parameter uri = urlParamRegex.ReplaceAllString(uri, "{}") - return uri + uri = strings.TrimSuffix(uri, "/") + return strings.ToLower(uri) } -func updateChildResourceDefinitionsWithOwner( - definitions astmodel.TypeDefinitionSet, +// uriOfParentResource removes the last two segments of the URI, which are the resource type and resource name +func (*ownershipStage) uriOfParentResource(uri string) string { + parts := strings.Split(uri, "/") + if len(parts) < 2 { + return "" + } + + return strings.Join(parts[:len(parts)-2], "/") +} + +func (o *ownershipStage) updateChildResourceDefinitionsWithOwner( childResourceTypeNames []astmodel.InternalTypeName, owningResourceName astmodel.InternalTypeName, updatedDefs astmodel.TypeDefinitionSet, @@ -136,11 +153,20 @@ func updateChildResourceDefinitionsWithOwner( typeName = typeName.Singular() // Confirm the type really exists - childResourceDef, ok := definitions[typeName] + childResourceDef, ok := o.definitions[typeName] if !ok { return errors.Errorf("couldn't find child resource type %s", typeName) } + // TODO: If it ever arises that we have a resource whose owner doesn't exist in the same API + // TODO: version this might be an issue. + // Ownership transcends APIVersion, but in order for things like $exportAs to work, it's best if + // ownership for each resource points to the owner in the same package. This ensures that standard tools + // like renamingVisitor work. + if !typeName.InternalPackageReference().Equals(owningResourceName.InternalPackageReference()) { + continue // Don't own if in a different package + } + // Update the definition of the child resource type to point to its owner childResource, ok := childResourceDef.Type().(*astmodel.ResourceType) if !ok { @@ -174,13 +200,11 @@ func updateChildResourceDefinitionsWithOwner( // setDefaultOwner sets a default owner for all resources which don't have one. The default owner is ResourceGroup. // Extension resources have no owner set, as they are a special case. -func setDefaultOwner( - configuration *config.Configuration, - definitions astmodel.TypeDefinitionSet, +func (o *ownershipStage) setDefaultOwner( updatedDefs astmodel.TypeDefinitionSet, ) { // Go over all the resource types and flag any that don't have an owner as having resource group as their owner - for _, def := range definitions { + for _, def := range o.definitions { // Check if we've already modified this type - we need to use the already modified value if updatedDef, ok := updatedDefs[def.Name()]; ok { def = updatedDef @@ -195,7 +219,7 @@ func setDefaultOwner( ownerTypeName := astmodel.MakeInternalTypeName( // Note that the version doesn't really matter here -- it's removed later. We just need to refer to the logical // resource group really - configuration.MakeLocalPackageReference("resources", "v20191001"), + o.configuration.MakeLocalPackageReference("resources", "v20191001"), "ResourceGroup") updatedType := resourceType.WithOwner(ownerTypeName) // TODO: Note that right now... this type doesn't actually exist... // This can overwrite because a resource with no owner may have had child resources, diff --git a/v2/tools/generator/internal/codegen/pipeline/determine_resource_ownership_test.go b/v2/tools/generator/internal/codegen/pipeline/determine_resource_ownership_test.go index a2080ccd9cd..5d37b988fb9 100644 --- a/v2/tools/generator/internal/codegen/pipeline/determine_resource_ownership_test.go +++ b/v2/tools/generator/internal/codegen/pipeline/determine_resource_ownership_test.go @@ -11,6 +11,7 @@ import ( "github.com/onsi/gomega" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/config" ) var pr astmodel.InternalPackageReference = astmodel.MakeLocalPackageReference("prefix", "group", "v", "20000101") @@ -20,13 +21,17 @@ func Test_FindChildren_ResourceDoesNotOwnItself(t *testing.T) { g := gomega.NewWithT(t) + cfg := config.NewConfiguration() resources := make(astmodel.TypeDefinitionSet) ownerType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/owner") ownerName := astmodel.MakeInternalTypeName(pr, "Owner") - resources.Add(astmodel.MakeTypeDefinition(ownerName, ownerType)) + owner := astmodel.MakeTypeDefinition(ownerName, ownerType) + resources.Add(owner) - children := findChildren(ownerType, ownerName, resources) + stage := newOwnershipStage(cfg, resources) + + children := stage.findChildren(owner) g.Expect(children).To(gomega.BeEmpty()) } @@ -35,17 +40,21 @@ func Test_FindChildren_ResourceOwnsChild(t *testing.T) { g := gomega.NewWithT(t) + cfg := config.NewConfiguration() resources := make(astmodel.TypeDefinitionSet) ownerType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/owner") ownerName := astmodel.MakeInternalTypeName(pr, "Owner") - resources.Add(astmodel.MakeTypeDefinition(ownerName, ownerType)) + owner := astmodel.MakeTypeDefinition(ownerName, ownerType) + resources.Add(owner) childType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/owner/subresources/child") childName := astmodel.MakeInternalTypeName(pr, "Child") - resources.Add(astmodel.MakeTypeDefinition(childName, childType)) + child := astmodel.MakeTypeDefinition(childName, childType) + resources.Add(child) - children := findChildren(ownerType, ownerName, resources) + stage := newOwnershipStage(cfg, resources) + children := stage.findChildren(owner) g.Expect(children).To(gomega.ConsistOf(childName)) } @@ -54,17 +63,21 @@ func Test_FindChildren_ResourceOwnsChildWhenNameParametersAreDifferent(t *testin g := gomega.NewWithT(t) + cfg := config.NewConfiguration() resources := make(astmodel.TypeDefinitionSet) ownerType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/{name}") ownerName := astmodel.MakeInternalTypeName(pr, "Owner") - resources.Add(astmodel.MakeTypeDefinition(ownerName, ownerType)) + owner := astmodel.MakeTypeDefinition(ownerName, ownerType) + resources.Add(owner) childType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/{otherName}/subresources/child") childName := astmodel.MakeInternalTypeName(pr, "Child") - resources.Add(astmodel.MakeTypeDefinition(childName, childType)) + child := astmodel.MakeTypeDefinition(childName, childType) + resources.Add(child) - children := findChildren(ownerType, ownerName, resources) + stage := newOwnershipStage(cfg, resources) + children := stage.findChildren(owner) g.Expect(children).To(gomega.ConsistOf(childName)) } @@ -73,17 +86,21 @@ func Test_FindChildren_ResourceOwnsChildWhenNameIsDefault(t *testing.T) { g := gomega.NewWithT(t) + cfg := config.NewConfiguration() resources := make(astmodel.TypeDefinitionSet) ownerType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/default") ownerName := astmodel.MakeInternalTypeName(pr, "Owner") - resources.Add(astmodel.MakeTypeDefinition(ownerName, ownerType)) + owner := astmodel.MakeTypeDefinition(ownerName, ownerType) + resources.Add(owner) childType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/default/subresources/child") childName := astmodel.MakeInternalTypeName(pr, "Child") - resources.Add(astmodel.MakeTypeDefinition(childName, childType)) + child := astmodel.MakeTypeDefinition(childName, childType) + resources.Add(child) - children := findChildren(ownerType, ownerName, resources) + stage := newOwnershipStage(cfg, resources) + children := stage.findChildren(owner) g.Expect(children).To(gomega.ConsistOf(childName)) } @@ -92,17 +109,21 @@ func Test_FindChildren_ResourceDoesNotOwnGrandChild(t *testing.T) { g := gomega.NewWithT(t) + cfg := config.NewConfiguration() resources := make(astmodel.TypeDefinitionSet) ownerType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/owner") ownerName := astmodel.MakeInternalTypeName(pr, "Owner") - resources.Add(astmodel.MakeTypeDefinition(ownerName, ownerType)) + owner := astmodel.MakeTypeDefinition(ownerName, ownerType) + resources.Add(owner) grandChildType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/owner/subresources/child/subsubresources/grandchild") grandChildName := astmodel.MakeInternalTypeName(pr, "GrandChild") - resources.Add(astmodel.MakeTypeDefinition(grandChildName, grandChildType)) + grandChild := astmodel.MakeTypeDefinition(grandChildName, grandChildType) + resources.Add(grandChild) - children := findChildren(ownerType, ownerName, resources) + stage := newOwnershipStage(cfg, resources) + children := stage.findChildren(owner) g.Expect(children).To(gomega.BeEmpty()) } @@ -111,16 +132,20 @@ func Test_FindChildren_ResourceDoesNotOwnExtendedVersionOfName(t *testing.T) { g := gomega.NewWithT(t) + cfg := config.NewConfiguration() resources := make(astmodel.TypeDefinitionSet) ownerType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/owner") ownerName := astmodel.MakeInternalTypeName(pr, "Owner") - resources.Add(astmodel.MakeTypeDefinition(ownerName, ownerType)) + owner := astmodel.MakeTypeDefinition(ownerName, ownerType) + resources.Add(owner) grandChildType := astmodel.NewResourceType(nil, nil).WithARMURI("/resources/ownerLonger") grandChildName := astmodel.MakeInternalTypeName(pr, "GrandChild") - resources.Add(astmodel.MakeTypeDefinition(grandChildName, grandChildType)) + grandChild := astmodel.MakeTypeDefinition(grandChildName, grandChildType) + resources.Add(grandChild) - children := findChildren(ownerType, ownerName, resources) + stage := newOwnershipStage(cfg, resources) + children := stage.findChildren(owner) g.Expect(children).To(gomega.BeEmpty()) } diff --git a/v2/tools/generator/internal/codegen/pipeline/fix_optional_collection_aliases.go b/v2/tools/generator/internal/codegen/pipeline/fix_optional_collection_aliases.go index 4887dc2a7c7..2ca9d9c7404 100644 --- a/v2/tools/generator/internal/codegen/pipeline/fix_optional_collection_aliases.go +++ b/v2/tools/generator/internal/codegen/pipeline/fix_optional_collection_aliases.go @@ -20,7 +20,6 @@ func FixOptionalCollectionAliases() *Stage { FixOptionalCollectionAliasesStageId, "Replace types which are optional aliases to collections with just the collection alias", func(ctx context.Context, state *State) (*State, error) { - fixer := optionalCollectionAliasFixer{ definitions: state.Definitions(), } diff --git a/v2/tools/generator/internal/codegen/pipeline/handle_user_assigned_identities.go b/v2/tools/generator/internal/codegen/pipeline/handle_user_assigned_identities.go index 629dbbb9923..1ab9de333d7 100644 --- a/v2/tools/generator/internal/codegen/pipeline/handle_user_assigned_identities.go +++ b/v2/tools/generator/internal/codegen/pipeline/handle_user_assigned_identities.go @@ -20,7 +20,6 @@ func HandleUserAssignedIdentities() *Stage { HandleUserAssignedIdentitiesStageID, "Transform UserAssignedIdentities on spec types be resource references with the expected shape", func(ctx context.Context, state *State) (*State, error) { - transformer := newUserAssignedIdentityTransformer() updatedDefs := make(astmodel.TypeDefinitionSet) diff --git a/v2/tools/generator/internal/codegen/pipeline/implement_convertible_spec_interface.go b/v2/tools/generator/internal/codegen/pipeline/implement_convertible_spec_interface.go index c82447fec6c..b18d8db2fab 100644 --- a/v2/tools/generator/internal/codegen/pipeline/implement_convertible_spec_interface.go +++ b/v2/tools/generator/internal/codegen/pipeline/implement_convertible_spec_interface.go @@ -49,7 +49,8 @@ func ImplementConvertibleSpecInterface(idFactory astmodel.IdentifierFactory) *St // actual code generated. func createConvertibleSpecInterfaceImplementation( spec astmodel.TypeDefinition, - idFactory astmodel.IdentifierFactory) *astmodel.InterfaceImplementation { + idFactory astmodel.IdentifierFactory, +) *astmodel.InterfaceImplementation { container, ok := astmodel.AsFunctionContainer(spec.Type()) if !ok { // This shouldn't happen due to earlier filtering @@ -71,8 +72,8 @@ func createConvertibleSpecInterfaceImplementation( func createConvertibleSpecFunction( direction conversions.Direction, container astmodel.FunctionContainer, - idFactory astmodel.IdentifierFactory) astmodel.Function { - + idFactory astmodel.IdentifierFactory, +) astmodel.Function { for _, fn := range container.Functions() { if propertyAssignmentFn, ok := fn.(*functions.PropertyAssignmentFunction); ok { if propertyAssignmentFn.Direction() != direction { diff --git a/v2/tools/generator/internal/codegen/pipeline/implement_convertible_status_interface.go b/v2/tools/generator/internal/codegen/pipeline/implement_convertible_status_interface.go index f20c7bee2e7..1d36566d49b 100644 --- a/v2/tools/generator/internal/codegen/pipeline/implement_convertible_status_interface.go +++ b/v2/tools/generator/internal/codegen/pipeline/implement_convertible_status_interface.go @@ -49,7 +49,8 @@ func ImplementConvertibleStatusInterface(idFactory astmodel.IdentifierFactory) * // actual code generated. func createConvertibleStatusInterfaceImplementation( status astmodel.TypeDefinition, - idFactory astmodel.IdentifierFactory) *astmodel.InterfaceImplementation { + idFactory astmodel.IdentifierFactory, +) *astmodel.InterfaceImplementation { container, ok := astmodel.AsFunctionContainer(status.Type()) if !ok { // This shouldn't happen due to earlier filtering @@ -71,8 +72,8 @@ func createConvertibleStatusInterfaceImplementation( func createConvertibleStatusFunction( direction conversions.Direction, container astmodel.FunctionContainer, - idFactory astmodel.IdentifierFactory) astmodel.Function { - + idFactory astmodel.IdentifierFactory, +) astmodel.Function { for _, fn := range container.Functions() { if propertyAssignmentFn, ok := fn.(*functions.PropertyAssignmentFunction); ok { if propertyAssignmentFn.Direction() != direction { diff --git a/v2/tools/generator/internal/codegen/pipeline/implement_importable_resource_interface.go b/v2/tools/generator/internal/codegen/pipeline/implement_importable_resource_interface.go index 1847999bf74..fdc42ecca39 100644 --- a/v2/tools/generator/internal/codegen/pipeline/implement_importable_resource_interface.go +++ b/v2/tools/generator/internal/codegen/pipeline/implement_importable_resource_interface.go @@ -22,7 +22,8 @@ const ImplementImportableResourceInterfaceStageID = "implementImportableResource func ImplementImportableResourceInterface( configuration *config.Configuration, - idFactory astmodel.IdentifierFactory) *Stage { + idFactory astmodel.IdentifierFactory, +) *Stage { stage := NewStage( ImplementImportableResourceInterfaceStageID, "Implement the ImportableResource interface for resources that support import via asoctl", diff --git a/v2/tools/generator/internal/codegen/pipeline/improve-property-descriptions.go b/v2/tools/generator/internal/codegen/pipeline/improve-property-descriptions.go index ad6746f510d..2477a3631ad 100644 --- a/v2/tools/generator/internal/codegen/pipeline/improve-property-descriptions.go +++ b/v2/tools/generator/internal/codegen/pipeline/improve-property-descriptions.go @@ -7,8 +7,9 @@ package pipeline import ( "context" - "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "strings" + + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" ) // ImprovePropertyDescriptionsStageId is the unique identifier for this pipeline stage diff --git a/v2/tools/generator/internal/codegen/pipeline/inject_property_assignment_functions.go b/v2/tools/generator/internal/codegen/pipeline/inject_property_assignment_functions.go index 04908179ccc..77173078fa5 100644 --- a/v2/tools/generator/internal/codegen/pipeline/inject_property_assignment_functions.go +++ b/v2/tools/generator/internal/codegen/pipeline/inject_property_assignment_functions.go @@ -122,7 +122,8 @@ func newPropertyAssignmentFunctionsFactory( graph *storage.ConversionGraph, idFactory astmodel.IdentifierFactory, configuration *config.Configuration, - definitions astmodel.TypeDefinitionSet) *propertyAssignmentFunctionsFactory { + definitions astmodel.TypeDefinitionSet, +) *propertyAssignmentFunctionsFactory { return &propertyAssignmentFunctionsFactory{ graph: graph, idFactory: idFactory, diff --git a/v2/tools/generator/internal/codegen/pipeline/inject_spec_initialization_functions.go b/v2/tools/generator/internal/codegen/pipeline/inject_spec_initialization_functions.go index 7f3573b275f..d5451a09a11 100644 --- a/v2/tools/generator/internal/codegen/pipeline/inject_spec_initialization_functions.go +++ b/v2/tools/generator/internal/codegen/pipeline/inject_spec_initialization_functions.go @@ -26,7 +26,8 @@ const InjectSpecInitializationFunctionsStageID = "injectSpecInitializationFuncti // resource is imported. func InjectSpecInitializationFunctions( configuration *config.Configuration, - idFactory astmodel.IdentifierFactory) *Stage { + idFactory astmodel.IdentifierFactory, +) *Stage { stage := NewStage( InjectSpecInitializationFunctionsStageID, "Inject spec initialization functions Initialize_From_*() into resources and objects", diff --git a/v2/tools/generator/internal/codegen/pipeline/load_types.go b/v2/tools/generator/internal/codegen/pipeline/load_types.go index 09b33cb3416..0c4c421ec10 100644 --- a/v2/tools/generator/internal/codegen/pipeline/load_types.go +++ b/v2/tools/generator/internal/codegen/pipeline/load_types.go @@ -125,8 +125,10 @@ func LoadTypes( }) } -var resourceGroupScopeRegex = regexp.MustCompile(`(?i)^/subscriptions/[^/]+/resourcegroups/[^/]+/.*`) -var locationScopeRegex = regexp.MustCompile(`(?i)^/subscriptions/[^/]+/.*`) +var ( + resourceGroupScopeRegex = regexp.MustCompile(`(?i)^/subscriptions/[^/]+/resourcegroups/[^/]+/.*`) + locationScopeRegex = regexp.MustCompile(`(?i)^/subscriptions/[^/]+/.*`) +) func categorizeResourceScope(armURI string) astmodel.ResourceScope { // this is a bit of a hack, eventually we should have better scope support. diff --git a/v2/tools/generator/internal/codegen/pipeline/mark_latest_api_version_as_storage_version.go b/v2/tools/generator/internal/codegen/pipeline/mark_latest_api_version_as_storage_version.go index 249c3141ad5..7a1be23fe8e 100644 --- a/v2/tools/generator/internal/codegen/pipeline/mark_latest_api_version_as_storage_version.go +++ b/v2/tools/generator/internal/codegen/pipeline/mark_latest_api_version_as_storage_version.go @@ -7,6 +7,7 @@ package pipeline import ( "context" + "github.com/pkg/errors" "golang.org/x/exp/slices" diff --git a/v2/tools/generator/internal/codegen/pipeline/name_types_for_crd.go b/v2/tools/generator/internal/codegen/pipeline/name_types_for_crd.go index c4bded3212f..ccb650772f1 100644 --- a/v2/tools/generator/internal/codegen/pipeline/name_types_for_crd.go +++ b/v2/tools/generator/internal/codegen/pipeline/name_types_for_crd.go @@ -196,7 +196,6 @@ var suffixesToFloat = []string{ } func newNameHint(name astmodel.TypeName) nameHint { - baseName := name.Name() var suffixes []string done := false @@ -230,7 +229,6 @@ func (n nameHint) WithBasePart(part string) nameHint { } func (n nameHint) WithSuffixPart(suffix string) nameHint { - newSuffix := strings.TrimPrefix(suffix, "_") if n.suffix != "" { newSuffix = n.suffix + "_" + newSuffix diff --git a/v2/tools/generator/internal/codegen/pipeline/name_types_for_crd_test.go b/v2/tools/generator/internal/codegen/pipeline/name_types_for_crd_test.go index 5860db13595..32413c6df50 100644 --- a/v2/tools/generator/internal/codegen/pipeline/name_types_for_crd_test.go +++ b/v2/tools/generator/internal/codegen/pipeline/name_types_for_crd_test.go @@ -6,10 +6,11 @@ package pipeline import ( + "testing" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/test" . "github.com/onsi/gomega" - "testing" ) func Test_NewNameHint(t *testing.T) { diff --git a/v2/tools/generator/internal/codegen/pipeline/pipeline_diagram.go b/v2/tools/generator/internal/codegen/pipeline/pipeline_diagram.go index d9bf57a3487..52ee895a8bd 100644 --- a/v2/tools/generator/internal/codegen/pipeline/pipeline_diagram.go +++ b/v2/tools/generator/internal/codegen/pipeline/pipeline_diagram.go @@ -36,7 +36,7 @@ func NewPipelineDiagram(settings *DebugSettings) *PipelineDiagram { func (diagram *PipelineDiagram) WriteDiagram(stages []*Stage) error { dotsrc := diagram.createDiagram(stages) filename := filepath.Join(diagram.debugDir, "pipeline.dot") - err := os.WriteFile(filename, dotsrc, 0600) + err := os.WriteFile(filename, dotsrc, 0o600) return errors.Wrapf(err, "failed to write diagram to %s", filename) } diff --git a/v2/tools/generator/internal/codegen/pipeline/prune_resources_with_lifecycle_owned_by_parent.go b/v2/tools/generator/internal/codegen/pipeline/prune_resources_with_lifecycle_owned_by_parent.go index 45b5e326320..cd5857e24dd 100644 --- a/v2/tools/generator/internal/codegen/pipeline/prune_resources_with_lifecycle_owned_by_parent.go +++ b/v2/tools/generator/internal/codegen/pipeline/prune_resources_with_lifecycle_owned_by_parent.go @@ -96,7 +96,6 @@ func flagPrunedEmptyProperties( result, err := emptyObjectVisitor.VisitDefinitions(defs, emptyPrunedPropertiesArm) if err != nil { return nil, err - } return result, nil diff --git a/v2/tools/generator/internal/codegen/pipeline/remove_armid_from_status.go b/v2/tools/generator/internal/codegen/pipeline/remove_armid_from_status.go index 82db3f4641a..e5ad564b092 100644 --- a/v2/tools/generator/internal/codegen/pipeline/remove_armid_from_status.go +++ b/v2/tools/generator/internal/codegen/pipeline/remove_armid_from_status.go @@ -18,7 +18,6 @@ func FixIDFields() *Stage { "fixIdFields", "Remove ARM ID annotations from status, and Id from Spec types", func(ctx context.Context, state *State) (*State, error) { - updatedStatusDefs, err := replaceStatusARMIDWithString(state.Definitions()) if err != nil { return nil, err diff --git a/v2/tools/generator/internal/codegen/pipeline/rename_properties_test.go b/v2/tools/generator/internal/codegen/pipeline/rename_properties_test.go index 9fe208f9e26..31c571bb3d8 100644 --- a/v2/tools/generator/internal/codegen/pipeline/rename_properties_test.go +++ b/v2/tools/generator/internal/codegen/pipeline/rename_properties_test.go @@ -171,7 +171,7 @@ func Test_RenameProperties_WhenFlattening_PopulatesExpectedARMProperty(t *testin test.FullNameProperty, test.KnownAsProperty, test.FamilyNameProperty, - //addressProperty, + // addressProperty, ) personResourceType := astmodel.NewResourceType(personSpec.Name(), personStatus.Name()). diff --git a/v2/tools/generator/internal/codegen/pipeline/repair_skipping_properties_test.go b/v2/tools/generator/internal/codegen/pipeline/repair_skipping_properties_test.go index e276cc6820d..9bfa109bf02 100644 --- a/v2/tools/generator/internal/codegen/pipeline/repair_skipping_properties_test.go +++ b/v2/tools/generator/internal/codegen/pipeline/repair_skipping_properties_test.go @@ -243,7 +243,8 @@ func AssertLinkExists( g *WithT, repairer *skippingPropertyRepairer, def astmodel.TypeDefinition, - property astmodel.PropertyName) { + property astmodel.PropertyName, +) { ref := astmodel.MakePropertyReference(def.Name(), property) g.Expect(repairer.links).To(HaveKey(ref)) } diff --git a/v2/tools/generator/internal/codegen/pipeline/report_resource_versions.go b/v2/tools/generator/internal/codegen/pipeline/report_resource_versions.go index a9972156edf..06cd05c32d5 100644 --- a/v2/tools/generator/internal/codegen/pipeline/report_resource_versions.go +++ b/v2/tools/generator/internal/codegen/pipeline/report_resource_versions.go @@ -153,7 +153,6 @@ func (report *ResourceVersionsReport) loadFragments() error { report.availableFragments[name] = string(content) return nil }) - if err != nil { return errors.Wrapf(err, "Unable to load fragments from %q", fragmentsPath) } @@ -265,7 +264,6 @@ func (report *ResourceVersionsReport) WriteAllResourcesReportToBuffer( frontMatter string, buffer *strings.Builder, ) error { - if frontMatter != "" { buffer.WriteString(frontMatter) } else { @@ -663,7 +661,6 @@ func (report *ResourceVersionsReport) supportedFrom(typeName astmodel.InternalTy // Read in any front matter present in our output file, so we preserve it when writing out the new file. // Returns an empty string if the file doesn't exist func (report *ResourceVersionsReport) readFrontMatter(outputPath string) string { - if _, err := os.Stat(outputPath); os.IsNotExist(err) { return "" } diff --git a/v2/tools/generator/internal/codegen/pipeline/report_type_versions.go b/v2/tools/generator/internal/codegen/pipeline/report_type_versions.go index 95ce1517c69..73f8e1d8ec3 100644 --- a/v2/tools/generator/internal/codegen/pipeline/report_type_versions.go +++ b/v2/tools/generator/internal/codegen/pipeline/report_type_versions.go @@ -93,12 +93,12 @@ func (report *PackagesMatrixReport) WriteTableTo(table *reporting.SparseTable, p outputFolder := filepath.Join(outputPath, pkg) if _, err := os.Stat(outputFolder); os.IsNotExist(err) { - err = os.MkdirAll(outputFolder, 0700) + err = os.MkdirAll(outputFolder, 0o700) if err != nil { return errors.Wrapf(err, "Unable to create directory %q", outputFolder) } } destination := filepath.Join(outputFolder, "versions_matrix.md") - return os.WriteFile(destination, []byte(buffer.String()), 0600) + return os.WriteFile(destination, []byte(buffer.String()), 0o600) } diff --git a/v2/tools/generator/internal/codegen/pipeline/resource_registration_file.go b/v2/tools/generator/internal/codegen/pipeline/resource_registration_file.go index 56198759175..33166d083e1 100644 --- a/v2/tools/generator/internal/codegen/pipeline/resource_registration_file.go +++ b/v2/tools/generator/internal/codegen/pipeline/resource_registration_file.go @@ -6,11 +6,13 @@ package pipeline import ( - "github.com/pkg/errors" "go/token" - kerrors "k8s.io/apimachinery/pkg/util/errors" "sort" + "github.com/pkg/errors" + + kerrors "k8s.io/apimachinery/pkg/util/errors" + "github.com/dave/dst" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astbuilder" @@ -99,9 +101,6 @@ func (r *ResourceRegistrationFile) AsAst() (*dst.File, error) { // Create Resource Extensions resourceExtensionTypes := r.createGetResourceExtensions(codeGenContext) - if err != nil { - return nil, err - } decls = append(decls, resourceExtensionTypes) // All the index functions @@ -275,7 +274,6 @@ func createGetKnownTypesFunc( func (r *ResourceRegistrationFile) createGetKnownStorageTypesFunc( codeGenerationContext *astmodel.CodeGenerationContext, ) dst.Decl { - funcName := "getKnownStorageTypes" funcComment := "returns the list of storage types which can be reconciled." diff --git a/v2/tools/generator/internal/codegen/pipeline/strip_unused_types.go b/v2/tools/generator/internal/codegen/pipeline/strip_unused_types.go index ed76ae37fd8..fb3980f70d0 100644 --- a/v2/tools/generator/internal/codegen/pipeline/strip_unused_types.go +++ b/v2/tools/generator/internal/codegen/pipeline/strip_unused_types.go @@ -32,7 +32,8 @@ func StripUnreferencedTypeDefinitions() *Stage { // generated as a byproduct of an allOf element. func StripUnusedDefinitions( roots astmodel.TypeNameSet, - defs astmodel.TypeDefinitionSet) (astmodel.TypeDefinitionSet, error) { + defs astmodel.TypeDefinitionSet, +) (astmodel.TypeDefinitionSet, error) { graph := astmodel.MakeReferenceGraphWithRoots(roots, defs) connectedDefinitions := graph.Connected() diff --git a/v2/tools/generator/internal/codegen/pipeline/strip_unused_types_test.go b/v2/tools/generator/internal/codegen/pipeline/strip_unused_types_test.go index b28e7fd9dc1..10a915015ea 100644 --- a/v2/tools/generator/internal/codegen/pipeline/strip_unused_types_test.go +++ b/v2/tools/generator/internal/codegen/pipeline/strip_unused_types_test.go @@ -20,7 +20,6 @@ func TestConnectionChecker_Avoids_Cycles(t *testing.T) { t.Parallel() g := NewGomegaWithT(t) makeName := func(name string) astmodel.TypeName { - return astmodel.MakeInternalTypeName( test.MakeLocalPackageReference("demo", "v1"), name) diff --git a/v2/tools/generator/internal/codegen/pipeline/transform_validated_floats.go b/v2/tools/generator/internal/codegen/pipeline/transform_validated_floats.go index 5c39c6b2810..9c9ab3d13c9 100644 --- a/v2/tools/generator/internal/codegen/pipeline/transform_validated_floats.go +++ b/v2/tools/generator/internal/codegen/pipeline/transform_validated_floats.go @@ -21,7 +21,6 @@ func TransformValidatedFloats() *Stage { TransformValidatedFloatsStageID, "Transform validated 'spec' float type values to validated integer types for compatibility with controller-gen", func(ctx context.Context, state *State) (*State, error) { - definitions := state.Definitions() result, err := getFloatTransformations(definitions) diff --git a/v2/tools/generator/internal/codegen/storage/conversion_graph.go b/v2/tools/generator/internal/codegen/storage/conversion_graph.go index 5697877f1b0..f7ddd394d89 100644 --- a/v2/tools/generator/internal/codegen/storage/conversion_graph.go +++ b/v2/tools/generator/internal/codegen/storage/conversion_graph.go @@ -7,10 +7,11 @@ package storage import ( "bytes" - "github.com/pkg/errors" "io" "os" + "github.com/pkg/errors" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/config" ) @@ -179,7 +180,8 @@ func (graph *ConversionGraph) TransitionCount() int { // definitions is a set of known definitions. func (graph *ConversionGraph) FindNextProperty( ref astmodel.PropertyReference, - definitions astmodel.TypeDefinitionSet) (astmodel.PropertyReference, error) { + definitions astmodel.TypeDefinitionSet, +) (astmodel.PropertyReference, error) { nextType, err := graph.FindNextType(ref.DeclaringType(), definitions) if err != nil { // Something went wrong @@ -192,7 +194,7 @@ func (graph *ConversionGraph) FindNextProperty( return astmodel.EmptyPropertyReference, nil } - //TODO: property renaming support goes here (when implemented) + // TODO: property renaming support goes here (when implemented) return astmodel.MakePropertyReference(nextType, ref.Property()), nil } diff --git a/v2/tools/generator/internal/codegen/storage/group_conversion_graph.go b/v2/tools/generator/internal/codegen/storage/group_conversion_graph.go index 6cd3afaffef..587d36f0c5a 100644 --- a/v2/tools/generator/internal/codegen/storage/group_conversion_graph.go +++ b/v2/tools/generator/internal/codegen/storage/group_conversion_graph.go @@ -6,9 +6,10 @@ package storage import ( - "github.com/pkg/errors" "io" + "github.com/pkg/errors" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/config" ) @@ -52,7 +53,6 @@ func (graph *GroupConversionGraph) searchForRenamedType( name astmodel.InternalTypeName, definitions astmodel.TypeDefinitionSet, ) (astmodel.InternalTypeName, error) { - // No configuration, or we're not looking at a storage package if graph.configuration == nil || !astmodel.IsStoragePackageReference(name.PackageReference()) { return astmodel.InternalTypeName{}, nil diff --git a/v2/tools/generator/internal/codegen/storage/hub_version_marker.go b/v2/tools/generator/internal/codegen/storage/hub_version_marker.go index 379ce13b512..d4b391d4a0f 100644 --- a/v2/tools/generator/internal/codegen/storage/hub_version_marker.go +++ b/v2/tools/generator/internal/codegen/storage/hub_version_marker.go @@ -31,6 +31,7 @@ func (m *HubVersionMarker) MarkAsStorageVersion(def astmodel.TypeDefinition) (as // markResourceAsStorageVersion marks the supplied resource as the canonical hub (storage) version func (m *HubVersionMarker) markResourceAsStorageVersion( - _ *astmodel.TypeVisitor[any], rt *astmodel.ResourceType, _ interface{}) (astmodel.Type, error) { + _ *astmodel.TypeVisitor[any], rt *astmodel.ResourceType, _ interface{}, +) (astmodel.Type, error) { return rt.MarkAsStorageVersion(), nil } diff --git a/v2/tools/generator/internal/codegen/storage/property_converter.go b/v2/tools/generator/internal/codegen/storage/property_converter.go index be74c9c815c..d0b2d09a2ee 100644 --- a/v2/tools/generator/internal/codegen/storage/property_converter.go +++ b/v2/tools/generator/internal/codegen/storage/property_converter.go @@ -67,7 +67,8 @@ func (p *PropertyConverter) ConvertProperty(property *astmodel.PropertyDefinitio // stripAllValidations removes all validations func (p *PropertyConverter) stripAllValidations( - this *astmodel.TypeVisitor[any], v *astmodel.ValidatedType, ctx any) (astmodel.Type, error) { + this *astmodel.TypeVisitor[any], v *astmodel.ValidatedType, ctx any, +) (astmodel.Type, error) { // strip all type validations from storage properties // act as if they do not exist return this.Visit(v.ElementType(), ctx) @@ -75,7 +76,8 @@ func (p *PropertyConverter) stripAllValidations( // useBaseTypeForEnumerations replaces an enumeration with its underlying base type func (p *PropertyConverter) useBaseTypeForEnumerations( - tv *astmodel.TypeVisitor[any], et *astmodel.EnumType, ctx any) (astmodel.Type, error) { + tv *astmodel.TypeVisitor[any], et *astmodel.EnumType, ctx any, +) (astmodel.Type, error) { return tv.Visit(et.BaseType(), ctx) } @@ -87,8 +89,8 @@ func (p *PropertyConverter) useBaseTypeForEnumerations( // o If a TypeName references an alias for a primitive type (these are used to specify validations), it is replaced // with the primitive type func (p *PropertyConverter) shortCircuitNamesOfSimpleTypes( - tv *astmodel.TypeVisitor[any], tn astmodel.InternalTypeName, ctx any) (astmodel.Type, error) { - + tv *astmodel.TypeVisitor[any], tn astmodel.InternalTypeName, ctx any, +) (astmodel.Type, error) { // for nonlocal packages, preserve the name as is if astmodel.IsExternalPackageReference(tn.PackageReference()) { return tn, nil @@ -134,8 +136,8 @@ type propertyConversion = func(property *astmodel.PropertyDefinition) (*astmodel // preserveKubernetesResourceStorageProperties preserves properties required by the // KubernetesResource interface as they're always required exactly as declared func (p *PropertyConverter) preserveKubernetesResourceStorageProperties( - prop *astmodel.PropertyDefinition) (*astmodel.PropertyDefinition, error) { - + prop *astmodel.PropertyDefinition, +) (*astmodel.PropertyDefinition, error) { if astmodel.IsKubernetesResourceProperty(prop.PropertyName()) { // Keep these (mostly) unchanged if vt, ok := prop.PropertyType().(*astmodel.ValidatedType); ok { @@ -155,8 +157,8 @@ func (p *PropertyConverter) preserveKubernetesResourceStorageProperties( // preserveResourceReferenceProperties preserves properties required by the // KubernetesResource interface as they're always required exactly as declared func (p *PropertyConverter) preserveResourceReferenceProperties( - prop *astmodel.PropertyDefinition) (*astmodel.PropertyDefinition, error) { - + prop *astmodel.PropertyDefinition, +) (*astmodel.PropertyDefinition, error) { propertyType := prop.PropertyType() if opt, ok := astmodel.AsOptionalType(propertyType); ok { if astmodel.TypeEquals(opt.Element(), astmodel.ResourceReferenceType) { @@ -175,8 +177,8 @@ func (p *PropertyConverter) preserveResourceReferenceProperties( } func (p *PropertyConverter) defaultPropertyConversion( - property *astmodel.PropertyDefinition) (*astmodel.PropertyDefinition, error) { - + property *astmodel.PropertyDefinition, +) (*astmodel.PropertyDefinition, error) { propertyType, err := p.visitor.Visit(property.PropertyType(), nil) if err != nil { return nil, errors.Wrapf(err, "converting property %q", property.PropertyName()) diff --git a/v2/tools/generator/internal/codegen/storage/resource_conversion_graph_test.go b/v2/tools/generator/internal/codegen/storage/resource_conversion_graph_test.go index a120f812b82..d814ba35d49 100644 --- a/v2/tools/generator/internal/codegen/storage/resource_conversion_graph_test.go +++ b/v2/tools/generator/internal/codegen/storage/resource_conversion_graph_test.go @@ -74,6 +74,7 @@ func TestResourceConversionGraph_WithTwoGAReferences_HasExpectedTransitions(t *t after2020s := graph.LookupTransition(after2020) g.Expect(after2020s).NotTo(BeNil()) } + func TestResourceConversionGraph_WithGAAndPreviewReferences_HasExpectedTransitions(t *testing.T) { /* * Test that a graph containing two GA and one *Preview* API release (and matching storage versions) ends up with diff --git a/v2/tools/generator/internal/config/configuration_visitor.go b/v2/tools/generator/internal/config/configuration_visitor.go index 49e5a95b996..30a43784858 100644 --- a/v2/tools/generator/internal/config/configuration_visitor.go +++ b/v2/tools/generator/internal/config/configuration_visitor.go @@ -34,7 +34,8 @@ type configurationVisitor struct { func newSinglePropertyConfigurationVisitor( typeName astmodel.InternalTypeName, property astmodel.PropertyName, - action func(configuration *PropertyConfiguration) error) *configurationVisitor { + action func(configuration *PropertyConfiguration) error, +) *configurationVisitor { return &configurationVisitor{ ref: typeName.InternalPackageReference(), typeName: typeName.Name(), @@ -49,7 +50,8 @@ func newSinglePropertyConfigurationVisitor( // Returns nil if every call to action was successful (returned nil); otherwise returns an aggregated error containing // all the errors returned. func newEveryPropertyConfigurationVisitor( - action func(configuration *PropertyConfiguration) error) *configurationVisitor { + action func(configuration *PropertyConfiguration) error, +) *configurationVisitor { return &configurationVisitor{ handleProperty: action, } @@ -62,7 +64,8 @@ func newEveryPropertyConfigurationVisitor( // an error, and (false, nil) if the type does not exist. func newSingleTypeConfigurationVisitor( typeName astmodel.InternalTypeName, - action func(configuration *TypeConfiguration) error) *configurationVisitor { + action func(configuration *TypeConfiguration) error, +) *configurationVisitor { return &configurationVisitor{ ref: typeName.InternalPackageReference(), typeName: typeName.Name(), @@ -75,7 +78,8 @@ func newSingleTypeConfigurationVisitor( // action is the action to apply to each type. // Returns nil if every call to action returned nil; otherwise returns an aggregated error containing all the errors returned. func newEveryTypeConfigurationVisitor( - action func(configuration *TypeConfiguration) error) *configurationVisitor { + action func(configuration *TypeConfiguration) error, +) *configurationVisitor { return &configurationVisitor{ handleType: action, } @@ -88,7 +92,8 @@ func newEveryTypeConfigurationVisitor( // an error, and (false, nil) if the type does not exist. func newSingleVersionConfigurationVisitor( ref astmodel.InternalPackageReference, - action func(configuration *VersionConfiguration) error) *configurationVisitor { + action func(configuration *VersionConfiguration) error, +) *configurationVisitor { return &configurationVisitor{ ref: ref, handleVersion: action, @@ -98,7 +103,8 @@ func newSingleVersionConfigurationVisitor( // newEveryVersionConfigurationVisitor creates a ConfigurationVisitor to apply an action to every version configuration. // action is the action to apply to each version. func newEveryVersionConfigurationVisitor( - action func(configuration *VersionConfiguration) error) *configurationVisitor { + action func(configuration *VersionConfiguration) error, +) *configurationVisitor { return &configurationVisitor{ handleVersion: action, } @@ -111,7 +117,8 @@ func newEveryVersionConfigurationVisitor( // an error, and (false, nil) if the group does not exist. func newSingleGroupConfigurationVisitor( ref astmodel.InternalPackageReference, - action func(configuration *GroupConfiguration) error) *configurationVisitor { + action func(configuration *GroupConfiguration) error, +) *configurationVisitor { return &configurationVisitor{ ref: ref, handleGroup: action, @@ -121,7 +128,8 @@ func newSingleGroupConfigurationVisitor( // newEveryGroupConfigurationVisitor creates a ConfigurationVisitor to apply an action to every group configuration. // action is the action to apply to each group. func newEveryGroupConfigurationVisitor( - action func(configuration *GroupConfiguration) error) *configurationVisitor { + action func(configuration *GroupConfiguration) error, +) *configurationVisitor { return &configurationVisitor{ handleGroup: action, } diff --git a/v2/tools/generator/internal/config/group_access.go b/v2/tools/generator/internal/config/group_access.go index bbdd8a180c8..344001fd393 100644 --- a/v2/tools/generator/internal/config/group_access.go +++ b/v2/tools/generator/internal/config/group_access.go @@ -23,7 +23,8 @@ func makeGroupAccess[T any]( ) groupAccess[T] { return groupAccess[T]{ model: model, - accessor: accessor} + accessor: accessor, + } } func (a groupAccess[T]) withTypeOverride( diff --git a/v2/tools/generator/internal/config/group_access_test.go b/v2/tools/generator/internal/config/group_access_test.go index 50bd9d71c08..9cee0c47843 100644 --- a/v2/tools/generator/internal/config/group_access_test.go +++ b/v2/tools/generator/internal/config/group_access_test.go @@ -6,9 +6,10 @@ package config import ( - "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "testing" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/test" . "github.com/onsi/gomega" ) diff --git a/v2/tools/generator/internal/config/group_configuration_test.go b/v2/tools/generator/internal/config/group_configuration_test.go index 55e51400212..7a81618c9ea 100644 --- a/v2/tools/generator/internal/config/group_configuration_test.go +++ b/v2/tools/generator/internal/config/group_configuration_test.go @@ -83,7 +83,6 @@ func TestGroupConfiguration_FindVersion_GivenTypeName_ReturnsExpectedVersion(t * } }) } - } func loadTestData(t *testing.T) []byte { diff --git a/v2/tools/generator/internal/config/property_access.go b/v2/tools/generator/internal/config/property_access.go index a13f6512a9d..85d77d9aad3 100644 --- a/v2/tools/generator/internal/config/property_access.go +++ b/v2/tools/generator/internal/config/property_access.go @@ -18,10 +18,12 @@ type propertyAccess[T any] struct { func makePropertyAccess[T any]( model *ObjectModelConfiguration, accessor func(*PropertyConfiguration, - ) *configurable[T]) propertyAccess[T] { + ) *configurable[T], +) propertyAccess[T] { return propertyAccess[T]{ model: model, - accessor: accessor} + accessor: accessor, + } } // Lookup returns the configured value for the given type name and property name diff --git a/v2/tools/generator/internal/config/property_access_test.go b/v2/tools/generator/internal/config/property_access_test.go index b0085a0748f..5493769bdc6 100644 --- a/v2/tools/generator/internal/config/property_access_test.go +++ b/v2/tools/generator/internal/config/property_access_test.go @@ -6,9 +6,10 @@ package config import ( - "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "testing" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/test" . "github.com/onsi/gomega" ) diff --git a/v2/tools/generator/internal/config/transform_selector.go b/v2/tools/generator/internal/config/transform_selector.go index 5be545f9576..5a0396acb65 100644 --- a/v2/tools/generator/internal/config/transform_selector.go +++ b/v2/tools/generator/internal/config/transform_selector.go @@ -115,7 +115,6 @@ func (ts *TransformSelector) appliesToTypeName(tn astmodel.InternalTypeName) boo } if ts.Version.IsRestrictive() { - // Need to handle both full (v1beta20200101) and API (2020-01-01) formats switch ref := tn.PackageReference().(type) { case astmodel.LocalPackageReference: diff --git a/v2/tools/generator/internal/config/type_access.go b/v2/tools/generator/internal/config/type_access.go index 1120ffeecdb..bb548239d0a 100644 --- a/v2/tools/generator/internal/config/type_access.go +++ b/v2/tools/generator/internal/config/type_access.go @@ -25,7 +25,8 @@ func makeTypeAccess[T any]( ) typeAccess[T] { return typeAccess[T]{ model: model, - accessor: accessor} + accessor: accessor, + } } func (a typeAccess[T]) withPropertyOverride( diff --git a/v2/tools/generator/internal/config/type_access_test.go b/v2/tools/generator/internal/config/type_access_test.go index e778d6017f8..379389cd61c 100644 --- a/v2/tools/generator/internal/config/type_access_test.go +++ b/v2/tools/generator/internal/config/type_access_test.go @@ -6,9 +6,10 @@ package config import ( - "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "testing" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/test" . "github.com/onsi/gomega" ) diff --git a/v2/tools/generator/internal/config/type_transformer.go b/v2/tools/generator/internal/config/type_transformer.go index 52cbccd569e..25117ab53e8 100644 --- a/v2/tools/generator/internal/config/type_transformer.go +++ b/v2/tools/generator/internal/config/type_transformer.go @@ -7,6 +7,7 @@ package config import ( "fmt" + "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" "github.com/go-logr/logr" "github.com/pkg/errors" diff --git a/v2/tools/generator/internal/conversions/property_conversion_context.go b/v2/tools/generator/internal/conversions/property_conversion_context.go index 72e9aae1dc9..8ea56ce9bb4 100644 --- a/v2/tools/generator/internal/conversions/property_conversion_context.go +++ b/v2/tools/generator/internal/conversions/property_conversion_context.go @@ -43,7 +43,8 @@ type PropertyConversionContext struct { func NewPropertyConversionContext( functionBaseName string, definitions astmodel.TypeDefinitionSet, - idFactory astmodel.IdentifierFactory) *PropertyConversionContext { + idFactory astmodel.IdentifierFactory, +) *PropertyConversionContext { return &PropertyConversionContext{ functionBaseName: functionBaseName, definitions: definitions, diff --git a/v2/tools/generator/internal/conversions/property_conversions.go b/v2/tools/generator/internal/conversions/property_conversions.go index b535736c09e..6f49603808f 100644 --- a/v2/tools/generator/internal/conversions/property_conversions.go +++ b/v2/tools/generator/internal/conversions/property_conversions.go @@ -159,7 +159,8 @@ func init() { func CreateTypeConversion( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { var result PropertyConversion var err error for _, f := range propertyConversionFactories { @@ -252,8 +253,8 @@ func directAssignmentPropertyConversion( func writeToBagItem( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require destination to be a property bag item destinationBagItem, destinationIsBagItem := AsPropertyBagMemberType(destinationEndpoint.Type()) if !destinationIsBagItem { @@ -374,8 +375,8 @@ func writeToBagItem( func assignToOptional( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require destination to not be a bag item if destinationEndpoint.IsBagItem() { return nil, nil @@ -606,8 +607,8 @@ func pullFromBagItem( func assignFromOptional( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require source to not be a bag item if sourceEndpoint.IsBagItem() { return nil, nil @@ -693,8 +694,8 @@ func assignFromOptional( func assignToEnumeration( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require destination to not be a bag item if destinationEndpoint.IsBagItem() { return nil, nil @@ -754,8 +755,8 @@ func assignToEnumeration( func assignPrimitiveFromPrimitive( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - _ *PropertyConversionContext) (PropertyConversion, error) { - + _ *PropertyConversionContext, +) (PropertyConversion, error) { // Require both source and destination to not be bag items if sourceEndpoint.IsBagItem() || destinationEndpoint.IsBagItem() { return nil, nil @@ -793,8 +794,8 @@ func assignPrimitiveFromPrimitive( func assignAliasedPrimitiveFromAliasedPrimitive( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require both source and destination to not be bag items if sourceEndpoint.IsBagItem() || destinationEndpoint.IsBagItem() { return nil, nil @@ -848,8 +849,8 @@ func assignAliasedPrimitiveFromAliasedPrimitive( func assignFromAliasedType( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require source to not be a bag item if sourceEndpoint.IsBagItem() { return nil, nil @@ -905,8 +906,8 @@ func assignFromAliasedType( func assignToAliasedType( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require destination to not be a bag item if destinationEndpoint.IsBagItem() { return nil, nil @@ -1117,8 +1118,8 @@ var forbiddenConversions = []forbiddenConversion{ func neuterForbiddenConversions( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - _ *PropertyConversionContext) (PropertyConversion, error) { - + _ *PropertyConversionContext, +) (PropertyConversion, error) { // Require both source and destination to not be bag items if sourceEndpoint.IsBagItem() || destinationEndpoint.IsBagItem() { return nil, nil @@ -1156,8 +1157,8 @@ func neuterForbiddenConversions( func assignArrayFromArray( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require both source and destination to not be bag items if sourceEndpoint.IsBagItem() || destinationEndpoint.IsBagItem() { return nil, nil @@ -1290,8 +1291,8 @@ func assignArrayFromArray( func assignMapFromMap( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require both source and destination to not be bag items if sourceEndpoint.IsBagItem() || destinationEndpoint.IsBagItem() { return nil, nil @@ -1436,8 +1437,8 @@ func assignMapFromMap( func assignUserAssignedIdentityMapFromArray( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // There's no conversion in the other direction (array -> map) for this because property_conversions only deals with: // 1. Conversions between storage types, where UserAssignedIdentity's are arrays on both sides and don't need // special handling. @@ -1501,7 +1502,6 @@ func assignUserAssignedIdentityMapFromArray( knownLocals *astmodel.KnownLocalsSet, generationContext *astmodel.CodeGenerationContext, ) ([]dst.Stmt, error) { - // List := make([], 0, len() tempId := knownLocals.CreateSingularLocal(sourceEndpoint.Name(), "List") declaration := astbuilder.ShortDeclaration( @@ -1578,8 +1578,8 @@ func assignUserAssignedIdentityMapFromArray( func assignEnumFromEnum( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require both source and destination to not be bag items if sourceEndpoint.IsBagItem() || destinationEndpoint.IsBagItem() { return nil, nil @@ -1643,8 +1643,8 @@ func assignEnumFromEnum( func assignPrimitiveFromEnum( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require both source and destination to not be bag items if sourceEndpoint.IsBagItem() || destinationEndpoint.IsBagItem() { return nil, nil @@ -1700,8 +1700,8 @@ func assignPrimitiveFromEnum( func assignObjectDirectlyFromObject( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require expected direction if conversionContext.direction != ConvertFrom { return nil, nil @@ -1824,8 +1824,8 @@ func assignObjectDirectlyFromObject( func assignObjectDirectlyToObject( sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, - conversionContext *PropertyConversionContext) (PropertyConversion, error) { - + conversionContext *PropertyConversionContext, +) (PropertyConversion, error) { // Require expected direction if conversionContext.direction != ConvertTo { return nil, nil @@ -2063,7 +2063,6 @@ func assignInlineObjectsViaIntermediateObject( knownLocals *astmodel.KnownLocalsSet, generationContext *astmodel.CodeGenerationContext, ) ([]dst.Stmt, error) { - // We capture the expression written by the first step pass it to the second step, // allowing us to avoid extra local variable (this is a bit sneaky, as we rely on assignObjectDirectlyFromObject // and assignObjectDirectlyToObject using a local variable themselves.) @@ -2220,7 +2219,6 @@ func assignNonInlineObjectsViaPivotObject( knownLocals *astmodel.KnownLocalsSet, generationContext *astmodel.CodeGenerationContext, ) ([]dst.Stmt, error) { - // We capture the expression written by the first step pass it to the second step, // allowing us to avoid extra local variable (this is a bit sneaky, as we rely on assignObjectDirectlyFromObject // and assignObjectDirectlyToObject using a local variable themselves.) diff --git a/v2/tools/generator/internal/functions/chained_conversion_function.go b/v2/tools/generator/internal/functions/chained_conversion_function.go index 93021e000da..994657483bd 100644 --- a/v2/tools/generator/internal/functions/chained_conversion_function.go +++ b/v2/tools/generator/internal/functions/chained_conversion_function.go @@ -65,7 +65,8 @@ var _ astmodel.Function = &ChainedConversionFunction{} // idFactory is an identifier factory to use for generating local identifiers func NewSpecChainedConversionFunction( propertyFunction *PropertyAssignmentFunction, - idFactory astmodel.IdentifierFactory) *ChainedConversionFunction { + idFactory astmodel.IdentifierFactory, +) *ChainedConversionFunction { result := &ChainedConversionFunction{ name: propertyFunction.direction.SelectString("ConvertSpecFrom", "ConvertSpecTo"), parameterType: astmodel.ConvertibleSpecInterfaceType, @@ -85,7 +86,8 @@ func NewSpecChainedConversionFunction( // idFactory is an identifier factory to use for generating local identifiers func NewStatusChainedConversionFunction( propertyFunction *PropertyAssignmentFunction, - idFactory astmodel.IdentifierFactory) *ChainedConversionFunction { + idFactory astmodel.IdentifierFactory, +) *ChainedConversionFunction { result := &ChainedConversionFunction{ name: propertyFunction.direction.SelectString("ConvertStatusFrom", "ConvertStatusTo"), parameterType: astmodel.ConvertibleStatusInterfaceType, @@ -167,8 +169,8 @@ func (fn *ChainedConversionFunction) AsFunc( // // For ConvertTo, we have essentially the same structure, but two-step conversion is done in the other order. func (fn *ChainedConversionFunction) bodyForConvert( - receiverName string, parameterName string, generationContext *astmodel.CodeGenerationContext) []dst.Stmt { - + receiverName string, parameterName string, generationContext *astmodel.CodeGenerationContext, +) []dst.Stmt { errorsPackage := generationContext.MustGetImportedPackageName(astmodel.GitHubErrorsReference) receiver := dst.NewIdent(receiverName) diff --git a/v2/tools/generator/internal/functions/empty_status_function.go b/v2/tools/generator/internal/functions/empty_status_function.go index af7bec2001c..a86e273c8fd 100644 --- a/v2/tools/generator/internal/functions/empty_status_function.go +++ b/v2/tools/generator/internal/functions/empty_status_function.go @@ -15,7 +15,8 @@ import ( // NewEmptyStatusFunction creates a new function to generate NewEmptyStatus() on resource types func NewEmptyStatusFunction( status astmodel.TypeName, - idFactory astmodel.IdentifierFactory) *ObjectFunction { + idFactory astmodel.IdentifierFactory, +) *ObjectFunction { result := NewObjectFunction( "NewEmptyStatus", idFactory, diff --git a/v2/tools/generator/internal/functions/get_resource_type_function.go b/v2/tools/generator/internal/functions/get_resource_type_function.go index 14f20daa99a..43dd60a8b37 100644 --- a/v2/tools/generator/internal/functions/get_resource_type_function.go +++ b/v2/tools/generator/internal/functions/get_resource_type_function.go @@ -23,8 +23,8 @@ const ( func NewGetTypeFunction( armType string, idFactory astmodel.IdentifierFactory, - receiverType ReceiverType) astmodel.Function { - + receiverType ReceiverType, +) astmodel.Function { // Trim any "'s around armType armType = strings.Trim(armType, "\"") diff --git a/v2/tools/generator/internal/functions/index_registration_function.go b/v2/tools/generator/internal/functions/index_registration_function.go index b46028e4a74..293e9bbaa5b 100644 --- a/v2/tools/generator/internal/functions/index_registration_function.go +++ b/v2/tools/generator/internal/functions/index_registration_function.go @@ -146,7 +146,6 @@ func (f *IndexRegistrationFunction) singleValue(selector *dst.SelectorExpr) []ds // multipleValues is used when there are collections in the property path. func (f *IndexRegistrationFunction) multipleValues(selector *dst.SelectorExpr) ([]dst.Stmt, error) { - // var result []string resultVar := astbuilder.LocalVariableDeclaration( "result", diff --git a/v2/tools/generator/internal/functions/initialize_spec_function.go b/v2/tools/generator/internal/functions/initialize_spec_function.go index 0f3c0c5477b..70fcea2ca29 100644 --- a/v2/tools/generator/internal/functions/initialize_spec_function.go +++ b/v2/tools/generator/internal/functions/initialize_spec_function.go @@ -23,7 +23,8 @@ import ( func NewInitializeSpecFunction( def astmodel.TypeDefinition, specInitializeFunction string, - idFactory astmodel.IdentifierFactory) (astmodel.Function, error) { + idFactory astmodel.IdentifierFactory, +) (astmodel.Function, error) { rsrc, ok := astmodel.AsResourceType(def.Type()) if !ok { return nil, errors.Errorf("expected %q to be a resource", def.Name()) diff --git a/v2/tools/generator/internal/functions/kubernetes_admissions_validations.go b/v2/tools/generator/internal/functions/kubernetes_admissions_validations.go index a0a66e60e73..92a271ac76f 100644 --- a/v2/tools/generator/internal/functions/kubernetes_admissions_validations.go +++ b/v2/tools/generator/internal/functions/kubernetes_admissions_validations.go @@ -184,7 +184,6 @@ func validateWriteOncePropertiesFunction( // // return genruntime.ValidateWriteOnceProperties(oldObj, ) func validateWriteOncePropertiesFunctionBody(receiver astmodel.TypeName, codeGenerationContext *astmodel.CodeGenerationContext, receiverIdent string) []dst.Stmt { - genRuntime := codeGenerationContext.MustGetImportedPackageName(astmodel.GenRuntimeReference) obj := dst.NewIdent("oldObj") diff --git a/v2/tools/generator/internal/functions/kubernetes_admissions_validator.go b/v2/tools/generator/internal/functions/kubernetes_admissions_validator.go index 44d098bf21b..07d5cd37e7f 100644 --- a/v2/tools/generator/internal/functions/kubernetes_admissions_validator.go +++ b/v2/tools/generator/internal/functions/kubernetes_admissions_validator.go @@ -7,9 +7,10 @@ package functions import ( "fmt" + "strings" + "github.com/pkg/errors" kerrors "k8s.io/apimachinery/pkg/util/errors" - "strings" "github.com/dave/dst" @@ -253,7 +254,8 @@ func (v *ValidatorBuilder) validateBody( implFunctionName string, overrideFunctionName string, validationFunctionName string, - funcParamIdent string) []dst.Stmt { + funcParamIdent string, +) []dst.Stmt { overrideInterfaceType := astmodel.GenRuntimeValidatorInterfaceName.AsType(codeGenerationContext) validationsIdent := "validations" diff --git a/v2/tools/generator/internal/functions/kubernetes_exporter_function.go b/v2/tools/generator/internal/functions/kubernetes_exporter_function.go index 6224cc87475..f3bd0f96e59 100644 --- a/v2/tools/generator/internal/functions/kubernetes_exporter_function.go +++ b/v2/tools/generator/internal/functions/kubernetes_exporter_function.go @@ -6,10 +6,11 @@ package functions import ( - "github.com/pkg/errors" "go/token" "sort" + "github.com/pkg/errors" + "github.com/dave/dst" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astbuilder" @@ -30,7 +31,8 @@ func NewKubernetesExporterBuilder( resourceName astmodel.TypeName, resource *astmodel.ResourceType, idFactory astmodel.IdentifierFactory, - mappings map[string][]*astmodel.PropertyDefinition) *KubernetesExporterBuilder { + mappings map[string][]*astmodel.PropertyDefinition, +) *KubernetesExporterBuilder { return &KubernetesExporterBuilder{ resourceName: resourceName, resource: resource, @@ -222,7 +224,8 @@ func (d *KubernetesExporterBuilder) addCollectorStmt( collectorIdent string, propertyPath []*astmodel.PropertyDefinition, propertyNames []string, - operatorSpecPropertyName string) dst.Stmt { + operatorSpecPropertyName string, +) dst.Stmt { operatorSpecSelector := astbuilder.Selector(dst.NewIdent(receiverIdent), "Spec", astmodel.OperatorSpecProperty) operatorSpecConfigMapsSelector := astbuilder.Selector(operatorSpecSelector, astmodel.OperatorSpecConfigMapsProperty) diff --git a/v2/tools/generator/internal/functions/locatable_resource_function.go b/v2/tools/generator/internal/functions/locatable_resource_function.go index 50019aaff6a..41818f60e22 100644 --- a/v2/tools/generator/internal/functions/locatable_resource_function.go +++ b/v2/tools/generator/internal/functions/locatable_resource_function.go @@ -16,7 +16,6 @@ func NewLocatableResource( idFactory astmodel.IdentifierFactory, resourceType *astmodel.ResourceType, ) *astmodel.InterfaceImplementation { - f := NewResourceFunction( "Location", resourceType, diff --git a/v2/tools/generator/internal/functions/new_empty_arm_value_function.go b/v2/tools/generator/internal/functions/new_empty_arm_value_function.go index f2aeddf922c..f0620451088 100644 --- a/v2/tools/generator/internal/functions/new_empty_arm_value_function.go +++ b/v2/tools/generator/internal/functions/new_empty_arm_value_function.go @@ -16,7 +16,8 @@ import ( // It should be equivalent to ConvertToARM("") on a default struct value. func NewNewEmptyARMValueFunc( armType astmodel.TypeName, - idFactory astmodel.IdentifierFactory) astmodel.Function { + idFactory astmodel.IdentifierFactory, +) astmodel.Function { result := NewObjectFunction( "NewEmptyARMValue", idFactory, diff --git a/v2/tools/generator/internal/functions/object_function.go b/v2/tools/generator/internal/functions/object_function.go index 09c311d4480..b7958467ab3 100644 --- a/v2/tools/generator/internal/functions/object_function.go +++ b/v2/tools/generator/internal/functions/object_function.go @@ -27,7 +27,8 @@ var _ astmodel.Function = &ObjectFunction{} func NewObjectFunction( name string, idFactory astmodel.IdentifierFactory, - asFunc ObjectFunctionHandler) *ObjectFunction { + asFunc ObjectFunctionHandler, +) *ObjectFunction { return &ObjectFunction{ name: name, asFunc: asFunc, diff --git a/v2/tools/generator/internal/functions/one_of_json_unmarshal_function.go b/v2/tools/generator/internal/functions/one_of_json_unmarshal_function.go index a948c924763..e7dea7afb89 100644 --- a/v2/tools/generator/internal/functions/one_of_json_unmarshal_function.go +++ b/v2/tools/generator/internal/functions/one_of_json_unmarshal_function.go @@ -7,9 +7,10 @@ package functions import ( "fmt" - "github.com/pkg/errors" "sort" + "github.com/pkg/errors" + "github.com/dave/dst" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astbuilder" diff --git a/v2/tools/generator/internal/functions/pivot_conversion_function.go b/v2/tools/generator/internal/functions/pivot_conversion_function.go index cb01670cfde..99ddad73aaa 100644 --- a/v2/tools/generator/internal/functions/pivot_conversion_function.go +++ b/v2/tools/generator/internal/functions/pivot_conversion_function.go @@ -83,7 +83,8 @@ var _ astmodel.Function = &PivotConversionFunction{} // idFactory is an identifier factory to use for generating local identifiers func NewSpecPivotConversionFunction( direction conversions.Direction, - idFactory astmodel.IdentifierFactory) *PivotConversionFunction { + idFactory astmodel.IdentifierFactory, +) *PivotConversionFunction { result := &PivotConversionFunction{ nameFrom: "ConvertSpecFrom", nameTo: "ConvertSpecTo", @@ -101,7 +102,8 @@ func NewSpecPivotConversionFunction( // idFactory is an identifier factory to use for generating local identifiers func NewStatusPivotConversionFunction( direction conversions.Direction, - idFactory astmodel.IdentifierFactory) *PivotConversionFunction { + idFactory astmodel.IdentifierFactory, +) *PivotConversionFunction { result := &PivotConversionFunction{ nameFrom: "ConvertStatusFrom", nameTo: "ConvertStatusTo", @@ -166,8 +168,8 @@ func (fn *PivotConversionFunction) AsFunc( func (fn *PivotConversionFunction) bodyForPivot( receiverName string, parameterName string, - generationContext *astmodel.CodeGenerationContext) []dst.Stmt { - + generationContext *astmodel.CodeGenerationContext, +) []dst.Stmt { errorsPkg := generationContext.MustGetImportedPackageName(astmodel.GitHubErrorsReference) fnNameForOtherDirection := fn.direction.SelectString(fn.nameTo, fn.nameFrom) diff --git a/v2/tools/generator/internal/functions/property_assignment_function.go b/v2/tools/generator/internal/functions/property_assignment_function.go index 1e804e62296..36b1711afa8 100644 --- a/v2/tools/generator/internal/functions/property_assignment_function.go +++ b/v2/tools/generator/internal/functions/property_assignment_function.go @@ -7,10 +7,11 @@ package functions import ( "fmt" - "github.com/pkg/errors" "go/token" "sort" + "github.com/pkg/errors" + "github.com/dave/dst" "golang.org/x/exp/maps" diff --git a/v2/tools/generator/internal/functions/property_assignment_function_builder.go b/v2/tools/generator/internal/functions/property_assignment_function_builder.go index caa757ff1cd..fcada816a1c 100644 --- a/v2/tools/generator/internal/functions/property_assignment_function_builder.go +++ b/v2/tools/generator/internal/functions/property_assignment_function_builder.go @@ -70,7 +70,6 @@ func NewPropertyAssignmentFunctionBuilder( otherDefinition astmodel.TypeDefinition, direction conversions.Direction, ) *PropertyAssignmentFunctionBuilder { - result := &PropertyAssignmentFunctionBuilder{ receiverDefinition: receiver, otherDefinition: otherDefinition, diff --git a/v2/tools/generator/internal/functions/pure_function.go b/v2/tools/generator/internal/functions/pure_function.go index c91b83a4e46..5a0ea824322 100644 --- a/v2/tools/generator/internal/functions/pure_function.go +++ b/v2/tools/generator/internal/functions/pure_function.go @@ -27,7 +27,8 @@ var _ astmodel.Function = &PureFunction{} func NewPureFunction( name string, idFactory astmodel.IdentifierFactory, - asFunc PureFunctionHandler) *PureFunction { + asFunc PureFunctionHandler, +) *PureFunction { return &PureFunction{ name: name, asFunc: asFunc, diff --git a/v2/tools/generator/internal/functions/resource_conversion_function.go b/v2/tools/generator/internal/functions/resource_conversion_function.go index d7e2139a8a0..26dbf12ebdf 100644 --- a/v2/tools/generator/internal/functions/resource_conversion_function.go +++ b/v2/tools/generator/internal/functions/resource_conversion_function.go @@ -62,7 +62,8 @@ var _ astmodel.Function = &ResourceConversionFunction{} func NewResourceConversionFunction( hub astmodel.InternalTypeName, propertyFunction *PropertyAssignmentFunction, - idFactory astmodel.IdentifierFactory) *ResourceConversionFunction { + idFactory astmodel.IdentifierFactory, +) *ResourceConversionFunction { result := &ResourceConversionFunction{ hub: hub, propertyFunction: propertyFunction, @@ -98,7 +99,6 @@ func (fn *ResourceConversionFunction) AsFunc( codeGenerationContext *astmodel.CodeGenerationContext, receiver astmodel.InternalTypeName, ) (*dst.FuncDecl, error) { - // Create a sensible name for our receiver receiverName := fn.idFactory.CreateReceiver(receiver.Name()) @@ -149,7 +149,8 @@ func (fn *ResourceConversionFunction) Hub() astmodel.TypeName { // // return .AssignProperties(To|From)() func (fn *ResourceConversionFunction) directConversion( - receiverName string, generationContext *astmodel.CodeGenerationContext) []dst.Stmt { + receiverName string, generationContext *astmodel.CodeGenerationContext, +) []dst.Stmt { fmtPackage := generationContext.MustGetImportedPackageName(astmodel.FmtReference) hubPackage := fn.hub.InternalPackageReference().FolderPath() @@ -196,7 +197,8 @@ func (fn *ResourceConversionFunction) directConversion( // // return nil func (fn *ResourceConversionFunction) indirectConversionFromHub( - receiverName string, generationContext *astmodel.CodeGenerationContext) []dst.Stmt { + receiverName string, generationContext *astmodel.CodeGenerationContext, +) []dst.Stmt { errorsPackage := generationContext.MustGetImportedPackageName(astmodel.GitHubErrorsReference) localId := fn.localVariableId() errIdent := dst.NewIdent("err") @@ -255,7 +257,8 @@ func (fn *ResourceConversionFunction) indirectConversionFromHub( // // return nil func (fn *ResourceConversionFunction) indirectConversionToHub( - receiverName string, generationContext *astmodel.CodeGenerationContext) []dst.Stmt { + receiverName string, generationContext *astmodel.CodeGenerationContext, +) []dst.Stmt { errorsPackage := generationContext.MustGetImportedPackageName(astmodel.GitHubErrorsReference) localId := fn.localVariableId() errIdent := dst.NewIdent("err") diff --git a/v2/tools/generator/internal/functions/resource_function.go b/v2/tools/generator/internal/functions/resource_function.go index 956a68207c1..1a87fdc0c8f 100644 --- a/v2/tools/generator/internal/functions/resource_function.go +++ b/v2/tools/generator/internal/functions/resource_function.go @@ -36,7 +36,8 @@ func NewResourceFunction( resource *astmodel.ResourceType, idFactory astmodel.IdentifierFactory, asFunc ResourceFunctionHandler, - requiredPackages *astmodel.PackageReferenceSet) *ResourceFunction { + requiredPackages *astmodel.PackageReferenceSet, +) *ResourceFunction { return &ResourceFunction{ name: name, resource: resource, diff --git a/v2/tools/generator/internal/functions/resource_status_setter_function.go b/v2/tools/generator/internal/functions/resource_status_setter_function.go index 8aab1c9b03a..e02f399ffd0 100644 --- a/v2/tools/generator/internal/functions/resource_status_setter_function.go +++ b/v2/tools/generator/internal/functions/resource_status_setter_function.go @@ -27,8 +27,8 @@ var _ astmodel.Function = &ResourceStatusSetterFunction{} // idFactory is an IdentifierFactory for creating local variable names func NewResourceStatusSetterFunction( resource *astmodel.ResourceType, - idFactory astmodel.IdentifierFactory) *ResourceStatusSetterFunction { - + idFactory astmodel.IdentifierFactory, +) *ResourceStatusSetterFunction { statusTypeName, ok := astmodel.AsTypeName(resource.StatusType()) if !ok { panic(fmt.Sprintf("expected Status to be a TypeName but found %T", resource.StatusType())) diff --git a/v2/tools/generator/internal/interfaces/defaulter_interface.go b/v2/tools/generator/internal/interfaces/defaulter_interface.go index 44183df8584..255f30bcb88 100644 --- a/v2/tools/generator/internal/interfaces/defaulter_interface.go +++ b/v2/tools/generator/internal/interfaces/defaulter_interface.go @@ -17,7 +17,6 @@ func AddDefaulterInterface( idFactory astmodel.IdentifierFactory, defaultFunctions []*functions.ResourceFunction, ) (astmodel.TypeDefinition, error) { - resourceType, ok := resourceDef.Type().(*astmodel.ResourceType) if !ok { return astmodel.TypeDefinition{}, errors.Errorf("cannot add defaulter interface to non-resource type: %s %T", resourceDef.Name(), resourceDef.Type()) diff --git a/v2/tools/generator/internal/interfaces/kubernetes_resource_interface.go b/v2/tools/generator/internal/interfaces/kubernetes_resource_interface.go index 6f8dd0560fc..ee088814ba8 100644 --- a/v2/tools/generator/internal/interfaces/kubernetes_resource_interface.go +++ b/v2/tools/generator/internal/interfaces/kubernetes_resource_interface.go @@ -151,7 +151,6 @@ func getAzureNameFunctionsForType( definitions astmodel.TypeDefinitionSet, log logr.Logger, ) (functions.ObjectFunctionHandler, functions.ObjectFunctionHandler, error) { - if opt, ok := astmodel.AsOptionalType(t); ok { t = opt.BaseType() } diff --git a/v2/tools/generator/internal/interfaces/validator_interface.go b/v2/tools/generator/internal/interfaces/validator_interface.go index bed91eb85a2..f8cec0bfc33 100644 --- a/v2/tools/generator/internal/interfaces/validator_interface.go +++ b/v2/tools/generator/internal/interfaces/validator_interface.go @@ -16,8 +16,8 @@ func AddValidatorInterface( resourceDef astmodel.TypeDefinition, idFactory astmodel.IdentifierFactory, definitions astmodel.TypeDefinitionSet, - validations map[functions.ValidationKind][]*functions.ResourceFunction) (astmodel.TypeDefinition, error) { - + validations map[functions.ValidationKind][]*functions.ResourceFunction, +) (astmodel.TypeDefinition, error) { resolved, err := definitions.ResolveResourceSpecAndStatus(resourceDef) if err != nil { return astmodel.TypeDefinition{}, errors.Wrapf(err, "unable to resolve resource %s", resourceDef.Name()) @@ -28,7 +28,6 @@ func AddValidatorInterface( for _, validation := range vs { validatorBuilder.AddValidation(validationKind, validation) } - } resourceType := resolved.ResourceType.WithInterface(validatorBuilder.ToInterfaceImplementation()) diff --git a/v2/tools/generator/internal/jsonast/jsonast.go b/v2/tools/generator/internal/jsonast/jsonast.go index 85fffb19dbe..66c7acaa4c2 100644 --- a/v2/tools/generator/internal/jsonast/jsonast.go +++ b/v2/tools/generator/internal/jsonast/jsonast.go @@ -866,7 +866,6 @@ func withArrayValidations(schema Schema, t *astmodel.ArrayType) astmodel.Type { } func getSubSchemaType(schema Schema) (SchemaType, error) { - // handle special nodes: switch { case len(schema.enumValues()) > 0: // this should come before the primitive checks below diff --git a/v2/tools/generator/internal/jsonast/schema_abstraction_gojson.go b/v2/tools/generator/internal/jsonast/schema_abstraction_gojson.go index 35f8df12be4..ecd9210706b 100644 --- a/v2/tools/generator/internal/jsonast/schema_abstraction_gojson.go +++ b/v2/tools/generator/internal/jsonast/schema_abstraction_gojson.go @@ -6,12 +6,13 @@ package jsonast import ( - "github.com/Azure/azure-service-operator/v2/internal/set" "math/big" "net/url" "regexp" "strings" + "github.com/Azure/azure-service-operator/v2/internal/set" + "github.com/pkg/errors" "github.com/xeipuuv/gojsonschema" diff --git a/v2/tools/generator/internal/jsonast/swagger_type_extractor.go b/v2/tools/generator/internal/jsonast/swagger_type_extractor.go index 5a63bf3315e..cc8dae20d58 100644 --- a/v2/tools/generator/internal/jsonast/swagger_type_extractor.go +++ b/v2/tools/generator/internal/jsonast/swagger_type_extractor.go @@ -592,8 +592,8 @@ func (extractor *SwaggerTypeExtractor) getNameParameterType( ctx context.Context, operationPath string, scanner *SchemaScanner, - parameters []spec.Parameter) astmodel.Type { - + parameters []spec.Parameter, +) astmodel.Type { lastParam, ok := extractor.extractLastPathParam(operationPath, parameters) if !ok { panic(fmt.Sprintf("couldn't find path parameter for %s", operationPath)) @@ -645,9 +645,9 @@ func (extractor *SwaggerTypeExtractor) expandAndCanonicalizePath( ctx context.Context, operationPath string, scanner *SchemaScanner, - parameters []spec.Parameter) []string { - - var results = []string{ + parameters []spec.Parameter, +) []string { + results := []string{ operationPath, } diff --git a/v2/tools/generator/internal/kustomization/conversion_patch_file.go b/v2/tools/generator/internal/kustomization/conversion_patch_file.go index c02cf0d8a1a..4cc5ec8c0ff 100644 --- a/v2/tools/generator/internal/kustomization/conversion_patch_file.go +++ b/v2/tools/generator/internal/kustomization/conversion_patch_file.go @@ -85,7 +85,7 @@ func (p *ConversionPatchFile) Save(destination string) error { return errors.Wrap(err, "serializing to yaml") } - err = ioutil.WriteFile(destination, data, 0644) // #nosec G306 + err = ioutil.WriteFile(destination, data, 0o644) // #nosec G306 if err != nil { return errors.Wrapf(err, "writing to %s", destination) } diff --git a/v2/tools/generator/internal/kustomization/crd_kustomization_file.go b/v2/tools/generator/internal/kustomization/crd_kustomization_file.go index 67fe1856207..46f584dde42 100644 --- a/v2/tools/generator/internal/kustomization/crd_kustomization_file.go +++ b/v2/tools/generator/internal/kustomization/crd_kustomization_file.go @@ -57,7 +57,7 @@ func (k *CRDKustomizeFile) Save(destination string) error { return errors.Wrap(err, "serializing to yaml") } - err = ioutil.WriteFile(destination, data, 0644) // #nosec G306 + err = ioutil.WriteFile(destination, data, 0o644) // #nosec G306 if err != nil { return errors.Wrapf(err, "writing to %s", destination) } diff --git a/v2/tools/generator/internal/reporting/structure_report.go b/v2/tools/generator/internal/reporting/structure_report.go index f14dffb3a3e..b32b0180e42 100644 --- a/v2/tools/generator/internal/reporting/structure_report.go +++ b/v2/tools/generator/internal/reporting/structure_report.go @@ -46,8 +46,8 @@ func (sr *StructureReport) writeBlock( writer io.Writer, indents []string, prefixForItem string, - prefixForSubItems string) error { - + prefixForSubItems string, +) error { // Write existing prefix for _, i := range indents { _, err := io.WriteString(writer, i) diff --git a/v2/tools/generator/internal/reporting/type_catalog_report.go b/v2/tools/generator/internal/reporting/type_catalog_report.go index 7e4e2e93fa1..91cb56fedad 100644 --- a/v2/tools/generator/internal/reporting/type_catalog_report.go +++ b/v2/tools/generator/internal/reporting/type_catalog_report.go @@ -7,12 +7,13 @@ package reporting import ( "fmt" - "github.com/Azure/azure-service-operator/v2/internal/set" - "golang.org/x/exp/slices" "io" "os" "sort" + "github.com/Azure/azure-service-operator/v2/internal/set" + "golang.org/x/exp/slices" + "github.com/pkg/errors" "github.com/Azure/azure-service-operator/v2/tools/generator/internal/astmodel" @@ -60,7 +61,6 @@ func (tcr *TypeCatalogReport) AddHeader(lines ...string) { // SaveTo writes the report to the specified file func (tcr *TypeCatalogReport) SaveTo(filePath string) error { - file, err := os.Create(filePath) if err != nil { return err @@ -146,7 +146,8 @@ func (tcr *TypeCatalogReport) WriteTo(writer io.Writer) error { // Definitions are written in alphabetical order, by case-sensitive sort func (tcr *TypeCatalogReport) writeDefinitions( rpt *StructureReport, - definitions astmodel.TypeDefinitionSet) { + definitions astmodel.TypeDefinitionSet, +) { defs := definitions.AsSlice() sort.Slice(defs, func(i, j int) bool { return defs[i].Name().Name() < defs[j].Name().Name() @@ -314,8 +315,8 @@ func (tcr *TypeCatalogReport) writeComplexType( rpt *StructureReport, propertyType astmodel.Type, currentPackage astmodel.InternalPackageReference, - parentTypes astmodel.TypeNameSet) { - + parentTypes astmodel.TypeNameSet, +) { // If we have a complex type, we may need to write it out in detail switch t := propertyType.(type) { case *astmodel.ObjectType, @@ -330,6 +331,7 @@ func (tcr *TypeCatalogReport) writeComplexType( tcr.writeComplexType(rpt, t.Element(), currentPackage, parentTypes) } } + func (tcr *TypeCatalogReport) writeErroredType( rpt *StructureReport, et *astmodel.ErroredType, @@ -365,7 +367,6 @@ func (tcr *TypeCatalogReport) asDefinitionToInline( t astmodel.Type, parentTypes astmodel.TypeNameSet, ) (*astmodel.TypeDefinition, bool) { - // We can inline a typename if we have a definition for it, and if it's not already inlined if n, ok := astmodel.AsInternalTypeName(t); ok { if parentTypes.Contains(n) { diff --git a/v2/tools/generator/internal/test/resource.go b/v2/tools/generator/internal/test/resource.go index 6f4d00fa862..d28bc862733 100644 --- a/v2/tools/generator/internal/test/resource.go +++ b/v2/tools/generator/internal/test/resource.go @@ -17,8 +17,8 @@ func CreateResource( name string, spec astmodel.TypeDefinition, status astmodel.TypeDefinition, - functions ...astmodel.Function) astmodel.TypeDefinition { - + functions ...astmodel.Function, +) astmodel.TypeDefinition { resourceType := astmodel.NewResourceType(spec.Name(), status.Name()) for _, fn := range functions { resourceType = resourceType.WithFunction(fn) @@ -33,8 +33,8 @@ func CreateARMResource( spec astmodel.TypeDefinition, status astmodel.TypeDefinition, apiVersion astmodel.TypeDefinition, - functions ...astmodel.Function) astmodel.TypeDefinition { - + functions ...astmodel.Function, +) astmodel.TypeDefinition { resourceType := astmodel.NewResourceType(spec.Name(), status.Name()) for _, fn := range functions { resourceType = resourceType.WithFunction(fn) @@ -55,7 +55,8 @@ func CreateARMResource( func MakeSpecName( pkg astmodel.InternalPackageReference, - name string) astmodel.InternalTypeName { + name string, +) astmodel.InternalTypeName { return astmodel.MakeInternalTypeName(pkg, name+astmodel.SpecSuffix) } @@ -63,7 +64,8 @@ func MakeSpecName( func CreateSpec( pkg astmodel.InternalPackageReference, name string, - properties ...*astmodel.PropertyDefinition) astmodel.TypeDefinition { + properties ...*astmodel.PropertyDefinition, +) astmodel.TypeDefinition { specName := MakeSpecName(pkg, name) return astmodel.MakeTypeDefinition( specName, @@ -72,7 +74,8 @@ func CreateSpec( func MakeStatusName( pkg astmodel.InternalPackageReference, - name string) astmodel.InternalTypeName { + name string, +) astmodel.InternalTypeName { return astmodel.MakeInternalTypeName(pkg, name+astmodel.StatusSuffix) } @@ -80,7 +83,8 @@ func MakeStatusName( func CreateStatus( pkg astmodel.InternalPackageReference, name string, - properties ...*astmodel.PropertyDefinition) astmodel.TypeDefinition { + properties ...*astmodel.PropertyDefinition, +) astmodel.TypeDefinition { statusName := MakeStatusName(pkg, name) return astmodel.MakeTypeDefinition( statusName, @@ -91,8 +95,8 @@ func CreateStatus( func CreateObjectDefinition( pkg astmodel.InternalPackageReference, name string, - properties ...*astmodel.PropertyDefinition) astmodel.TypeDefinition { - + properties ...*astmodel.PropertyDefinition, +) astmodel.TypeDefinition { typeName := astmodel.MakeInternalTypeName(pkg, name) return astmodel.MakeTypeDefinition( typeName, @@ -104,8 +108,8 @@ func CreateObjectDefinitionWithFunction( pkg astmodel.InternalPackageReference, name string, function astmodel.Function, - properties ...*astmodel.PropertyDefinition) astmodel.TypeDefinition { - + properties ...*astmodel.PropertyDefinition, +) astmodel.TypeDefinition { typeName := astmodel.MakeInternalTypeName(pkg, name) return astmodel.MakeTypeDefinition( typeName, @@ -119,7 +123,8 @@ func CreateObjectType(properties ...*astmodel.PropertyDefinition) *astmodel.Obje func CreateSimpleResource( pkg astmodel.InternalPackageReference, name string, - specProperties ...*astmodel.PropertyDefinition) astmodel.TypeDefinition { + specProperties ...*astmodel.PropertyDefinition, +) astmodel.TypeDefinition { spec := CreateSpec(pkg, name, specProperties...) status := CreateStatus(pkg, name) return CreateResource(pkg, name, spec, status) diff --git a/v2/tools/generator/internal/test/type_asserter.go b/v2/tools/generator/internal/test/type_asserter.go index 41636a43f27..1626f7785bd 100644 --- a/v2/tools/generator/internal/test/type_asserter.go +++ b/v2/tools/generator/internal/test/type_asserter.go @@ -73,7 +73,8 @@ func (a *typeAsserter) assertFile( name string, defs []astmodel.TypeDefinition, refs []astmodel.TypeDefinition, - renderer func(defs []astmodel.TypeDefinition) (string, error)) { + renderer func(defs []astmodel.TypeDefinition) (string, error), +) { content, err := renderer(defs) if err != nil { a.t.Fatalf("rendering content: %s", err) diff --git a/v2/tools/generator/internal/testcases/property_assignment_test_case.go b/v2/tools/generator/internal/testcases/property_assignment_test_case.go index 2b3b8285bee..e28e032b9e0 100644 --- a/v2/tools/generator/internal/testcases/property_assignment_test_case.go +++ b/v2/tools/generator/internal/testcases/property_assignment_test_case.go @@ -33,8 +33,8 @@ var _ astmodel.TestCase = &PropertyAssignmentTestCase{} func NewPropertyAssignmentTestCase( name astmodel.TypeName, container astmodel.FunctionContainer, - idFactory astmodel.IdentifierFactory) *PropertyAssignmentTestCase { - + idFactory astmodel.IdentifierFactory, +) *PropertyAssignmentTestCase { result := &PropertyAssignmentTestCase{ subject: name, idFactory: idFactory, @@ -115,7 +115,8 @@ func (p *PropertyAssignmentTestCase) RequiredImports() *astmodel.PackageImportSe // subject is the name of the type under test // codeGenerationContext contains reference material to use when generating func (p *PropertyAssignmentTestCase) AsFuncs( - receiver astmodel.TypeName, codeGenerationContext *astmodel.CodeGenerationContext) []dst.Decl { + receiver astmodel.TypeName, codeGenerationContext *astmodel.CodeGenerationContext, +) []dst.Decl { return []dst.Decl{ p.createTestRunner(codeGenerationContext), p.createTestMethod(receiver, codeGenerationContext), @@ -222,7 +223,8 @@ func (p *PropertyAssignmentTestCase) createTestRunner(codegenContext *astmodel.C // createTestMethod generates the AST for a method to run a single test conversion and back again func (p *PropertyAssignmentTestCase) createTestMethod( subject astmodel.TypeName, - codegenContext *astmodel.CodeGenerationContext) dst.Decl { + codegenContext *astmodel.CodeGenerationContext, +) dst.Decl { const ( errId = "err" copiedId = "copied" diff --git a/v2/tools/generator/internal/testcases/resource_conversion_test_case.go b/v2/tools/generator/internal/testcases/resource_conversion_test_case.go index 5e09ef2309d..66434b1914b 100644 --- a/v2/tools/generator/internal/testcases/resource_conversion_test_case.go +++ b/v2/tools/generator/internal/testcases/resource_conversion_test_case.go @@ -35,8 +35,8 @@ var _ astmodel.TestCase = &ResourceConversionTestCase{} func NewResourceConversionTestCase( name astmodel.TypeName, resourceType *astmodel.ResourceType, - idFactory astmodel.IdentifierFactory) (*ResourceConversionTestCase, error) { - + idFactory astmodel.IdentifierFactory, +) (*ResourceConversionTestCase, error) { result := &ResourceConversionTestCase{ subject: name, idFactory: idFactory, @@ -258,7 +258,8 @@ func (tc *ResourceConversionTestCase) createTestRunner(codegenContext *astmodel. // return "" func (tc *ResourceConversionTestCase) createTestMethod( subject astmodel.TypeName, - codegenContext *astmodel.CodeGenerationContext) dst.Decl { + codegenContext *astmodel.CodeGenerationContext, +) dst.Decl { const ( errId = "err" hubId = "hub" diff --git a/v2/tools/generator/root.go b/v2/tools/generator/root.go index 1ad039690cc..7c4adaf5574 100644 --- a/v2/tools/generator/root.go +++ b/v2/tools/generator/root.go @@ -91,7 +91,6 @@ var ( // CreateLogger creates a logger for console output. func CreateLogger() logr.Logger { - // Configure console writer for ZeroLog output := zerolog.ConsoleWriter{ Out: os.Stderr, // Write to StdErr diff --git a/workspace.code-workspace b/workspace.code-workspace index d21772942fb..c858a9aa9f5 100644 --- a/workspace.code-workspace +++ b/workspace.code-workspace @@ -15,7 +15,8 @@ ], "settings": { "gopls": { - "formatting.local": "github.com/Azure/azure-service-operator" + "formatting.local": "github.com/Azure/azure-service-operator", + "formatting.gofumpt": true } } }