diff --git a/.github/workflows/automation-open-pull-request.yaml b/.github/workflows/automation-open-pull-request.yaml index 6ac47f2ba325..2a6d33dfbfd3 100644 --- a/.github/workflows/automation-open-pull-request.yaml +++ b/.github/workflows/automation-open-pull-request.yaml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: "open a pull request" id: open-pr diff --git a/.github/workflows/breaking-change-detection.yaml b/.github/workflows/breaking-change-detection.yaml index 0bd20d3df8ee..e4d475cb377c 100644 --- a/.github/workflows/breaking-change-detection.yaml +++ b/.github/workflows/breaking-change-detection.yaml @@ -22,7 +22,7 @@ jobs: detect: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: ./.go-version diff --git a/.github/workflows/depscheck.yaml b/.github/workflows/depscheck.yaml index 2e503f5dc64f..be8571fe19fe 100644 --- a/.github/workflows/depscheck.yaml +++ b/.github/workflows/depscheck.yaml @@ -16,7 +16,7 @@ jobs: depscheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: ./.go-version diff --git a/.github/workflows/gencheck.yaml b/.github/workflows/gencheck.yaml index 0242765fa67f..e5e2d6af9960 100644 --- a/.github/workflows/gencheck.yaml +++ b/.github/workflows/gencheck.yaml @@ -19,7 +19,7 @@ jobs: gencheck: runs-on: [custom, linux, large] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: ./.go-version diff --git a/.github/workflows/golint.yaml b/.github/workflows/golint.yaml index 29cca99d8189..d0a37548695d 100644 --- a/.github/workflows/golint.yaml +++ b/.github/workflows/golint.yaml @@ -20,7 +20,7 @@ jobs: golint: runs-on: [custom, linux, large] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: ./.go-version diff --git a/.github/workflows/gradually-deprecated.yaml b/.github/workflows/gradually-deprecated.yaml index 279994a30585..442c5c9a8d58 100644 --- a/.github/workflows/gradually-deprecated.yaml +++ b/.github/workflows/gradually-deprecated.yaml @@ -16,7 +16,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: fetch-depth: 0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 diff --git a/.github/workflows/increment-milestone.yaml b/.github/workflows/increment-milestone.yaml index 6b9384bd0a3b..4e130b591af9 100644 --- a/.github/workflows/increment-milestone.yaml +++ b/.github/workflows/increment-milestone.yaml @@ -14,7 +14,7 @@ jobs: increment-milestone: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: fetch-depth: 0 - name: "Increment Milestone" diff --git a/.github/workflows/issue-opened.yaml b/.github/workflows/issue-opened.yaml index b64d83a1aa4b..25008ab534b2 100644 --- a/.github/workflows/issue-opened.yaml +++ b/.github/workflows/issue-opened.yaml @@ -12,7 +12,7 @@ jobs: issue_triage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: github/issue-labeler@98b5412841f6c4b0b3d9c29d53c13fad16bd7de2 # v3.2 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/mkdocs.yaml b/.github/workflows/mkdocs.yaml index a4fa013b46bc..7de4d2f5fd35 100644 --- a/.github/workflows/mkdocs.yaml +++ b/.github/workflows/mkdocs.yaml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Publish uses: mhausenblas/mkdocs-deploy-gh-pages@d77dd03172e96abbcdb081d8c948224762033653 # 1.26 diff --git a/.github/workflows/provider-test.yaml b/.github/workflows/provider-test.yaml index 71c6de19d3d9..f2e508690a0e 100644 --- a/.github/workflows/provider-test.yaml +++ b/.github/workflows/provider-test.yaml @@ -36,7 +36,7 @@ jobs: if: needs.secrets-check.outputs.available == 'true' steps: - name: Checkout - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Install Go uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 diff --git a/.github/workflows/pull-request-reviewed.yaml b/.github/workflows/pull-request-reviewed.yaml index 306e21133ddb..ec359edac72d 100644 --- a/.github/workflows/pull-request-reviewed.yaml +++ b/.github/workflows/pull-request-reviewed.yaml @@ -30,7 +30,7 @@ jobs: echo ${{ github.repository }} > wr_actions/ghrepo.txt echo ${{ github.event.pull_request.number }} > wr_actions/prnumber.txt echo "remove-waiting-response" > wr_actions/action.txt - - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: artifact path: wr_actions diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 75fc48d748fa..74ae22c4f7ad 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -15,18 +15,18 @@ jobs: outputs: version: ${{ steps.go-version.outputs.version }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - id: go-version run: echo "version=$(cat ./.go-version)" >>${GITHUB_OUTPUT} release-notes: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 with: fetch-depth: 0 - name: Generate Release Notes run: sed -n -e "1{/# /d;}" -e "2{/^$/d;}" -e "/# $(git describe --abbrev=0 --exclude="$(git describe --abbrev=0 --match='v*.*.*' --tags)" --match='v*.*.*' --tags | tr -d v)/q;p" CHANGELOG.md > release-notes.txt - - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: release-notes path: release-notes.txt diff --git a/.github/workflows/save-artifacts.yaml b/.github/workflows/save-artifacts.yaml index 1f98c7f4bc23..5c95f66d749f 100644 --- a/.github/workflows/save-artifacts.yaml +++ b/.github/workflows/save-artifacts.yaml @@ -14,7 +14,7 @@ jobs: echo ${{ github.repository_owner }} > wr_actions/ghowner.txt echo ${{ github.event.repository.name }} > wr_actions/ghrepo.txt echo ${{ github.event.pull_request.number }} > wr_actions/prnumber.txt - - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: artifact path: wr_actions diff --git a/.github/workflows/teamcity-test.yaml b/.github/workflows/teamcity-test.yaml index 565b1eb683d9..646c0b4009a1 100644 --- a/.github/workflows/teamcity-test.yaml +++ b/.github/workflows/teamcity-test.yaml @@ -21,13 +21,13 @@ jobs: teamcity-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0 with: distribution: zulu java-version: 17 java-package: jdk - - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 + - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/tflint.yaml b/.github/workflows/tflint.yaml index 88257fbedbc8..0768b8322644 100644 --- a/.github/workflows/tflint.yaml +++ b/.github/workflows/tflint.yaml @@ -20,7 +20,7 @@ jobs: tflint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: ./.go-version diff --git a/.github/workflows/thirty-two-bit.yaml b/.github/workflows/thirty-two-bit.yaml index 4f6e0a83f39b..9892137d419b 100644 --- a/.github/workflows/thirty-two-bit.yaml +++ b/.github/workflows/thirty-two-bit.yaml @@ -21,7 +21,7 @@ jobs: compatibility-32bit-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: ./.go-version diff --git a/.github/workflows/unit-test.yaml b/.github/workflows/unit-test.yaml index 1164903027b4..ab17db22f3bf 100644 --- a/.github/workflows/unit-test.yaml +++ b/.github/workflows/unit-test.yaml @@ -21,7 +21,7 @@ jobs: test: runs-on: [custom, linux, large] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: ./.go-version diff --git a/.github/workflows/validate-examples.yaml b/.github/workflows/validate-examples.yaml index 084295689cc2..16f47cf87c1a 100644 --- a/.github/workflows/validate-examples.yaml +++ b/.github/workflows/validate-examples.yaml @@ -20,7 +20,7 @@ jobs: website-lint: runs-on: [custom, linux, large] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: ./.go-version diff --git a/.github/workflows/website-lint.yaml b/.github/workflows/website-lint.yaml index 14e5c6bff561..81c401acfc9c 100644 --- a/.github/workflows/website-lint.yaml +++ b/.github/workflows/website-lint.yaml @@ -16,7 +16,7 @@ jobs: website-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: ./.go-version diff --git a/CHANGELOG.md b/CHANGELOG.md index 8db2e264228a..787f6cdf66e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ ENHANCEMENTS: -* dependencies - swap Virtual Machine Scale Set Ids to `hashicorp/go-azure-helpers/commonids` [GH-23272] +* dependencies: downgrading to `v1.12.5` of `github.com/rickb777/date` [GH-23296] +* `azurerm_container_app` - add support for scale rules [GH-23294] + +BUG FIXES: + +* `azurerm_servicebus_queue` - fixing an issue where `auto_delete_on_idle` couldn't be set to `P10675199DT2H48M5.4775807S` [GH-23296] +* `azurerm_servicebus_topic` - fixing an issue where `auto_delete_on_idle` couldn't be set to `P10675199DT2H48M5.4775807S` [GH-23296] ## 3.73.0 (September 14, 2023) diff --git a/go.mod b/go.mod index cf3450f061bf..bbc7ea7409a6 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/hashicorp/terraform-plugin-testing v1.5.1 github.com/magodo/terraform-provider-azurerm-example-gen v0.0.0-20220407025246-3a3ee0ab24a8 github.com/mitchellh/mapstructure v1.5.0 - github.com/rickb777/date v1.20.2 + github.com/rickb777/date v1.12.5-0.20200422084442-6300e543c4d9 github.com/sergi/go-diff v1.2.0 github.com/tombuildsstuff/giovanni v0.20.0 github.com/tombuildsstuff/kermit v0.20230703.1101016 diff --git a/go.sum b/go.sum index f3d63273ba7a..f52848aef91e 100644 --- a/go.sum +++ b/go.sum @@ -175,6 +175,7 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -192,12 +193,12 @@ github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DV github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rickb777/date v1.20.2 h1:CUpAaa4ksqvcRaidSgwzK7zeO2wUG5/VGy6Zlfcu/d4= -github.com/rickb777/date v1.20.2/go.mod h1:PVaM/Zn0IOzjm1uj84Eh9NJ/imtQSm1SVKtOvIunaYw= +github.com/rickb777/date v1.12.5-0.20200422084442-6300e543c4d9 h1:czJCcoUR3FMpHnRQow2E84H/0CPrX1fMAGn9HugzyI4= +github.com/rickb777/date v1.12.5-0.20200422084442-6300e543c4d9/go.mod h1:L8WrssTzvgYw34/Ppa0JpJfI7KKXZ2cVGI6Djt0brUU= +github.com/rickb777/plural v1.2.0/go.mod h1:UdpyWFCGbo3mvK3f/PfZOAOrkjzJlYN/sD46XNWJ+Es= github.com/rickb777/plural v1.4.1 h1:5MMLcbIaapLFmvDGRT5iPk8877hpTPt8Y9cdSKRw9sU= github.com/rickb777/plural v1.4.1/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7x4umxNw= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= diff --git a/helpers/validate/time_test.go b/helpers/validate/time_test.go index e455d0dab155..d275f76bfeb1 100644 --- a/helpers/validate/time_test.go +++ b/helpers/validate/time_test.go @@ -81,6 +81,11 @@ func TestISO8601Duration(t *testing.T) { Value: "P1Y2M3DT7H42M3S", Errors: 0, }, + { + // .Net's TimeSpan.Max value (used as the default value for ServiceBus Topics) + Value: "P10675199DT2H48M5.4775807S", + Errors: 0, + }, { // Invalid prefix Value: "1Y2M3DT7H42M3S", diff --git a/internal/services/compute/shared_image_version_resource.go b/internal/services/compute/shared_image_version_resource.go index aba48fd63a71..26535cc429df 100644 --- a/internal/services/compute/shared_image_version_resource.go +++ b/internal/services/compute/shared_image_version_resource.go @@ -10,6 +10,7 @@ import ( "time" "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" @@ -99,6 +100,12 @@ func resourceSharedImageVersion() *pluginsdk.Resource { ValidateFunc: validate.DiskEncryptionSetID, }, + "exclude_from_latest_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + // The Service API doesn't support to update `storage_account_type`. So it has to recreate the resource for updating `storage_account_type`. // However, `ForceNew` cannot be used since resource would be recreated while adding or removing `target_region`. // And `CustomizeDiff` also cannot be used since it doesn't support in a `Set`. @@ -177,6 +184,13 @@ func resourceSharedImageVersion() *pluginsdk.Resource { Default: false, }, + "deletion_of_replicated_locations_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + "tags": tags.Schema(), }, @@ -222,6 +236,9 @@ func resourceSharedImageVersionCreateUpdate(d *pluginsdk.ResourceData, meta inte ReplicationMode: compute.ReplicationMode(d.Get("replication_mode").(string)), TargetRegions: targetRegions, }, + SafetyProfile: &compute.GalleryImageVersionSafetyProfile{ + AllowDeletionOfReplicatedLocations: utils.Bool(d.Get("deletion_of_replicated_locations_enabled").(bool)), + }, StorageProfile: &compute.GalleryImageVersionStorageProfile{}, }, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), @@ -343,6 +360,10 @@ func resourceSharedImageVersionRead(d *pluginsdk.ResourceData, meta interface{}) d.Set("os_disk_snapshot_id", osDiskSnapShotID) d.Set("storage_account_id", storageAccountID) } + + if safetyProfile := props.SafetyProfile; safetyProfile != nil { + d.Set("deletion_of_replicated_locations_enabled", pointer.From(safetyProfile.AllowDeletionOfReplicatedLocations)) + } } return tags.FlattenAndSet(d, resp.Tags) @@ -415,9 +436,11 @@ func expandSharedImageVersionTargetRegions(d *pluginsdk.ResourceData) (*[]comput regionalReplicaCount := input["regional_replica_count"].(int) storageAccountType := input["storage_account_type"].(string) diskEncryptionSetId := input["disk_encryption_set_id"].(string) + excludeFromLatest := input["exclude_from_latest_enabled"].(bool) output := compute.TargetRegion{ Name: utils.String(name), + ExcludeFromLatest: utils.Bool(excludeFromLatest), RegionalReplicaCount: utils.Int32(int32(regionalReplicaCount)), StorageAccountType: compute.StorageAccountType(storageAccountType), } @@ -463,6 +486,8 @@ func flattenSharedImageVersionTargetRegions(input *[]compute.TargetRegion) []int } output["disk_encryption_set_id"] = diskEncryptionSetId + output["exclude_from_latest_enabled"] = pointer.From(v.ExcludeFromLatest) + results = append(results, output) } } diff --git a/internal/services/compute/shared_image_version_resource_test.go b/internal/services/compute/shared_image_version_resource_test.go index 14e44081c7b2..fee03ee1c86d 100644 --- a/internal/services/compute/shared_image_version_resource_test.go +++ b/internal/services/compute/shared_image_version_resource_test.go @@ -234,6 +234,29 @@ func TestAccSharedImageVersion_replicationMode(t *testing.T) { }) } +func TestAccSharedImageVersion_replicatedRegionDeletion(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_shared_image_version", "test") + r := SharedImageVersionResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + // need to create a vm and then reference it in the image creation + Config: r.setup(data), + Check: acceptance.ComposeTestCheckFunc( + data.CheckWithClientForResource(ImageResource{}.virtualMachineExists, "azurerm_virtual_machine.testsource"), + data.CheckWithClientForResource(ImageResource{}.generalizeVirtualMachine(data), "azurerm_virtual_machine.testsource"), + ), + }, + { + Config: r.replicatedRegionDeletion(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccSharedImageVersion_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_shared_image_version", "test") r := SharedImageVersionResource{} @@ -697,3 +720,26 @@ resource "azurerm_shared_image_version" "test" { } `, template) } + +func (r SharedImageVersionResource) replicatedRegionDeletion(data acceptance.TestData) string { + template := r.provision(data) + return fmt.Sprintf(` +%s + +resource "azurerm_shared_image_version" "test" { + name = "0.0.1" + gallery_name = azurerm_shared_image_gallery.test.name + image_name = azurerm_shared_image.test.name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + managed_image_id = azurerm_image.test.id + deletion_of_replicated_locations_enabled = true + + target_region { + name = azurerm_resource_group.test.location + regional_replica_count = 1 + exclude_from_latest_enabled = true + } +} +`, template) +} diff --git a/internal/services/containerapps/container_app_resource_test.go b/internal/services/containerapps/container_app_resource_test.go index ade9bad6aac0..e4a1e48a5266 100644 --- a/internal/services/containerapps/container_app_resource_test.go +++ b/internal/services/containerapps/container_app_resource_test.go @@ -303,6 +303,57 @@ func TestAccContainerAppResource_secretRemoveWithAddShouldFail(t *testing.T) { }) } +func TestAccContainerAppResource_scaleRules(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_app", "test") + r := ContainerAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.scaleRules(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccContainerAppResource_scaleRulesUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_app", "test") + r := ContainerAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.scaleRules(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.scaleRulesUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basicWithRetainedSecret(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (r ContainerAppResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := containerapps.ParseContainerAppID(state.ID) if err != nil { @@ -342,6 +393,33 @@ resource "azurerm_container_app" "test" { `, r.template(data), data.RandomInteger) } +func (r ContainerAppResource) basicWithRetainedSecret(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_container_app" "test" { + name = "acctest-capp-%[2]d" + resource_group_name = azurerm_resource_group.test.name + container_app_environment_id = azurerm_container_app_environment.test.id + revision_mode = "Single" + + secret { + name = "queue-auth-secret" + value = "VGhpcyBJcyBOb3QgQSBHb29kIFBhc3N3b3JkCg==" + } + + template { + container { + name = "acctest-cont-%[2]d" + image = "jackofallops/azure-containerapps-python-acctest:v0.0.1" + cpu = 0.25 + memory = "0.5Gi" + } + } +} +`, r.template(data), data.RandomInteger) +} + func (r ContainerAppResource) withSystemIdentity(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -1325,6 +1403,145 @@ resource "azurerm_container_app" "test" { `, r.templatePlusExtras(data), data.RandomInteger, revisionSuffix) } +func (r ContainerAppResource) scaleRules(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_container_app" "test" { + name = "acctest-capp-%[2]d" + resource_group_name = azurerm_resource_group.test.name + container_app_environment_id = azurerm_container_app_environment.test.id + revision_mode = "Single" + + secret { + name = "queue-auth-secret" + value = "VGhpcyBJcyBOb3QgQSBHb29kIFBhc3N3b3JkCg==" + } + + template { + container { + name = "acctest-cont-%[2]d" + image = "jackofallops/azure-containerapps-python-acctest:v0.0.1" + cpu = 0.25 + memory = "0.5Gi" + } + + azure_queue_scale_rule { + name = "azq-1" + queue_name = "foo" + queue_length = 10 + + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } + } + + custom_scale_rule { + name = "csr-1" + custom_rule_type = "azure-monitor" + metadata = { + foo = "bar" + } + } + + http_scale_rule { + name = "http-1" + concurrent_requests = "100" + } + + tcp_scale_rule { + name = "tcp-1" + concurrent_requests = "1000" + } + } +} +`, r.template(data), data.RandomInteger) +} + +func (r ContainerAppResource) scaleRulesUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_container_app" "test" { + name = "acctest-capp-%[2]d" + resource_group_name = azurerm_resource_group.test.name + container_app_environment_id = azurerm_container_app_environment.test.id + revision_mode = "Single" + + secret { + name = "queue-auth-secret" + value = "VGhpcyBJcyBOb3QgQSBHb29kIFBhc3N3b3JkCg==" + } + + template { + container { + name = "acctest-cont-%[2]d" + image = "jackofallops/azure-containerapps-python-acctest:v0.0.1" + cpu = 0.25 + memory = "0.5Gi" + } + + azure_queue_scale_rule { + name = "azq-1" + queue_name = "foo" + queue_length = 10 + + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } + } + + azure_queue_scale_rule { + name = "azq-2" + queue_name = "bar" + queue_length = 20 + + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "another_password" + } + } + + custom_scale_rule { + name = "csr-1" + custom_rule_type = "rabbitmq" + + metadata = { + foo = "bar" + } + + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } + } + + http_scale_rule { + name = "http-1" + concurrent_requests = "200" + + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } + } + + tcp_scale_rule { + name = "tcp-1" + concurrent_requests = "1000" + + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } + } + } +} +`, r.template(data), data.RandomInteger) +} + func (ContainerAppResource) template(data acceptance.TestData) string { return ContainerAppEnvironmentResource{}.basic(data) } diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index f088b5508df8..8584503f08ff 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -669,11 +669,15 @@ func ContainerAppEnvironmentDaprMetadataSchema() *pluginsdk.Schema { } type ContainerTemplate struct { - Containers []Container `tfschema:"container"` - Suffix string `tfschema:"revision_suffix"` - MinReplicas int `tfschema:"min_replicas"` - MaxReplicas int `tfschema:"max_replicas"` - Volumes []ContainerVolume `tfschema:"volume"` + Containers []Container `tfschema:"container"` + Suffix string `tfschema:"revision_suffix"` + MinReplicas int `tfschema:"min_replicas"` + MaxReplicas int `tfschema:"max_replicas"` + AzureQueueScaleRules []AzureQueueScaleRule `tfschema:"azure_queue_scale_rule"` + CustomScaleRules []CustomScaleRule `tfschema:"custom_scale_rule"` + HTTPScaleRules []HTTPScaleRule `tfschema:"http_scale_rule"` + TCPScaleRules []TCPScaleRule `tfschema:"tcp_scale_rule"` + Volumes []ContainerVolume `tfschema:"volume"` } func ContainerTemplateSchema() *pluginsdk.Schema { @@ -701,6 +705,14 @@ func ContainerTemplateSchema() *pluginsdk.Schema { Description: "The maximum number of replicas for this container.", }, + "azure_queue_scale_rule": AzureQueueScaleRuleSchema(), + + "custom_scale_rule": CustomScaleRuleSchema(), + + "http_scale_rule": HTTPScaleRuleSchema(), + + "tcp_scale_rule": TCPScaleRuleSchema(), + "volume": ContainerVolumeSchema(), "revision_suffix": { @@ -734,12 +746,19 @@ func ContainerTemplateSchemaComputed() *pluginsdk.Schema { Description: "The maximum number of replicas for this container.", }, - "volume": ContainerVolumeSchema(), + "azure_queue_scale_rule": AzureQueueScaleRuleSchemaComputed(), + + "custom_scale_rule": CustomScaleRuleSchemaComputed(), + + "http_scale_rule": HTTPScaleRuleSchemaComputed(), + + "tcp_scale_rule": TCPScaleRuleSchemaComputed(), + + "volume": ContainerVolumeSchemaComputed(), "revision_suffix": { - Type: pluginsdk.TypeString, - Computed: true, - Description: "The suffix for the revision. This value must be unique for the lifetime of the Resource. If omitted the service will use a hash function to create one.", + Type: pluginsdk.TypeString, + Computed: true, }, }, }, @@ -771,6 +790,14 @@ func ExpandContainerAppTemplate(input []ContainerTemplate, metadata sdk.Resource template.Scale.MinReplicas = pointer.To(int64(config.MinReplicas)) } + if rules := config.expandContainerAppScaleRules(); len(rules) != 0 { + if template.Scale == nil { + template.Scale = &containerapps.Scale{} + } + + template.Scale.Rules = pointer.To(rules) + } + if config.Suffix != "" { if metadata.ResourceData.HasChange("template.0.revision_suffix") { template.RevisionSuffix = pointer.To(config.Suffix) @@ -793,6 +820,7 @@ func FlattenContainerAppTemplate(input *containerapps.Template) []ContainerTempl if scale := input.Scale; scale != nil { result.MaxReplicas = int(pointer.From(scale.MaxReplicas)) result.MinReplicas = int(pointer.From(scale.MinReplicas)) + result.flattenContainerAppScaleRules(scale.Rules) } return []ContainerTemplate{result} @@ -1074,6 +1102,31 @@ func ContainerVolumeSchema() *pluginsdk.Schema { } } +func ContainerVolumeSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "storage_type": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "storage_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + } +} + func expandContainerAppVolumes(input []ContainerVolume) *[]containerapps.Volume { if input == nil { return nil @@ -2347,3 +2400,617 @@ func ContainerAppProbesRemoved(metadata sdk.ResourceMetaData) bool { return !(hasLiveness || hasReadiness || hasStartup) } + +type AzureQueueScaleRule struct { + Name string `tfschema:"name"` + QueueLength int `tfschema:"queue_length"` + QueueName string `tfschema:"queue_name"` + Authentications []ScaleRuleAuthentication `tfschema:"authentication"` +} + +func AzureQueueScaleRuleSchema() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "queue_length": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(1), + }, + + "queue_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.SecretName, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + }, + } +} +func AzureQueueScaleRuleSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "queue_length": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "queue_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + } +} + +type CustomScaleRule struct { + Name string `tfschema:"name"` + Metadata map[string]string `tfschema:"metadata"` + CustomRuleType string `tfschema:"custom_rule_type"` + Authentications []ScaleRuleAuthentication `tfschema:"authentication"` +} + +func CustomScaleRuleSchema() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "metadata": { + Type: pluginsdk.TypeMap, + Required: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "custom_rule_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "activemq", "artemis-queue", "kafka", "pulsar", "aws-cloudwatch", + "aws-dynamodb", "aws-dynamodb-streams", "aws-kinesis-stream", "aws-sqs-queue", + "azure-app-insights", "azure-blob", "azure-data-explorer", "azure-eventhub", + "azure-log-analytics", "azure-monitor", "azure-pipelines", "azure-servicebus", + "azure-queue", "cassandra", "cpu", "cron", "datadog", "elasticsearch", "external", + "external-push", "gcp-stackdriver", "gcp-storage", "gcp-pubsub", "graphite", "http", + "huawei-cloudeye", "ibmmq", "influxdb", "kubernetes-workload", "liiklus", "memory", + "metrics-api", "mongodb", "mssql", "mysql", "nats-jetstream", "stan", "tcp", "new-relic", + "openstack-metric", "openstack-swift", "postgresql", "predictkube", "prometheus", + "rabbitmq", "redis", "redis-cluster", "redis-sentinel", "redis-streams", + "redis-cluster-streams", "redis-sentinel-streams", "selenium-grid", + "solace-event-queue", "github-runner", + }, false), // Note - this can be any KEDA compatible source in a user's environment + }, + + "authentication": { + Type: pluginsdk.TypeList, + Optional: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.SecretName, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + }, + } +} + +func CustomScaleRuleSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "metadata": { + Type: pluginsdk.TypeMap, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "custom_rule_type": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + } +} + +type HTTPScaleRule struct { + Name string `tfschema:"name"` + ConcurrentRequests string `tfschema:"concurrent_requests"` + Authentications []ScaleRuleAuthentication `tfschema:"authentication"` +} + +func HTTPScaleRuleSchema() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "concurrent_requests": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.ContainerAppScaleRuleConcurrentRequests, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Optional: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.SecretName, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + }, + } +} + +func HTTPScaleRuleSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "concurrent_requests": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + } +} + +type TCPScaleRule struct { + Name string `tfschema:"name"` + ConcurrentRequests string `tfschema:"concurrent_requests"` + Authentications []ScaleRuleAuthentication `tfschema:"authentication"` +} + +func TCPScaleRuleSchema() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "concurrent_requests": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.ContainerAppScaleRuleConcurrentRequests, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Optional: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.SecretName, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + }, + } +} + +func TCPScaleRuleSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "concurrent_requests": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + } +} + +type ScaleRuleAuthentication struct { + SecretRef string `tfschema:"secret_name"` + TriggerParam string `tfschema:"trigger_parameter"` +} + +func (c *ContainerTemplate) expandContainerAppScaleRules() []containerapps.ScaleRule { + if len(c.AzureQueueScaleRules) == 0 && len(c.CustomScaleRules) == 0 && len(c.HTTPScaleRules) == 0 && len(c.TCPScaleRules) == 0 { + return nil + } + result := make([]containerapps.ScaleRule, 0) + for _, v := range c.AzureQueueScaleRules { + r := containerapps.ScaleRule{ + Name: pointer.To(v.Name), + AzureQueue: &containerapps.QueueScaleRule{ + QueueLength: pointer.To(int64(v.QueueLength)), + QueueName: pointer.To(v.QueueName), + }, + } + + auths := make([]containerapps.ScaleRuleAuth, 0) + for _, a := range v.Authentications { + auth := containerapps.ScaleRuleAuth{ + TriggerParameter: pointer.To(a.TriggerParam), + SecretRef: pointer.To(a.SecretRef), + } + auths = append(auths, auth) + } + + r.AzureQueue.Auth = pointer.To(auths) + + result = append(result, r) + } + + for _, v := range c.CustomScaleRules { + r := containerapps.ScaleRule{ + Name: pointer.To(v.Name), + Custom: &containerapps.CustomScaleRule{ + Metadata: &v.Metadata, + Type: pointer.To(v.CustomRuleType), + }, + } + + auths := make([]containerapps.ScaleRuleAuth, 0) + for _, a := range v.Authentications { + auth := containerapps.ScaleRuleAuth{ + TriggerParameter: pointer.To(a.TriggerParam), + SecretRef: pointer.To(a.SecretRef), + } + auths = append(auths, auth) + } + + r.Custom.Auth = pointer.To(auths) + + result = append(result, r) + } + + for _, v := range c.HTTPScaleRules { + metaData := make(map[string]string, 0) + metaData["concurrentRequests"] = v.ConcurrentRequests + r := containerapps.ScaleRule{ + Name: pointer.To(v.Name), + HTTP: &containerapps.HTTPScaleRule{ + Metadata: pointer.To(metaData), + }, + } + + auths := make([]containerapps.ScaleRuleAuth, 0) + for _, a := range v.Authentications { + auth := containerapps.ScaleRuleAuth{ + TriggerParameter: pointer.To(a.TriggerParam), + SecretRef: pointer.To(a.SecretRef), + } + auths = append(auths, auth) + } + + r.HTTP.Auth = pointer.To(auths) + + result = append(result, r) + } + + for _, v := range c.TCPScaleRules { + metaData := make(map[string]string, 0) + metaData["concurrentRequests"] = v.ConcurrentRequests + r := containerapps.ScaleRule{ + Name: pointer.To(v.Name), + Tcp: &containerapps.TcpScaleRule{ + Metadata: pointer.To(metaData), + }, + } + + auths := make([]containerapps.ScaleRuleAuth, 0) + for _, a := range v.Authentications { + auth := containerapps.ScaleRuleAuth{ + TriggerParameter: pointer.To(a.TriggerParam), + SecretRef: pointer.To(a.SecretRef), + } + auths = append(auths, auth) + } + + r.Tcp.Auth = pointer.To(auths) + + result = append(result, r) + } + + return result +} + +func (c *ContainerTemplate) flattenContainerAppScaleRules(input *[]containerapps.ScaleRule) { + if input != nil && len(*input) != 0 { + rules := *input + azureQueueScaleRules := make([]AzureQueueScaleRule, 0) + customScaleRules := make([]CustomScaleRule, 0) + httpScaleRules := make([]HTTPScaleRule, 0) + tcpScaleRules := make([]TCPScaleRule, 0) + for _, v := range rules { + if q := v.AzureQueue; q != nil { + rule := AzureQueueScaleRule{ + Name: pointer.From(v.Name), + QueueLength: int(pointer.From(q.QueueLength)), + QueueName: pointer.From(q.QueueName), + } + + authentications := make([]ScaleRuleAuthentication, 0) + if auths := q.Auth; auths != nil { + for _, a := range *auths { + authentications = append(authentications, ScaleRuleAuthentication{ + SecretRef: pointer.From(a.SecretRef), + TriggerParam: pointer.From(a.TriggerParameter), + }) + } + } + + rule.Authentications = authentications + + azureQueueScaleRules = append(azureQueueScaleRules, rule) + continue + } + + if r := v.Custom; r != nil { + rule := CustomScaleRule{ + Name: pointer.From(v.Name), + Metadata: pointer.From(r.Metadata), + CustomRuleType: pointer.From(r.Type), + } + + authentications := make([]ScaleRuleAuthentication, 0) + if auths := r.Auth; auths != nil { + for _, a := range *auths { + authentications = append(authentications, ScaleRuleAuthentication{ + SecretRef: pointer.From(a.SecretRef), + TriggerParam: pointer.From(a.TriggerParameter), + }) + } + } + rule.Authentications = authentications + + customScaleRules = append(customScaleRules, rule) + continue + } + + if r := v.HTTP; r != nil { + metaData := pointer.From(r.Metadata) + concurrentReqs := "" + + if m, ok := metaData["concurrentRequests"]; ok { + concurrentReqs = m + } + + rule := HTTPScaleRule{ + Name: pointer.From(v.Name), + ConcurrentRequests: concurrentReqs, + } + + authentications := make([]ScaleRuleAuthentication, 0) + if auths := r.Auth; auths != nil { + for _, a := range *auths { + authentications = append(authentications, ScaleRuleAuthentication{ + SecretRef: pointer.From(a.SecretRef), + TriggerParam: pointer.From(a.TriggerParameter), + }) + } + } + + rule.Authentications = authentications + + httpScaleRules = append(httpScaleRules, rule) + continue + } + + if r := v.Tcp; r != nil { + metaData := pointer.From(r.Metadata) + concurrentReqs := "" + + if m, ok := metaData["concurrentRequests"]; ok { + concurrentReqs = m + } + + rule := TCPScaleRule{ + Name: pointer.From(v.Name), + ConcurrentRequests: concurrentReqs, + } + + authentications := make([]ScaleRuleAuthentication, 0) + if auths := r.Auth; auths != nil { + for _, a := range *auths { + authentications = append(authentications, ScaleRuleAuthentication{ + SecretRef: pointer.From(a.SecretRef), + TriggerParam: pointer.From(a.TriggerParameter), + }) + } + } + rule.Authentications = authentications + + tcpScaleRules = append(tcpScaleRules, rule) + continue + } + } + + c.AzureQueueScaleRules = azureQueueScaleRules + c.CustomScaleRules = customScaleRules + c.HTTPScaleRules = httpScaleRules + c.TCPScaleRules = tcpScaleRules + } +} diff --git a/internal/services/containerapps/validate/validate.go b/internal/services/containerapps/validate/validate.go index b6147f522772..8c43ef0fac75 100644 --- a/internal/services/containerapps/validate/validate.go +++ b/internal/services/containerapps/validate/validate.go @@ -6,6 +6,7 @@ package validate import ( "fmt" "regexp" + "strconv" "strings" ) @@ -138,3 +139,23 @@ func ContainerAppContainerName(i interface{}, k string) (warnings []string, erro } return } + +func ContainerAppScaleRuleConcurrentRequests(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return + } + + c, err := strconv.Atoi(v) + if err != nil { + errors = append(errors, fmt.Errorf("expected %s to be a string representation of an integer, got %+v", k, v)) + return + } + + if c <= 0 { + errors = append(errors, fmt.Errorf("value for %s must be at least `1`, got %d", k, c)) + } + + return +} diff --git a/internal/services/containerapps/validate/validate_test.go b/internal/services/containerapps/validate/validate_test.go index 183e2c93f8c2..44203f9dc88f 100644 --- a/internal/services/containerapps/validate/validate_test.go +++ b/internal/services/containerapps/validate/validate_test.go @@ -274,3 +274,49 @@ func TestValidateInitTimeout(t *testing.T) { } } } + +func TestContainerAppScaleRuleConcurrentRequests(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + { + Input: "5", + Valid: true, + }, + { + Input: "m", + Valid: false, + }, + { + Input: "6d", + Valid: false, + }, + { + Input: "10s", + Valid: false, + }, + { + Input: "1h", + Valid: false, + }, + { + Input: "1200s", + Valid: false, + }, + { + Input: "-1", + Valid: false, + }, + } + + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := ContainerAppScaleRuleConcurrentRequests(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t for %s", tc.Valid, valid, tc.Input) + } + } +} diff --git a/internal/services/monitor/monitor_diagnostic_setting_resource.go b/internal/services/monitor/monitor_diagnostic_setting_resource.go index 5be8bae6c9d8..338ebcfe66d5 100644 --- a/internal/services/monitor/monitor_diagnostic_setting_resource.go +++ b/internal/services/monitor/monitor_diagnostic_setting_resource.go @@ -129,7 +129,7 @@ func resourceMonitorDiagnosticSetting() *pluginsdk.Resource { Type: pluginsdk.TypeList, Optional: true, MaxItems: 1, - Deprecated: "`retention_policy` has been deprecated - to learn more https://aka.ms/diagnostic_settings_log_retention", + Deprecated: "`retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention", Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "enabled": { @@ -168,9 +168,10 @@ func resourceMonitorDiagnosticSetting() *pluginsdk.Resource { }, "retention_policy": { - Type: pluginsdk.TypeList, - Optional: true, - MaxItems: 1, + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Deprecated: "`retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention", Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "enabled": { @@ -218,9 +219,10 @@ func resourceMonitorDiagnosticSetting() *pluginsdk.Resource { }, "retention_policy": { - Type: pluginsdk.TypeList, - Optional: true, - MaxItems: 1, + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Deprecated: "`retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention", Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "enabled": { diff --git a/internal/services/postgres/postgresql_flexible_server_resource.go b/internal/services/postgres/postgresql_flexible_server_resource.go index fc345581f7ec..28f6be42abcd 100644 --- a/internal/services/postgres/postgresql_flexible_server_resource.go +++ b/internal/services/postgres/postgresql_flexible_server_resource.go @@ -129,7 +129,7 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { Type: pluginsdk.TypeInt, Optional: true, Computed: true, - ValidateFunc: validation.IntInSlice([]int{32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33553408}), + ValidateFunc: validation.IntInSlice([]int{32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4193280, 4194304, 8388608, 16777216, 33553408}), }, "version": { diff --git a/internal/services/storage/storage_account_resource.go b/internal/services/storage/storage_account_resource.go index 6c2052d63a06..0927a1e08c5e 100644 --- a/internal/services/storage/storage_account_resource.go +++ b/internal/services/storage/storage_account_resource.go @@ -189,12 +189,6 @@ func resourceStorageAccount() *pluginsdk.Resource { MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ - "storage_sid": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "domain_guid": { Type: pluginsdk.TypeString, Required: true, @@ -207,21 +201,27 @@ func resourceStorageAccount() *pluginsdk.Resource { ValidateFunc: validation.StringIsNotEmpty, }, + "storage_sid": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "domain_sid": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, ValidateFunc: validation.StringIsNotEmpty, }, "forest_name": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, ValidateFunc: validation.StringIsNotEmpty, }, "netbios_domain_name": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, ValidateFunc: validation.StringIsNotEmpty, }, }, @@ -1324,7 +1324,7 @@ func resourceStorageAccountCreate(d *pluginsdk.ResourceData, meta interface{}) e } blobClient := meta.(*clients.Client).Storage.BlobServicesClient - blobProperties, err := expandBlobProperties(val.([]interface{})) + blobProperties, err := expandBlobProperties(storage.Kind(accountKind), val.([]interface{})) if err != nil { return err } @@ -1794,7 +1794,7 @@ func resourceStorageAccountUpdate(d *pluginsdk.ResourceData, meta interface{}) e } blobClient := meta.(*clients.Client).Storage.BlobServicesClient - blobProperties, err := expandBlobProperties(d.Get("blob_properties").([]interface{})) + blobProperties, err := expandBlobProperties(storage.Kind(accountKind), d.Get("blob_properties").([]interface{})) if err != nil { return err } @@ -2198,7 +2198,7 @@ func resourceStorageAccountRead(d *pluginsdk.ResourceData, meta interface{}) err staticWebsiteProps, err := accountsClient.GetServiceProperties(ctx, id.StorageAccountName) if err != nil { - return fmt.Errorf("retrieving static website for %s: %+v", *id, err) + return fmt.Errorf("retrieving static website properties for %s: %+v", *id, err) } staticWebsite := flattenStaticWebsiteProperties(staticWebsiteProps) if err := d.Set("static_website", staticWebsite); err != nil { @@ -2462,14 +2462,31 @@ func expandArmStorageAccountAzureFilesAuthentication(input []interface{}) (*stor v := input[0].(map[string]interface{}) + ad := expandArmStorageAccountActiveDirectoryProperties(v["active_directory"].([]interface{})) + directoryOption := storage.DirectoryServiceOptions(v["directory_type"].(string)) - if _, ok := v["active_directory"]; directoryOption == storage.DirectoryServiceOptionsAD && !ok { - return nil, fmt.Errorf("`active_directory` is required when `directory_type` is `AD`") + + if directoryOption == storage.DirectoryServiceOptionsAD { + if ad == nil { + return nil, fmt.Errorf("`active_directory` is required when `directory_type` is `AD`") + } + if ad.AzureStorageSid == nil { + return nil, fmt.Errorf("`active_directory.0.storage_sid` is required when `directory_type` is `AD`") + } + if ad.DomainSid == nil { + return nil, fmt.Errorf("`active_directory.0.domain_sid` is required when `directory_type` is `AD`") + } + if ad.ForestName == nil { + return nil, fmt.Errorf("`active_directory.0.forest_name` is required when `directory_type` is `AD`") + } + if ad.NetBiosDomainName == nil { + return nil, fmt.Errorf("`active_directory.0.netbios_domain_name` is required when `directory_type` is `AD`") + } } return &storage.AzureFilesIdentityBasedAuthentication{ DirectoryServiceOptions: directoryOption, - ActiveDirectoryProperties: expandArmStorageAccountActiveDirectoryProperties(v["active_directory"].([]interface{})), + ActiveDirectoryProperties: ad, }, nil } @@ -2477,15 +2494,25 @@ func expandArmStorageAccountActiveDirectoryProperties(input []interface{}) *stor if len(input) == 0 { return nil } - v := input[0].(map[string]interface{}) - return &storage.ActiveDirectoryProperties{ - AzureStorageSid: utils.String(v["storage_sid"].(string)), - DomainGUID: utils.String(v["domain_guid"].(string)), - DomainName: utils.String(v["domain_name"].(string)), - DomainSid: utils.String(v["domain_sid"].(string)), - ForestName: utils.String(v["forest_name"].(string)), - NetBiosDomainName: utils.String(v["netbios_domain_name"].(string)), + m := input[0].(map[string]interface{}) + + output := &storage.ActiveDirectoryProperties{ + DomainGUID: utils.String(m["domain_guid"].(string)), + DomainName: utils.String(m["domain_name"].(string)), } + if v := m["storage_sid"]; v != "" { + output.AzureStorageSid = utils.String(v.(string)) + } + if v := m["domain_sid"]; v != "" { + output.DomainSid = utils.String(v.(string)) + } + if v := m["forest_name"]; v != "" { + output.ForestName = utils.String(v.(string)) + } + if v := m["netbios_domain_name"]; v != "" { + output.NetBiosDomainName = utils.String(v.(string)) + } + return output } func expandArmStorageAccountRouting(input []interface{}) *storage.RoutingPreference { @@ -2584,25 +2611,33 @@ func expandStorageAccountPrivateLinkAccess(inputs []interface{}, tenantId string return &privateLinkAccess } -func expandBlobProperties(input []interface{}) (*storage.BlobServiceProperties, error) { +func expandBlobProperties(kind storage.Kind, input []interface{}) (*storage.BlobServiceProperties, error) { props := storage.BlobServiceProperties{ BlobServicePropertiesProperties: &storage.BlobServicePropertiesProperties{ Cors: &storage.CorsRules{ CorsRules: &[]storage.CorsRule{}, }, - IsVersioningEnabled: utils.Bool(false), - ChangeFeed: &storage.ChangeFeed{ - Enabled: utils.Bool(false), - }, DeleteRetentionPolicy: &storage.DeleteRetentionPolicy{ Enabled: utils.Bool(false), }, - LastAccessTimeTrackingPolicy: &storage.LastAccessTimeTrackingPolicy{ - Enable: utils.Bool(false), - }, }, } + // `Storage` (v1) kind doesn't support: + // - LastAccessTimeTrackingPolicy: Confirmed by SRP. + // - ChangeFeed: See https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-change-feed?tabs=azure-portal#enable-and-disable-the-change-feed. + // - Versioning: See https://learn.microsoft.com/en-us/azure/storage/blobs/versioning-overview#how-blob-versioning-works + // - Restore Policy: See https://learn.microsoft.com/en-us/azure/storage/blobs/point-in-time-restore-overview#prerequisites-for-point-in-time-restore + if kind != storage.KindStorage { + props.LastAccessTimeTrackingPolicy = &storage.LastAccessTimeTrackingPolicy{ + Enable: utils.Bool(false), + } + props.ChangeFeed = &storage.ChangeFeed{ + Enabled: utils.Bool(false), + } + props.IsVersioningEnabled = utils.Bool(false) + } + if len(input) == 0 || input[0] == nil { return &props, nil } @@ -2615,28 +2650,53 @@ func expandBlobProperties(input []interface{}) (*storage.BlobServiceProperties, containerDeletePolicyRaw := v["container_delete_retention_policy"].([]interface{}) props.BlobServicePropertiesProperties.ContainerDeleteRetentionPolicy = expandBlobPropertiesDeleteRetentionPolicy(containerDeletePolicyRaw) - restorePolicyRaw := v["restore_policy"].([]interface{}) - props.BlobServicePropertiesProperties.RestorePolicy = expandBlobPropertiesRestorePolicy(restorePolicyRaw) - corsRaw := v["cors_rule"].([]interface{}) props.BlobServicePropertiesProperties.Cors = expandBlobPropertiesCors(corsRaw) props.IsVersioningEnabled = utils.Bool(v["versioning_enabled"].(bool)) - props.ChangeFeed = &storage.ChangeFeed{ - Enabled: utils.Bool(v["change_feed_enabled"].(bool)), - } - - if v := v["change_feed_retention_in_days"].(int); v != 0 { - props.ChangeFeed.RetentionInDays = utils.Int32((int32)(v)) - } - if version, ok := v["default_service_version"].(string); ok && version != "" { props.DefaultServiceVersion = utils.String(version) } - props.LastAccessTimeTrackingPolicy = &storage.LastAccessTimeTrackingPolicy{ - Enable: utils.Bool(v["last_access_time_enabled"].(bool)), + // `Storage` (v1) kind doesn't support: + // - LastAccessTimeTrackingPolicy + // - ChangeFeed + // - Versioning + // - RestorePolicy + lastAccessTimeEnabled := v["last_access_time_enabled"].(bool) + changeFeedEnabled := v["change_feed_enabled"].(bool) + changeFeedRetentionInDays := v["change_feed_retention_in_days"].(int) + restorePolicyRaw := v["restore_policy"].([]interface{}) + versioningEnabled := v["versioning_enabled"].(bool) + if kind != storage.KindStorage { + props.BlobServicePropertiesProperties.LastAccessTimeTrackingPolicy = &storage.LastAccessTimeTrackingPolicy{ + Enable: utils.Bool(lastAccessTimeEnabled), + } + props.BlobServicePropertiesProperties.ChangeFeed = &storage.ChangeFeed{ + Enabled: utils.Bool(changeFeedEnabled), + } + if changeFeedRetentionInDays != 0 { + props.BlobServicePropertiesProperties.ChangeFeed.RetentionInDays = utils.Int32(int32(changeFeedRetentionInDays)) + } + props.BlobServicePropertiesProperties.RestorePolicy = expandBlobPropertiesRestorePolicy(restorePolicyRaw) + props.BlobServicePropertiesProperties.IsVersioningEnabled = &versioningEnabled + } else { + if lastAccessTimeEnabled { + return nil, fmt.Errorf("`last_access_time_enabled` can not be configured when `kind` is set to `Storage` (v1)") + } + if changeFeedEnabled { + return nil, fmt.Errorf("`change_feed_enabled` can not be configured when `kind` is set to `Storage` (v1)") + } + if changeFeedRetentionInDays != 0 { + return nil, fmt.Errorf("`change_feed_retention_in_days` can not be configured when `kind` is set to `Storage` (v1)") + } + if len(restorePolicyRaw) != 0 { + return nil, fmt.Errorf("`restore_policy` can not be configured when `kind` is set to `Storage` (v1)") + } + if versioningEnabled { + return nil, fmt.Errorf("`versioning_enabled` can not be configured when `kind` is set to `Storage` (v1)") + } } // Sanity check for the prerequisites of restore_policy diff --git a/internal/services/storage/storage_account_resource_test.go b/internal/services/storage/storage_account_resource_test.go index 474b95a0d4da..75e1b987bd0c 100644 --- a/internal/services/storage/storage_account_resource_test.go +++ b/internal/services/storage/storage_account_resource_test.go @@ -695,6 +695,21 @@ func TestAccStorageAccount_blobPropertiesEmptyAllowedExposedHeaders(t *testing.T }) } +func TestAccStorageAccount_blobProperties_kindStorageNotSupportLastAccessTimeEnabled(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_account", "test") + r := StorageAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.blobPropertiesStorageKindNotSupportLastAccessTimeEnabled(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccStorageAccount_queueProperties(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_account", "test") r := StorageAccountResource{} @@ -909,6 +924,18 @@ func TestAccAzureRMStorageAccount_azureFilesAuthentication(t *testing.T) { ), }, data.ImportStep(), + { + Config: r.azureFilesAuthenticationAADKERBUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep( + "azure_files_authentication.0.active_directory.0.storage_sid", + "azure_files_authentication.0.active_directory.0.domain_sid", + "azure_files_authentication.0.active_directory.0.forest_name", + "azure_files_authentication.0.active_directory.0.netbios_domain_name", + ), { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( @@ -3287,6 +3314,48 @@ resource "azurerm_storage_account" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString) } +func (r StorageAccountResource) azureFilesAuthenticationAADKERBUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-storage-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "unlikely23exst2acct%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" + + azure_files_authentication { + directory_type = "AADKERB" + active_directory { + domain_name = "adtest2.com" + domain_guid = "13a20c9a-d491-47e6-8a39-299e7a32ea27" + } + } + + tags = { + environment = "production" + } + + lifecycle { + ignore_changes = [ + azure_files_authentication.0.active_directory.0.storage_sid, + azure_files_authentication.0.active_directory.0.domain_sid, + azure_files_authentication.0.active_directory.0.forest_name, + azure_files_authentication.0.active_directory.0.netbios_domain_name, + ] + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} + func (r StorageAccountResource) routing(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -4409,3 +4478,52 @@ resource "azurerm_storage_account" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomString) } + +func (r StorageAccountResource) blobPropertiesStorageKindNotSupportLastAccessTimeEnabled(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestAzureRMSA-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "unlikely23exst2acct%s" + resource_group_name = azurerm_resource_group.test.name + + location = azurerm_resource_group.test.location + account_kind = "Storage" + account_tier = "Standard" + account_replication_type = "LRS" + + blob_properties { + cors_rule { + allowed_origins = ["http://www.example.com"] + exposed_headers = ["x-tempo-*"] + allowed_headers = ["x-tempo-*"] + allowed_methods = ["GET", "PUT", "PATCH"] + max_age_in_seconds = "500" + } + + delete_retention_policy { + days = 300 + } + + default_service_version = "2019-07-07" + container_delete_retention_policy { + days = 7 + } + + # Following properties are not supported for "Storage" (v1) kind + last_access_time_enabled = false + change_feed_enabled = false + versioning_enabled = false + # change_feed_retention_in_days + # restore_policy + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} diff --git a/vendor/github.com/rickb777/date/period/arithmetic.go b/vendor/github.com/rickb777/date/period/arithmetic.go deleted file mode 100644 index 55993fe0b744..000000000000 --- a/vendor/github.com/rickb777/date/period/arithmetic.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2015 Rick Beton. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package period - -import ( - "time" -) - -// Add adds two periods together. Use this method along with Negate in order to subtract periods. -// -// The result is not normalised and may overflow arithmetically (to make this unlikely, use Normalise on -// the inputs before adding them). -func (period Period) Add(that Period) Period { - return Period{ - period.years + that.years, - period.months + that.months, - period.days + that.days, - period.hours + that.hours, - period.minutes + that.minutes, - period.seconds + that.seconds, - } -} - -//------------------------------------------------------------------------------------------------- - -// AddTo adds the period to a time, returning the result. -// A flag is also returned that is true when the conversion was precise and false otherwise. -// -// When the period specifies hours, minutes and seconds only, the result is precise. -// Also, when the period specifies whole years, months and days (i.e. without fractions), the -// result is precise. However, when years, months or days contains fractions, the result -// is only an approximation (it assumes that all days are 24 hours and every year is 365.2425 -// days, as per Gregorian calendar rules). -func (period Period) AddTo(t time.Time) (time.Time, bool) { - wholeYears := (period.years % 10) == 0 - wholeMonths := (period.months % 10) == 0 - wholeDays := (period.days % 10) == 0 - - if wholeYears && wholeMonths && wholeDays { - // in this case, time.AddDate provides an exact solution - stE3 := totalSecondsE3(period) - t1 := t.AddDate(int(period.years/10), int(period.months/10), int(period.days/10)) - return t1.Add(stE3 * time.Millisecond), true - } - - d, precise := period.Duration() - return t.Add(d), precise -} - -//------------------------------------------------------------------------------------------------- - -// Scale a period by a multiplication factor. Obviously, this can both enlarge and shrink it, -// and change the sign if negative. The result is normalised, but integer overflows are silently -// ignored. -// -// Bear in mind that the internal representation is limited by fixed-point arithmetic with two -// decimal places; each field is only int16. -// -// Known issue: scaling by a large reduction factor (i.e. much less than one) doesn't work properly. -func (period Period) Scale(factor float32) Period { - result, _ := period.ScaleWithOverflowCheck(factor) - return result -} - -// ScaleWithOverflowCheck a period by a multiplication factor. Obviously, this can both enlarge and shrink it, -// and change the sign if negative. The result is normalised. An error is returned if integer overflow -// happened. -// -// Bear in mind that the internal representation is limited by fixed-point arithmetic with one -// decimal place; each field is only int16. -// -// Known issue: scaling by a large reduction factor (i.e. much less than one) doesn't work properly. -func (period Period) ScaleWithOverflowCheck(factor float32) (Period, error) { - ap, neg := period.absNeg() - - if -0.5 < factor && factor < 0.5 { - d, pr1 := ap.Duration() - mul := float64(d) * float64(factor) - p2, pr2 := NewOf(time.Duration(mul)) - return p2.Normalise(pr1 && pr2), nil - } - - y := int64(float32(ap.years) * factor) - m := int64(float32(ap.months) * factor) - d := int64(float32(ap.days) * factor) - hh := int64(float32(ap.hours) * factor) - mm := int64(float32(ap.minutes) * factor) - ss := int64(float32(ap.seconds) * factor) - - p64 := &period64{years: y, months: m, days: d, hours: hh, minutes: mm, seconds: ss, neg: neg} - return p64.normalise64(true).toPeriod() -} - -// RationalScale scales a period by a rational multiplication factor. Obviously, this can both enlarge and shrink it, -// and change the sign if negative. The result is normalised. An error is returned if integer overflow -// happened. -// -// If the divisor is zero, a panic will arise. -// -// Bear in mind that the internal representation is limited by fixed-point arithmetic with two -// decimal places; each field is only int16. -//func (period Period) RationalScale(multiplier, divisor int) (Period, error) { -// return period.rationalScale64(int64(multiplier), int64(divisor)) -//} diff --git a/vendor/github.com/rickb777/date/period/designator.go b/vendor/github.com/rickb777/date/period/designator.go deleted file mode 100644 index 6d836ec4c638..000000000000 --- a/vendor/github.com/rickb777/date/period/designator.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015 Rick Beton. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package period - -type ymdDesignator byte -type hmsDesignator byte - -const ( - Year ymdDesignator = 'Y' - Month ymdDesignator = 'M' - Week ymdDesignator = 'W' - Day ymdDesignator = 'D' - - Hour hmsDesignator = 'H' - Minute hmsDesignator = 'M' - Second hmsDesignator = 'S' -) - -func (d ymdDesignator) IsOneOf(xx ...ymdDesignator) bool { - for _, x := range xx { - if x == d { - return true - } - } - return false -} - -func (d ymdDesignator) IsNotOneOf(xx ...ymdDesignator) bool { - for _, x := range xx { - if x == d { - return false - } - } - return true -} - -func (d hmsDesignator) IsOneOf(xx ...hmsDesignator) bool { - for _, x := range xx { - if x == d { - return true - } - } - return false -} - -func (d hmsDesignator) IsNotOneOf(xx ...hmsDesignator) bool { - for _, x := range xx { - if x == d { - return false - } - } - return true -} diff --git a/vendor/github.com/rickb777/date/period/doc.go b/vendor/github.com/rickb777/date/period/doc.go index 5abe8447b16c..c8a57b4a0d94 100644 --- a/vendor/github.com/rickb777/date/period/doc.go +++ b/vendor/github.com/rickb777/date/period/doc.go @@ -5,9 +5,6 @@ // Package period provides functionality for periods of time using ISO-8601 conventions. // This deals with years, months, weeks/days, hours, minutes and seconds. // -// *** Warning: this package is the subject of many issues, so a replacement is under -// development. Please see https://github.com/rickb777/period. -// // Because of the vagaries of calendar systems, the meaning of year lengths, month lengths // and even day lengths depends on context. So a period is not necessarily a fixed duration // of time in terms of seconds. @@ -43,4 +40,5 @@ // * "P2.5Y" is 2.5 years. // // * "PT12M7.5S" is 12 minutes and 7.5 seconds. +// package period diff --git a/vendor/github.com/rickb777/date/period/flag.go b/vendor/github.com/rickb777/date/period/flag.go deleted file mode 100644 index 9488efa7ca8d..000000000000 --- a/vendor/github.com/rickb777/date/period/flag.go +++ /dev/null @@ -1,16 +0,0 @@ -package period - -// Set enables use of Period by the flag API. -func (period *Period) Set(p string) error { - p2, err := Parse(p) - if err != nil { - return err - } - *period = p2 - return nil -} - -// Type is for compatibility with the spf13/pflag library. -func (period Period) Type() string { - return "period" -} diff --git a/vendor/github.com/rickb777/date/period/format.go b/vendor/github.com/rickb777/date/period/format.go index 3b095292674e..32cb6a8311f7 100644 --- a/vendor/github.com/rickb777/date/period/format.go +++ b/vendor/github.com/rickb777/date/period/format.go @@ -5,32 +5,25 @@ package period import ( + "bytes" "fmt" - "io" "strings" "github.com/rickb777/plural" ) // Format converts the period to human-readable form using the default localisation. -// Multiples of 7 days are shown as weeks. func (period Period) Format() string { return period.FormatWithPeriodNames(PeriodYearNames, PeriodMonthNames, PeriodWeekNames, PeriodDayNames, PeriodHourNames, PeriodMinuteNames, PeriodSecondNames) } -// FormatWithoutWeeks converts the period to human-readable form using the default localisation. -// Multiples of 7 days are not shown as weeks. -func (period Period) FormatWithoutWeeks() string { - return period.FormatWithPeriodNames(PeriodYearNames, PeriodMonthNames, plural.Plurals{}, PeriodDayNames, PeriodHourNames, PeriodMinuteNames, PeriodSecondNames) -} - // FormatWithPeriodNames converts the period to human-readable form in a localisable way. func (period Period) FormatWithPeriodNames(yearNames, monthNames, weekNames, dayNames, hourNames, minNames, secNames plural.Plurals) string { period = period.Abs() parts := make([]string, 0) - parts = appendNonBlank(parts, yearNames.FormatFloat(float10(period.years))) - parts = appendNonBlank(parts, monthNames.FormatFloat(float10(period.months))) + parts = appendNonBlank(parts, yearNames.FormatFloat(absFloat10(period.years))) + parts = appendNonBlank(parts, monthNames.FormatFloat(absFloat10(period.months))) if period.days > 0 || (period.IsZero()) { if len(weekNames) > 0 { @@ -41,15 +34,15 @@ func (period Period) FormatWithPeriodNames(yearNames, monthNames, weekNames, day parts = appendNonBlank(parts, weekNames.FormatInt(int(weeks))) } if mdays > 0 || weeks == 0 { - parts = appendNonBlank(parts, dayNames.FormatFloat(float10(mdays))) + parts = appendNonBlank(parts, dayNames.FormatFloat(absFloat10(mdays))) } } else { - parts = appendNonBlank(parts, dayNames.FormatFloat(float10(period.days))) + parts = appendNonBlank(parts, dayNames.FormatFloat(absFloat10(period.days))) } } - parts = appendNonBlank(parts, hourNames.FormatFloat(float10(period.hours))) - parts = appendNonBlank(parts, minNames.FormatFloat(float10(period.minutes))) - parts = appendNonBlank(parts, secNames.FormatFloat(float10(period.seconds))) + parts = appendNonBlank(parts, hourNames.FormatFloat(absFloat10(period.hours))) + parts = appendNonBlank(parts, minNames.FormatFloat(absFloat10(period.minutes))) + parts = appendNonBlank(parts, secNames.FormatFloat(absFloat10(period.seconds))) return strings.Join(parts, ", ") } @@ -86,54 +79,50 @@ var PeriodSecondNames = plural.FromZero("", "%v second", "%v seconds") // String converts the period to ISO-8601 form. func (period Period) String() string { - return period.toPeriod64("").String() -} - -func (p64 period64) String() string { - if p64 == (period64{}) { + if period.IsZero() { return "P0D" } - buf := &strings.Builder{} - if p64.neg { + buf := &bytes.Buffer{} + if period.Sign() < 0 { buf.WriteByte('-') } buf.WriteByte('P') - writeField64(buf, p64.years, byte(Year)) - writeField64(buf, p64.months, byte(Month)) - - if p64.days != 0 { - if p64.days%70 == 0 { - writeField64(buf, p64.days/7, byte(Week)) + if period.years != 0 { + fmt.Fprintf(buf, "%gY", absFloat10(period.years)) + } + if period.months != 0 { + fmt.Fprintf(buf, "%gM", absFloat10(period.months)) + } + if period.days != 0 { + if period.days%70 == 0 { + fmt.Fprintf(buf, "%gW", absFloat10(period.days/7)) } else { - writeField64(buf, p64.days, byte(Day)) + fmt.Fprintf(buf, "%gD", absFloat10(period.days)) } } - - if p64.hours != 0 || p64.minutes != 0 || p64.seconds != 0 { + if period.hours != 0 || period.minutes != 0 || period.seconds != 0 { buf.WriteByte('T') } - - writeField64(buf, p64.hours, byte(Hour)) - writeField64(buf, p64.minutes, byte(Minute)) - writeField64(buf, p64.seconds, byte(Second)) + if period.hours != 0 { + fmt.Fprintf(buf, "%gH", absFloat10(period.hours)) + } + if period.minutes != 0 { + fmt.Fprintf(buf, "%gM", absFloat10(period.minutes)) + } + if period.seconds != 0 { + fmt.Fprintf(buf, "%gS", absFloat10(period.seconds)) + } return buf.String() } -func writeField64(w io.Writer, field int64, designator byte) { - if field != 0 { - if field%10 != 0 { - fmt.Fprintf(w, "%g", float32(field)/10) - } else { - fmt.Fprintf(w, "%d", field/10) - } - w.(io.ByteWriter).WriteByte(designator) +func absFloat10(v int16) float32 { + f := float32(v) / 10 + if v < 0 { + return -f } -} - -func float10(v int16) float32 { - return float32(v) / 10 + return f } diff --git a/vendor/github.com/rickb777/date/period/marshal.go b/vendor/github.com/rickb777/date/period/marshal.go index 6e1f2f1ce99b..c87ad5f6fb90 100644 --- a/vendor/github.com/rickb777/date/period/marshal.go +++ b/vendor/github.com/rickb777/date/period/marshal.go @@ -12,21 +12,19 @@ func (period Period) MarshalBinary() ([]byte, error) { } // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. -// This also provides support for gob decoding. +// This also provides support for gob encoding. func (period *Period) UnmarshalBinary(data []byte) error { return period.UnmarshalText(data) } // MarshalText implements the encoding.TextMarshaler interface for Periods. -// This also provides support for JSON encoding. func (period Period) MarshalText() ([]byte, error) { return []byte(period.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for Periods. -// This also provides support for JSON decoding. func (period *Period) UnmarshalText(data []byte) (err error) { - u, err := Parse(string(data), false) + u, err := Parse(string(data)) if err == nil { *period = u } diff --git a/vendor/github.com/rickb777/date/period/parse.go b/vendor/github.com/rickb777/date/period/parse.go index 304bc5017227..270ae8d092df 100644 --- a/vendor/github.com/rickb777/date/period/parse.go +++ b/vendor/github.com/rickb777/date/period/parse.go @@ -12,10 +12,8 @@ import ( // MustParse is as per Parse except that it panics if the string cannot be parsed. // This is intended for setup code; don't use it for user inputs. -// By default, the value is normalised. -// Normalisation can be disabled using the optional flag. -func MustParse(value string, normalise ...bool) Period { - d, err := Parse(value, normalise...) +func MustParse(value string) Period { + d, err := Parse(value) if err != nil { panic(err) } @@ -26,27 +24,16 @@ func MustParse(value string, normalise ...bool) Period { // // In addition, a plus or minus sign can precede the period, e.g. "-P10D" // -// By default, the value is normalised, e.g. multiple of 12 months become years -// so "P24M" is the same as "P2Y". However, this is done without loss of precision, -// so for example whole numbers of days do not contribute to the months tally +// The value is normalised, e.g. multiple of 12 months become years so "P24M" +// is the same as "P2Y". However, this is done without loss of precision, so +// for example whole numbers of days do not contribute to the months tally // because the number of days per month is variable. // -// Normalisation can be disabled using the optional flag. -// // The zero value can be represented in several ways: all of the following // are equivalent: "P0Y", "P0M", "P0W", "P0D", "PT0H", PT0M", PT0S", and "P0". // The canonical zero is "P0D". -func Parse(period string, normalise ...bool) (Period, error) { - return ParseWithNormalise(period, len(normalise) == 0 || normalise[0]) -} - -// ParseWithNormalise parses strings that specify periods using ISO-8601 rules -// with an option to specify whether to normalise parsed period components. -// -// This method is deprecated and should not be used. It may be removed in a -// future version. -func ParseWithNormalise(period string, normalise bool) (Period, error) { - if period == "" || period == "-" || period == "+" { +func Parse(period string) (Period, error) { + if period == "" { return Period{}, fmt.Errorf("cannot parse a blank string as a period") } @@ -54,184 +41,118 @@ func ParseWithNormalise(period string, normalise bool) (Period, error) { return Period{}, nil } - p64, err := parse(period, normalise) - if err != nil { - return Period{}, err - } - return p64.toPeriod() -} - -func parse(period string, normalise bool) (*period64, error) { - neg := false - remaining := period - if remaining[0] == '-' { - neg = true - remaining = remaining[1:] - } else if remaining[0] == '+' { - remaining = remaining[1:] + result := period64{} + pcopy := period + if pcopy[0] == '-' { + result.neg = true + pcopy = pcopy[1:] + } else if pcopy[0] == '+' { + pcopy = pcopy[1:] } - if remaining[0] != 'P' { - return nil, fmt.Errorf("%s: expected 'P' period mark at the start", period) + if pcopy[0] != 'P' { + return Period{}, fmt.Errorf("expected 'P' period mark at the start: %s", period) } - remaining = remaining[1:] - - var number, weekValue, prevFraction int64 - result := &period64{input: period, neg: neg} - var years, months, weeks, days, hours, minutes, seconds itemState - var designator, prevDesignator byte - var err error - nComponents := 0 + pcopy = pcopy[1:] - years, months, weeks, days = Armed, Armed, Armed, Armed + st := parseState{period, pcopy, false, nil} + t := strings.IndexByte(pcopy, 'T') + if t >= 0 { + st.pcopy = pcopy[t+1:] - isHMS := false - for len(remaining) > 0 { - if remaining[0] == 'T' { - if isHMS { - return nil, fmt.Errorf("%s: 'T' designator cannot occur more than once", period) - } - isHMS = true + result.hours, st = parseField(st, 'H') + if st.err != nil { + return Period{}, fmt.Errorf("expected a number before the 'H' marker: %s", period) + } - years, months, weeks, days = Unready, Unready, Unready, Unready - hours, minutes, seconds = Armed, Armed, Armed + result.minutes, st = parseField(st, 'M') + if st.err != nil { + return Period{}, fmt.Errorf("expected a number before the 'M' marker: %s", period) + } - remaining = remaining[1:] + result.seconds, st = parseField(st, 'S') + if st.err != nil { + return Period{}, fmt.Errorf("expected a number before the 'S' marker: %s", period) + } - } else { - number, designator, remaining, err = parseNextField(remaining, period) - if err != nil { - return nil, err - } - - fraction := number % 10 - if prevFraction != 0 && fraction != 0 { - return nil, fmt.Errorf("%s: '%c' & '%c' only the last field can have a fraction", period, prevDesignator, designator) - } - - switch designator { - case 'Y': - years, err = years.testAndSet(number, 'Y', result, &result.years) - case 'W': - weeks, err = weeks.testAndSet(number, 'W', result, &weekValue) - case 'D': - days, err = days.testAndSet(number, 'D', result, &result.days) - case 'H': - hours, err = hours.testAndSet(number, 'H', result, &result.hours) - case 'S': - seconds, err = seconds.testAndSet(number, 'S', result, &result.seconds) - case 'M': - if isHMS { - minutes, err = minutes.testAndSet(number, 'M', result, &result.minutes) - } else { - months, err = months.testAndSet(number, 'M', result, &result.months) - } - default: - return nil, fmt.Errorf("%s: expected a number not '%c'", period, designator) - } - nComponents++ - - if err != nil { - return nil, err - } - - prevFraction = fraction - prevDesignator = designator + if len(st.pcopy) != 0 { + return Period{}, fmt.Errorf("unexpected remaining components %s: %s", st.pcopy, period) } - } - if nComponents == 0 { - return nil, fmt.Errorf("%s: expected 'Y', 'M', 'W', 'D', 'H', 'M', or 'S' designator", period) + st.pcopy = pcopy[:t] } - result.days += weekValue * 7 - - if normalise { - result = result.normalise64(true) + result.years, st = parseField(st, 'Y') + if st.err != nil { + return Period{}, fmt.Errorf("expected a number before the 'Y' marker: %s", period) + } + result.months, st = parseField(st, 'M') + if st.err != nil { + return Period{}, fmt.Errorf("expected a number before the 'M' marker: %s", period) + } + weeks, st := parseField(st, 'W') + if st.err != nil { + return Period{}, fmt.Errorf("expected a number before the 'W' marker: %s", period) } - return result, nil -} - -//------------------------------------------------------------------------------------------------- + days, st := parseField(st, 'D') + if st.err != nil { + return Period{}, fmt.Errorf("expected a number before the 'D' marker: %s", period) + } -type itemState int + if len(st.pcopy) != 0 { + return Period{}, fmt.Errorf("unexpected remaining components %s: %s", st.pcopy, period) + } -const ( - Unready itemState = iota - Armed - Set -) + result.days = weeks*7 + days + //fmt.Printf("%#v\n", st) -func (i itemState) testAndSet(number int64, designator byte, result *period64, value *int64) (itemState, error) { - switch i { - case Unready: - return i, fmt.Errorf("%s: '%c' designator cannot occur here", result.input, designator) - case Set: - return i, fmt.Errorf("%s: '%c' designator cannot occur more than once", result.input, designator) + if !st.ok { + return Period{}, fmt.Errorf("expected 'Y', 'M', 'W', 'D', 'H', 'M', or 'S' marker: %s", period) } - *value = number - return Set, nil + return result.normalise64(true).toPeriod(), nil } -//------------------------------------------------------------------------------------------------- +type parseState struct { + period, pcopy string + ok bool + err error +} -func parseNextField(str, original string) (int64, byte, string, error) { - i := scanDigits(str) - if i < 0 { - return 0, 0, "", fmt.Errorf("%s: missing designator at the end", original) +func parseField(st parseState, mark byte) (int64, parseState) { + //fmt.Printf("%c %#v\n", mark, st) + r := int64(0) + m := strings.IndexByte(st.pcopy, mark) + if m > 0 { + r, st.err = parseDecimalFixedPoint(st.pcopy[:m], st.period) + if st.err != nil { + return 0, st + } + st.pcopy = st.pcopy[m+1:] + st.ok = true } - - des := str[i] - number, err := parseDecimalNumber(str[:i], original, des) - return number, des, str[i+1:], err + return r, st } -// Fixed-point one decimal place -func parseDecimalNumber(number, original string, des byte) (int64, error) { - dec := strings.IndexByte(number, '.') +// Fixed-point three decimal places +func parseDecimalFixedPoint(s, original string) (int64, error) { + //was := s + dec := strings.IndexByte(s, '.') if dec < 0 { - dec = strings.IndexByte(number, ',') + dec = strings.IndexByte(s, ',') } - var integer, fraction int64 - var err error if dec >= 0 { - integer, err = strconv.ParseInt(number[:dec], 10, 64) - if err == nil { - number = number[dec+1:] - switch len(number) { - case 0: // skip - case 1: - fraction, err = strconv.ParseInt(number, 10, 64) - default: - fraction, err = strconv.ParseInt(number[:1], 10, 64) - } + dp := len(s) - dec + if dp > 1 { + s = s[:dec] + s[dec+1:dec+2] + } else { + s = s[:dec] + s[dec+1:] + "0" } } else { - integer, err = strconv.ParseInt(number, 10, 64) + s = s + "0" } - if err != nil { - return 0, fmt.Errorf("%s: expected a number but found '%c'", original, des) - } - - return integer*10 + fraction, err -} - -// scanDigits finds the first non-digit byte after a given starting point. -// Note that it does not care about runes or UTF-8 encoding; it assumes that -// a period string is always valid ASCII as well as UTF-8. -func scanDigits(s string) int { - for i, c := range s { - if !isDigit(c) { - return i - } - } - return -1 -} - -func isDigit(c rune) bool { - return ('0' <= c && c <= '9') || c == '.' || c == ',' + return strconv.ParseInt(s, 10, 64) } diff --git a/vendor/github.com/rickb777/date/period/period.go b/vendor/github.com/rickb777/date/period/period.go index 7eb2902a0f6f..a9b39c6a0d6b 100644 --- a/vendor/github.com/rickb777/date/period/period.go +++ b/vendor/github.com/rickb777/date/period/period.go @@ -9,14 +9,14 @@ import ( "time" ) -const daysPerYearE4 = 3652425 // 365.2425 days by the Gregorian rule -const daysPerMonthE4 = 304369 // 30.4369 days per month -const daysPerMonthE6 = 30436875 // 30.436875 days per month +const daysPerYearE4 int64 = 3652425 // 365.2425 days by the Gregorian rule +const daysPerMonthE4 int64 = 304369 // 30.4369 days per month +const daysPerMonthE6 int64 = 30436875 // 30.436875 days per month -const oneE4 = 10000 -const oneE5 = 100000 -const oneE6 = 1000000 -const oneE7 = 10000000 +const oneE4 int64 = 10000 +const oneE5 int64 = 100000 +const oneE6 int64 = 1000000 +const oneE7 int64 = 10000000 const hundredMs = 100 * time.Millisecond @@ -42,6 +42,7 @@ const hundredMs = 100 * time.Millisecond // // Note that although fractional weeks can be parsed, they will never be returned via String(). // This is because the number of weeks is always inferred from the number of days. +// type Period struct { years, months, days, hours, minutes, seconds int16 } @@ -51,8 +52,6 @@ type Period struct { // need to. // // All the parameters must have the same sign (otherwise a panic occurs). -// Because this implementation uses int16 internally, the paramters must -// be within the range ± 2^16 / 10. func NewYMD(years, months, days int) Period { return New(years, months, days, 0, 0, 0) } @@ -62,8 +61,6 @@ func NewYMD(years, months, days int) Period { // if you need to. // // All the parameters must have the same sign (otherwise a panic occurs). -// Because this implementation uses int16 internally, the paramters must -// be within the range ± 2^16 / 10. func NewHMS(hours, minutes, seconds int) Period { return New(0, 0, 0, hours, minutes, seconds) } @@ -85,6 +82,8 @@ func New(years, months, days, hours, minutes, seconds int) Period { years, months, days, hours, minutes, seconds)) } +// TODO NewFloat + // NewOf converts a time duration to a Period, and also indicates whether the conversion is precise. // Any time duration that spans more than ± 3276 hours will be approximated by assuming that there // are 24 hours per day, 365.2425 days per year (as per Gregorian calendar rules), and a month @@ -143,15 +142,15 @@ func NewOf(duration time.Duration) (p Period, precise bool) { // computations applied to the period can only be precise if they concern either the date (year, month, // day) part, or the clock (hour, minute, second) part, but not both. func Between(t1, t2 time.Time) (p Period) { + if t1.Location() != t2.Location() { + t2 = t2.In(t1.Location()) + } + sign := 1 if t2.Before(t1) { t1, t2, sign = t2, t1, -1 } - if t1.Location() != t2.Location() { - t2 = t2.In(t1.Location()) - } - year, month, day, hour, min, sec, hundredth := daysDiff(t1, t2) if sign < 0 { @@ -172,9 +171,9 @@ func daysDiff(t1, t2 time.Time) (year, month, day, hour, min, sec, hundredth int day = int(duration / (24 * time.Hour)) - hour = hh2 - hh1 - min = mm2 - mm1 - sec = ss2 - ss1 + hour = int(hh2 - hh1) + min = int(mm2 - mm1) + sec = int(ss2 - ss1) hundredth = (t2.Nanosecond() - t1.Nanosecond()) / 100000000 // Normalize negative values @@ -249,22 +248,15 @@ func (period Period) OnlyHMS() Period { // Abs converts a negative period to a positive one. func (period Period) Abs() Period { - a, _ := period.absNeg() - return a + return Period{absInt16(period.years), absInt16(period.months), absInt16(period.days), + absInt16(period.hours), absInt16(period.minutes), absInt16(period.seconds)} } -func (period Period) absNeg() (Period, bool) { - if period.IsNegative() { - return period.Negate(), true - } - return period, false -} - -func (period Period) condNegate(neg bool) Period { - if neg { - return period.Negate() +func absInt16(v int16) int16 { + if v < 0 { + return -v } - return period + return v } // Negate changes the sign of the period. @@ -272,11 +264,45 @@ func (period Period) Negate() Period { return Period{-period.years, -period.months, -period.days, -period.hours, -period.minutes, -period.seconds} } -func absInt16(v int16) int16 { - if v < 0 { - return -v +// Add adds two periods together. Use this method along with Negate in order to subtract periods. +// +// The result is not normalised and may overflow arithmetically (to make this unlikely, use Normalise on +// the inputs before adding them). +func (period Period) Add(that Period) Period { + return Period{ + period.years + that.years, + period.months + that.months, + period.days + that.days, + period.hours + that.hours, + period.minutes + that.minutes, + period.seconds + that.seconds, } - return v +} + +// Scale a period by a multiplication factor. Obviously, this can both enlarge and shrink it, +// and change the sign if negative. The result is normalised. +// +// Bear in mind that the internal representation is limited by fixed-point arithmetic with one +// decimal place; each field is only int16. +// +// Known issue: scaling by a large reduction factor (i.e. much less than one) doesn't work properly. +func (period Period) Scale(factor float32) Period { + + if -0.5 < factor && factor < 0.5 { + d, pr1 := period.Duration() + mul := float64(d) * float64(factor) + p2, pr2 := NewOf(time.Duration(mul)) + return p2.Normalise(pr1 && pr2) + } + + y := int64(float32(period.years) * factor) + m := int64(float32(period.months) * factor) + d := int64(float32(period.days) * factor) + hh := int64(float32(period.hours) * factor) + mm := int64(float32(period.minutes) * factor) + ss := int64(float32(period.seconds) * factor) + + return (&period64{y, m, d, hh, mm, ss, false}).normalise64(true).toPeriod() } // Years gets the whole number of years in the period. @@ -396,7 +422,29 @@ func (period Period) SecondsFloat() float32 { return float32(period.seconds) / 10 } -//------------------------------------------------------------------------------------------------- +// AddTo adds the period to a time, returning the result. +// A flag is also returned that is true when the conversion was precise and false otherwise. +// +// When the period specifies hours, minutes and seconds only, the result is precise. +// Also, when the period specifies whole years, months and days (i.e. without fractions), the +// result is precise. However, when years, months or days contains fractions, the result +// is only an approximation (it assumes that all days are 24 hours and every year is 365.2425 +// days, as per Gregorian calendar rules). +func (period Period) AddTo(t time.Time) (time.Time, bool) { + wholeYears := (period.years % 10) == 0 + wholeMonths := (period.months % 10) == 0 + wholeDays := (period.days % 10) == 0 + + if wholeYears && wholeMonths && wholeDays { + // in this case, time.AddDate provides an exact solution + stE3 := totalSecondsE3(period) + t1 := t.AddDate(int(period.years/10), int(period.months/10), int(period.days/10)) + return t1.Add(stE3 * time.Millisecond), true + } + + d, precise := period.Duration() + return t.Add(d), precise +} // DurationApprox converts a period to the equivalent duration in nanoseconds. // When the period specifies hours, minutes and seconds only, the result is precise. @@ -466,8 +514,7 @@ func (period Period) TotalMonthsApprox() int { // // Because the number of hours per day is imprecise (due to daylight savings etc), and because // the number of days per month is variable in the Gregorian calendar, there is a reluctance -// to transfer time to or from the days element, or to transfer days to or from the months -// element. To give control over this, there are two modes. +// to transfer time too or from the days element. To give control over this, there are two modes. // // In precise mode: // Multiples of 60 seconds become minutes. @@ -480,138 +527,220 @@ func (period Period) TotalMonthsApprox() int { // // Note that leap seconds are disregarded: every minute is assumed to have 60 seconds. func (period Period) Normalise(precise bool) Period { - n, _ := period.toPeriod64("").normalise64(precise).toPeriod() - return n + const limit = 32670 - (32670 / 60) + + // can we use a quicker algorithm for HHMMSS with int16 arithmetic? + if period.years == 0 && period.months == 0 && + (!precise || period.days == 0) && + period.hours > -limit && period.hours < limit { + + return period.normaliseHHMMSS(precise) + } + + // can we use a quicker algorithm for YYMM with int16 arithmetic? + if (period.years != 0 || period.months != 0) && //period.months%10 == 0 && + period.days == 0 && period.hours == 0 && period.minutes == 0 && period.seconds == 0 { + + return period.normaliseYYMM() + } + + // do things the no-nonsense way using int64 arithmetic + return period.toPeriod64().normalise64(precise).toPeriod() } -// Simplify applies some heuristic simplifications with the objective of reducing the number -// of non-zero fields and thus making the rendered form simpler. It should be applied to -// a normalised period, otherwise the results may be unpredictable. -// -// Note that months and days are never combined, due to the variability of month lengths. -// Days and hours are only combined when imprecise behaviour is selected; this is due to -// daylight savings transitions, during which there are more than or fewer than 24 hours -// per day. -// -// The following transformation rules are applied in order: -// -// * P1YnM becomes 12+n months for 0 < n <= 6 -// * P1DTnH becomes 24+n hours for 0 < n <= 6 (unless precise is true) -// * PT1HnM becomes 60+n minutes for 0 < n <= 10 -// * PT1MnS becomes 60+n seconds for 0 < n <= 10 -// -// At each step, if a fraction exists and would affect the calculation, the transformations -// stop. Also, when not precise, -// -// * for periods of at least ten years, month proper fractions are discarded -// * for periods of at least a year, day proper fractions are discarded -// * for periods of at least a month, hour proper fractions are discarded -// * for periods of at least a day, minute proper fractions are discarded -// * for periods of at least an hour, second proper fractions are discarded -// -// The thresholds can be set using the varargs th parameter. By default, the thresholds a, -// b, c, d are 6 months, 6 hours, 10 minutes, 10 seconds respectively as listed in the rules -// above. -// -// * No thresholds is equivalent to 6, 6, 10, 10. -// * A single threshold a is equivalent to a, a, a, a. -// * Two thresholds a, b are equivalent to a, a, b, b. -// * Three thresholds a, b, c are equivalent to a, b, c, c. -// * Four thresholds a, b, c, d are used as provided. -func (period Period) Simplify(precise bool, th ...int) Period { - switch len(th) { - case 0: - return period.doSimplify(precise, 60, 60, 100, 100) - case 1: - return period.doSimplify(precise, int16(th[0]*10), int16(th[0]*10), int16(th[0]*10), int16(th[0]*10)) - case 2: - return period.doSimplify(precise, int16(th[0]*10), int16(th[0]*10), int16(th[1]*10), int16(th[1]*10)) - case 3: - return period.doSimplify(precise, int16(th[0]*10), int16(th[1]*10), int16(th[2]*10), int16(th[2]*10)) - default: - return period.doSimplify(precise, int16(th[0]*10), int16(th[1]*10), int16(th[2]*10), int16(th[3]*10)) - } -} - -func (period Period) doSimplify(precise bool, a, b, c, d int16) Period { - if period.years%10 != 0 { - return period - } - - ap, neg := period.absNeg() - - // single year is dropped if there are some months - if ap.years == 10 && - 0 < ap.months && ap.months <= a && - ap.days == 0 { - ap.months += 120 - ap.years = 0 - } - - if ap.months%10 != 0 { - // month fraction is dropped for periods of at least ten years (1:120) - months := ap.months / 10 - if !precise && ap.years >= 100 && months == 0 { - ap.months = 0 - } - return ap.condNegate(neg) +func (period Period) normaliseHHMMSS(precise bool) Period { + s := period.Sign() + ap := period.Abs() + + // remember that the fields are all fixed-point 1E1 + ap.minutes += (ap.seconds / 600) * 10 + ap.seconds = ap.seconds % 600 + + ap.hours += (ap.minutes / 600) * 10 + ap.minutes = ap.minutes % 600 + + // up to 36 hours stays as hours + if !precise && ap.hours > 360 { + ap.days += (ap.hours / 240) * 10 + ap.hours = ap.hours % 240 } - if ap.days%10 != 0 { - // day fraction is dropped for periods of at least a year (1:365) - days := ap.days / 10 - if !precise && (ap.years > 0 || ap.months >= 120) && days == 0 { - ap.days = 0 - } - return ap.condNegate(neg) + d10 := ap.days % 10 + if d10 != 0 && (ap.hours != 0 || ap.minutes != 0 || ap.seconds != 0) { + ap.hours += d10 * 24 + ap.days -= d10 } - if !precise && ap.days == 10 && - ap.years == 0 && - ap.months == 0 && - 0 < ap.hours && ap.hours <= b { - ap.hours += 240 - ap.days = 0 + hh10 := ap.hours % 10 + if hh10 != 0 { + ap.minutes += hh10 * 60 + ap.hours -= hh10 } - if ap.hours%10 != 0 { - // hour fraction is dropped for periods of at least a month (1:720) - hours := ap.hours / 10 - if !precise && (ap.years > 0 || ap.months > 0 || ap.days >= 300) && hours == 0 { - ap.hours = 0 - } - return ap.condNegate(neg) + mm10 := ap.minutes % 10 + if mm10 != 0 { + ap.seconds += mm10 * 60 + ap.minutes -= mm10 + } + + if s < 0 { + return ap.Negate() + } + return ap +} + +func (period Period) normaliseYYMM() Period { + s := period.Sign() + ap := period.Abs() + + // remember that the fields are all fixed-point 1E1 + if ap.months > 129 { + ap.years += (ap.months / 120) * 10 + ap.months = ap.months % 120 } - if ap.hours == 10 && - 0 < ap.minutes && ap.minutes <= c { - ap.minutes += 600 - ap.hours = 0 + y10 := ap.years % 10 + if y10 != 0 && (ap.years < 10 || ap.months != 0) { + ap.months += y10 * 12 + ap.years -= y10 } - if ap.minutes%10 != 0 { - // minute fraction is dropped for periods of at least a day (1:1440) - minutes := ap.minutes / 10 - if !precise && (ap.years > 0 || ap.months > 0 || ap.days > 0 || ap.hours >= 240) && minutes == 0 { - ap.minutes = 0 + if s < 0 { + return ap.Negate() + } + return ap +} + +//------------------------------------------------------------------------------------------------- + +// used for stages in arithmetic +type period64 struct { + years, months, days, hours, minutes, seconds int64 + neg bool +} + +func (period Period) toPeriod64() *period64 { + return &period64{ + int64(period.years), int64(period.months), int64(period.days), + int64(period.hours), int64(period.minutes), int64(period.seconds), + false, + } +} + +func (p *period64) toPeriod() Period { + if p.neg { + return Period{ + int16(-p.years), int16(-p.months), int16(-p.days), + int16(-p.hours), int16(-p.minutes), int16(-p.seconds), } - return ap.condNegate(neg) } - if ap.minutes == 10 && - ap.hours == 0 && - 0 < ap.seconds && ap.seconds <= d { - ap.seconds += 600 - ap.minutes = 0 + return Period{ + int16(p.years), int16(p.months), int16(p.days), + int16(p.hours), int16(p.minutes), int16(p.seconds), } +} + +func (p *period64) normalise64(precise bool) *period64 { + return p.abs().rippleUp(precise).moveFractionToRight() +} + +func (p *period64) abs() *period64 { + + if !p.neg { + if p.years < 0 { + p.years = -p.years + p.neg = true + } + + if p.months < 0 { + p.months = -p.months + p.neg = true + } + + if p.days < 0 { + p.days = -p.days + p.neg = true + } - if ap.seconds%10 != 0 { - // second fraction is dropped for periods of at least an hour (1:3600) - seconds := ap.seconds / 10 - if !precise && (ap.years > 0 || ap.months > 0 || ap.days > 0 || ap.hours > 0 || ap.minutes >= 600) && seconds == 0 { - ap.seconds = 0 + if p.hours < 0 { + p.hours = -p.hours + p.neg = true } + + if p.minutes < 0 { + p.minutes = -p.minutes + p.neg = true + } + + if p.seconds < 0 { + p.seconds = -p.seconds + p.neg = true + } + } + return p +} + +func (p *period64) rippleUp(precise bool) *period64 { + // remember that the fields are all fixed-point 1E1 + + p.minutes = p.minutes + (p.seconds/600)*10 + p.seconds = p.seconds % 600 + + p.hours = p.hours + (p.minutes/600)*10 + p.minutes = p.minutes % 600 + + // 32670-(32670/60)-(32670/3600) = 32760 - 546 - 9.1 = 32204.9 + if !precise || p.hours > 32204 { + p.days += (p.hours / 240) * 10 + p.hours = p.hours % 240 + } + + if !precise || p.days > 32760 { + dE6 := p.days * oneE6 + p.months += dE6 / daysPerMonthE6 + p.days = (dE6 % daysPerMonthE6) / oneE6 + } + + p.years = p.years + (p.months/120)*10 + p.months = p.months % 120 + + return p +} + +// moveFractionToRight applies the rule that only the smallest field is permitted to have a decimal fraction. +func (p *period64) moveFractionToRight() *period64 { + // remember that the fields are all fixed-point 1E1 + + y10 := p.years % 10 + if y10 != 0 && (p.months != 0 || p.days != 0 || p.hours != 0 || p.minutes != 0 || p.seconds != 0) { + p.months += y10 * 12 + p.years = (p.years / 10) * 10 + } + + m10 := p.months % 10 + if m10 != 0 && (p.days != 0 || p.hours != 0 || p.minutes != 0 || p.seconds != 0) { + p.days += (m10 * daysPerMonthE6) / oneE6 + p.months = (p.months / 10) * 10 + } + + d10 := p.days % 10 + if d10 != 0 && (p.hours != 0 || p.minutes != 0 || p.seconds != 0) { + p.hours += d10 * 24 + p.days = (p.days / 10) * 10 + } + + hh10 := p.hours % 10 + if hh10 != 0 && (p.minutes != 0 || p.seconds != 0) { + p.minutes += hh10 * 60 + p.hours = (p.hours / 10) * 10 + } + + mm10 := p.minutes % 10 + if mm10 != 0 && p.seconds != 0 { + p.seconds += mm10 * 60 + p.minutes = (p.minutes / 10) * 10 } - return ap.condNegate(neg) + return p } diff --git a/vendor/github.com/rickb777/date/period/period64.go b/vendor/github.com/rickb777/date/period/period64.go deleted file mode 100644 index f3e2f4b0bce5..000000000000 --- a/vendor/github.com/rickb777/date/period/period64.go +++ /dev/null @@ -1,142 +0,0 @@ -package period - -import ( - "fmt" - "math" - "strings" -) - -// used for stages in arithmetic -type period64 struct { - // always positive values - years, months, days, hours, minutes, seconds int64 - // true if the period is negative - neg bool - input string -} - -func (period Period) toPeriod64(input string) *period64 { - if period.IsNegative() { - return &period64{ - years: int64(-period.years), months: int64(-period.months), days: int64(-period.days), - hours: int64(-period.hours), minutes: int64(-period.minutes), seconds: int64(-period.seconds), - neg: true, - input: input, - } - } - return &period64{ - years: int64(period.years), months: int64(period.months), days: int64(period.days), - hours: int64(period.hours), minutes: int64(period.minutes), seconds: int64(period.seconds), - input: input, - } -} - -func (p64 *period64) toPeriod() (Period, error) { - var f []string - if p64.years > math.MaxInt16 { - f = append(f, "years") - } - if p64.months > math.MaxInt16 { - f = append(f, "months") - } - if p64.days > math.MaxInt16 { - f = append(f, "days") - } - if p64.hours > math.MaxInt16 { - f = append(f, "hours") - } - if p64.minutes > math.MaxInt16 { - f = append(f, "minutes") - } - if p64.seconds > math.MaxInt16 { - f = append(f, "seconds") - } - - if len(f) > 0 { - if p64.input == "" { - p64.input = p64.String() - } - return Period{}, fmt.Errorf("%s: integer overflow occurred in %s", p64.input, strings.Join(f, ",")) - } - - if p64.neg { - return Period{ - int16(-p64.years), int16(-p64.months), int16(-p64.days), - int16(-p64.hours), int16(-p64.minutes), int16(-p64.seconds), - }, nil - } - - return Period{ - int16(p64.years), int16(p64.months), int16(p64.days), - int16(p64.hours), int16(p64.minutes), int16(p64.seconds), - }, nil -} - -func (p64 *period64) normalise64(precise bool) *period64 { - return p64.rippleUp(precise).moveFractionToRight() -} - -func (p64 *period64) rippleUp(precise bool) *period64 { - // remember that the fields are all fixed-point 1E1 - - p64.minutes += (p64.seconds / 600) * 10 - p64.seconds = p64.seconds % 600 - - p64.hours += (p64.minutes / 600) * 10 - p64.minutes = p64.minutes % 600 - - // 32670-(32670/60)-(32670/3600) = 32760 - 546 - 9.1 = 32204.9 - if !precise || p64.hours > 32204 { - p64.days += (p64.hours / 240) * 10 - p64.hours = p64.hours % 240 - } - - if !precise || p64.days > 32760 { - dE6 := p64.days * oneE5 - p64.months += (dE6 / daysPerMonthE6) * 10 - p64.days = (dE6 % daysPerMonthE6) / oneE5 - } - - p64.years += (p64.months / 120) * 10 - p64.months = p64.months % 120 - - return p64 -} - -// moveFractionToRight attempts to remove fractions in higher-order fields by moving their value to the -// next-lower-order field. For example, fractional years become months. -func (p64 *period64) moveFractionToRight() *period64 { - // remember that the fields are all fixed-point 1E1 - - y10 := p64.years % 10 - if y10 != 0 && (p64.months != 0 || p64.days != 0 || p64.hours != 0 || p64.minutes != 0 || p64.seconds != 0) { - p64.months += y10 * 12 - p64.years = (p64.years / 10) * 10 - } - - m10 := p64.months % 10 - if m10 != 0 && (p64.days != 0 || p64.hours != 0 || p64.minutes != 0 || p64.seconds != 0) { - p64.days += (m10 * daysPerMonthE6) / oneE6 - p64.months = (p64.months / 10) * 10 - } - - d10 := p64.days % 10 - if d10 != 0 && (p64.hours != 0 || p64.minutes != 0 || p64.seconds != 0) { - p64.hours += d10 * 24 - p64.days = (p64.days / 10) * 10 - } - - hh10 := p64.hours % 10 - if hh10 != 0 && (p64.minutes != 0 || p64.seconds != 0) { - p64.minutes += hh10 * 60 - p64.hours = (p64.hours / 10) * 10 - } - - mm10 := p64.minutes % 10 - if mm10 != 0 && p64.seconds != 0 { - p64.seconds += mm10 * 60 - p64.minutes = (p64.minutes / 10) * 10 - } - - return p64 -} diff --git a/vendor/github.com/rickb777/date/period/sql.go b/vendor/github.com/rickb777/date/period/sql.go deleted file mode 100644 index 31b546429aa3..000000000000 --- a/vendor/github.com/rickb777/date/period/sql.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015 Rick Beton. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package period - -import ( - "database/sql/driver" - "fmt" -) - -// Scan parses some value, which can be either string or []byte. -// It implements sql.Scanner, https://golang.org/pkg/database/sql/#Scanner -func (period *Period) Scan(value interface{}) (err error) { - if value == nil { - return nil - } - - err = nil - switch v := value.(type) { - case []byte: - *period, err = Parse(string(v), false) - case string: - *period, err = Parse(v, false) - default: - err = fmt.Errorf("%T %+v is not a meaningful period", value, value) - } - - return err -} - -// Value converts the period to a string. It implements driver.Valuer, -// https://golang.org/pkg/database/sql/driver/#Valuer -func (period Period) Value() (driver.Value, error) { - return period.String(), nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 354c5ccb2256..30827ab7ac5f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1114,8 +1114,8 @@ github.com/mitchellh/reflectwalk # github.com/oklog/run v1.1.0 ## explicit; go 1.13 github.com/oklog/run -# github.com/rickb777/date v1.20.2 -## explicit; go 1.17 +# github.com/rickb777/date v1.12.5-0.20200422084442-6300e543c4d9 +## explicit; go 1.13 github.com/rickb777/date/period # github.com/rickb777/plural v1.4.1 ## explicit; go 1.17 diff --git a/website/docs/d/public_ip.html.markdown b/website/docs/d/public_ip.html.markdown index 311bd6967149..2fd9d2b25849 100644 --- a/website/docs/d/public_ip.html.markdown +++ b/website/docs/d/public_ip.html.markdown @@ -102,6 +102,7 @@ output "public_ip_address" { ## Attributes Reference * `id` - The ID of the Public IP address. +* `allocation_method` - The allocation method for this IP address. Possible values are `Static` or `Dynamic`. * `domain_name_label` - The label for the Domain Name. * `idle_timeout_in_minutes` - Specifies the timeout for the TCP idle connection. * `ddos_protection_mode` - The DDoS protection mode of the public IP. @@ -109,6 +110,8 @@ output "public_ip_address" { * `fqdn` - Fully qualified domain name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone. * `ip_address` - The IP address value that was allocated. * `ip_version` - The IP version being used, for example `IPv4` or `IPv6`. +* `location` - The region that this public ip exists. +* `reverse_fqdn` - The fully qualified domain name that resolves to this public IP address. * `sku` - The SKU of the Public IP. * `ip_tags` - A mapping of tags to assigned to the resource. * `tags` - A mapping of tags to assigned to the resource. diff --git a/website/docs/r/container_app.html.markdown b/website/docs/r/container_app.html.markdown index c5ab110a8469..5394f1f43521 100644 --- a/website/docs/r/container_app.html.markdown +++ b/website/docs/r/container_app.html.markdown @@ -97,12 +97,72 @@ A `template` block supports the following: * `min_replicas` - (Optional) The minimum number of replicas for this container. +* `azure_queue_scale_rule` - (Optional) One or more `azure_queue_scale_rule` blocks as defined below. + +* `custom_scale_rule` - (Optional) One or more `custom_scale_rule` blocks as defined below. + +* `http_scale_rule` - (Optional) One or more `http_scale_rule` blocks as defined below. + +* `tcp_scale_rule` - (Optional) One or more `tcp_scale_rule` blocks as defined below. + * `revision_suffix` - (Optional) The suffix for the revision. This value must be unique for the lifetime of the Resource. If omitted the service will use a hash function to create one. * `volume` - (Optional) A `volume` block as detailed below. --- +An `azure_queue_scale_rule` block supports the following: + +* `name` - (Required) The name of the Scaling Rule + +* `queue_name` - (Required) The name of the Azure Queue + +* `queue_length` - (Required) The value of the length of the queue to trigger scaling actions. + +* `authentication` - (Required) One or more `authentication` blocks as defined below. + +--- + +A `custom_scale_rule` block supports the following: + +* `name` - (Required) The name of the Scaling Rule + +* `custom_rule_type` - (Required) The Custom rule type. Possible values include: `activemq`, `artemis-queue`, `kafka`, `pulsar`, `aws-cloudwatch`, `aws-dynamodb`, `aws-dynamodb-streams`, `aws-kinesis-stream`, `aws-sqs-queue`, `azure-app-insights`, `azure-blob`, `azure-data-explorer`, `azure-eventhub`, `azure-log-analytics`, `azure-monitor`, `azure-pipelines`, `azure-servicebus`, `azure-queue`, `cassandra`, `cpu`, `cron`, `datadog`, `elasticsearch`, `external`, `external-push`, `gcp-stackdriver`, `gcp-storage`, `gcp-pubsub`, `graphite`, `http`, `huawei-cloudeye`, `ibmmq`, `influxdb`, `kubernetes-workload`, `liiklus`, `memory`, `metrics-api`, `mongodb`, `mssql`, `mysql`, `nats-jetstream`, `stan`, `tcp`, `new-relic`, `openstack-metric`, `openstack-swift`, `postgresql`, `predictkube`, `prometheus`, `rabbitmq`, `redis`, `redis-cluster`, `redis-sentinel`, `redis-streams`, `redis-cluster-streams`, `redis-sentinel-streams`, `selenium-grid`,`solace-event-queue`, and `github-runner`. + +* `metadata`- (Required) - A map of string key-value pairs to configure the Custom Scale Rule. + +* `authentication` - (Optional) Zero or more `authentication` blocks as defined below. + +--- + +A `http_scale_rule` block supports the following: + +* `name` - (Required) The name of the Scaling Rule + +* `concurrent_requests`- (Required) - The number of concurrent requests to trigger scaling. + +* `authentication` - (Optional) Zero or more `authentication` blocks as defined below. + +--- + +A `tcp_scale_rule` block supports the following: + +* `name` - (Required) The name of the Scaling Rule + +* `concurrent_requests`- (Required) - The number of concurrent requests to trigger scaling. + +* `authentication` - (Optional) Zero or more `authentication` blocks as defined below. + +--- + +An `authentication` block supports the following: + +* `secret_name` - (Required) The name of the Container App Secret to use for this Scale Rule Authentication. + +* `trigger_parameter` - (Required) The Trigger Parameter name to use the supply the value retrieved from the `secret_name`. + +--- + A `volume` block supports the following: * `name` - (Required) The name of the volume. diff --git a/website/docs/r/monitor_diagnostic_setting.html.markdown b/website/docs/r/monitor_diagnostic_setting.html.markdown index 493de7b692a6..d3389703a611 100644 --- a/website/docs/r/monitor_diagnostic_setting.html.markdown +++ b/website/docs/r/monitor_diagnostic_setting.html.markdown @@ -124,6 +124,8 @@ A `log` block supports the following: * `retention_policy` - (Optional) A `retention_policy` block as defined below. +!> **NOTE:** `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more information on the deprecation [in the Azure documentation](https://aka.ms/diagnostic_settings_log_retention). + * `enabled` - (Optional) Is this Diagnostic Log enabled? Defaults to `true`. --- @@ -140,6 +142,8 @@ An `enabled_log` block supports the following: * `retention_policy` - (Optional) A `retention_policy` block as defined below. +!> **NOTE:** `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more information on the deprecation [in the Azure documentation](https://aka.ms/diagnostic_settings_log_retention). + --- A `metric` block supports the following: @@ -150,15 +154,20 @@ A `metric` block supports the following: * `retention_policy` - (Optional) A `retention_policy` block as defined below. +!> **NOTE:** `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more information on the deprecation [in the Azure documentation](https://aka.ms/diagnostic_settings_log_retention). + * `enabled` - (Optional) Is this Diagnostic Metric enabled? Defaults to `true`. --- A `retention_policy` block supports the following: +!> **NOTE:** `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more information on the deprecation [in the Azure documentation](https://aka.ms/diagnostic_settings_log_retention). + * `enabled` - (Required) Is this Retention Policy enabled? * `days` - (Optional) The number of days for which this Retention Policy should apply. + -> **NOTE:** Setting this to `0` will retain the events indefinitely. diff --git a/website/docs/r/monitor_scheduled_query_rules_alert_v2.html.markdown b/website/docs/r/monitor_scheduled_query_rules_alert_v2.html.markdown index 6318a3c48e2c..44257582c0fd 100644 --- a/website/docs/r/monitor_scheduled_query_rules_alert_v2.html.markdown +++ b/website/docs/r/monitor_scheduled_query_rules_alert_v2.html.markdown @@ -122,7 +122,7 @@ The following arguments are supported: * `mute_actions_after_alert_duration` - (Optional) Mute actions for the chosen period of time in ISO 8601 duration format after the alert is fired. Possible values are `PT5M`, `PT10M`, `PT15M`, `PT30M`, `PT45M`, `PT1H`, `PT2H`, `PT3H`, `PT4H`, `PT5H`, `PT6H`, `P1D` and `P2D`. --> **NOTE** `auto_mitigation_enabled` and `mute_actions_after_alert_duration` are mutually exclusive and cannot both be set. +-> **Note** `auto_mitigation_enabled` and `mute_actions_after_alert_duration` are mutually exclusive and cannot both be set. * `query_time_range_override` - (Optional) Set this if the alert evaluation period is different from the query time range. If not specified, the value is `window_duration`*`number_of_evaluation_periods`. Possible values are `PT5M`, `PT10M`, `PT15M`, `PT20M`, `PT30M`, `PT45M`, `PT1H`, `PT2H`, `PT3H`, `PT4H`, `PT5H`, `PT6H`, `P1D` and `P2D`. @@ -160,6 +160,8 @@ A `criteria` block supports the following: * `metric_measure_column` - (Optional) Specifies the column containing the metric measure number. +-> **Note** `metric_measure_column` is required if `time_aggregation_method` is `Average`, `Maximum`, `Minimum`, or `Total`. And `metric_measure_column` can not be specified if `time_aggregation_method` is `Count`. + * `resource_id_column` - (Optional) Specifies the column containing the resource ID. The content of the column must be an uri formatted as resource ID. --- diff --git a/website/docs/r/postgresql_flexible_server.html.markdown b/website/docs/r/postgresql_flexible_server.html.markdown index 9995637376b7..be26995aaa06 100644 --- a/website/docs/r/postgresql_flexible_server.html.markdown +++ b/website/docs/r/postgresql_flexible_server.html.markdown @@ -132,7 +132,7 @@ The following arguments are supported: * `auto_grow_enabled` - (Optional) Is the storage auto grow for PostgreSQL Flexible Server enabled? Defaults to `false`. -* `storage_mb` - (Optional) The max storage allowed for the PostgreSQL Flexible Server. Possible values are `32768`, `65536`, `131072`, `262144`, `524288`, `1048576`, `2097152`, `4194304`, `8388608`, `16777216` and `33553408`. +* `storage_mb` - (Optional) The max storage allowed for the PostgreSQL Flexible Server. Possible values are `32768`, `65536`, `131072`, `262144`, `524288`, `1048576`, `2097152`, `4193280`, `4194304`, `8388608`, `16777216` and `33553408`. * `tags` - (Optional) A mapping of tags which should be assigned to the PostgreSQL Flexible Server. diff --git a/website/docs/r/shared_image_version.html.markdown b/website/docs/r/shared_image_version.html.markdown index e357935c61f4..b96f0616618a 100644 --- a/website/docs/r/shared_image_version.html.markdown +++ b/website/docs/r/shared_image_version.html.markdown @@ -77,6 +77,8 @@ The following arguments are supported: -> **NOTE:** You must specify exact one of `blob_uri`, `managed_image_id` and `os_disk_snapshot_id`. +* `deletion_of_replicated_locations_enabled` - (Optional) Specifies whether this Shared Image Version can be deleted from the Azure Regions this is replicated to. Defaults to `false`. Changing this forces a new resource to be created. + * `replication_mode` - (Optional) Mode to be used for replication. Possible values are `Full` and `Shallow`. Defaults to `Full`. Changing this forces a new resource to be created. * `storage_account_id` - (Optional) The ID of the Storage Account where the Blob exists. Changing this forces a new resource to be created. @@ -95,6 +97,8 @@ The `target_region` block supports the following: * `disk_encryption_set_id` - (Optional) The ID of the Disk Encryption Set to encrypt the Image Version in the target region. Changing this forces a new resource to be created. +* `exclude_from_latest_enabled` - (Optional) Specifies whether this Shared Image Version should be excluded when querying for the `latest` version. Defaults to `false`. + * `storage_account_type` - (Optional) The storage account type for the image version. Possible values are `Standard_LRS`, `Premium_LRS` and `Standard_ZRS`. Defaults to `Standard_LRS`. You can store all of your image version replicas in Zone Redundant Storage by specifying `Standard_ZRS`. ## Attributes Reference diff --git a/website/docs/r/storage_account.html.markdown b/website/docs/r/storage_account.html.markdown index 166b2ecb83c4..26a0f722980e 100644 --- a/website/docs/r/storage_account.html.markdown +++ b/website/docs/r/storage_account.html.markdown @@ -183,16 +183,26 @@ A `blob_properties` block supports the following: * `restore_policy` - (Optional) A `restore_policy` block as defined below. This must be used together with `delete_retention_policy` set, `versioning_enabled` and `change_feed_enabled` set to `true`. +-> **NOTE:** This field cannot be configured when `kind` is set to `Storage` (V1). + * `versioning_enabled` - (Optional) Is versioning enabled? Default to `false`. +-> **NOTE:** This field cannot be configured when `kind` is set to `Storage` (V1). + * `change_feed_enabled` - (Optional) Is the blob service properties for change feed events enabled? Default to `false`. +-> **NOTE:** This field cannot be configured when `kind` is set to `Storage` (V1). + * `change_feed_retention_in_days` - (Optional) The duration of change feed events retention in days. The possible values are between 1 and 146000 days (400 years). Setting this to null (or omit this in the configuration file) indicates an infinite retention of the change feed. +-> **NOTE:** This field cannot be configured when `kind` is set to `Storage` (V1). + * `default_service_version` - (Optional) The API Version which should be used by default for requests to the Data Plane API if an incoming request doesn't specify an API Version. * `last_access_time_enabled` - (Optional) Is the last access time based tracking enabled? Default to `false`. +-> **NOTE:** This field cannot be configured when `kind` is set to `Storage` (V1). + * `container_delete_retention_policy` - (Optional) A `container_delete_retention_policy` block as defined below. --- @@ -344,23 +354,21 @@ A `azure_files_authentication` block supports the following: * `active_directory` - (Optional) A `active_directory` block as defined below. Required when `directory_type` is `AD`. -~> **Note:** If `directory_type` is set to `AADKERB`, `active_directory` is not supported. Use [icals](https://learn.microsoft.com/en-us/azure/storage/files/storage-files-identity-auth-azure-active-directory-enable?tabs=azure-portal#configure-directory-and-file-level-permissions) to configure directory and file level permissions. - --- A `active_directory` block supports the following: -* `storage_sid` - (Required) Specifies the security identifier (SID) for Azure Storage. - * `domain_name` - (Required) Specifies the primary domain that the AD DNS server is authoritative for. -* `domain_sid` - (Required) Specifies the security identifier (SID). - * `domain_guid` - (Required) Specifies the domain GUID. -* `forest_name` - (Required) Specifies the Active Directory forest. +* `domain_sid` - (Optional) Specifies the security identifier (SID). This is required when `directory_type` is set to `AD`. + +* `storage_sid` - (Optional) Specifies the security identifier (SID) for Azure Storage. This is required when `directory_type` is set to `AD`. + +* `forest_name` - (Optional) Specifies the Active Directory forest. This is required when `directory_type` is set to `AD`. -* `netbios_domain_name` - (Required) Specifies the NetBIOS domain name. +* `netbios_domain_name` - (Optional) Specifies the NetBIOS domain name. This is required when `directory_type` is set to `AD`. ---