diff --git a/.github/labeler-issue-triage.yml b/.github/labeler-issue-triage.yaml similarity index 100% rename from .github/labeler-issue-triage.yml rename to .github/labeler-issue-triage.yaml diff --git a/.github/labeler-pull-request-triage.yml b/.github/labeler-pull-request-triage.yaml similarity index 100% rename from .github/labeler-pull-request-triage.yml rename to .github/labeler-pull-request-triage.yaml diff --git a/.github/workflows/acctest-oidc.yaml b/.github/workflows/acctest-oidc.yaml deleted file mode 100644 index 0854700e43..0000000000 --- a/.github/workflows/acctest-oidc.yaml +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: "Acceptance Tests: OIDC" -on: - pull_request: - types: ['opened', 'synchronize'] - paths: - - '.github/workflows/acctest-oidc.yaml' - - 'internal/provider/**' - - 'vendor/github.com/hashicorp/go-azure-sdk/sdk/auth/**' - -permissions: - contents: read - id-token: write - -jobs: - oidc-check: - runs-on: ubuntu-latest - outputs: - available: "${{ steps.check-oidc.outputs.available }}" - steps: - - id: check-oidc - run: | - if [[ "${ACTIONS_ID_TOKEN_REQUEST_URL}" == "" ]]; then - echo "available=false" | tee ${GITHUB_OUTPUT} - else - echo "available=true" | tee ${GITHUB_OUTPUT} - fi - - acctest-oidc: - runs-on: ubuntu-latest - needs: [oidc-check] - if: needs.oidc-check.outputs.available == 'true' - steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 - with: - go-version-file: .go-version - - run: bash scripts/gogetcookie.sh - - run: make tools - - run: | - echo "ARM_OIDC_TOKEN=$(curl -H "Accept: application/json; api-version=2.0" -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" -H "Content-Type: application/json" -G --data-urlencode "audience=api://AzureADTokenExchange" "${ACTIONS_ID_TOKEN_REQUEST_URL}" | jq -r '.value')" >>${GITHUB_ENV} - - run: make testacc TEST=./internal/provider TESTARGS="-run '(?i)(TestAccProvider_.*oidc.*)'" - env: - ARM_CLIENT_ID: ${{ secrets.OIDC_CLIENT_ID }} - ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} diff --git a/.github/workflows/depscheck.yaml b/.github/workflows/depscheck.yaml index bd835935bd..0c6d54b37b 100644 --- a/.github/workflows/depscheck.yaml +++ b/.github/workflows/depscheck.yaml @@ -19,3 +19,7 @@ jobs: - run: bash scripts/gogetcookie.sh - run: make tools - run: make depscheck + + save-artifacts-on-fail: + if: ${{ needs.depscheck.result }} == 'failure' + uses: ./.github/workflows/save-artifacts.yaml diff --git a/.github/workflows/docs-lint.yaml b/.github/workflows/docs-lint.yaml index 178a25736e..350c484b14 100644 --- a/.github/workflows/docs-lint.yaml +++ b/.github/workflows/docs-lint.yaml @@ -18,3 +18,7 @@ jobs: - run: bash scripts/gogetcookie.sh - run: make tools - run: make docs-lint + + save-artifacts-on-fail: + if: ${{ needs.docs-lint.result }} == 'failure' + uses: ./.github/workflows/save-artifacts.yaml diff --git a/.github/workflows/golint.yaml b/.github/workflows/golint.yaml index 8fc3eb2e1a..214d3a5a66 100644 --- a/.github/workflows/golint.yaml +++ b/.github/workflows/golint.yaml @@ -20,3 +20,7 @@ jobs: with: version: 'v1.50.1' args: -v + + save-artifacts-on-fail: + if: ${{ needs.golint.result }} == 'failure' + uses: ./.github/workflows/save-artifacts.yaml diff --git a/.github/workflows/increment-milestone.yaml b/.github/workflows/increment-milestone.yaml new file mode 100644 index 0000000000..7a3733a0a5 --- /dev/null +++ b/.github/workflows/increment-milestone.yaml @@ -0,0 +1,23 @@ +--- +name: Increment Milestone + +on: + push: + tags: + - 'v*.*.*' + +permissions: + issues: write + contents: read + +jobs: + increment-milestone: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + with: + fetch-depth: 0 + + - name: "Increment Milestone" + shell: bash + run: bash ./scripts/increment-milestone.sh -u https://api.github.com/repos${{ github.owner }}/${{ github.repository }}/milestones -r ${{github.ref_name}} -t ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/issue-comment-created.yaml b/.github/workflows/issue-comment-created.yaml index ee1b339cf5..7a135f0e2c 100644 --- a/.github/workflows/issue-comment-created.yaml +++ b/.github/workflows/issue-comment-created.yaml @@ -10,28 +10,33 @@ permissions: issues: write jobs: - issue_comment_triage: - runs-on: ubuntu-latest - steps: - - uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1.3.0 - with: - github_token: "${{ secrets.GITHUB_TOKEN }}" - labels: stale - - uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1.3.0 - if: ${{ !github.event.issue.pull_request }} - with: - github_token: "${{ secrets.GITHUB_TOKEN }}" - labels: waiting-response - - uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1.3.0 - if: (github.event.issue.pull_request && github.actor == github.event.issue.user.login) - with: - github_token: "${{ secrets.GITHUB_TOKEN }}" - labels: waiting-response + remove-stale: + uses: ./.github/workflows/remove-issue-label.yaml + with: + label-name: "stale" + + remove-waiting-response-from-issue: + uses: ./.github/workflows/remove-issue-label.yaml + if: ${{ !github.event.issue.pull_request }} + with: + label-name: "waiting-response" + + remove-waiting-response-from-pr: + uses: ./.github/workflows/remove-issue-label.yaml + if: (github.event.issue.pull_request && github.actor == github.event.issue.user.login) + with: + label-name: "waiting-response" pull_request_comment: runs-on: ubuntu-latest if: github.event.issue.pull_request && endsWith(github.event.comment.body, '/wr') steps: - - shell: bash - run: | - curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos${{ github.owner }}/${{ github.repository }}/issues/${{ github.event.issue.number }}/labels" -d '{"labels":["waiting-response"]}' + - uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 + with: + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["waiting-response"] + }) diff --git a/.github/workflows/issue-opened.yaml b/.github/workflows/issue-opened.yaml index 47e02b0741..ea06246732 100644 --- a/.github/workflows/issue-opened.yaml +++ b/.github/workflows/issue-opened.yaml @@ -16,5 +16,5 @@ jobs: - uses: github/issue-labeler@98b5412841f6c4b0b3d9c29d53c13fad16bd7de2 # v3.2 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" - configuration-path: .github/labeler-issue-triage.yml + configuration-path: .github/labeler-issue-triage.yaml enable-versioned-regex: 0 diff --git a/.github/workflows/link-milestone.yaml b/.github/workflows/link-milestone.yaml index f3fb60db6b..e6dd367627 100644 --- a/.github/workflows/link-milestone.yaml +++ b/.github/workflows/link-milestone.yaml @@ -18,6 +18,7 @@ jobs: - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: .go-version + - run: | go install github.com/stephybun/link-milestone@latest link-milestone diff --git a/.github/workflows/provider-test.yaml b/.github/workflows/provider-test.yaml new file mode 100644 index 0000000000..649e68138d --- /dev/null +++ b/.github/workflows/provider-test.yaml @@ -0,0 +1,88 @@ +--- +name: Provider Tests +on: + pull_request: + types: ["opened", "synchronize"] + paths: + - '.github/workflows/provider-test.yaml' + - 'internal/**.go' + - 'vendor/github.com/hashicorp/go-azure-sdk/sdk/auth/**' + - 'vendor/github.com/hashicorp/go-azure-sdk/sdk/environments/**' + +permissions: + contents: read + id-token: write + pull-requests: read + +jobs: + secrets-check: + runs-on: ubuntu-latest + outputs: + available: "${{ steps.check-secrets.outputs.available }}" + steps: + # we check for the ACTIONS_ID_TOKEN_REQUEST_URL variable as a proxy for other secrets + # it will be unset when running for a PR from a fork, in which case we don't run these tests + - id: check-secrets + run: | + if [[ "${ACTIONS_ID_TOKEN_REQUEST_URL}" == "" ]]; then + echo "available=false" | tee ${GITHUB_OUTPUT} + else + echo "available=true" | tee ${GITHUB_OUTPUT} + fi + + provider-tests: + runs-on: [custom, linux, large] + needs: [secrets-check] + if: needs.secrets-check.outputs.available == 'true' + steps: + - name: Checkout + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + + - name: Install Go + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + with: + go-version-file: ./.go-version + + - name: Azure CLI login + run: az login --output none --username="${{ secrets.AZCLI_USERNAME }}" --password="${{ secrets.AZCLI_PASSWORD }}" + + - name: Set OIDC Token + run: | + echo "ARM_OIDC_TOKEN=$(curl -H "Accept: application/json; api-version=2.0" -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" -H "Content-Type: application/json" -G --data-urlencode "audience=api://AzureADTokenExchange" "${ACTIONS_ID_TOKEN_REQUEST_URL}" | jq -r '.value')" >>${GITHUB_ENV} + + - name: Set OIDC Token File Path + run: echo "${ARM_OIDC_TOKEN}" >"${RUNNER_TEMP}/oidc-token.jwt" && echo "ARM_OIDC_TOKEN_FILE_PATH=${RUNNER_TEMP}/oidc-token.jwt" >>${GITHUB_ENV} + + - name: Set Client ID Path + run: echo "${{ secrets.ARM_CLIENT_ID }}" >"${RUNNER_TEMP}/client-id" && echo "ARM_CLIENT_ID_PATH=${RUNNER_TEMP}/client-id" >>${GITHUB_ENV} + + - name: Set Client Secret Path + run: echo "${{ secrets.ARM_CLIENT_SECRET }}" >"${RUNNER_TEMP}/client-secret" && echo "ARM_CLIENT_SECRET_PATH=${RUNNER_TEMP}/client-secret" >>${GITHUB_ENV} + + - name: Set Client Certificate Path + run: echo "${{ secrets.ARM_CLIENT_CERTIFICATE }}" | base64 -d >"${RUNNER_TEMP}/client-certificate.pfx" && echo "ARM_CLIENT_CERTIFICATE_PATH=${RUNNER_TEMP}/client-certificate.pfx" >>${GITHUB_ENV} + + - name: Run provider tests + run: make testacc TEST=./internal/provider TESTARGS="-run '^TestAcc'" + env: + ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} + ARM_CLIENT_CERTIFICATE: ${{ secrets.ARM_CLIENT_CERTIFICATE }} + ARM_CLIENT_CERTIFICATE_PASSWORD: ${{ secrets.ARM_CLIENT_CERTIFICATE_PASSWORD }} + ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }} + ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} + + - name: Clean Up OIDC Token File Path + run: rm -f "${RUNNER_TEMP}/oidc-token.jwt" + if: always() + + - name: Clean Up Client ID Path + run: rm -f "${RUNNER_TEMP}/client-id" + if: always() + + - name: Clean Up Client Secret Path + run: rm -f "${RUNNER_TEMP}/client-secret" + if: always() + + save-artifacts-on-fail: + if: ${{ needs.secrets-check.result }} == 'failure' || ${{ needs.provider-tests.result }} == 'failure' + uses: ./.github/workflows/save-artifacts.yaml diff --git a/.github/workflows/pull-request-new-commit.yaml b/.github/workflows/pull-request-new-commit.yaml new file mode 100644 index 0000000000..a58eeb7f0d --- /dev/null +++ b/.github/workflows/pull-request-new-commit.yaml @@ -0,0 +1,15 @@ +--- +name: Pull Request New Commit + +permissions: + pull-requests: write + +on: + pull_request_target: + types: [synchronize] + +jobs: + remove-waiting-response: + uses: ./.github/workflows/remove-issue-label.yaml + with: + label-name: "waiting-response" diff --git a/.github/workflows/pull-request-reviewed-workflow.yaml b/.github/workflows/pull-request-reviewed-workflow.yaml new file mode 100644 index 0000000000..f2a7858944 --- /dev/null +++ b/.github/workflows/pull-request-reviewed-workflow.yaml @@ -0,0 +1,55 @@ +--- +name: "Pull Request Reviewed Workflow" + +on: + workflow_run: + workflows: + - "Pull Request Reviewed" + types: + - completed + +permissions: + pull-requests: write + +jobs: + add-or-remove-waiting-response: + runs-on: ubuntu-latest + outputs: + ghrepo: ${{ steps.env_vars.outputs.ghrepo }} + ghowner: ${{ steps.env_vars.outputs.ghowner }} + prnumber: ${{ steps.env_vars.outputs.prnumber }} + action: ${{ steps.env_vars.outputs.action }} + artifact_outcome: ${{ steps.env_vars.outputs.artifact_outcome }} + steps: + - name: Get Artifact + id: get_artifact + continue-on-error: true + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: pull-request-reviewed.yaml + + - name: env_vars + id: env_vars + if: steps.get_artifact.outcome == 'success' + run: | + echo "ghrepo=$(cat artifact/ghrepo.txt)" >>${GITHUB_OUTPUT} + echo "ghowner=$(cat artifact/ghowner.txt)" >>${GITHUB_OUTPUT} + echo "prnumber=$(cat artifact/prnumber.txt)" >>${GITHUB_OUTPUT} + echo "action=$(cat artifact/action.txt)" >>${GITHUB_OUTPUT} + echo "artifact_outcome=success" >>${GITHUB_OUTPUT} + + add-waiting-reponse: + needs: add-or-remove-waiting-response + runs-on: ubuntu-latest + if: needs.add-or-remove-waiting-response.outputs.artifact_outcome == 'success' && needs.add-or-remove-waiting-response.outputs.action == 'add-waiting-response' + steps: + - run: | + curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos${{ needs.add-or-remove-waiting-response.outputs.ghowner }}/${{ needs.add-or-remove-waiting-response.outputs.ghrepo }}/issues/${{ needs.add-or-remove-waiting-response.outputs.prnumber }}/labels" -d '{"labels":["waiting-response"]}' + + remove-waiting-reponse: + needs: add-or-remove-waiting-response + if: needs.add-or-remove-waiting-response.outputs.artifact_outcome == 'success' && needs.add-or-remove-waiting-response.outputs.action == 'remove-waiting-response' + uses: ./.github/workflows/remove-issue-label.yaml + with: + label-name: "waiting-response" diff --git a/.github/workflows/pull-request-reviewed.yaml b/.github/workflows/pull-request-reviewed.yaml new file mode 100644 index 0000000000..940834855b --- /dev/null +++ b/.github/workflows/pull-request-reviewed.yaml @@ -0,0 +1,38 @@ +--- +name: "Pull Request Reviewed" + +on: + pull_request_review: + types: [submitted] + +permissions: + pull-requests: read + +jobs: + add-or-remove-waiting-response: + runs-on: ubuntu-latest + steps: + - name: "Set Artifacts for add-waiting-response" + if: github.event.review.state != 'approved' && github.actor != github.event.pull_request.user.login + shell: bash + run: | + mkdir -p wr_actions + echo ${{ github.owner }} > wr_actions/ghowner.txt + echo ${{ github.repository }} > wr_actions/ghrepo.txt + echo ${{ github.event.pull_request.number }} > wr_actions/prnumber.txt + echo "add-waiting-response" > wr_actions/action.txt + + - name: "Set Artifacts for remove-waiting-response" + if: github.actor == github.event.pull_request.user.login + shell: bash + run: | + mkdir -p wr_actions + echo ${{ github.owner }} > wr_actions/ghowner.txt + 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@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + with: + name: artifact + path: wr_actions diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 001da138ef..3ffa51e976 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -13,8 +13,9 @@ jobs: steps: - uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594 # v4.3.0 with: - configuration-path: .github/labeler-pull-request-triage.yml + configuration-path: .github/labeler-pull-request-triage.yaml repo-token: "${{ secrets.GITHUB_TOKEN }}" + - uses: CodelyTV/pr-size-labeler@54ef36785e9f4cb5ecf1949cfc9b00dbb621d761 # v1.8.1 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/remove-issue-label.yaml b/.github/workflows/remove-issue-label.yaml new file mode 100644 index 0000000000..010e073c56 --- /dev/null +++ b/.github/workflows/remove-issue-label.yaml @@ -0,0 +1,50 @@ +name: Remove specified label from issue + +on: + # This file is reused, and called from other workflows + workflow_call: + inputs: + label-name: + required: true + type: string + + +jobs: + remove-label: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 + env: + REMOVE_LABEL: ${{ inputs.label-name }} + with: + script: | + const { REMOVE_LABEL } = process.env + console.log(`Attempting to remove label "${REMOVE_LABEL}"`) + + const { data } = await github.rest.issues.listLabelsOnIssue({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }) + + // Return early if there are no labels + if (data.length == 0){ + console.log(`Issue has no labels; not attempting to remove label "${REMOVE_LABEL}"`) + return + } + + // Check if REMOVE_LABEL is present + const filteredData = data.filter(label => label.name == REMOVE_LABEL) + + // Return early if filtering didn't identify the label as present + if (filteredData.length == 0){ + console.log(`Label "${REMOVE_LABEL}" not found on issue; not attempting to remove it.`) + return + } + console.log(`Label "${REMOVE_LABEL}" found! Now deleting it from the issue...`) + await github.rest.issues.removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: REMOVE_LABEL + }) diff --git a/.github/workflows/save-artifacts.yaml b/.github/workflows/save-artifacts.yaml new file mode 100644 index 0000000000..5c95f66d74 --- /dev/null +++ b/.github/workflows/save-artifacts.yaml @@ -0,0 +1,20 @@ +name: Save Artifacts + +on: + # This file is reused, and called from other workflows + workflow_call: + +jobs: + save-artifacts: + runs-on: ubuntu-latest + steps: + - shell: bash + run: | + mkdir -p wr_actions + 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@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + with: + name: artifact + path: wr_actions diff --git a/.github/workflows/tflint.yaml b/.github/workflows/tflint.yaml index 8f848db5c5..951a4b3fad 100644 --- a/.github/workflows/tflint.yaml +++ b/.github/workflows/tflint.yaml @@ -27,3 +27,7 @@ jobs: - run: bash scripts/gogetcookie.sh - run: make tools - run: make tflint + + save-artifacts-on-fail: + if: ${{ needs.tflint.result }} == 'failure' + uses: ./.github/workflows/save-artifacts.yaml diff --git a/.github/workflows/thirty-two-bit.yaml b/.github/workflows/thirty-two-bit.yaml index 5ad042a1e8..f9888f41fa 100644 --- a/.github/workflows/thirty-two-bit.yaml +++ b/.github/workflows/thirty-two-bit.yaml @@ -27,3 +27,7 @@ jobs: go-version-file: ./.go-version - run: bash scripts/gogetcookie.sh - run: GOARCH=386 GOOS=linux go build -o 32bitbuild . + + save-artifacts-on-fail: + if: ${{ needs.compatibility-32bit-test.result }} == 'failure' + uses: ./.github/workflows/save-artifacts.yaml diff --git a/.github/workflows/unit-test.yaml b/.github/workflows/unit-test.yaml index 82e527ffbe..ef3ec4e0d0 100644 --- a/.github/workflows/unit-test.yaml +++ b/.github/workflows/unit-test.yaml @@ -29,3 +29,7 @@ jobs: - run: make test env: GITHUB_ACTIONS_STAGE: "UNIT_TESTS" + + save-artifacts-on-fail: + if: ${{ needs.test.result }} == 'failure' + uses: ./.github/workflows/save-artifacts.yaml diff --git a/.github/workflows/validate-examples.yaml b/.github/workflows/validate-examples.yaml index 0d23a2b439..6e0806b319 100644 --- a/.github/workflows/validate-examples.yaml +++ b/.github/workflows/validate-examples.yaml @@ -17,8 +17,8 @@ concurrency: cancel-in-progress: true jobs: - website-lint: - runs-on: [custom, linux, large] + validate-examples: + runs-on: ubuntu-latest steps: - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 @@ -27,3 +27,7 @@ jobs: - run: bash scripts/gogetcookie.sh - run: make tools - run: make validate-examples + + save-artifacts-on-fail: + if: ${{ needs.validate-examples.result }} == 'failure' + uses: ./.github/workflows/save-artifacts.yaml diff --git a/internal/clients/builder.go b/internal/clients/builder.go index 44fa8192ce..41c277746f 100644 --- a/internal/clients/builder.go +++ b/internal/clients/builder.go @@ -34,7 +34,7 @@ func (b *ClientBuilder) Build(ctx context.Context) (*Client, error) { authorizer, err := auth.NewAuthorizerFromCredentials(ctx, *b.AuthConfig, b.AuthConfig.Environment.MicrosoftGraph) if err != nil { - return nil, fmt.Errorf("unable to build authorizer for Resource Manager API: %+v", err) + return nil, fmt.Errorf("unable to build authorizer: %+v", err) } client.Environment = b.AuthConfig.Environment diff --git a/internal/provider/provider.go b/internal/provider/provider.go index c755ba1854..5b5e07ad89 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -287,7 +287,7 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc { ClientCertificatePassword: d.Get("client_certificate_password").(string), ClientCertificatePath: d.Get("client_certificate_path").(string), ClientSecret: *clientSecret, - OIDCAssertionToken: idToken, + OIDCAssertionToken: *idToken, GitHubOIDCTokenRequestURL: d.Get("oidc_request_url").(string), GitHubOIDCTokenRequestToken: d.Get("oidc_request_token").(string), EnableAuthenticatingUsingClientCertificate: true, @@ -344,24 +344,26 @@ func decodeCertificate(clientCertificate string) ([]byte, error) { return pfx, nil } -func oidcToken(d *schema.ResourceData) (string, error) { +func oidcToken(d *schema.ResourceData) (*string, error) { idToken := d.Get("oidc_token").(string) if path := d.Get("oidc_token_file_path").(string); path != "" { - fileToken, err := os.ReadFile(path) + fileTokenRaw, err := os.ReadFile(path) if err != nil { - return "", fmt.Errorf("reading OIDC Token from file %q: %v", path, err) + return nil, fmt.Errorf("reading OIDC Token from file %q: %v", path, err) } - if idToken != "" && idToken != string(fileToken) { - return "", fmt.Errorf("mismatch between supplied OIDC token and supplied OIDC token file contents - please either remove one or ensure they match") + fileToken := strings.TrimSpace(string(fileTokenRaw)) + + if idToken != "" && idToken != fileToken { + return nil, fmt.Errorf("mismatch between supplied OIDC token and supplied OIDC token file contents - please either remove one or ensure they match") } - idToken = string(fileToken) + idToken = fileToken } - return idToken, nil + return &idToken, nil } func getClientId(d *schema.ResourceData) (*string, error) { diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 326b1378ef..00de46dafc 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -284,6 +284,63 @@ func testAccProvider_clientSecretAuthFromFiles(t *testing.T) { } func TestAccProvider_genericOidcAuth(t *testing.T) { + t.Run("fromEnvironment", testAccProvider_genericOidcAuthFromEnvironment) + t.Run("fromFiles", testAccProvider_genericOidcAuthFromFiles) +} + +func testAccProvider_genericOidcAuthFromEnvironment(t *testing.T) { + if os.Getenv("TF_ACC") == "" { + t.Skip("TF_ACC not set") + } + if os.Getenv("ARM_OIDC_TOKEN_FILE_PATH") == "" { + t.Skip("ARM_OIDC_TOKEN_FILE_PATH not set") + } + + // Ensure we are running using the expected env-vars + // t.SetEnv does automatic cleanup / resets the values after the test + t.Setenv("ARM_OIDC_TOKEN", "") + + provider := AzureADProvider() + ctx := context.Background() + + // Support only oidc authentication + provider.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { + envName := d.Get("environment").(string) + env, err := environments.FromName(envName) + if err != nil { + t.Fatalf("configuring environment %q: %v", envName, err) + } + + idToken, err := oidcToken(d) + if err != nil { + return nil, diag.FromErr(err) + } + + authConfig := &auth.Credentials{ + Environment: *env, + TenantID: d.Get("tenant_id").(string), + ClientID: d.Get("client_id").(string), + + EnableAuthenticationUsingOIDC: true, + OIDCAssertionToken: *idToken, + } + + return buildClient(ctx, provider, authConfig, "") + } + + d := provider.Configure(ctx, terraform.NewResourceConfigRaw(nil)) + if d != nil && d.HasError() { + t.Fatalf("err: %+v", d) + } + + if errs := testCheckProvider(provider); len(errs) > 0 { + for _, err := range errs { + t.Error(err) + } + } +} + +func testAccProvider_genericOidcAuthFromFiles(t *testing.T) { if os.Getenv("TF_ACC") == "" { t.Skip("TF_ACC not set") } @@ -291,6 +348,10 @@ func TestAccProvider_genericOidcAuth(t *testing.T) { t.Skip("ARM_OIDC_TOKEN not set") } + // Ensure we are running using the expected env-vars + // t.SetEnv does automatic cleanup / resets the values after the test + t.Setenv("ARM_OIDC_TOKEN_FILE_PATH", "") + provider := AzureADProvider() ctx := context.Background() @@ -313,7 +374,7 @@ func TestAccProvider_genericOidcAuth(t *testing.T) { ClientID: d.Get("client_id").(string), EnableAuthenticationUsingOIDC: true, - OIDCAssertionToken: idToken, + OIDCAssertionToken: *idToken, } return buildClient(ctx, provider, authConfig, "") diff --git a/scripts/increment-milestone.sh b/scripts/increment-milestone.sh new file mode 100644 index 0000000000..a0e981329b --- /dev/null +++ b/scripts/increment-milestone.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + + +while getopts u:r:t: flag +do + case "${flag}" in + r) release=${OPTARG};; + u) milestone_url=${OPTARG};; + t) token=${OPTARG};; + esac +done + +echo "Getting current milestone number..." +milestones=$(curl -L \ +-H "Accept: application/vnd.github+json" \ +-H "Authorization: Bearer $token" \ +-H "X-GitHub-Api-Version: 2022-11-28" \ +"${milestone_url}?state=open&sort=due_on&direction=desc") + +milestone_number=0 +milestones_json=$(echo "$milestones" | jq -c -r '.[]') +for milestone in ${milestones_json[@]}; do + if [[ $(echo $milestone | jq -r .title) == "$release" ]]; then + milestone_number=$(echo $milestone | jq -r .number) + break + fi +done + +if [[ $milestone_number != 0 ]]; then + + echo "Closing current milestone..." + curl -L \ + -X PATCH \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $token" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${milestone_url}/${milestone_number}" \ + -d '{"state":"closed"}' + + major=0 + weekly=0 + patch=0 + regex="v([0-9]+).([0-9]+).([0-9]+)" + if [[ $release =~ $regex ]]; then + major="${BASH_REMATCH[1]}" + weekly="${BASH_REMATCH[2]}" + patch="${BASH_REMATCH[3]}" + fi + + if [[ $patch == 0 ]]; then + if [[ $major != 0 ]]; then + + # Get next release version + weekly=$((weekly + 1 )) + new_milestone="v$major.$weekly.0" + # Get next release due date + date=$(date -d "next Thursday" +%Y-%m-%d) + date+="T12:00:00Z" + + echo "Creating new milestone..." + curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $token" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${milestone_url}" \ + -d "{\"title\":\"$new_milestone\", \"state\":\"open\", \"due_on\":\"$date\"}" + else + echo "Could not increment milestone" + exit 1 + fi + fi + +else + echo "Could not retrieve current milestone number to close" + exit 1 +fi