├── .github
├── no-response.yml
└── workflows
│ ├── cd.yml
│ └── ci.yml
├── .gitignore
├── .goreleaser.yml
├── CHANGELOG.md
├── GNUmakefile
├── LICENSE
├── README.md
├── README.release.md
├── b2
├── bindings.go
├── client.go
├── data_source_b2_account_info.go
├── data_source_b2_account_info_test.go
├── data_source_b2_application_key.go
├── data_source_b2_application_key_test.go
├── data_source_b2_bucket.go
├── data_source_b2_bucket_file.go
├── data_source_b2_bucket_file_signed_url.go
├── data_source_b2_bucket_file_signed_url_test.go
├── data_source_b2_bucket_file_test.go
├── data_source_b2_bucket_files.go
├── data_source_b2_bucket_files_test.go
├── data_source_b2_bucket_notification_rules.go
├── data_source_b2_bucket_notification_rules_test.go
├── data_source_b2_bucket_test.go
├── provider.go
├── provider_test.go
├── resource_b2_application_key.go
├── resource_b2_application_key_test.go
├── resource_b2_bucket.go
├── resource_b2_bucket_file_version.go
├── resource_b2_bucket_file_version_test.go
├── resource_b2_bucket_notification_rules.go
├── resource_b2_bucket_notification_rules_test.go
├── resource_b2_bucket_test.go
├── templates.go
├── utils.go
└── validators.go
├── docs
├── data-sources
│ ├── account_info.md
│ ├── application_key.md
│ ├── bucket.md
│ ├── bucket_file.md
│ ├── bucket_file_signed_url.md
│ ├── bucket_files.md
│ └── bucket_notification_rules.md
├── index.md
└── resources
│ ├── application_key.md
│ ├── bucket.md
│ ├── bucket_file_version.md
│ └── bucket_notification_rules.md
├── examples
├── application_key
│ └── main.tf
├── bucket
│ ├── example.txt
│ └── main.tf
├── bucket_file_lock
│ └── main.tf
├── bucket_notification_rules
│ └── main.tf
├── file_version_sse_c
│ └── main.tf
└── provider
│ └── provider.tf
├── go.mod
├── go.sum
├── main.go
├── python-bindings
├── GNUmakefile
├── b2_terraform
│ ├── __init__.py
│ ├── arg_parser.py
│ ├── json_encoder.py
│ ├── provider_tool.py
│ └── terraform_structures.py
├── py-terraform-provider-b2.spec
├── requirements-dev.txt
└── requirements.txt
├── scripts
├── check-gofmt.py
└── check-headers.py
├── templates
└── index.md.tmpl
└── tools
├── go.mod
├── go.sum
└── main.go
/.github/no-response.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-no-response - https://github.com/probot/no-response
2 |
3 | # Number of days of inactivity before an Issue is closed for lack of response
4 | daysUntilClose: 14
5 |
6 | # Label requiring a response
7 | responseRequiredLabel: more-information-needed
8 |
9 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable
10 | closeComment: >
11 | This issue has been automatically closed because there has been no response
12 | to our request for more information from the original author. With only the
13 | information that is currently in the issue, we don't have enough information
14 | to take action. Please reach out if you have or find the answers we need so
15 | that we can investigate or assist you further.
16 |
--------------------------------------------------------------------------------
/.github/workflows/cd.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Delivery
2 |
3 | on:
4 | push:
5 | tags: ['v*'] # push events to matching v*, i.e. v1.0, v20.15.10
6 |
7 | defaults:
8 | run:
9 | shell: bash
10 |
11 | env:
12 | PYTHON_DEFAULT_VERSION: '3.13'
13 | GO_DEFAULT_VERSION: '1.24'
14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15 |
16 | jobs:
17 | build-pybindings:
18 | runs-on: ${{ matrix.conf.runner }}
19 | strategy:
20 | fail-fast: false
21 | matrix:
22 | conf:
23 | - { runner: ubuntu-latest, os: linux, arch: amd64 }
24 | - { runner: ubuntu-latest, os: linux, arch: arm64 }
25 | - { runner: macos-13, os: darwin, arch: amd64 }
26 | - { runner: macos-14, os: darwin, arch: arm64 }
27 | - { runner: windows-2019, os: windows, arch: amd64 }
28 | outputs:
29 | version: ${{ steps.build.outputs.version }}
30 | steps:
31 | - uses: actions/checkout@v4
32 | - name: Start a Docker container (linux-arm64)
33 | if: matrix.conf.os == 'linux' && matrix.conf.arch == 'arm64'
34 | run: |
35 | docker run --privileged --rm tonistiigi/binfmt:qemu-v8.0.4 --install arm64
36 | docker run --detach \
37 | --platform linux/arm64 \
38 | --volume .:/work \
39 | --name builder \
40 | arm64v8/python:${{ env.PYTHON_DEFAULT_VERSION }}-bullseye \
41 | /bin/bash -c "sleep infinity"
42 | - name: Start a Docker container (linux-amd64)
43 | if: matrix.conf.os == 'linux' && matrix.conf.arch == 'amd64'
44 | run: |
45 | docker run --detach \
46 | --volume .:/work \
47 | --name builder \
48 | python:${{ env.PYTHON_DEFAULT_VERSION }}-bullseye \
49 | /bin/bash -c "sleep infinity"
50 | - name: Set up Python ${{ env.PYTHON_DEFAULT_VERSION }} (darwin, windows)
51 | if: matrix.conf.os != 'linux'
52 | uses: actions/setup-python@v5
53 | with:
54 | python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
55 | - name: Define command wrapper (linux)
56 | if: matrix.conf.os == 'linux'
57 | run: |
58 | # We'll define a 'run' command that will run our commands in the container.
59 | echo '#!/bin/bash' > run
60 | echo 'command="$*"; docker exec --workdir /work/python-bindings builder /bin/bash -c "$command"' >> run
61 | chmod +x run
62 | sudo mv run /usr/local/bin/run
63 | - name: Define command wrapper (darwin)
64 | if: matrix.conf.os == 'darwin'
65 | run: |
66 | # MacOS wrapper just runs commands in python-bindings directory
67 | echo '#!/bin/bash' > run
68 | echo 'pushd python-bindings; "$@"; popd' >> run
69 | chmod +x run
70 | sudo mv run /usr/local/bin/run
71 | - name: Define command wrapper (windows)
72 | if: matrix.conf.os == 'windows'
73 | run: |
74 | # Windows wrapper just runs commands in python-bindings directory
75 | echo '#!/bin/bash' > run
76 | echo 'pushd python-bindings; "$@"; popd' >> run
77 | chmod +x run
78 | mv run /usr/bin/run
79 | - name: Install system dependencies (linux)
80 | if: matrix.conf.os == 'linux'
81 | run: |
82 | run apt update -y
83 | run apt install scons patchelf libnss3-dev -y
84 | - name: Install Python dependencies
85 | run: |
86 | run make deps
87 | - name: Build Python bindings
88 | id: build
89 | run: |
90 | run make build
91 | - if: matrix.conf.os == 'linux'
92 | run: |
93 | sudo chmod -R a+r python-bindings
94 | - name: Upload Python bindings
95 | uses: actions/upload-artifact@v4
96 | with:
97 | name: py-terraform-provider-b2-${{ matrix.conf.os }}-${{ matrix.conf.arch }}
98 | path: python-bindings/dist/py-terraform-provider-b2
99 | if-no-files-found: error
100 | retention-days: 1
101 | build-and-deploy:
102 | needs: [build-pybindings]
103 | env:
104 | NOPYBINDINGS: 1 # do not build python bindings
105 | runs-on: ubuntu-latest
106 | outputs:
107 | upload_url: ${{ steps.create-release.outputs.upload_url }}
108 | steps:
109 | - uses: actions/checkout@v4
110 | with:
111 | fetch-depth: 0
112 | - name: Set up Go ${{ env.GO_DEFAULT_VERSION }}
113 | uses: actions/setup-go@v5
114 | with:
115 | go-version: ${{ env.GO_DEFAULT_VERSION }}
116 | - name: Install dependencies
117 | run: |
118 | make deps
119 | - name: Download python bindings for all OSes
120 | uses: actions/download-artifact@v4
121 | with:
122 | path: python-bindings/dist/artifacts/
123 | - name: Postprocess python bindings
124 | working-directory: python-bindings/dist
125 | run: |
126 | mv artifacts/py-terraform-provider-b2-linux-amd64/py-terraform-provider-b2 py-terraform-provider-b2-linux-amd64
127 | mv artifacts/py-terraform-provider-b2-linux-arm64/py-terraform-provider-b2 py-terraform-provider-b2-linux-arm64
128 | mv artifacts/py-terraform-provider-b2-darwin-amd64/py-terraform-provider-b2 py-terraform-provider-b2-darwin-amd64
129 | mv artifacts/py-terraform-provider-b2-darwin-arm64/py-terraform-provider-b2 py-terraform-provider-b2-darwin-arm64
130 | mv artifacts/py-terraform-provider-b2-windows-amd64/py-terraform-provider-b2 py-terraform-provider-b2-windows-amd64
131 | - name: Set release version output
132 | id: version
133 | run: |
134 | tag=${{ github.ref_name }}
135 | # Strip the prefix 'v'
136 | version=${tag:1}
137 | echo "version=$version" >> $GITHUB_OUTPUT
138 | - name: Read the Changelog
139 | id: read-changelog
140 | uses: mindsers/changelog-reader-action@v2
141 | with:
142 | version: ${{ steps.version.outputs.version }}
143 | - name: Import GPG key
144 | id: import_gpg
145 | uses: crazy-max/ghaction-import-gpg@v6
146 | with:
147 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
148 | passphrase: ${{ secrets.PASSPHRASE }}
149 | - name: Create GitHub release
150 | uses: goreleaser/goreleaser-action@v6
151 | with:
152 | version: '~> v2'
153 | args: release --clean -p 1
154 | env:
155 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
156 | - name: Update GitHub release
157 | uses: softprops/action-gh-release@v2
158 | with:
159 | name: v${{ steps.version.outputs.version }}
160 | body: ${{ steps.read-changelog.outputs.changes }}
161 | draft: false
162 | prerelease: false
163 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | paths-ignore:
7 | - '.github/no-response.yml'
8 | - '.github/workflows/cd.yml'
9 | - 'LICENSE'
10 | - 'README.md'
11 | - 'README.release.md'
12 | pull_request:
13 | branches: [master]
14 |
15 | defaults:
16 | run:
17 | shell: bash
18 |
19 | env:
20 | PYTHON_DEFAULT_VERSION: '3.13'
21 | GO_DEFAULT_VERSION: '1.24'
22 |
23 | jobs:
24 | lint:
25 | runs-on: ubuntu-latest
26 | steps:
27 | - uses: actions/checkout@v4
28 | - name: Set up Python ${{ env.PYTHON_DEFAULT_VERSION }}
29 | uses: deadsnakes/action@v3.1.0 # staticx doesn't work with python installed by setup-python action
30 | with:
31 | python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
32 | - name: Set up Go ${{ env.GO_DEFAULT_VERSION }}
33 | uses: actions/setup-go@v5
34 | with:
35 | go-version: ${{ env.GO_DEFAULT_VERSION }}
36 | - name: Install dependencies
37 | run: |
38 | make deps
39 | - name: Run dependency checker
40 | run: |
41 | make deps-check
42 | - name: Run linters
43 | run: |
44 | make lint
45 | - name: Run docs linters
46 | run: |
47 | make docs-lint
48 | - name: Validate changelog
49 | # Library was designed to be used with pull requests only.
50 | if: ${{ github.event_name == 'pull_request' && github.actor != 'dependabot[bot]' }}
51 | uses: zattoo/changelog@v2
52 | with:
53 | token: ${{ github.token }}
54 | build:
55 | needs: lint
56 | runs-on: ${{ matrix.conf.runner }}
57 | strategy:
58 | fail-fast: false
59 | matrix:
60 | conf:
61 | - { runner: ubuntu-latest, os: linux, arch: amd64 }
62 | - { runner: macos-13, os: darwin, arch: amd64 }
63 | - { runner: macos-14, os: darwin, arch: arm64 }
64 | - { runner: windows-2019, os: windows, arch: amd64 }
65 | steps:
66 | - uses: actions/checkout@v4
67 | - name: Set up Python ${{ env.PYTHON_DEFAULT_VERSION }} (ubuntu-latest)
68 | if: matrix.conf.os == 'linux'
69 | uses: deadsnakes/action@v3.1.0 # staticx doesn't work with python installed by setup-python action
70 | with:
71 | python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
72 | - name: Set up Python ${{ env.PYTHON_DEFAULT_VERSION }}
73 | if: matrix.conf.os != 'linux'
74 | uses: actions/setup-python@v5
75 | with:
76 | python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
77 | - name: Set up Go ${{ env.GO_DEFAULT_VERSION }}
78 | uses: actions/setup-go@v5
79 | with:
80 | go-version: ${{ env.GO_DEFAULT_VERSION }}
81 | - name: Install dependencies
82 | run: |
83 | make deps
84 | - name: Build the provider
85 | run: |
86 | make build
87 | - name: Upload python bindings
88 | uses: actions/upload-artifact@v4
89 | with:
90 | name: py-terraform-provider-b2-${{ runner.os }}-${{ runner.arch }}
91 | path: python-bindings/dist/py-terraform-provider-b2
92 | if-no-files-found: error
93 | retention-days: 1
94 | test:
95 | needs: build
96 | env:
97 | B2_TEST_APPLICATION_KEY: ${{ secrets.B2_TEST_APPLICATION_KEY }}
98 | B2_TEST_APPLICATION_KEY_ID: ${{ secrets.B2_TEST_APPLICATION_KEY_ID }}
99 | NOPYBINDINGS: 1 # do not build python buildings
100 | runs-on: ${{ matrix.conf.runner }}
101 | strategy:
102 | fail-fast: false
103 | matrix:
104 | conf:
105 | - { runner: ubuntu-latest, os: linux, arch: amd64, terraform: '1.9.*' }
106 | - { runner: ubuntu-latest, os: linux, arch: amd64, terraform: '1.8.*' }
107 | # for macOS, the latest terraform is enough for ACC tests
108 | - { runner: macos-13, os: darwin, arch: amd64, terraform: '1.9.*' }
109 | - { runner: macos-14, os: darwin, arch: arm64, terraform: '1.9.*' }
110 | # for Windows, the latest terraform is enough for ACC tests
111 | - { runner: windows-2019, os: windows, arch: amd64, terraform: '1.9.*' }
112 | steps:
113 | - uses: actions/checkout@v4
114 | - name: Set up Go ${{ env.GO_DEFAULT_VERSION }}
115 | if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }}
116 | uses: actions/setup-go@v5
117 | with:
118 | go-version: ${{ env.GO_DEFAULT_VERSION }}
119 | - uses: hashicorp/setup-terraform@v3
120 | name: Set up Terraform ${{ matrix.conf.terraform }}
121 | with:
122 | terraform_version: ${{ matrix.conf.terraform }}
123 | terraform_wrapper: false
124 | - name: Download python bindings for given OS
125 | if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }}
126 | uses: actions/download-artifact@v4
127 | with:
128 | name: py-terraform-provider-b2-${{ runner.os }}-${{ runner.arch }}
129 | path: python-bindings/dist/
130 | - name: Run acceptance tests
131 | if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }}
132 | timeout-minutes: 120
133 | run: |
134 | make testacc
135 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.dll
2 | *.exe
3 | .DS_Store
4 | example.tf
5 | terraform.tfplan
6 | terraform.tfstate
7 | bin/
8 | dist/
9 | modules-dev/
10 | /pkg/
11 | website/.vagrant
12 | website/.bundle
13 | website/build
14 | website/node_modules
15 | .vagrant/
16 | *.backup
17 | ./*.tfstate
18 | .terraform/
19 | .terraform.lock.hcl
20 | *.log
21 | *.bak
22 | *~
23 | .*.swp
24 | .idea
25 | *.iml
26 | *.test
27 | *.iml
28 |
29 | website/vendor
30 |
31 | # Test exclusions
32 | !command/test-fixtures/**/*.tfstate
33 | !command/test-fixtures/**/.terraform/
34 |
35 | # Keep windows files with windows line endings
36 | *.winfile eol=crlf
37 |
38 | # Go
39 | .go-version
40 |
41 | # Provider binary
42 | /terraform-provider-b2
43 |
44 | # Python bindings
45 | *.pyc
46 | *.egg-info
47 | .python-version
48 | b2/py-terraform-provider-b2
49 | build/
50 | dist/
51 | venv/
52 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | before:
3 | hooks:
4 | - go mod download
5 | builds:
6 | - binary: '{{ .ProjectName }}_{{ .Version }}'
7 | mod_timestamp: '{{ .CommitTimestamp }}'
8 | flags:
9 | - -trimpath
10 | ldflags:
11 | - '-s -w -X main.version={{.Version}}'
12 | goos:
13 | - linux
14 | - darwin
15 | - windows
16 | goarch:
17 | - amd64
18 | - arm64
19 | ignore:
20 | - goos: windows
21 | goarch: arm64
22 | hooks:
23 | pre:
24 | - cp python-bindings/dist/py-terraform-provider-b2-{{ .Os }}-{{ .Arch }} b2/py-terraform-provider-b2
25 | post:
26 | - rm -f b2/py-terraform-provider-b2
27 | env:
28 | # goreleaser does not work with CGO, it could also complicate
29 | # usage by users in CI/CD systems like Terraform Cloud where
30 | # they are unable to install libraries.
31 | - CGO_ENABLED=0
32 | archives:
33 | - format: zip
34 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
35 | checksum:
36 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
37 | algorithm: sha256
38 | signs:
39 | - artifacts: checksum
40 | args:
41 | # if you are using this in a GitHub action or some other automated pipeline, you
42 | # need to pass the batch flag to indicate its not interactive.
43 | - "--batch"
44 | - "--local-user"
45 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key
46 | - "--output"
47 | - "${signature}"
48 | - "--detach-sign"
49 | - "${artifact}"
50 | changelog:
51 | disable: true
52 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [Unreleased]
8 |
9 | ### Changed
10 | * Upgraded b2sdk from v2.4.1 to v2.8.0
11 | * Use `b2sdk.v2` in python bindings
12 |
13 | ### Infrastructure
14 | * Upgrade pyinstaller to 6.11.1
15 | * Use `go vet` for linting
16 | * Use Go 1.24.1
17 |
18 | ### Fixed
19 | * Fix an issue with missing `content_md5` in case the file has been uploaded as a large file
20 |
21 | ## [0.10.0] - 2025-01-10
22 |
23 | ### Added
24 | * Added support for Event Notifications via `b2_bucket_notification_rules` resource and data source
25 |
26 | ### Infrastructure
27 | * Replace deprecated macos-12 with macos-13
28 | * Use crazy-max/ghaction-import-gpg action as a replacement of deprecated paultyng/ghaction-import-gpg
29 | * Use Python 3.13 for embedded pybindings
30 |
31 | ## [0.9.0] - 2024-10-20
32 |
33 | ### Infrastructure
34 | * Replace removed macOS 11 Big Sur in favour of macOS 12 Monterey in CI/CD
35 | * Add support for Windows
36 | * Use Python 3.12 for embedded pybindings
37 | * Use Go 1.22
38 |
39 | ### Fixed
40 | * Fixed `allowed_operations` stability issue
41 | * Use macos-14 for ARM as macos-13-large is Intel
42 | * Clarified the purpose of the `endpoint`/`B2_ENDPOINT` configuration value
43 |
44 | ## [0.8.12] - 2024-06-20
45 |
46 | ### Infrastructure
47 | * Fixed goreleaser v2 configuration
48 |
49 | ## [0.8.11] - 2024-06-20
50 |
51 | ### Infrastructure
52 | * Fixed continuous delivery issue caused by goreleaser update
53 |
54 | ## [0.8.10] - 2024-06-20
55 |
56 | ### Changed
57 | * Upgraded b2sdk from v1.18.0 to v2.4.1
58 | * Upgraded pyinstaller from v6.3.0 to v6.8.0
59 |
60 | ## [0.8.9] - 2024-01-05
61 |
62 | ### Changed
63 | * Upgraded github.com/cloudflare/circl from v1.3.3 to v1.3.7
64 |
65 | ### Fixed
66 | * Fixed bucket update when is_file_lock_enabled is not set
67 |
68 | ## [0.8.8] - 2023-12-28
69 |
70 | ### Changed
71 | * Upgraded pyinstaller to v6.3.0
72 |
73 | ## [0.8.7] - 2023-12-27
74 |
75 | ### Changed
76 | * Upgraded github.com/hashicorp/terraform-plugin-sdk to v2.31.0
77 |
78 | ## [0.8.6] - 2023-12-22
79 |
80 | ### Fixed
81 | * Fix arm64 builds
82 |
83 | ## [0.8.5] - 2023-11-24
84 |
85 | ### Changed
86 | * Upgraded go to 1.20 and github.com/hashicorp/terraform-plugin-sdk to v2.30.0
87 |
88 | ### Infrastructure
89 | * Disable changelog verification for dependabot PRs
90 | * Upgrade macOS version in CI/CD
91 | * Upgrade Terraform version for ACC tests
92 | * Run ACC tests for all supported Terraform versions
93 | * Do not use deprecated `::set-output` GitHub Actions command in favor of `GITHUB_OUTPUT` env
94 |
95 | ### Fixed
96 | * Reconcile missing Application Key caused by the resource drift
97 | * Fix reconciliation of missing Bucket caused by the resource drift
98 | * Fix bucket cleanup after failed creation
99 |
100 | ## [0.8.4] - 2023-03-13
101 |
102 | ### Infrastructure
103 | * Upgraded terraform-plugin-docs 0.5.1 -> 0.13.0
104 | * Upgraded golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd -> 0.7.0
105 |
106 | ## [0.8.3] - 2023-02-20
107 |
108 | ### Infrastructure
109 | * Upgraded golang.org/x/net 0.5.0 -> 0.7.0
110 |
111 | ## [0.8.2] - 2023-02-17
112 |
113 | ### Infrastructure
114 | * Upgraded goutils 1.1.0 -> 1.1.1 and aws to 1.33.0
115 | * Ensured that changelog validation only happens on pull requests
116 |
117 | ## [0.8.1] - 2022-06-24
118 |
119 | ### Changed
120 | * Upgraded github.com/hashicorp/terraform-plugin-sdk/ to v2.17.0 and github.com/hashicorp/go-getter to v1.6.2
121 |
122 | ### Fixed
123 | * Fixed golangcli-lint breaking on Github
124 |
125 | ## [0.8.0] - 2022-03-27
126 |
127 | ### Added
128 | * Added importer for b2_bucket and b2_application_key resources
129 | * Added signed URL as data source to allow downloading files from private bucket during provisioning without storing an API key
130 |
131 | ### Changed
132 | * Upgraded go to 1.18 and github.com/hashicorp/terraform-plugin-sdk/ to v2.12.0
133 | * Upgraded b2sdk to 1.14.1, which allowed using improved API calls for listing files and making Python parts simpler
134 | * Upgraded PyInstaller to 4.10, which should help resolve some issues with running on Apple M1 silicon
135 |
136 | ## [0.7.1] - 2021-10-14
137 |
138 | ### Changed
139 | * When a bucket that is in state no longer exists, warning is logged and the bucket is removed from state
140 |
141 | ## [0.7.0] - 2021-09-24
142 |
143 | ### Fixed
144 | * Fix for static linking of Python bindings for Linux (CD uses python container)
145 |
146 | ## [0.6.1] - 2021-09-01
147 |
148 | ### Changed
149 | * When deleting bucket that bucket no longer exists, error is silently ignored
150 | * Terraform 1.0.0 or later required
151 |
152 | ## [0.6.0] - 2021-07-06
153 |
154 | ### Added
155 | * Support SSE-C encryption mode for files
156 | * Initial (experimental) support for Alpine Linux
157 | * Support for Apple M1 (arm64) architecture on Mac OS (for main plugin binary, Python bindings will still use Rosetta)
158 |
159 | ## [0.5.0] - 2021-05-31
160 |
161 | ### Added
162 | * Support isFileLockEnabled for buckets
163 | * Support defaultRetention for buckets
164 |
165 | ### Fixed
166 | * Fix acceptance tests breaking when new response fields are added to the API
167 |
168 | ### Changed
169 | * Upgraded b2sdk version to 1.8.0
170 |
171 | ## [0.4.0] - 2021-04-08
172 |
173 | ### Added
174 | * Show S3-compatible API URL in `b2_account_info` data source
175 |
176 | ### Fixed
177 | * Upgrade b2sdk version - fix for server response change regarding SSE
178 |
179 | ## [0.3.0] - 2021-03-27
180 |
181 | ### Added
182 | * Added `b2_account_info` data source
183 | * Add support for SSE-B2 server-side encryption mode
184 |
185 | ### Changed
186 | * Better handling sensitive data in Terraform logs
187 | * Upgrade b2sdk version `>=1.4.0,<2.0.0`
188 |
189 | ## [0.2.1] - 2021-02-11
190 |
191 | ### Changed
192 | * Upgrade b2sdk version
193 |
194 | ### Fixed
195 | * Append Terraform versions to the User-Agent
196 | * Fix the defaults for lifecycle rules #4
197 |
198 | ## [0.2.0] - 2021-01-22
199 |
200 | ### Added
201 | * Added `b2_bucket` data source
202 | * Added `b2_bucket_file` data source
203 | * Added `b2_bucket_files` data source
204 | * Added `b2_application_key` resource
205 | * Added `b2_bucket` resource
206 | * Added `b2_bucket_file_version` resource
207 |
208 | ### Changed
209 | * Extended `b2` provider
210 | * Extended `b2_application_key` data source
211 | * Improved python bindings
212 |
213 | ## [0.1.0] - 2020-11-30
214 |
215 | ### Added
216 | * Implementation of PoC (simple `b2_application_key` data source)
217 |
218 | [Unreleased]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.10.0...HEAD
219 | [0.10.0]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.9.0...v0.10.0
220 | [0.9.0]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.12...v0.9.0
221 | [0.8.12]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.11...v0.8.12
222 | [0.8.11]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.10...v0.8.11
223 | [0.8.10]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.9...v0.8.10
224 | [0.8.9]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.8...v0.8.9
225 | [0.8.8]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.7...v0.8.8
226 | [0.8.7]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.6...v0.8.7
227 | [0.8.6]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.5...v0.8.6
228 | [0.8.5]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.4...v0.8.5
229 | [0.8.4]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.3...v0.8.4
230 | [0.8.3]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.2...v0.8.3
231 | [0.8.2]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.1...v0.8.2
232 | [0.8.1]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.8.0...v0.8.1
233 | [0.8.0]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.7.1...v0.8.0
234 | [0.7.1]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.7.0...v0.7.1
235 | [0.7.0]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.6.1...v0.7.0
236 | [0.6.1]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.6.0...v0.6.1
237 | [0.6.0]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.5.0...v0.6.0
238 | [0.5.0]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.4.0...v0.5.0
239 | [0.4.0]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.3.0...v0.4.0
240 | [0.3.0]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.2.1...v0.3.0
241 | [0.2.1]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.2.0...v0.2.1
242 | [0.2.0]: https://github.com/Backblaze/terraform-provider-b2/compare/v0.1.0...v0.2.0
243 | [0.1.0]: https://github.com/Backblaze/terraform-provider-b2/compare/240851d...v0.1.0
244 |
--------------------------------------------------------------------------------
/GNUmakefile:
--------------------------------------------------------------------------------
1 | HOSTNAME=localhost
2 | NAMESPACE=backblaze
3 | NAME=b2
4 | BINARY=terraform-provider-${NAME}
5 | VERSION=$(shell git describe --tags --abbrev=0 | cut -c2-)
6 | OS_ARCH=$(shell go env GOOS)_$(shell go env GOARCH)
7 |
8 | default: build
9 |
10 | .PHONY: _pybindings deps deps-check format lint testacc clean build install docs docs-lint
11 |
12 | _pybindings:
13 | ifeq ($(origin NOPYBINDINGS), undefined)
14 | @$(MAKE) -C python-bindings $(MAKECMDGOALS)
15 | else
16 | $(info Skipping python bindings (NOPYBINDINGS is defined))
17 | endif
18 |
19 | deps: _pybindings
20 | @go mod download
21 | @go mod tidy
22 | @cd tools && go mod download
23 | @cd tools && go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
24 | @cd tools && go mod tidy
25 |
26 | deps-check:
27 | @go mod tidy
28 | @cd tools && go mod tidy
29 | @git diff --exit-code -- go.mod go.sum tools/go.mod tools/go.sum || \
30 | (echo; echo "Unexpected difference in go.mod/go.sum files. Run 'make deps' command or revert any go.mod/go.sum changes and commit."; exit 1)
31 |
32 | format: _pybindings
33 | @go fmt ./...
34 | @terraform fmt -recursive ./examples/
35 |
36 | lint: _pybindings
37 | @python scripts/check-gofmt.py '**/*.go'
38 | @python scripts/check-headers.py '**/*.go'
39 | @test -f b2/py-terraform-provider-b2 || touch b2/py-terraform-provider-b2 # required by go:embed in bindings.go
40 | @go vet ./...
41 |
42 | testacc: _pybindings
43 | @cp python-bindings/dist/py-terraform-provider-b2 b2/
44 | @chmod +rx b2/py-terraform-provider-b2
45 | TF_ACC=1 go test ./${NAME} -v -count 1 -parallel 4 -timeout 120m $(TESTARGS)
46 |
47 | clean: _pybindings
48 | @rm -rf dist b2/py-terraform-provider-b2 ${BINARY}
49 |
50 | build: _pybindings
51 | @cp python-bindings/dist/py-terraform-provider-b2 b2/
52 | @go build -tags netgo -o ${BINARY}
53 |
54 | install: build
55 | @mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
56 | @mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
57 |
58 | docs: build
59 | @tfplugindocs
60 |
61 | docs-lint: build
62 | @tfplugindocs validate
63 | @tfplugindocs
64 | @git diff --exit-code -- docs/ || \
65 | (echo; echo "Unexpected difference in docs. Run 'make docs' command or revert any changes in the schema."; exit 1)
66 |
67 | all: deps lint build testacc
68 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Backblaze wants developers and organization to copy and re-use our
2 | code examples, so we make the samples available by several different
3 | licenses. One option is the MIT license (below). Other options are
4 | available here:
5 |
6 | https://www.backblaze.com/using_b2_code.html
7 |
8 |
9 | The MIT License (MIT)
10 |
11 | Copyright (c) 2020 Backblaze
12 |
13 | Permission is hereby granted, free of charge, to any person obtaining a copy
14 | of this software and associated documentation files (the "Software"), to deal
15 | in the Software without restriction, including without limitation the rights
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 | copies of the Software, and to permit persons to whom the Software is
18 | furnished to do so, subject to the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be included in all
21 | copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 | SOFTWARE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Terraform Provider B2
2 | =====================
3 | [](https://github.com/Backblaze/terraform-provider-b2/actions?query=workflow%3A%22Continuous+Integration%22)
4 |
5 | Terraform provider for Backblaze B2.
6 |
7 | The provider is written in go, but it uses official [B2 python SDK](https://github.com/Backblaze/b2-sdk-python/) embedded into the binary.
8 |
9 | Requirements
10 | ------------
11 |
12 | Runtime requirements:
13 | - [Terraform](https://www.terraform.io/downloads.html) >= 1.0.0
14 |
15 | Development requirements:
16 | - [Go](https://golang.org/doc/install) == 1.24
17 | - [Python](https://github.com/pyenv/pyenv) == 3.13
18 |
19 | Dependencies
20 | ------------
21 | *Note:* You should run it inside python virtualenv as it installs the dependencies for the python bindings as well.
22 |
23 | ```
24 | make deps
25 | ```
26 |
27 | Building
28 | --------
29 |
30 | ```
31 | make build
32 | ```
33 |
34 | Documentation
35 | -------------
36 |
37 | The documentation is generated from the provider source code using
38 | [`tfplugindocs`](https://github.com/hashicorp/terraform-plugin-docs). You will need to regenerate the documentation if
39 | you add or change a data source, resource or argument.
40 |
41 | ```
42 | make docs
43 | ```
44 |
45 | Installing
46 | ----------
47 |
48 | ```
49 | make install
50 | ```
51 |
52 | Testing
53 | -------
54 |
55 | *Note:* Acceptance tests create real resources, and often cost money to run.
56 |
57 | ```
58 | export B2_TEST_APPLICATION_KEY=your_app_key
59 | export B2_TEST_APPLICATION_KEY_ID=your_app_key_id
60 | make testacc
61 | ```
62 |
63 | Debugging
64 | ---------
65 |
66 | Set TF_LOG_PROVIDER and TF_LOG_PATH env variables to see detailed information from the provider.
67 | Check https://www.terraform.io/docs/internals/debugging.html for details
68 |
69 | Release History
70 | -----------------
71 |
72 | Please refer to the [changelog](CHANGELOG.md).
73 |
--------------------------------------------------------------------------------
/README.release.md:
--------------------------------------------------------------------------------
1 | # Release Process
2 |
3 | - Install dependencies:
4 | - `make deps`
5 | - Update the release history in `CHANGELOG.md`:
6 | - Change "Unreleased" to the current release version and date.
7 | - Create empty "Unreleased" section.
8 | - Add proper link to the new release (at the bottom of the file). Use GitHub [compare feature](https://docs.github.com/en/free-pro-team@latest/github/committing-changes-to-your-project/comparing-commits#comparing-tags) between two tags.
9 | - Update "Unreleased" link (at the bottom of the file).
10 | - Run linters:
11 | - `make lint`
12 | - Run tests:
13 | - `export B2_TEST_APPLICATION_KEY=your_app_key`
14 | - `export B2_TEST_APPLICATION_KEY_ID=your_app_key_id`
15 | - `make testacc`
16 | - Update docs:
17 | - `make docs`
18 | - Commit and push to GitHub, then wait for CI workflow to complete successfully.
19 | - No need to make a branch. Push straight to `master`.
20 | - Tag in git and push tag to `origin`. (Version tags look like `v0.4.6`.)
21 | - `git tag vx.x.x`
22 | - `git push origin vx.x.x`
23 | - Wait for CD workflow to complete successfully.
24 | - Verify that the GitHub release is created
25 | - Verify that the release has been uploaded to the Terraform Registry
26 |
--------------------------------------------------------------------------------
/b2/bindings.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/bindings.go
4 | //
5 | // Copyright 2024 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "bufio"
15 | "embed"
16 | "io"
17 | "log"
18 | "os"
19 | "path/filepath"
20 | "runtime"
21 | "sync"
22 | )
23 |
24 | var (
25 | bindings *string
26 | //go:embed py-terraform-provider-b2
27 | content embed.FS
28 | lock = &sync.Mutex{}
29 | )
30 |
31 | func GetBindings() (string, error) {
32 | if bindings == nil {
33 | lock.Lock()
34 | defer lock.Unlock()
35 | }
36 | if bindings != nil {
37 | return *bindings, nil
38 | }
39 |
40 | sourceFile, err := content.Open("py-terraform-provider-b2")
41 | if err != nil {
42 | return "", err
43 | }
44 | defer sourceFile.Close()
45 |
46 | var tmpPattern string
47 | if runtime.GOOS == "windows" {
48 | tmpPattern = "py-terraform-provider*.exe"
49 | } else {
50 | tmpPattern = "py-terraform-provider*"
51 | }
52 |
53 | destinationFile, err := os.CreateTemp("", tmpPattern)
54 | if err != nil {
55 | return "", err
56 | }
57 | defer destinationFile.Close()
58 |
59 | destinationPath := filepath.ToSlash(destinationFile.Name())
60 | reader := bufio.NewReader(sourceFile)
61 | buf := make([]byte, 2048)
62 |
63 | for {
64 | _, err := reader.Read(buf)
65 |
66 | if err != nil {
67 | if err != io.EOF {
68 | return destinationPath, err
69 | }
70 |
71 | _, err = destinationFile.Seek(0, 0)
72 | if err != nil {
73 | return destinationPath, err
74 | }
75 |
76 | break
77 | }
78 |
79 | _, err = destinationFile.Write(buf)
80 | if err != nil {
81 | return destinationPath, err
82 | }
83 | }
84 |
85 | destinationFile.Close()
86 |
87 | err = os.Chmod(destinationPath, 0770)
88 | if err != nil {
89 | return destinationPath, err
90 | }
91 |
92 | bindings = &destinationPath
93 | log.Printf("Extracted pybindings: %s\n", *bindings)
94 | return *bindings, nil
95 | }
96 |
--------------------------------------------------------------------------------
/b2/client.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/client.go
4 | //
5 | // Copyright 2020 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "bytes"
15 | "context"
16 | "encoding/json"
17 | "fmt"
18 | "os"
19 | "os/exec"
20 |
21 | "github.com/hashicorp/terraform-plugin-log/tflog"
22 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
23 | )
24 |
25 | const (
26 | DATA_SOURCE_READ string = "data_source_read"
27 |
28 | RESOURCE_CREATE string = "resource_create"
29 | RESOURCE_READ string = "resource_read"
30 | RESOURCE_UPDATE string = "resource_update"
31 | RESOURCE_DELETE string = "resource_delete"
32 | )
33 |
34 | type Client struct {
35 | Exec string
36 | UserAgentAppend string
37 | ApplicationKeyId string
38 | ApplicationKey string
39 | Endpoint string
40 | DataSources map[string][]string
41 | Resources map[string][]string
42 | SensitiveDataSources map[string]map[string]bool
43 | SensitiveResources map[string]map[string]bool
44 | }
45 |
46 | func (c Client) apply(ctx context.Context, name string, op string, input map[string]interface{}) (map[string]interface{}, error) {
47 | tflog.Info(ctx, "Executing pybindings", map[string]interface{}{
48 | "name": name,
49 | "op": op,
50 | })
51 |
52 | tflog.Debug(ctx, "Input for pybindings", map[string]interface{}{
53 | "input": input,
54 | })
55 |
56 | cmd := exec.Command(c.Exec, name, op)
57 | cmd.Env = os.Environ()
58 | cmd.Env = append(cmd.Env, fmt.Sprintf("B2_USER_AGENT_APPEND=%s", c.UserAgentAppend))
59 |
60 | input["provider_application_key_id"] = c.ApplicationKeyId
61 | input["provider_application_key"] = c.ApplicationKey
62 | input["provider_endpoint"] = c.Endpoint
63 |
64 | inputJson, err := json.Marshal(input)
65 | if err != nil {
66 | // Should never happen
67 | return nil, err
68 | }
69 | cmd.Stdin = bytes.NewReader(inputJson)
70 |
71 | outputJson, err := cmd.Output()
72 |
73 | if err != nil {
74 | if exitErr, ok := err.(*exec.ExitError); ok {
75 | if exitErr.Stderr != nil && len(exitErr.Stderr) > 0 {
76 | err := fmt.Errorf("%s", string(exitErr.Stderr))
77 | tflog.Error(ctx, "Error in pybindings", map[string]interface{}{
78 | "stderr": err,
79 | })
80 | return nil, err
81 | }
82 | return nil, fmt.Errorf("failed to execute")
83 | } else {
84 | tflog.Error(ctx, "Error", map[string]interface{}{
85 | "err": err,
86 | })
87 | return nil, err
88 | }
89 | }
90 |
91 | output := map[string]interface{}{}
92 | err = json.Unmarshal(outputJson, &output)
93 | if err != nil {
94 | return nil, err
95 | }
96 |
97 | resourceName := "b2_" + name
98 | var sensitiveSchemaMap map[string]bool
99 | if op == DATA_SOURCE_READ {
100 | sensitiveSchemaMap = c.SensitiveDataSources[resourceName]
101 | } else {
102 | sensitiveSchemaMap = c.SensitiveResources[resourceName]
103 | }
104 |
105 | // Do not log application_key
106 | safeOutput := map[string]interface{}{}
107 | for k, v := range output {
108 | if sensitiveSchemaMap[k] {
109 | safeOutput[k] = "***"
110 | } else {
111 | safeOutput[k] = v
112 | }
113 | }
114 | tflog.Debug(ctx, "Safe output from pybindings", map[string]interface{}{
115 | "output": safeOutput,
116 | })
117 |
118 | return output, nil
119 | }
120 |
121 | func (c Client) populate(ctx context.Context, name string, op string, output map[string]interface{}, d *schema.ResourceData) error {
122 | tflog.Info(ctx, "Populating data from pybindings", map[string]interface{}{
123 | "name": name,
124 | "op": op,
125 | })
126 |
127 | resourceName := "b2_" + name
128 | var schemaList []string
129 | if op == DATA_SOURCE_READ {
130 | schemaList = c.DataSources[resourceName]
131 | } else {
132 | schemaList = c.Resources[resourceName]
133 | }
134 |
135 | for _, k := range schemaList {
136 | v, ok := output[k]
137 | if !ok {
138 | return fmt.Errorf("error getting %s", k)
139 | }
140 | if err := d.Set(k, v); err != nil {
141 | return fmt.Errorf("error setting %s: %s", k, err)
142 | }
143 | }
144 |
145 | return nil
146 | }
147 |
--------------------------------------------------------------------------------
/b2/data_source_b2_account_info.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_account_info.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18 | )
19 |
20 | func dataSourceB2AccountInfo() *schema.Resource {
21 | return &schema.Resource{
22 | Description: "B2 account info data source.",
23 |
24 | ReadContext: dataSourceB2AccountInfoRead,
25 |
26 | Schema: map[string]*schema.Schema{
27 | "account_id": {
28 | Description: "The identifier for the account.",
29 | Type: schema.TypeString,
30 | Computed: true,
31 | },
32 | "account_auth_token": {
33 | Description: "An authorization token to use with all calls, other than b2_authorize_account, that need an Authorization header. This authorization token is valid for at most 24 hours.",
34 | Type: schema.TypeString,
35 | Sensitive: true,
36 | Computed: true,
37 | },
38 | "api_url": {
39 | Description: "The base URL to use for all API calls except for uploading and downloading files.",
40 | Type: schema.TypeString,
41 | Computed: true,
42 | },
43 | "allowed": {
44 | Description: "An object containing the capabilities of this auth token, and any restrictions on using it.",
45 | Type: schema.TypeList,
46 | Elem: getDataSourceAllowedElem(),
47 | Computed: true,
48 | },
49 | "download_url": {
50 | Description: "The base URL to use for downloading files.",
51 | Type: schema.TypeString,
52 | Computed: true,
53 | },
54 | "s3_api_url": {
55 | Description: "The base URL to use for S3-compatible API calls.",
56 | Type: schema.TypeString,
57 | Computed: true,
58 | },
59 | },
60 | }
61 | }
62 |
63 | func dataSourceB2AccountInfoRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
64 | client := meta.(*Client)
65 | const name = "account_info"
66 | const op = DATA_SOURCE_READ
67 |
68 | input := map[string]interface{}{}
69 |
70 | output, err := client.apply(ctx, name, op, input)
71 | if err != nil {
72 | return diag.FromErr(err)
73 | }
74 |
75 | d.SetId(output["account_id"].(string))
76 |
77 | err = client.populate(ctx, name, op, output, d)
78 | if err != nil {
79 | return diag.FromErr(err)
80 | }
81 |
82 | return nil
83 | }
84 |
--------------------------------------------------------------------------------
/b2/data_source_b2_account_info_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_account_info_test.go
4 | //
5 | // Copyright 2020 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "regexp"
15 | "testing"
16 |
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
18 | )
19 |
20 | func TestAccDataSourceB2AccountInfo_basic(t *testing.T) {
21 | dataSourceName := "data.b2_account_info.test"
22 |
23 | resource.Test(t, resource.TestCase{
24 | PreCheck: func() { testAccPreCheck(t) },
25 | ProviderFactories: providerFactories,
26 | Steps: []resource.TestStep{
27 | {
28 | Config: testAccDataSourceB2AccountInfoConfig_basic(),
29 | Check: resource.ComposeTestCheckFunc(
30 | resource.TestMatchResourceAttr(dataSourceName, "account_id", regexp.MustCompile("^[a-z0-9]{12}$")),
31 | resource.TestMatchResourceAttr(dataSourceName, "account_auth_token", regexp.MustCompile("^[-=_a-zA-Z0-9]{77}$")),
32 | resource.TestMatchResourceAttr(dataSourceName, "api_url", regexp.MustCompile("https://api00[0-9].backblazeb2.com")),
33 | resource.TestCheckResourceAttr(dataSourceName, "allowed.#", "1"),
34 | resource.TestMatchResourceAttr(dataSourceName, "allowed.0.capabilities.#", regexp.MustCompile("[1-9][0-9]*")),
35 | resource.TestCheckResourceAttr(dataSourceName, "allowed.0.bucket_name", ""),
36 | resource.TestCheckResourceAttr(dataSourceName, "allowed.0.bucket_id", ""),
37 | resource.TestCheckResourceAttr(dataSourceName, "allowed.0.bucket_id", ""),
38 | resource.TestMatchResourceAttr(dataSourceName, "download_url", regexp.MustCompile("https://f00[0-9].backblazeb2.com")),
39 | resource.TestMatchResourceAttr(dataSourceName, "s3_api_url", regexp.MustCompile("https://s3.(us-west|eu-central)-00[0-9].backblazeb2.com")),
40 | ),
41 | },
42 | },
43 | })
44 | }
45 |
46 | func testAccDataSourceB2AccountInfoConfig_basic() string {
47 | return `
48 | data "b2_account_info" "test" {
49 | }
50 | `
51 | }
52 |
--------------------------------------------------------------------------------
/b2/data_source_b2_application_key.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_application_key.go
4 | //
5 | // Copyright 2020 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
19 | )
20 |
21 | func dataSourceB2ApplicationKey() *schema.Resource {
22 | return &schema.Resource{
23 | Description: "B2 application key data source.",
24 |
25 | ReadContext: dataSourceB2ApplicationKeyRead,
26 |
27 | Schema: map[string]*schema.Schema{
28 | "key_name": {
29 | Description: "The name assigned when the key was created.",
30 | Type: schema.TypeString,
31 | Required: true,
32 | ValidateFunc: validation.NoZeroValues,
33 | },
34 | "application_key_id": {
35 | Description: "The ID of the key.",
36 | Type: schema.TypeString,
37 | Computed: true,
38 | },
39 | "bucket_id": {
40 | Description: "When present, restricts access to one bucket.",
41 | Type: schema.TypeString,
42 | Computed: true,
43 | },
44 | "capabilities": {
45 | Description: "A set of strings, each one naming a capability the key has.",
46 | Type: schema.TypeSet,
47 | Elem: &schema.Schema{
48 | Type: schema.TypeString,
49 | },
50 | Computed: true,
51 | },
52 | "name_prefix": {
53 | Description: "When present, restricts access to files whose names start with the prefix.",
54 | Type: schema.TypeString,
55 | Optional: true,
56 | Computed: true,
57 | },
58 | "options": {
59 | Description: "A list of application key options.",
60 | Type: schema.TypeSet,
61 | Elem: &schema.Schema{
62 | Type: schema.TypeString,
63 | },
64 | Computed: true,
65 | },
66 | },
67 | }
68 | }
69 |
70 | func dataSourceB2ApplicationKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
71 | client := meta.(*Client)
72 | const name = "application_key"
73 | const op = DATA_SOURCE_READ
74 |
75 | input := map[string]interface{}{
76 | "key_name": d.Get("key_name").(string),
77 | }
78 |
79 | output, err := client.apply(ctx, name, op, input)
80 | if err != nil {
81 | return diag.FromErr(err)
82 | }
83 |
84 | d.SetId(output["application_key_id"].(string))
85 |
86 | err = client.populate(ctx, name, op, output, d)
87 | if err != nil {
88 | return diag.FromErr(err)
89 | }
90 |
91 | return nil
92 | }
93 |
--------------------------------------------------------------------------------
/b2/data_source_b2_application_key_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_application_key_test.go
4 | //
5 | // Copyright 2020 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "fmt"
15 | "testing"
16 |
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
19 | )
20 |
21 | func TestAccDataSourceB2ApplicationKey_basic(t *testing.T) {
22 | resourceName := "b2_application_key.test"
23 | dataSourceName := "data.b2_application_key.test"
24 |
25 | keyName := acctest.RandomWithPrefix("test-b2-tfp")
26 |
27 | resource.Test(t, resource.TestCase{
28 | PreCheck: func() { testAccPreCheck(t) },
29 | ProviderFactories: providerFactories,
30 | Steps: []resource.TestStep{
31 | {
32 | Config: testAccDataSourceB2ApplicationKeyConfig_basic(keyName),
33 | Check: resource.ComposeTestCheckFunc(
34 | resource.TestCheckResourceAttrPair(dataSourceName, "application_key_id", resourceName, "application_key_id"),
35 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", resourceName, "bucket_id"),
36 | resource.TestCheckResourceAttr(dataSourceName, "capabilities.#", "1"),
37 | resource.TestCheckResourceAttr(dataSourceName, "capabilities.0", "readFiles"),
38 | resource.TestCheckResourceAttrPair(dataSourceName, "capabilities", resourceName, "capabilities"),
39 | resource.TestCheckResourceAttr(dataSourceName, "key_name", keyName),
40 | resource.TestCheckResourceAttrPair(dataSourceName, "key_name", resourceName, "key_name"),
41 | resource.TestCheckResourceAttrPair(dataSourceName, "name_prefix", resourceName, "name_prefix"),
42 | resource.TestCheckResourceAttrPair(dataSourceName, "options", resourceName, "options"),
43 | ),
44 | },
45 | },
46 | })
47 | }
48 |
49 | func TestAccDataSourceB2ApplicationKey_all(t *testing.T) {
50 | resourceName := "b2_application_key.test"
51 | dataSourceName := "data.b2_application_key.test"
52 |
53 | keyName := acctest.RandomWithPrefix("test-b2-tfp")
54 |
55 | resource.Test(t, resource.TestCase{
56 | PreCheck: func() { testAccPreCheck(t) },
57 | ProviderFactories: providerFactories,
58 | Steps: []resource.TestStep{
59 | {
60 | Config: testAccDataSourceB2ApplicationKeyConfig_all(keyName),
61 | Check: resource.ComposeTestCheckFunc(
62 | resource.TestCheckResourceAttrPair(dataSourceName, "application_key_id", resourceName, "application_key_id"),
63 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", resourceName, "bucket_id"),
64 | resource.TestCheckResourceAttr(dataSourceName, "capabilities.#", "1"),
65 | resource.TestCheckResourceAttr(dataSourceName, "capabilities.0", "writeFiles"),
66 | resource.TestCheckResourceAttrPair(dataSourceName, "capabilities", resourceName, "capabilities"),
67 | resource.TestCheckResourceAttr(dataSourceName, "key_name", keyName),
68 | resource.TestCheckResourceAttrPair(dataSourceName, "key_name", resourceName, "key_name"),
69 | resource.TestCheckResourceAttr(dataSourceName, "name_prefix", "prefix"),
70 | resource.TestCheckResourceAttrPair(dataSourceName, "name_prefix", resourceName, "name_prefix"),
71 | resource.TestCheckResourceAttrPair(dataSourceName, "options", resourceName, "options"),
72 | ),
73 | },
74 | },
75 | })
76 | }
77 |
78 | func testAccDataSourceB2ApplicationKeyConfig_basic(keyName string) string {
79 | return fmt.Sprintf(`
80 | resource "b2_application_key" "test" {
81 | key_name = "%s"
82 | capabilities = ["readFiles"]
83 | }
84 |
85 | data "b2_application_key" "test" {
86 | key_name = b2_application_key.test.key_name
87 |
88 | depends_on = [
89 | b2_application_key.test,
90 | ]
91 | }
92 | `, keyName)
93 | }
94 |
95 | func testAccDataSourceB2ApplicationKeyConfig_all(keyName string) string {
96 | return fmt.Sprintf(`
97 | resource "b2_bucket" "test" {
98 | bucket_name = "%s"
99 | bucket_type = "allPrivate"
100 | }
101 |
102 | resource "b2_application_key" "test" {
103 | key_name = "%s"
104 | capabilities = ["writeFiles"]
105 | bucket_id = b2_bucket.test.bucket_id
106 | name_prefix = "prefix"
107 | }
108 |
109 | data "b2_application_key" "test" {
110 | key_name = b2_application_key.test.key_name
111 |
112 | depends_on = [
113 | b2_application_key.test,
114 | ]
115 | }
116 | `, keyName, keyName)
117 | }
118 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
19 | )
20 |
21 | func dataSourceB2Bucket() *schema.Resource {
22 | return &schema.Resource{
23 | Description: "B2 bucket data source.",
24 |
25 | ReadContext: dataSourceB2BucketRead,
26 |
27 | Schema: map[string]*schema.Schema{
28 | "bucket_name": {
29 | Description: "The name of the bucket.",
30 | Type: schema.TypeString,
31 | Required: true,
32 | ValidateFunc: validation.NoZeroValues,
33 | },
34 | "account_id": {
35 | Description: "Account ID that the bucket belongs to.",
36 | Type: schema.TypeString,
37 | Computed: true,
38 | },
39 | "bucket_id": {
40 | Description: "The ID of the bucket.",
41 | Type: schema.TypeString,
42 | Computed: true,
43 | },
44 | "bucket_info": {
45 | Description: "User-defined information to be stored with the bucket.",
46 | Type: schema.TypeMap,
47 | Elem: &schema.Schema{
48 | Type: schema.TypeString,
49 | },
50 | Computed: true,
51 | },
52 | "bucket_type": {
53 | Description: "The bucket type. Either 'allPublic', meaning that files in this bucket can be downloaded by anybody, or 'allPrivate'.",
54 | Type: schema.TypeString,
55 | Computed: true,
56 | },
57 | "cors_rules": {
58 | Description: "The initial list of CORS rules for this bucket.",
59 | Type: schema.TypeList,
60 | Elem: getCorsRulesElem(true),
61 | Computed: true,
62 | },
63 | "file_lock_configuration": {
64 | Description: "The default File Lock retention settings for this bucket.",
65 | Type: schema.TypeList,
66 | Elem: getFileLockConfigurationElem(true),
67 | Computed: true,
68 | },
69 | "default_server_side_encryption": {
70 | Description: "The default server-side encryption settings of this bucket.",
71 | Type: schema.TypeList,
72 | Elem: getServerSideEncryptionElem(true),
73 | Computed: true,
74 | },
75 | "lifecycle_rules": {
76 | Description: "The initial list of lifecycle rules for this bucket.",
77 | Type: schema.TypeList,
78 | Elem: getLifecycleRulesElem(true),
79 | Computed: true,
80 | },
81 | "options": {
82 | Description: "List of bucket options.",
83 | Type: schema.TypeSet,
84 | Elem: &schema.Schema{
85 | Type: schema.TypeString,
86 | },
87 | Computed: true,
88 | },
89 | "revision": {
90 | Description: "Bucket revision.",
91 | Type: schema.TypeInt,
92 | Computed: true,
93 | },
94 | },
95 | }
96 | }
97 |
98 | func dataSourceB2BucketRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
99 | client := meta.(*Client)
100 | const name = "bucket"
101 | const op = DATA_SOURCE_READ
102 |
103 | input := map[string]interface{}{
104 | "bucket_name": d.Get("bucket_name").(string),
105 | }
106 |
107 | output, err := client.apply(ctx, name, op, input)
108 | if err != nil {
109 | return diag.FromErr(err)
110 | }
111 |
112 | d.SetId(output["bucket_id"].(string))
113 |
114 | err = client.populate(ctx, name, op, output, d)
115 | if err != nil {
116 | return diag.FromErr(err)
117 | }
118 |
119 | return nil
120 | }
121 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket_file.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket_file.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
19 | )
20 |
21 | func dataSourceB2BucketFile() *schema.Resource {
22 | return &schema.Resource{
23 | Description: "B2 bucket file data source.",
24 |
25 | ReadContext: dataSourceB2BucketFileRead,
26 |
27 | Schema: map[string]*schema.Schema{
28 | "bucket_id": {
29 | Description: "The ID of the bucket.",
30 | Type: schema.TypeString,
31 | Required: true,
32 | ValidateFunc: validation.NoZeroValues,
33 | },
34 | "file_name": {
35 | Description: "The file name.",
36 | Type: schema.TypeString,
37 | Required: true,
38 | ValidateFunc: validation.NoZeroValues,
39 | },
40 | "show_versions": {
41 | Description: "Show all file versions.",
42 | Type: schema.TypeBool,
43 | Optional: true,
44 | },
45 | "file_versions": {
46 | Description: "File versions.",
47 | Type: schema.TypeList,
48 | Elem: getDataSourceFileVersionsElem(),
49 | Computed: true,
50 | },
51 | },
52 | }
53 | }
54 |
55 | func dataSourceB2BucketFileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
56 | client := meta.(*Client)
57 | const name = "bucket_file"
58 | const op = DATA_SOURCE_READ
59 |
60 | input := map[string]interface{}{
61 | "bucket_id": d.Get("bucket_id").(string),
62 | "file_name": d.Get("file_name").(string),
63 | "show_versions": d.Get("show_versions").(bool),
64 | }
65 |
66 | output, err := client.apply(ctx, name, op, input)
67 | if err != nil {
68 | return diag.FromErr(err)
69 | }
70 |
71 | d.SetId(output["_sha1"].(string))
72 |
73 | err = client.populate(ctx, name, op, output, d)
74 | if err != nil {
75 | return diag.FromErr(err)
76 | }
77 |
78 | return nil
79 | }
80 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket_file_signed_url.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket_file_signed_url.go
4 | //
5 | // Copyright 2022 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
19 | )
20 |
21 | func dataSourceB2BucketFileSignedUrl() *schema.Resource {
22 | return &schema.Resource{
23 | Description: "B2 signed URL for a bucket file data source.",
24 |
25 | ReadContext: dataSourceB2BucketFileSignedUrlRead,
26 |
27 | Schema: map[string]*schema.Schema{
28 | "bucket_id": {
29 | Description: "The ID of the bucket.",
30 | Type: schema.TypeString,
31 | Required: true,
32 | ValidateFunc: validation.StringIsNotEmpty,
33 | },
34 | "file_name": {
35 | Description: "The file name.",
36 | Type: schema.TypeString,
37 | Required: true,
38 | ValidateFunc: validation.StringIsNotEmpty,
39 | },
40 | "duration": {
41 | Description: "The duration for which the presigned URL is valid",
42 | Type: schema.TypeInt,
43 | Optional: true,
44 | },
45 | "signed_url": {
46 | Description: "The signed URL for the given file",
47 | Type: schema.TypeString,
48 | Computed: true,
49 | },
50 | },
51 | }
52 | }
53 |
54 | func dataSourceB2BucketFileSignedUrlRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
55 | client := meta.(*Client)
56 | const name = "bucket_file_signed_url"
57 | const op = DATA_SOURCE_READ
58 |
59 | input := map[string]interface{}{
60 | "bucket_id": d.Get("bucket_id").(string),
61 | "file_name": d.Get("file_name").(string),
62 | "duration": d.Get("duration").(int),
63 | }
64 |
65 | output, err := client.apply(ctx, name, op, input)
66 | if err != nil {
67 | return diag.FromErr(err)
68 | }
69 |
70 | d.SetId(output["signed_url"].(string))
71 |
72 | err = client.populate(ctx, name, op, output, d)
73 | if err != nil {
74 | return diag.FromErr(err)
75 | }
76 |
77 | return nil
78 | }
79 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket_file_signed_url_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket_file_signed_url_test.go
4 | //
5 | // Copyright 2022 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "fmt"
15 | "os"
16 | "testing"
17 |
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
19 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
20 | )
21 |
22 | func TestAccDataSourceB2BucketFileSignedUrl_singleFile(t *testing.T) {
23 | parentResourceName := "b2_bucket.test"
24 | resourceName := "b2_bucket_file_version.test"
25 | dataSourceName := "data.b2_bucket_file_signed_url.test"
26 |
27 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
28 | tempFile := createTempFileString(t, "hello")
29 | defer os.Remove(tempFile)
30 |
31 | resource.Test(t, resource.TestCase{
32 | PreCheck: func() { testAccPreCheck(t) },
33 | ProviderFactories: providerFactories,
34 | Steps: []resource.TestStep{
35 | {
36 | Config: testAccDataSourceB2BucketFileSignedUrlConfig_singleFile(bucketName, tempFile),
37 | Check: resource.ComposeTestCheckFunc(
38 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
39 | resource.TestCheckResourceAttrPair(dataSourceName, "file_name", resourceName, "file_name"),
40 | resource.TestCheckResourceAttrSet(dataSourceName, "signed_url"),
41 | resource.TestCheckResourceAttrPair(dataSourceName, "id", dataSourceName, "signed_url"),
42 | ),
43 | },
44 | },
45 | })
46 | }
47 |
48 | func testAccDataSourceB2BucketFileSignedUrlConfig_singleFile(bucketName string, tempFile string) string {
49 | return fmt.Sprintf(`
50 | resource "b2_bucket" "test" {
51 | bucket_name = "%s"
52 | bucket_type = "allPrivate"
53 | }
54 |
55 | resource "b2_bucket_file_version" "test" {
56 | bucket_id = b2_bucket.test.id
57 | file_name = "temp.txt"
58 | source = "%s"
59 | }
60 |
61 | data "b2_bucket_file_signed_url" "test" {
62 | bucket_id = b2_bucket_file_version.test.bucket_id
63 | file_name = b2_bucket_file_version.test.file_name
64 | duration = 3600
65 | }
66 | `, bucketName, tempFile)
67 | }
68 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket_file_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket_file_test.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "fmt"
15 | "os"
16 | "testing"
17 |
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
19 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
20 | )
21 |
22 | func TestAccDataSourceB2BucketFile_noFiles(t *testing.T) {
23 | parentResourceName := "b2_bucket.test"
24 | dataSourceName := "data.b2_bucket_file.test"
25 |
26 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
27 |
28 | resource.Test(t, resource.TestCase{
29 | PreCheck: func() { testAccPreCheck(t) },
30 | ProviderFactories: providerFactories,
31 | Steps: []resource.TestStep{
32 | {
33 | Config: testAccDataSourceB2BucketFileConfig_noFiles(bucketName),
34 | Check: resource.ComposeTestCheckFunc(
35 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
36 | resource.TestCheckResourceAttr(dataSourceName, "file_name", "non_existing_file.txt"),
37 | resource.TestCheckResourceAttr(dataSourceName, "file_versions.#", "0"),
38 | resource.TestCheckResourceAttr(dataSourceName, "show_versions", "false"),
39 | ),
40 | },
41 | },
42 | })
43 | }
44 |
45 | func TestAccDataSourceB2BucketFile_singleFile(t *testing.T) {
46 | parentResourceName := "b2_bucket.test"
47 | resourceName := "b2_bucket_file_version.test"
48 | dataSourceName := "data.b2_bucket_file.test"
49 |
50 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
51 | tempFile := createTempFileString(t, "hello")
52 | defer os.Remove(tempFile)
53 |
54 | resource.Test(t, resource.TestCase{
55 | PreCheck: func() { testAccPreCheck(t) },
56 | ProviderFactories: providerFactories,
57 | Steps: []resource.TestStep{
58 | {
59 | Config: testAccDataSourceB2BucketFileConfig_singleFile(bucketName, tempFile),
60 | Check: resource.ComposeTestCheckFunc(
61 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
62 | resource.TestCheckResourceAttrPair(dataSourceName, "file_name", resourceName, "file_name"),
63 | resource.TestCheckResourceAttr(dataSourceName, "file_versions.#", "1"),
64 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resourceName, "action"),
65 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_md5", resourceName, "content_md5"),
66 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_sha1", resourceName, "content_sha1"),
67 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_type", resourceName, "content_type"),
68 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resourceName, "action"),
69 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_id", resourceName, "file_id"),
70 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_info", resourceName, "file_info"),
71 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_name", resourceName, "file_name"),
72 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resourceName, "server_side_encryption"),
73 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.size", resourceName, "size"),
74 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.upload_timestamp", resourceName, "upload_timestamp"),
75 | resource.TestCheckResourceAttr(dataSourceName, "show_versions", "false"),
76 | ),
77 | },
78 | },
79 | })
80 | }
81 |
82 | func TestAccDataSourceB2BucketFile_multipleFilesWithoutVersions(t *testing.T) {
83 | parentResourceName := "b2_bucket.test"
84 | resourceName := "b2_bucket_file_version.test2"
85 | dataSourceName := "data.b2_bucket_file.test"
86 |
87 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
88 | tempFile := createTempFileString(t, "hello")
89 |
90 | resource.Test(t, resource.TestCase{
91 | PreCheck: func() { testAccPreCheck(t) },
92 | ProviderFactories: providerFactories,
93 | Steps: []resource.TestStep{
94 | {
95 | Config: testAccDataSourceB2BucketFileConfig_multipleFiles(bucketName, tempFile, "false"),
96 | Check: resource.ComposeTestCheckFunc(
97 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
98 | resource.TestCheckResourceAttrPair(dataSourceName, "file_name", resourceName, "file_name"),
99 | resource.TestCheckResourceAttr(dataSourceName, "file_versions.#", "1"),
100 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resourceName, "action"),
101 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_md5", resourceName, "content_md5"),
102 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_sha1", resourceName, "content_sha1"),
103 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_type", resourceName, "content_type"),
104 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resourceName, "action"),
105 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_id", resourceName, "file_id"),
106 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_info", resourceName, "file_info"),
107 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_name", resourceName, "file_name"),
108 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resourceName, "server_side_encryption"),
109 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.size", resourceName, "size"),
110 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.upload_timestamp", resourceName, "upload_timestamp"),
111 | resource.TestCheckResourceAttr(dataSourceName, "show_versions", "false"),
112 | ),
113 | },
114 | },
115 | })
116 | }
117 |
118 | func TestAccDataSourceB2BucketFile_multipleFilesWithVersions(t *testing.T) {
119 | parentResourceName := "b2_bucket.test"
120 | resource1Name := "b2_bucket_file_version.test1"
121 | resource2Name := "b2_bucket_file_version.test2"
122 | dataSourceName := "data.b2_bucket_file.test"
123 |
124 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
125 | tempFile := createTempFileString(t, "hello")
126 |
127 | resource.Test(t, resource.TestCase{
128 | PreCheck: func() { testAccPreCheck(t) },
129 | ProviderFactories: providerFactories,
130 | Steps: []resource.TestStep{
131 | {
132 | Config: testAccDataSourceB2BucketFileConfig_multipleFiles(bucketName, tempFile, "true"),
133 | Check: resource.ComposeTestCheckFunc(
134 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
135 | resource.TestCheckResourceAttrPair(dataSourceName, "file_name", resource2Name, "file_name"),
136 | resource.TestCheckResourceAttr(dataSourceName, "file_versions.#", "2"),
137 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resource2Name, "action"),
138 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_md5", resource2Name, "content_md5"),
139 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_sha1", resource2Name, "content_sha1"),
140 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_type", resource2Name, "content_type"),
141 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resource2Name, "action"),
142 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_id", resource2Name, "file_id"),
143 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_info", resource2Name, "file_info"),
144 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_name", resource2Name, "file_name"),
145 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resource2Name, "server_side_encryption"),
146 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.size", resource2Name, "size"),
147 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.upload_timestamp", resource2Name, "upload_timestamp"),
148 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.action", resource1Name, "action"),
149 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.content_md5", resource1Name, "content_md5"),
150 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.content_sha1", resource1Name, "content_sha1"),
151 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.content_type", resource1Name, "content_type"),
152 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.action", resource1Name, "action"),
153 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.file_id", resource1Name, "file_id"),
154 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.file_info", resource1Name, "file_info"),
155 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.file_name", resource1Name, "file_name"),
156 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resource1Name, "server_side_encryption"),
157 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.size", resource1Name, "size"),
158 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.upload_timestamp", resource1Name, "upload_timestamp"),
159 | resource.TestCheckResourceAttr(dataSourceName, "show_versions", "true"),
160 | ),
161 | },
162 | },
163 | })
164 | }
165 |
166 | func testAccDataSourceB2BucketFileConfig_noFiles(bucketName string) string {
167 | return fmt.Sprintf(`
168 | resource "b2_bucket" "test" {
169 | bucket_name = "%s"
170 | bucket_type = "allPublic"
171 | }
172 |
173 | data "b2_bucket_file" "test" {
174 | bucket_id = b2_bucket.test.bucket_id
175 | file_name = "non_existing_file.txt"
176 | }
177 | `, bucketName)
178 | }
179 |
180 | func testAccDataSourceB2BucketFileConfig_singleFile(bucketName string, tempFile string) string {
181 | return fmt.Sprintf(`
182 | resource "b2_bucket" "test" {
183 | bucket_name = "%s"
184 | bucket_type = "allPublic"
185 | }
186 |
187 | resource "b2_bucket_file_version" "test" {
188 | bucket_id = b2_bucket.test.id
189 | file_name = "temp.txt"
190 | source = "%s"
191 | }
192 |
193 | data "b2_bucket_file" "test" {
194 | bucket_id = b2_bucket_file_version.test.bucket_id
195 | file_name = b2_bucket_file_version.test.file_name
196 | }
197 | `, bucketName, tempFile)
198 | }
199 |
200 | func testAccDataSourceB2BucketFileConfig_multipleFiles(bucketName string, tempFile string, showVersions string) string {
201 | return fmt.Sprintf(`
202 | resource "b2_bucket" "test" {
203 | bucket_name = "%s"
204 | bucket_type = "allPublic"
205 | }
206 |
207 | resource "b2_bucket_file_version" "test1" {
208 | bucket_id = b2_bucket.test.id
209 | file_name = "temp1.txt"
210 | source = "%s"
211 | }
212 |
213 | resource "b2_bucket_file_version" "test2" {
214 | bucket_id = b2_bucket_file_version.test1.bucket_id
215 | file_name = b2_bucket_file_version.test1.file_name
216 | source = b2_bucket_file_version.test1.source
217 | file_info = {
218 | description = "second version"
219 | }
220 |
221 | depends_on = [
222 | b2_bucket_file_version.test1,
223 | ]
224 | }
225 |
226 | resource "b2_bucket_file_version" "test3" {
227 | bucket_id = b2_bucket_file_version.test2.bucket_id
228 | file_name = "temp2.txt"
229 | source = b2_bucket_file_version.test2.source
230 | server_side_encryption {
231 | mode = "SSE-B2"
232 | algorithm = "AES256"
233 | }
234 |
235 | depends_on = [
236 | b2_bucket_file_version.test2,
237 | ]
238 | }
239 |
240 | data "b2_bucket_file" "test" {
241 | bucket_id = b2_bucket_file_version.test3.bucket_id
242 | file_name = b2_bucket_file_version.test1.file_name
243 | show_versions = %s
244 | }
245 | `, bucketName, tempFile, showVersions)
246 | }
247 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket_files.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket_files.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
19 | )
20 |
21 | func dataSourceB2BucketFiles() *schema.Resource {
22 | return &schema.Resource{
23 | Description: "B2 bucket files data source.",
24 |
25 | ReadContext: dataSourceB2BucketFilesRead,
26 |
27 | Schema: map[string]*schema.Schema{
28 | "bucket_id": {
29 | Description: "The ID of the bucket.",
30 | Type: schema.TypeString,
31 | Required: true,
32 | ValidateFunc: validation.NoZeroValues,
33 | },
34 | "folder_name": {
35 | Description: "The folder name (B2 file name prefix).",
36 | Type: schema.TypeString,
37 | Optional: true,
38 | },
39 | "show_versions": {
40 | Description: "Show all file versions.",
41 | Type: schema.TypeBool,
42 | Optional: true,
43 | },
44 | "recursive": {
45 | Description: "Recursive mode.",
46 | Type: schema.TypeBool,
47 | Optional: true,
48 | },
49 | "file_versions": {
50 | Description: "File versions in the folder.",
51 | Type: schema.TypeList,
52 | Elem: getDataSourceFileVersionsElem(),
53 | Computed: true,
54 | },
55 | },
56 | }
57 | }
58 |
59 | func dataSourceB2BucketFilesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
60 | client := meta.(*Client)
61 | const name = "bucket_files"
62 | const op = DATA_SOURCE_READ
63 |
64 | input := map[string]interface{}{
65 | "bucket_id": d.Get("bucket_id").(string),
66 | "folder_name": d.Get("folder_name").(string),
67 | "show_versions": d.Get("show_versions").(bool),
68 | "recursive": d.Get("recursive").(bool),
69 | }
70 |
71 | output, err := client.apply(ctx, name, op, input)
72 | if err != nil {
73 | return diag.FromErr(err)
74 | }
75 |
76 | d.SetId(output["_sha1"].(string))
77 |
78 | err = client.populate(ctx, name, op, output, d)
79 | if err != nil {
80 | return diag.FromErr(err)
81 | }
82 |
83 | return nil
84 | }
85 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket_files_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket_files_test.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "fmt"
15 | "os"
16 | "testing"
17 |
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
19 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
20 | )
21 |
22 | func TestAccDataSourceB2BucketFiles_noFiles(t *testing.T) {
23 | parentResourceName := "b2_bucket.test"
24 | dataSourceName := "data.b2_bucket_files.test"
25 |
26 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
27 | tempFile := createTempFileString(t, "hello")
28 | defer os.Remove(tempFile)
29 |
30 | resource.Test(t, resource.TestCase{
31 | PreCheck: func() { testAccPreCheck(t) },
32 | ProviderFactories: providerFactories,
33 | Steps: []resource.TestStep{
34 | {
35 | Config: testAccDataSourceB2BucketFilesConfig_noFiles(bucketName, tempFile),
36 | Check: resource.ComposeTestCheckFunc(
37 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
38 | resource.TestCheckResourceAttr(dataSourceName, "file_versions.#", "0"),
39 | resource.TestCheckResourceAttr(dataSourceName, "folder_name", "non_existing_folder"),
40 | ),
41 | },
42 | },
43 | })
44 | }
45 |
46 | func TestAccDataSourceB2BucketFiles_singleFile(t *testing.T) {
47 | parentResourceName := "b2_bucket.test"
48 | resourceName := "b2_bucket_file_version.test"
49 | dataSourceName := "data.b2_bucket_files.test"
50 |
51 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
52 | tempFile := createTempFileString(t, "hello")
53 | defer os.Remove(tempFile)
54 |
55 | resource.Test(t, resource.TestCase{
56 | PreCheck: func() { testAccPreCheck(t) },
57 | ProviderFactories: providerFactories,
58 | Steps: []resource.TestStep{
59 | {
60 | Config: testAccDataSourceB2BucketFilesConfig_singleFile(bucketName, tempFile),
61 | Check: resource.ComposeTestCheckFunc(
62 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
63 | resource.TestCheckResourceAttr(dataSourceName, "file_versions.#", "1"),
64 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resourceName, "action"),
65 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_md5", resourceName, "content_md5"),
66 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_sha1", resourceName, "content_sha1"),
67 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_type", resourceName, "content_type"),
68 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resourceName, "action"),
69 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_id", resourceName, "file_id"),
70 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_info", resourceName, "file_info"),
71 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_name", resourceName, "file_name"),
72 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resourceName, "server_side_encryption"),
73 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.size", resourceName, "size"),
74 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.upload_timestamp", resourceName, "upload_timestamp"),
75 | resource.TestCheckResourceAttr(dataSourceName, "folder_name", ""),
76 | ),
77 | },
78 | },
79 | })
80 | }
81 |
82 | func TestAccDataSourceB2BucketFiles_multipleFilesWithoutVersions(t *testing.T) {
83 | parentResourceName := "b2_bucket.test"
84 | resource2Name := "b2_bucket_file_version.test2"
85 | resource3Name := "b2_bucket_file_version.test3"
86 | dataSourceName := "data.b2_bucket_files.test"
87 |
88 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
89 | tempFile := createTempFileString(t, "hello")
90 |
91 | resource.Test(t, resource.TestCase{
92 | PreCheck: func() { testAccPreCheck(t) },
93 | ProviderFactories: providerFactories,
94 | Steps: []resource.TestStep{
95 | {
96 | Config: testAccDataSourceB2BucketFilesConfig_multipleFiles(bucketName, tempFile, "false"),
97 | Check: resource.ComposeTestCheckFunc(
98 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
99 | resource.TestCheckResourceAttr(dataSourceName, "file_versions.#", "2"),
100 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resource2Name, "action"),
101 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_md5", resource2Name, "content_md5"),
102 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_sha1", resource2Name, "content_sha1"),
103 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_type", resource2Name, "content_type"),
104 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resource2Name, "action"),
105 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_id", resource2Name, "file_id"),
106 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_info", resource2Name, "file_info"),
107 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_name", resource2Name, "file_name"),
108 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resource2Name, "server_side_encryption"),
109 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.size", resource2Name, "size"),
110 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.upload_timestamp", resource2Name, "upload_timestamp"),
111 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.action", resource3Name, "action"),
112 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.content_md5", resource3Name, "content_md5"),
113 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.content_sha1", resource3Name, "content_sha1"),
114 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.content_type", resource3Name, "content_type"),
115 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.action", resource3Name, "action"),
116 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.file_id", resource3Name, "file_id"),
117 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.file_info", resource3Name, "file_info"),
118 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.file_name", resource3Name, "file_name"),
119 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resource3Name, "server_side_encryption"),
120 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.size", resource3Name, "size"),
121 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.upload_timestamp", resource3Name, "upload_timestamp"),
122 | resource.TestCheckResourceAttr(dataSourceName, "folder_name", ""),
123 | ),
124 | },
125 | },
126 | })
127 | }
128 |
129 | func TestAccDataSourceB2BucketFiles_multipleFilesWithVersions(t *testing.T) {
130 | parentResourceName := "b2_bucket.test"
131 | resource1Name := "b2_bucket_file_version.test1"
132 | resource2Name := "b2_bucket_file_version.test2"
133 | resource3Name := "b2_bucket_file_version.test3"
134 | dataSourceName := "data.b2_bucket_files.test"
135 |
136 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
137 | tempFile := createTempFileString(t, "hello")
138 |
139 | resource.Test(t, resource.TestCase{
140 | PreCheck: func() { testAccPreCheck(t) },
141 | ProviderFactories: providerFactories,
142 | Steps: []resource.TestStep{
143 | {
144 | Config: testAccDataSourceB2BucketFilesConfig_multipleFiles(bucketName, tempFile, "true"),
145 | Check: resource.ComposeTestCheckFunc(
146 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
147 | resource.TestCheckResourceAttr(dataSourceName, "file_versions.#", "3"),
148 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resource2Name, "action"),
149 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_md5", resource2Name, "content_md5"),
150 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_sha1", resource2Name, "content_sha1"),
151 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.content_type", resource2Name, "content_type"),
152 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.action", resource2Name, "action"),
153 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_id", resource2Name, "file_id"),
154 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_info", resource2Name, "file_info"),
155 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.file_name", resource2Name, "file_name"),
156 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resource2Name, "server_side_encryption"),
157 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.size", resource2Name, "size"),
158 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.upload_timestamp", resource2Name, "upload_timestamp"),
159 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.action", resource1Name, "action"),
160 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.content_md5", resource1Name, "content_md5"),
161 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.content_sha1", resource1Name, "content_sha1"),
162 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.content_type", resource1Name, "content_type"),
163 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.action", resource1Name, "action"),
164 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.file_id", resource1Name, "file_id"),
165 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.file_info", resource1Name, "file_info"),
166 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.file_name", resource1Name, "file_name"),
167 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resource1Name, "server_side_encryption"),
168 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.size", resource1Name, "size"),
169 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.1.upload_timestamp", resource1Name, "upload_timestamp"),
170 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.action", resource3Name, "action"),
171 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.content_md5", resource3Name, "content_md5"),
172 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.content_sha1", resource3Name, "content_sha1"),
173 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.content_type", resource3Name, "content_type"),
174 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.action", resource3Name, "action"),
175 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.file_id", resource3Name, "file_id"),
176 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.file_info", resource3Name, "file_info"),
177 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.file_name", resource3Name, "file_name"),
178 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.size", resource3Name, "size"),
179 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.0.server_side_encryption", resource3Name, "server_side_encryption"),
180 | resource.TestCheckResourceAttrPair(dataSourceName, "file_versions.2.upload_timestamp", resource3Name, "upload_timestamp"),
181 | resource.TestCheckResourceAttr(dataSourceName, "folder_name", ""),
182 | ),
183 | },
184 | },
185 | })
186 | }
187 |
188 | func testAccDataSourceB2BucketFilesConfig_noFiles(bucketName string, tempFile string) string {
189 | return fmt.Sprintf(`
190 | resource "b2_bucket" "test" {
191 | bucket_name = "%s"
192 | bucket_type = "allPublic"
193 | }
194 |
195 | resource "b2_bucket_file_version" "test" {
196 | bucket_id = b2_bucket.test.id
197 | file_name = "existing_folder/temp.txt"
198 | source = "%s"
199 | }
200 |
201 | data "b2_bucket_files" "test" {
202 | bucket_id = b2_bucket_file_version.test.bucket_id
203 | folder_name = "non_existing_folder"
204 | }
205 | `, bucketName, tempFile)
206 | }
207 |
208 | func testAccDataSourceB2BucketFilesConfig_singleFile(bucketName string, tempFile string) string {
209 | return fmt.Sprintf(`
210 | resource "b2_bucket" "test" {
211 | bucket_name = "%s"
212 | bucket_type = "allPublic"
213 | }
214 |
215 | resource "b2_bucket_file_version" "test" {
216 | bucket_id = b2_bucket.test.id
217 | file_name = "temp.txt"
218 | source = "%s"
219 | }
220 |
221 | data "b2_bucket_files" "test" {
222 | bucket_id = b2_bucket_file_version.test.bucket_id
223 | }
224 | `, bucketName, tempFile)
225 | }
226 |
227 | func testAccDataSourceB2BucketFilesConfig_multipleFiles(bucketName string, tempFile string, showVersions string) string {
228 | return fmt.Sprintf(`
229 | resource "b2_bucket" "test" {
230 | bucket_name = "%s"
231 | bucket_type = "allPublic"
232 | }
233 |
234 | resource "b2_bucket_file_version" "test1" {
235 | bucket_id = b2_bucket.test.id
236 | file_name = "temp1.txt"
237 | source = "%s"
238 | }
239 |
240 | resource "b2_bucket_file_version" "test2" {
241 | bucket_id = b2_bucket_file_version.test1.bucket_id
242 | file_name = b2_bucket_file_version.test1.file_name
243 | source = b2_bucket_file_version.test1.source
244 | file_info = {
245 | description = "second version"
246 | }
247 |
248 | depends_on = [
249 | b2_bucket_file_version.test1,
250 | ]
251 | }
252 |
253 | resource "b2_bucket_file_version" "test3" {
254 | bucket_id = b2_bucket_file_version.test2.bucket_id
255 | file_name = "temp2.txt"
256 | source = b2_bucket_file_version.test2.source
257 | server_side_encryption {
258 | mode = "SSE-B2"
259 | algorithm = "AES256"
260 | }
261 |
262 | depends_on = [
263 | b2_bucket_file_version.test2,
264 | ]
265 | }
266 |
267 | data "b2_bucket_files" "test" {
268 | bucket_id = b2_bucket_file_version.test3.bucket_id
269 | show_versions = %s
270 | }
271 | `, bucketName, tempFile, showVersions)
272 | }
273 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket_notification_rules.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket_notification_rules.go
4 | //
5 | // Copyright 2024 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
19 | )
20 |
21 | func dataSourceB2BucketNotificationRules() *schema.Resource {
22 | return &schema.Resource{
23 | Description: "B2 bucket notification rules data source.",
24 |
25 | ReadContext: dataSourceB2BucketNotificationRulesRead,
26 |
27 | Schema: map[string]*schema.Schema{
28 | "bucket_id": {
29 | Description: "The ID of the bucket.",
30 | Type: schema.TypeString,
31 | Required: true,
32 | ValidateFunc: validation.NoZeroValues,
33 | },
34 | "notification_rules": {
35 | Description: "An array of Event Notification Rules.",
36 | Type: schema.TypeList,
37 | Elem: getNotificationRulesElem(true),
38 | Computed: true,
39 | },
40 | },
41 | }
42 | }
43 |
44 | func dataSourceB2BucketNotificationRulesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
45 | client := meta.(*Client)
46 | const name = "bucket_notification_rules"
47 | const op = DATA_SOURCE_READ
48 |
49 | input := map[string]interface{}{
50 | "bucket_id": d.Get("bucket_id").(string),
51 | }
52 |
53 | output, err := client.apply(ctx, name, op, input)
54 | if err != nil {
55 | return diag.FromErr(err)
56 | }
57 |
58 | d.SetId(output["bucket_id"].(string))
59 |
60 | err = client.populate(ctx, name, op, output, d)
61 | if err != nil {
62 | return diag.FromErr(err)
63 | }
64 |
65 | return nil
66 | }
67 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket_notification_rules_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket_notification_rules_test.go
4 | //
5 | // Copyright 2024 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "fmt"
15 | "testing"
16 |
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
19 | )
20 |
21 | func TestAccDataSourceB2BucketNotificationRules_basic(t *testing.T) {
22 | parentResourceName := "b2_bucket.test"
23 | resourceName := "b2_bucket_notification_rules.test"
24 | dataSourceName := "data.b2_bucket_notification_rules.test"
25 |
26 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
27 | ruleName := acctest.RandomWithPrefix("test-b2-tfp")
28 |
29 | resource.Test(t, resource.TestCase{
30 | PreCheck: func() { testAccPreCheck(t) },
31 | ProviderFactories: providerFactories,
32 | Steps: []resource.TestStep{
33 | {
34 | Config: testAccDataSourceB2BucketNotificationRulesConfig_basic(bucketName, ruleName),
35 | Check: resource.ComposeTestCheckFunc(
36 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", resourceName, "bucket_id"),
37 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", parentResourceName, "bucket_id"),
38 | resource.TestCheckResourceAttrPair(dataSourceName, "notification_rules", resourceName, "notification_rules"),
39 | ),
40 | },
41 | },
42 | })
43 | }
44 |
45 | func testAccDataSourceB2BucketNotificationRulesConfig_basic(bucketName string, ruleName string) string {
46 | return fmt.Sprintf(`
47 | resource "b2_bucket" "test" {
48 | bucket_name = "%s"
49 | bucket_type = "allPublic"
50 | }
51 |
52 | resource "b2_bucket_notification_rules" "test" {
53 | bucket_id = b2_bucket.test.id
54 | notification_rules {
55 | name = "%s"
56 | event_types = ["b2:ObjectCreated:*"]
57 | target_configuration {
58 | target_type = "webhook"
59 | url = "https://example.com/webhook"
60 | }
61 | }
62 | }
63 |
64 | data "b2_bucket_notification_rules" "test" {
65 | bucket_id = b2_bucket.test.bucket_id
66 | depends_on = [
67 | b2_bucket_notification_rules.test,
68 | ]
69 | }
70 | `, bucketName, ruleName)
71 | }
72 |
--------------------------------------------------------------------------------
/b2/data_source_b2_bucket_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/data_source_b2_bucket_test.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "fmt"
15 | "testing"
16 |
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
19 | )
20 |
21 | func TestAccDataSourceB2Bucket_basic(t *testing.T) {
22 | resourceName := "b2_bucket.test"
23 | dataSourceName := "data.b2_bucket.test"
24 |
25 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
26 |
27 | resource.Test(t, resource.TestCase{
28 | PreCheck: func() { testAccPreCheck(t) },
29 | ProviderFactories: providerFactories,
30 | Steps: []resource.TestStep{
31 | {
32 | Config: testAccDataSourceB2BucketConfig_basic(bucketName),
33 | Check: resource.ComposeTestCheckFunc(
34 | resource.TestCheckResourceAttrPair(dataSourceName, "account_id", resourceName, "account_id"),
35 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", resourceName, "bucket_id"),
36 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_info", resourceName, "bucket_info"),
37 | resource.TestCheckResourceAttr(dataSourceName, "bucket_name", bucketName),
38 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_name", resourceName, "bucket_name"),
39 | resource.TestCheckResourceAttr(dataSourceName, "bucket_type", "allPublic"),
40 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_type", resourceName, "bucket_type"),
41 | resource.TestCheckResourceAttrPair(dataSourceName, "cors_rules", resourceName, "cors_rules"),
42 | resource.TestCheckResourceAttrPair(dataSourceName, "default_server_side_encryption", resourceName, "default_server_side_encryption"),
43 | resource.TestCheckResourceAttrPair(dataSourceName, "lifecycle_rules", resourceName, "lifecycle_rules"),
44 | resource.TestCheckResourceAttrPair(dataSourceName, "options", resourceName, "options"),
45 | resource.TestCheckResourceAttrPair(dataSourceName, "revision", resourceName, "revision"),
46 | ),
47 | },
48 | },
49 | })
50 | }
51 |
52 | func TestAccDataSourceB2Bucket_all(t *testing.T) {
53 | resourceName := "b2_bucket.test"
54 | dataSourceName := "data.b2_bucket.test"
55 |
56 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
57 |
58 | resource.Test(t, resource.TestCase{
59 | PreCheck: func() { testAccPreCheck(t) },
60 | ProviderFactories: providerFactories,
61 | Steps: []resource.TestStep{
62 | {
63 | Config: testAccDataSourceB2BucketConfig_all(bucketName),
64 | Check: resource.ComposeTestCheckFunc(
65 | resource.TestCheckResourceAttrPair(dataSourceName, "account_id", resourceName, "account_id"),
66 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_id", resourceName, "bucket_id"),
67 | resource.TestCheckResourceAttr(dataSourceName, "bucket_info.%", "1"),
68 | resource.TestCheckResourceAttr(dataSourceName, "bucket_info.description", "the bucket"),
69 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_info", resourceName, "bucket_info"),
70 | resource.TestCheckResourceAttr(dataSourceName, "bucket_name", bucketName),
71 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_name", resourceName, "bucket_name"),
72 | resource.TestCheckResourceAttr(dataSourceName, "bucket_type", "allPrivate"),
73 | resource.TestCheckResourceAttrPair(dataSourceName, "bucket_type", resourceName, "bucket_type"),
74 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.#", "1"),
75 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.cors_rule_name", "downloadFromAnyOrigin"),
76 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.allowed_origins.#", "1"),
77 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.allowed_origins.0", "https"),
78 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.allowed_operations.#", "2"),
79 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.allowed_operations.0", "b2_download_file_by_id"),
80 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.allowed_operations.1", "b2_download_file_by_name"),
81 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.expose_headers.#", "1"),
82 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.expose_headers.0", "x-bz-content-sha1"),
83 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.allowed_headers.#", "1"),
84 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.allowed_headers.0", "range"),
85 | resource.TestCheckResourceAttr(dataSourceName, "cors_rules.0.max_age_seconds", "3600"),
86 | resource.TestCheckResourceAttrPair(dataSourceName, "cors_rules", resourceName, "cors_rules"),
87 | resource.TestCheckResourceAttr(dataSourceName, "default_server_side_encryption.#", "1"),
88 | resource.TestCheckResourceAttr(dataSourceName, "default_server_side_encryption.0.mode", "SSE-B2"),
89 | resource.TestCheckResourceAttr(dataSourceName, "default_server_side_encryption.0.algorithm", "AES256"),
90 | resource.TestCheckResourceAttrPair(dataSourceName, "default_server_side_encryption", resourceName, "default_server_side_encryption"),
91 | resource.TestCheckResourceAttr(dataSourceName, "lifecycle_rules.#", "1"),
92 | resource.TestCheckResourceAttr(dataSourceName, "lifecycle_rules.0.file_name_prefix", ""),
93 | resource.TestCheckResourceAttr(dataSourceName, "lifecycle_rules.0.days_from_hiding_to_deleting", "2"),
94 | resource.TestCheckResourceAttr(dataSourceName, "lifecycle_rules.0.days_from_uploading_to_hiding", "1"),
95 | resource.TestCheckResourceAttrPair(dataSourceName, "lifecycle_rules", resourceName, "lifecycle_rules"),
96 | resource.TestCheckResourceAttrPair(dataSourceName, "options", resourceName, "options"),
97 | resource.TestCheckResourceAttrPair(dataSourceName, "revision", resourceName, "revision"),
98 | ),
99 | },
100 | },
101 | })
102 | }
103 |
104 | func testAccDataSourceB2BucketConfig_basic(bucketName string) string {
105 | return fmt.Sprintf(`
106 | resource "b2_bucket" "test" {
107 | bucket_name = "%s"
108 | bucket_type = "allPublic"
109 | }
110 |
111 | data "b2_bucket" "test" {
112 | bucket_name = b2_bucket.test.bucket_name
113 |
114 | depends_on = [
115 | b2_bucket.test,
116 | ]
117 | }
118 | `, bucketName)
119 | }
120 |
121 | func testAccDataSourceB2BucketConfig_all(bucketName string) string {
122 | return fmt.Sprintf(`
123 | resource "b2_bucket" "test" {
124 | bucket_name = "%s"
125 | bucket_type = "allPrivate"
126 | bucket_info = {
127 | description = "the bucket"
128 | }
129 | cors_rules {
130 | cors_rule_name = "downloadFromAnyOrigin"
131 | allowed_origins = [
132 | "https"
133 | ]
134 | allowed_operations = [
135 | "b2_download_file_by_id",
136 | "b2_download_file_by_name"
137 | ]
138 | expose_headers = ["x-bz-content-sha1"]
139 | allowed_headers = ["range"]
140 | max_age_seconds = 3600
141 | }
142 | default_server_side_encryption {
143 | mode = "SSE-B2"
144 | algorithm = "AES256"
145 | }
146 | lifecycle_rules {
147 | file_name_prefix = ""
148 | days_from_hiding_to_deleting = 2
149 | days_from_uploading_to_hiding = 1
150 | }
151 | }
152 |
153 | data "b2_bucket" "test" {
154 | bucket_name = b2_bucket.test.bucket_name
155 |
156 | depends_on = [
157 | b2_bucket.test,
158 | ]
159 | }
160 | `, bucketName)
161 | }
162 |
--------------------------------------------------------------------------------
/b2/provider.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/provider.go
4 | //
5 | // Copyright 2020 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "bytes"
15 | "context"
16 | "fmt"
17 | "strings"
18 |
19 | "github.com/hashicorp/terraform-plugin-log/tflog"
20 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
21 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
22 | )
23 |
24 | func init() {
25 | schema.DescriptionKind = schema.StringMarkdown
26 |
27 | schema.SchemaDescriptionBuilder = func(s *schema.Schema) string {
28 | desc := s.Description
29 | desc = strings.TrimSpace(desc)
30 |
31 | if !bytes.HasSuffix([]byte(desc), []byte(".")) && desc != "" {
32 | desc += "."
33 | }
34 |
35 | if s.Default != nil || s.DefaultFunc != nil {
36 | if s.DefaultFunc != nil {
37 | val, err := s.DefaultFunc()
38 | if err == nil && val != nil {
39 | desc += fmt.Sprintf(" Defaults to `%v`.", val)
40 | }
41 | } else if s.Default == "" {
42 | desc += " Defaults to `\"\"`."
43 | } else {
44 | desc += fmt.Sprintf(" Defaults to `%v`.", s.Default)
45 | }
46 | }
47 |
48 | if s.RequiredWith != nil && len(s.RequiredWith) > 0 {
49 | requiredWith := make([]string, len(s.RequiredWith))
50 | for i, c := range s.RequiredWith {
51 | requiredWith[i] = fmt.Sprintf("`%s`", c)
52 | }
53 | desc += fmt.Sprintf(" Required when using %s.", strings.Join(requiredWith, ", "))
54 | }
55 |
56 | if s.ConflictsWith != nil && len(s.ConflictsWith) > 0 {
57 | conflicts := make([]string, len(s.ConflictsWith))
58 | for i, c := range s.ConflictsWith {
59 | conflicts[i] = fmt.Sprintf("`%s`", c)
60 | }
61 | desc += fmt.Sprintf(" Conflicts with %s.", strings.Join(conflicts, ", "))
62 | }
63 |
64 | if s.ExactlyOneOf != nil && len(s.ExactlyOneOf) > 0 {
65 | exactlyOneOfs := make([]string, len(s.ExactlyOneOf))
66 | for i, c := range s.ExactlyOneOf {
67 | exactlyOneOfs[i] = fmt.Sprintf("`%s`", c)
68 | }
69 | desc += fmt.Sprintf(" Must provide only one of %s.", strings.Join(exactlyOneOfs, ", "))
70 | }
71 |
72 | if s.AtLeastOneOf != nil && len(s.AtLeastOneOf) > 0 {
73 | atLeastOneOfs := make([]string, len(s.AtLeastOneOf))
74 | for i, c := range s.AtLeastOneOf {
75 | atLeastOneOfs[i] = fmt.Sprintf("`%s`", c)
76 | }
77 | desc += fmt.Sprintf(" Must provide at least one of %s.", strings.Join(atLeastOneOfs, ", "))
78 | }
79 |
80 | if s.ForceNew {
81 | desc += " **Modifying this attribute will force creation of a new resource.**"
82 | }
83 |
84 | return strings.TrimSpace(desc)
85 | }
86 | }
87 |
88 | func New(version string, exec string) func() *schema.Provider {
89 | return func() *schema.Provider {
90 | p := &schema.Provider{
91 | Schema: map[string]*schema.Schema{
92 | "application_key_id": {
93 | Description: "B2 Application Key ID (B2_APPLICATION_KEY_ID env)",
94 | Type: schema.TypeString,
95 | Optional: true,
96 | Sensitive: true,
97 | DefaultFunc: schema.EnvDefaultFunc("B2_APPLICATION_KEY_ID", nil),
98 | },
99 | "application_key": {
100 | Description: "B2 Application Key (B2_APPLICATION_KEY env)",
101 | Type: schema.TypeString,
102 | Optional: true,
103 | Sensitive: true,
104 | DefaultFunc: schema.EnvDefaultFunc("B2_APPLICATION_KEY", nil),
105 | },
106 | "endpoint": {
107 | Description: "B2 endpoint - the string 'production' or a custom B2 API URL (B2_ENDPOINT env)." +
108 | " You should not need to set this unless you work at Backblaze.",
109 | Type: schema.TypeString,
110 | Optional: true,
111 | DefaultFunc: schema.EnvDefaultFunc("B2_ENDPOINT", "production"),
112 | },
113 | },
114 | DataSourcesMap: map[string]*schema.Resource{
115 | "b2_account_info": dataSourceB2AccountInfo(),
116 | "b2_application_key": dataSourceB2ApplicationKey(),
117 | "b2_bucket": dataSourceB2Bucket(),
118 | "b2_bucket_file": dataSourceB2BucketFile(),
119 | "b2_bucket_file_signed_url": dataSourceB2BucketFileSignedUrl(),
120 | "b2_bucket_files": dataSourceB2BucketFiles(),
121 | "b2_bucket_notification_rules": dataSourceB2BucketNotificationRules(),
122 | },
123 | ResourcesMap: map[string]*schema.Resource{
124 | "b2_application_key": resourceB2ApplicationKey(),
125 | "b2_bucket": resourceB2Bucket(),
126 | "b2_bucket_file_version": resourceB2BucketFileVersion(),
127 | "b2_bucket_notification_rules": resourceB2BucketNotificationRules(),
128 | },
129 | }
130 |
131 | p.ConfigureContextFunc = configure(version, exec, p)
132 |
133 | return p
134 | }
135 | }
136 |
137 | func configure(version string, exec string, p *schema.Provider) func(context.Context, *schema.ResourceData) (interface{}, diag.Diagnostics) {
138 | return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
139 | dataSources := map[string][]string{}
140 | sensitiveDataSources := map[string]map[string]bool{}
141 | for k, v := range p.DataSourcesMap {
142 | sensitiveDataSources[k] = make(map[string]bool)
143 | for kk, vv := range v.Schema {
144 | dataSources[k] = append(dataSources[k], kk)
145 | if vv.Sensitive {
146 | sensitiveDataSources[k][kk] = true
147 | }
148 | }
149 | }
150 |
151 | resources := map[string][]string{}
152 | sensitiveResources := map[string]map[string]bool{}
153 | for k, v := range p.ResourcesMap {
154 | sensitiveResources[k] = make(map[string]bool)
155 | for kk, vv := range v.Schema {
156 | resources[k] = append(resources[k], kk)
157 | if vv.Sensitive {
158 | sensitiveResources[k][kk] = true
159 | }
160 | }
161 | }
162 |
163 | userAgent := p.UserAgent("Terraform-B2-Provider", version)
164 | client := &Client{
165 | Exec: exec,
166 | UserAgentAppend: userAgent,
167 | ApplicationKeyId: d.Get("application_key_id").(string),
168 | ApplicationKey: d.Get("application_key").(string),
169 | Endpoint: d.Get("endpoint").(string),
170 | DataSources: dataSources,
171 | Resources: resources,
172 | SensitiveDataSources: sensitiveDataSources,
173 | SensitiveResources: sensitiveResources,
174 | }
175 |
176 | tflog.Info(ctx, "User Agent append", map[string]interface{}{
177 | "user_agent_append": userAgent,
178 | })
179 |
180 | return client, nil
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/b2/provider_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/provider_test.go
4 | //
5 | // Copyright 2020 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "log"
15 | "os"
16 | "path/filepath"
17 | "testing"
18 |
19 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
20 | )
21 |
22 | // providerFactories are used to instantiate a provider during acceptance testing.
23 | // The factory function will be invoked for every Terraform CLI command executed
24 | // to create a provider server to which the CLI can reattach.
25 | var providerFactories = map[string]func() (*schema.Provider, error){
26 | "b2": func() (*schema.Provider, error) {
27 | pybindings, err := GetBindings()
28 | if err != nil {
29 | log.Fatal(err.Error())
30 | return nil, err
31 | }
32 | return New("test", pybindings)(), nil
33 | },
34 | }
35 |
36 | func TestProvider(t *testing.T) {
37 | pybindings, err := GetBindings()
38 | if err != nil {
39 | t.Fatalf("err: %s", err)
40 | }
41 | if err := New("test", pybindings)().InternalValidate(); err != nil {
42 | t.Fatalf("err: %s", err)
43 | }
44 | }
45 |
46 | func testAccPreCheck(t *testing.T) {
47 | _, present := os.LookupEnv("B2_TEST_APPLICATION_KEY_ID")
48 | if !present {
49 | t.Fatal("B2_TEST_APPLICATION_KEY_ID is not set")
50 | }
51 | _ = os.Setenv("B2_APPLICATION_KEY_ID", os.Getenv("B2_TEST_APPLICATION_KEY_ID"))
52 |
53 | _, present = os.LookupEnv("B2_TEST_APPLICATION_KEY")
54 | if !present {
55 | t.Fatal("B2_TEST_APPLICATION_KEY is not set")
56 | }
57 | _ = os.Setenv("B2_APPLICATION_KEY", os.Getenv("B2_TEST_APPLICATION_KEY"))
58 | }
59 |
60 | // Utility functions
61 |
62 | func createTempFile(t *testing.T) *os.File {
63 | tmpFile, err := os.CreateTemp("", "test-b2-tfp")
64 | if err != nil {
65 | t.Fatal(err)
66 | }
67 |
68 | return tmpFile
69 | }
70 |
71 | func createTempFileString(t *testing.T, data string) string {
72 | tmpFile := createTempFile(t)
73 |
74 | _, err := tmpFile.WriteString(data)
75 | if err != nil {
76 | os.Remove(tmpFile.Name())
77 | t.Fatal(err)
78 | }
79 |
80 | return filepath.ToSlash(tmpFile.Name())
81 | }
82 |
83 | func createTempFileTruncate(t *testing.T, size int64) string {
84 | tmpFile := createTempFile(t)
85 |
86 | err := tmpFile.Truncate(size)
87 | if err != nil {
88 | os.Remove(tmpFile.Name())
89 | t.Fatal(err)
90 | }
91 |
92 | return filepath.ToSlash(tmpFile.Name())
93 | }
94 |
--------------------------------------------------------------------------------
/b2/resource_b2_application_key.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/resource_b2_application_key.go
4 | //
5 | // Copyright 2020 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-log/tflog"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
19 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
20 | )
21 |
22 | func resourceB2ApplicationKey() *schema.Resource {
23 | return &schema.Resource{
24 | Description: "B2 application key resource.",
25 |
26 | CreateContext: resourceB2ApplicationKeyCreate,
27 | ReadContext: resourceB2ApplicationKeyRead,
28 | DeleteContext: resourceB2ApplicationKeyDelete,
29 | Importer: &schema.ResourceImporter{
30 | StateContext: schema.ImportStatePassthroughContext,
31 | },
32 |
33 | Schema: map[string]*schema.Schema{
34 | "capabilities": {
35 | Description: "A set of strings, each one naming a capability the key has.",
36 | Type: schema.TypeSet,
37 | Elem: &schema.Schema{
38 | Type: schema.TypeString,
39 | },
40 | Required: true,
41 | ForceNew: true,
42 | },
43 | "key_name": {
44 | Description: "The name of the key.",
45 | Type: schema.TypeString,
46 | Required: true,
47 | ForceNew: true,
48 | ValidateFunc: validation.NoZeroValues,
49 | },
50 | "bucket_id": {
51 | Description: "When present, restricts access to one bucket.",
52 | Type: schema.TypeString,
53 | Optional: true,
54 | ForceNew: true,
55 | },
56 | "name_prefix": {
57 | Description: "When present, restricts access to files whose names start with the prefix.",
58 | Type: schema.TypeString,
59 | Optional: true,
60 | ForceNew: true,
61 | RequiredWith: []string{"bucket_id"},
62 | },
63 | "application_key": {
64 | Description: "The key.",
65 | Type: schema.TypeString,
66 | Computed: true,
67 | Sensitive: true,
68 | },
69 | "application_key_id": {
70 | Description: "The ID of the newly created key.",
71 | Type: schema.TypeString,
72 | Computed: true,
73 | },
74 | "options": {
75 | Description: "List of application key options.",
76 | Type: schema.TypeSet,
77 | Elem: &schema.Schema{
78 | Type: schema.TypeString,
79 | },
80 | Computed: true,
81 | },
82 | },
83 | }
84 | }
85 |
86 | func resourceB2ApplicationKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
87 | client := meta.(*Client)
88 | const name = "application_key"
89 | const op = RESOURCE_CREATE
90 |
91 | input := map[string]interface{}{
92 | "key_name": d.Get("key_name").(string),
93 | "capabilities": d.Get("capabilities").(*schema.Set).List(),
94 | "bucket_id": d.Get("bucket_id").(string),
95 | "name_prefix": d.Get("name_prefix").(string),
96 | }
97 |
98 | output, err := client.apply(ctx, name, op, input)
99 | if err != nil {
100 | return diag.FromErr(err)
101 | }
102 |
103 | d.SetId(output["application_key_id"].(string))
104 |
105 | err = client.populate(ctx, name, op, output, d)
106 | if err != nil {
107 | return diag.FromErr(err)
108 | }
109 |
110 | return nil
111 | }
112 |
113 | func resourceB2ApplicationKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
114 | client := meta.(*Client)
115 | const name = "application_key"
116 | const op = RESOURCE_READ
117 |
118 | input := map[string]interface{}{
119 | "application_key_id": d.Id(),
120 | }
121 |
122 | output, err := client.apply(ctx, name, op, input)
123 | if err != nil {
124 | return diag.FromErr(err)
125 | }
126 | if _, ok := output["application_key_id"]; !ok && !d.IsNewResource() {
127 | // deleted application key
128 | tflog.Warn(ctx, "Application Key not found, possible resource drift", map[string]interface{}{
129 | "application_key_id": d.Id(),
130 | })
131 | d.SetId("")
132 | return nil
133 | }
134 |
135 | output["application_key"] = d.Get("application_key").(string)
136 |
137 | err = client.populate(ctx, name, op, output, d)
138 | if err != nil {
139 | return diag.FromErr(err)
140 | }
141 |
142 | return nil
143 | }
144 |
145 | func resourceB2ApplicationKeyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
146 | client := meta.(*Client)
147 | const name = "application_key"
148 | const op = RESOURCE_DELETE
149 |
150 | input := map[string]interface{}{
151 | "application_key_id": d.Id(),
152 | }
153 |
154 | _, err := client.apply(ctx, name, op, input)
155 | if err != nil {
156 | return diag.FromErr(err)
157 | }
158 |
159 | d.SetId("")
160 |
161 | return nil
162 | }
163 |
--------------------------------------------------------------------------------
/b2/resource_b2_application_key_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/resource_b2_application_key_test.go
4 | //
5 | // Copyright 2020 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "fmt"
15 | "regexp"
16 | "testing"
17 |
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
19 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
20 | )
21 |
22 | func TestAccResourceB2ApplicationKey_basic(t *testing.T) {
23 | resourceName := "b2_application_key.test"
24 |
25 | keyName := acctest.RandomWithPrefix("test-b2-tfp")
26 |
27 | resource.Test(t, resource.TestCase{
28 | PreCheck: func() { testAccPreCheck(t) },
29 | ProviderFactories: providerFactories,
30 | Steps: []resource.TestStep{
31 | {
32 | Config: testAccResourceB2ApplicationKeyConfig_basic(keyName),
33 | Check: resource.ComposeTestCheckFunc(
34 | resource.TestMatchResourceAttr(resourceName, "application_key", regexp.MustCompile("^[\x20-\x7E]{31}$")),
35 | resource.TestMatchResourceAttr(resourceName, "application_key_id", regexp.MustCompile("^[a-zA-Z0-9]{25}$")),
36 | resource.TestCheckResourceAttr(resourceName, "bucket_id", ""),
37 | resource.TestCheckResourceAttr(resourceName, "bucket_id", ""),
38 | resource.TestCheckResourceAttr(resourceName, "capabilities.#", "1"),
39 | resource.TestCheckResourceAttr(resourceName, "capabilities.0", "readFiles"),
40 | resource.TestCheckResourceAttr(resourceName, "key_name", keyName),
41 | resource.TestCheckResourceAttr(resourceName, "name_prefix", ""),
42 | resource.TestCheckResourceAttr(resourceName, "options.#", "1"),
43 | resource.TestCheckResourceAttr(resourceName, "options.0", "s3"),
44 | ),
45 | },
46 | {
47 | ResourceName: resourceName,
48 | ImportState: true,
49 | ImportStateVerify: true,
50 | ImportStateVerifyIgnore: []string{"application_key"},
51 | },
52 | },
53 | })
54 | }
55 |
56 | func TestAccResourceB2ApplicationKey_all(t *testing.T) {
57 | parentResourceName := "b2_bucket.test"
58 | resourceName := "b2_application_key.test"
59 |
60 | keyName := acctest.RandomWithPrefix("test-b2-tfp")
61 |
62 | resource.Test(t, resource.TestCase{
63 | PreCheck: func() { testAccPreCheck(t) },
64 | ProviderFactories: providerFactories,
65 | Steps: []resource.TestStep{
66 | {
67 | Config: testAccResourceB2ApplicationKeyConfig_all(keyName),
68 | Check: resource.ComposeTestCheckFunc(
69 | resource.TestMatchResourceAttr(resourceName, "application_key", regexp.MustCompile("^[\x20-\x7E]{31}$")),
70 | resource.TestMatchResourceAttr(resourceName, "application_key_id", regexp.MustCompile("^[a-zA-Z0-9]{25}$")),
71 | resource.TestCheckResourceAttrPair(resourceName, "bucket_id", parentResourceName, "bucket_id"),
72 | resource.TestCheckResourceAttr(resourceName, "capabilities.#", "1"),
73 | resource.TestCheckResourceAttr(resourceName, "capabilities.0", "writeFiles"),
74 | resource.TestCheckResourceAttr(resourceName, "key_name", keyName),
75 | resource.TestCheckResourceAttr(resourceName, "name_prefix", "prefix"),
76 | resource.TestCheckResourceAttr(resourceName, "options.#", "1"),
77 | resource.TestCheckResourceAttr(resourceName, "options.0", "s3"),
78 | ),
79 | },
80 | {
81 | ResourceName: resourceName,
82 | ImportState: true,
83 | ImportStateVerify: true,
84 | ImportStateVerifyIgnore: []string{"application_key"},
85 | },
86 | },
87 | })
88 | }
89 |
90 | func testAccResourceB2ApplicationKeyConfig_basic(keyName string) string {
91 | return fmt.Sprintf(`
92 | resource "b2_application_key" "test" {
93 | key_name = "%s"
94 | capabilities = ["readFiles"]
95 | }
96 | `, keyName)
97 | }
98 |
99 | func testAccResourceB2ApplicationKeyConfig_all(keyName string) string {
100 | return fmt.Sprintf(`
101 | resource "b2_bucket" "test" {
102 | bucket_name = "%s"
103 | bucket_type = "allPrivate"
104 | }
105 |
106 | resource "b2_application_key" "test" {
107 | key_name = "%s"
108 | capabilities = ["writeFiles"]
109 | bucket_id = b2_bucket.test.bucket_id
110 | name_prefix = "prefix"
111 | }
112 | `, keyName, keyName)
113 | }
114 |
--------------------------------------------------------------------------------
/b2/resource_b2_bucket.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/resource_b2_bucket.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-log/tflog"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
19 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
20 | )
21 |
22 | func resourceB2Bucket() *schema.Resource {
23 | return &schema.Resource{
24 | Description: "B2 bucket resource.",
25 |
26 | CreateContext: resourceB2BucketCreate,
27 | ReadContext: resourceB2BucketRead,
28 | UpdateContext: resourceB2BucketUpdate,
29 | DeleteContext: resourceB2BucketDelete,
30 | Importer: &schema.ResourceImporter{
31 | StateContext: schema.ImportStatePassthroughContext,
32 | },
33 |
34 | Schema: map[string]*schema.Schema{
35 | "bucket_name": {
36 | Description: "The name of the bucket.",
37 | Type: schema.TypeString,
38 | Required: true,
39 | ForceNew: true,
40 | ValidateFunc: validation.NoZeroValues,
41 | },
42 | "bucket_type": {
43 | Description: "The bucket type. Either 'allPublic', meaning that files in this bucket can be downloaded by anybody, or 'allPrivate'.",
44 | Type: schema.TypeString,
45 | Required: true,
46 | ValidateFunc: validation.NoZeroValues,
47 | },
48 | "bucket_info": {
49 | Description: "User-defined information to be stored with the bucket.",
50 | Type: schema.TypeMap,
51 | Elem: &schema.Schema{
52 | Type: schema.TypeString,
53 | },
54 | Optional: true,
55 | },
56 | "cors_rules": {
57 | Description: "The initial list of CORS rules for this bucket.",
58 | Type: schema.TypeList,
59 | Elem: getCorsRulesElem(false),
60 | Optional: true,
61 | },
62 | "file_lock_configuration": {
63 | Description: "File lock enabled flag, and default retention settings.",
64 | Type: schema.TypeList,
65 | Elem: getFileLockConfigurationElem(false),
66 | Optional: true,
67 | DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
68 | // The API sets default value
69 | if k == "file_lock_configuration.#" {
70 | return old == "1" && new == "0"
71 | }
72 | return old == "none" && new == ""
73 | },
74 | },
75 | "default_server_side_encryption": {
76 | Description: "The default server-side encryption settings for this bucket.",
77 | Type: schema.TypeList,
78 | Elem: getServerSideEncryptionElem(false),
79 | Optional: true,
80 | MaxItems: 1,
81 | DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
82 | // The API sets default value
83 | if k == "default_server_side_encryption.#" {
84 | return old == "1" && new == "0"
85 | }
86 | return old == "none" && new == ""
87 | },
88 | },
89 | "lifecycle_rules": {
90 | Description: "The initial list of lifecycle rules for this bucket.",
91 | Type: schema.TypeList,
92 | Elem: getLifecycleRulesElem(false),
93 | Optional: true,
94 | },
95 | "bucket_id": {
96 | Description: "The ID of the bucket.",
97 | Type: schema.TypeString,
98 | Computed: true,
99 | },
100 | "account_id": {
101 | Description: "Account ID that the bucket belongs to.",
102 | Type: schema.TypeString,
103 | Computed: true,
104 | },
105 | "options": {
106 | Description: "List of bucket options.",
107 | Type: schema.TypeSet,
108 | Elem: &schema.Schema{
109 | Type: schema.TypeString,
110 | },
111 | Computed: true,
112 | },
113 | "revision": {
114 | Description: "Bucket revision.",
115 | Type: schema.TypeInt,
116 | Computed: true,
117 | },
118 | },
119 | }
120 | }
121 |
122 | func resourceB2BucketCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
123 | client := meta.(*Client)
124 | const name = "bucket"
125 | const op = RESOURCE_CREATE
126 |
127 | input := map[string]interface{}{
128 | "bucket_name": d.Get("bucket_name").(string),
129 | "bucket_type": d.Get("bucket_type").(string),
130 | "bucket_info": d.Get("bucket_info").(map[string]interface{}),
131 | "cors_rules": d.Get("cors_rules").([]interface{}),
132 | "file_lock_configuration": d.Get("file_lock_configuration").([]interface{}),
133 | "default_server_side_encryption": d.Get("default_server_side_encryption").([]interface{}),
134 | "lifecycle_rules": d.Get("lifecycle_rules").([]interface{}),
135 | }
136 |
137 | output, err := client.apply(ctx, name, op, input)
138 | if err != nil {
139 | return diag.FromErr(err)
140 | }
141 |
142 | d.SetId(output["bucket_id"].(string))
143 |
144 | err = client.populate(ctx, name, op, output, d)
145 | if err != nil {
146 | return diag.FromErr(err)
147 | }
148 |
149 | return nil
150 | }
151 |
152 | func resourceB2BucketRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
153 | client := meta.(*Client)
154 | const name = "bucket"
155 | const op = RESOURCE_READ
156 |
157 | input := map[string]interface{}{
158 | "bucket_id": d.Id(),
159 | "cors_rules": d.Get("cors_rules"),
160 | }
161 |
162 | output, err := client.apply(ctx, name, op, input)
163 | if err != nil {
164 | return diag.FromErr(err)
165 | }
166 | if _, ok := output["bucket_id"]; !ok && !d.IsNewResource() {
167 | // deleted bucket
168 | tflog.Warn(ctx, "Bucket not found, possible resource drift", map[string]interface{}{
169 | "bucket_id": d.Id(),
170 | })
171 | d.SetId("")
172 | return nil
173 | }
174 |
175 | err = client.populate(ctx, name, op, output, d)
176 | if err != nil {
177 | return diag.FromErr(err)
178 | }
179 |
180 | return nil
181 | }
182 |
183 | func resourceB2BucketUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
184 | client := meta.(*Client)
185 | const name = "bucket"
186 | const op = RESOURCE_UPDATE
187 |
188 | input := map[string]interface{}{
189 | "bucket_id": d.Id(),
190 | "account_id": d.Get("account_id").(string),
191 | "bucket_type": d.Get("bucket_type").(string),
192 | "bucket_info": d.Get("bucket_info").(map[string]interface{}),
193 | "cors_rules": d.Get("cors_rules").([]interface{}),
194 | "file_lock_configuration": d.Get("file_lock_configuration").([]interface{}),
195 | "default_server_side_encryption": d.Get("default_server_side_encryption").([]interface{}),
196 | "lifecycle_rules": d.Get("lifecycle_rules").([]interface{}),
197 | }
198 |
199 | output, err := client.apply(ctx, name, op, input)
200 | if err != nil {
201 | return diag.FromErr(err)
202 | }
203 |
204 | err = client.populate(ctx, name, op, output, d)
205 | if err != nil {
206 | return diag.FromErr(err)
207 | }
208 |
209 | return nil
210 | }
211 |
212 | func resourceB2BucketDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
213 | client := meta.(*Client)
214 | const name = "bucket"
215 | const op = RESOURCE_DELETE
216 |
217 | input := map[string]interface{}{
218 | "bucket_id": d.Id(),
219 | }
220 |
221 | _, err := client.apply(ctx, name, op, input)
222 | if err != nil {
223 | return diag.FromErr(err)
224 | }
225 |
226 | d.SetId("")
227 |
228 | return nil
229 | }
230 |
--------------------------------------------------------------------------------
/b2/resource_b2_bucket_file_version.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/resource_b2_bucket_file_version.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
19 | )
20 |
21 | func resourceB2BucketFileVersion() *schema.Resource {
22 | return &schema.Resource{
23 | Description: "B2 bucket file version resource.",
24 |
25 | CreateContext: resourceB2BucketFileVersionCreate,
26 | ReadContext: resourceB2BucketFileVersionRead,
27 | DeleteContext: resourceB2BucketFileVersionDelete,
28 |
29 | Schema: map[string]*schema.Schema{
30 | "bucket_id": {
31 | Description: "The ID of the bucket.",
32 | Type: schema.TypeString,
33 | Required: true,
34 | ForceNew: true,
35 | ValidateFunc: validation.NoZeroValues,
36 | },
37 | "file_name": {
38 | Description: "The name of the B2 file.",
39 | Type: schema.TypeString,
40 | Required: true,
41 | ForceNew: true,
42 | ValidateFunc: validation.NoZeroValues,
43 | },
44 | "source": {
45 | Description: "Path to the local file.",
46 | Type: schema.TypeString,
47 | Required: true,
48 | ForceNew: true,
49 | ValidateFunc: validation.NoZeroValues,
50 | },
51 | "content_type": {
52 | Description: "Content type. If not set, it will be set based on the file extension.",
53 | Type: schema.TypeString,
54 | Optional: true,
55 | ForceNew: true,
56 | DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
57 | return new == "" // The API sets default value
58 | },
59 | },
60 | "file_info": {
61 | Description: "The custom information that is uploaded with the file.",
62 | Type: schema.TypeMap,
63 | Elem: &schema.Schema{
64 | Type: schema.TypeString,
65 | },
66 | Optional: true,
67 | ForceNew: true,
68 | Computed: true,
69 | DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
70 | return k == "file_info.sse_c_key_id" || old == new
71 | },
72 | },
73 | "server_side_encryption": {
74 | Description: "Server-side encryption settings.",
75 | Type: schema.TypeList,
76 | Elem: getResourceFileEncryptionElem(),
77 | Optional: true,
78 | ForceNew: true,
79 | MaxItems: 1,
80 | DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
81 | // The API sets default value
82 | if k == "server_side_encryption.#" {
83 | return old == "1" && new == "0"
84 | }
85 | return old == "none" && new == ""
86 | },
87 | },
88 | "action": {
89 | Description: "One of 'start', 'upload', 'hide', 'folder', or other values added in the future.",
90 | Type: schema.TypeString,
91 | Computed: true,
92 | },
93 | "content_md5": {
94 | Description: "MD5 sum of the content.",
95 | Type: schema.TypeString,
96 | Computed: true,
97 | },
98 | "content_sha1": {
99 | Description: "SHA1 hash of the content.",
100 | Type: schema.TypeString,
101 | Computed: true,
102 | },
103 | "file_id": {
104 | Description: "The unique identifier for this version of this file.",
105 | Type: schema.TypeString,
106 | Computed: true,
107 | },
108 | "size": {
109 | Description: "The file size.",
110 | Type: schema.TypeInt,
111 | Computed: true,
112 | },
113 | "upload_timestamp": {
114 | Description: "This is a UTC time when this file was uploaded.",
115 | Type: schema.TypeInt,
116 | Computed: true,
117 | },
118 | },
119 | }
120 | }
121 |
122 | func resourceB2BucketFileVersionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
123 | client := meta.(*Client)
124 | const name = "bucket_file_version"
125 | const op = RESOURCE_CREATE
126 |
127 | input := map[string]interface{}{
128 | "bucket_id": d.Get("bucket_id").(string),
129 | "file_name": d.Get("file_name").(string),
130 | "source": d.Get("source").(string),
131 | "content_type": d.Get("content_type").(string),
132 | "file_info": d.Get("file_info").(map[string]interface{}),
133 | "server_side_encryption": d.Get("server_side_encryption").([]interface{}),
134 | }
135 |
136 | output, err := client.apply(ctx, name, op, input)
137 | if err != nil {
138 | return diag.FromErr(err)
139 | }
140 |
141 | d.SetId(output["file_id"].(string))
142 |
143 | err = client.populate(ctx, name, op, output, d)
144 | if err != nil {
145 | return diag.FromErr(err)
146 | }
147 |
148 | return nil
149 | }
150 |
151 | func resourceB2BucketFileVersionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
152 | client := meta.(*Client)
153 | const name = "bucket_file_version"
154 | const op = RESOURCE_READ
155 |
156 | input := map[string]interface{}{
157 | "file_id": d.Id(),
158 | }
159 |
160 | output, err := client.apply(ctx, name, op, input)
161 | if err != nil {
162 | return diag.FromErr(err)
163 | }
164 |
165 | output["bucket_id"] = d.Get("bucket_id").(string)
166 | output["size"] = d.Get("size").(int)
167 | output["source"] = d.Get("source").(string)
168 |
169 | err = client.populate(ctx, name, op, output, d)
170 | if err != nil {
171 | return diag.FromErr(err)
172 | }
173 |
174 | return nil
175 | }
176 |
177 | func resourceB2BucketFileVersionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
178 | client := meta.(*Client)
179 | const name = "bucket_file_version"
180 | const op = RESOURCE_DELETE
181 |
182 | input := map[string]interface{}{
183 | "file_id": d.Id(),
184 | "file_name": d.Get("file_name").(string),
185 | }
186 |
187 | _, err := client.apply(ctx, name, op, input)
188 | if err != nil {
189 | return diag.FromErr(err)
190 | }
191 |
192 | d.SetId("")
193 |
194 | return nil
195 | }
196 |
--------------------------------------------------------------------------------
/b2/resource_b2_bucket_file_version_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/resource_b2_bucket_file_version_test.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "fmt"
15 | "os"
16 | "regexp"
17 | "strconv"
18 | "testing"
19 |
20 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
21 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
22 | )
23 |
24 | func TestAccResourceB2BucketFileVersion_basic(t *testing.T) {
25 | parentResourceName := "b2_bucket.test"
26 | resourceName := "b2_bucket_file_version.test"
27 |
28 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
29 | tempFile := createTempFileString(t, "hello")
30 | defer os.Remove(tempFile)
31 |
32 | resource.Test(t, resource.TestCase{
33 | PreCheck: func() { testAccPreCheck(t) },
34 | ProviderFactories: providerFactories,
35 | Steps: []resource.TestStep{
36 | {
37 | Config: testAccResourceB2BucketFileVersionConfig_basic(bucketName, tempFile),
38 | Check: resource.ComposeTestCheckFunc(
39 | resource.TestCheckResourceAttr(resourceName, "action", "upload"),
40 | resource.TestCheckResourceAttrPair(resourceName, "bucket_id", parentResourceName, "bucket_id"),
41 | resource.TestCheckResourceAttr(resourceName, "content_md5", "5d41402abc4b2a76b9719d911017c592"),
42 | resource.TestCheckResourceAttr(resourceName, "content_sha1", "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"),
43 | resource.TestCheckResourceAttr(resourceName, "content_type", "text/plain"),
44 | resource.TestCheckResourceAttr(resourceName, "file_info.%", "0"),
45 | resource.TestCheckResourceAttr(resourceName, "file_name", "temp.txt"),
46 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.#", "1"),
47 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.mode", "none"),
48 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.algorithm", ""),
49 | resource.TestCheckResourceAttr(resourceName, "size", "5"),
50 | resource.TestCheckResourceAttr(resourceName, "source", tempFile),
51 | resource.TestMatchResourceAttr(resourceName, "upload_timestamp", regexp.MustCompile("^[0-9]{13}$")),
52 | ),
53 | },
54 | },
55 | })
56 | }
57 |
58 | func TestAccResourceB2BucketFileVersion_all(t *testing.T) {
59 | parentResourceName := "b2_bucket.test"
60 | resourceName := "b2_bucket_file_version.test"
61 |
62 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
63 | tempFile := createTempFileString(t, "hello")
64 | defer os.Remove(tempFile)
65 |
66 | resource.Test(t, resource.TestCase{
67 | PreCheck: func() { testAccPreCheck(t) },
68 | ProviderFactories: providerFactories,
69 | Steps: []resource.TestStep{
70 | {
71 | Config: testAccResourceB2BucketFileVersionConfig_all(bucketName, tempFile),
72 | Check: resource.ComposeTestCheckFunc(
73 | resource.TestCheckResourceAttr(resourceName, "action", "upload"),
74 | resource.TestCheckResourceAttrPair(resourceName, "bucket_id", parentResourceName, "bucket_id"),
75 | resource.TestCheckResourceAttr(resourceName, "content_md5", "5d41402abc4b2a76b9719d911017c592"),
76 | resource.TestCheckResourceAttr(resourceName, "content_sha1", "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"),
77 | resource.TestCheckResourceAttr(resourceName, "content_type", "octet/stream"),
78 | resource.TestCheckResourceAttr(resourceName, "file_info.%", "1"),
79 | resource.TestCheckResourceAttr(resourceName, "file_info.description", "the file"),
80 | resource.TestCheckResourceAttr(resourceName, "file_name", "temp.bin"),
81 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.#", "1"),
82 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.mode", "SSE-B2"),
83 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.algorithm", "AES256"),
84 | resource.TestCheckResourceAttr(resourceName, "source", tempFile),
85 | resource.TestCheckResourceAttr(resourceName, "size", "5"),
86 | resource.TestMatchResourceAttr(resourceName, "upload_timestamp", regexp.MustCompile("^[0-9]{13}$")),
87 | ),
88 | },
89 | },
90 | })
91 | }
92 |
93 | func TestAccResourceB2BucketFileVersion_forceNew(t *testing.T) {
94 | parentResourceName := "b2_bucket.test"
95 | resourceName := "b2_bucket_file_version.test"
96 |
97 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
98 | tempFile := createTempFileString(t, "hello")
99 | defer os.Remove(tempFile)
100 |
101 | resource.Test(t, resource.TestCase{
102 | PreCheck: func() { testAccPreCheck(t) },
103 | ProviderFactories: providerFactories,
104 | Steps: []resource.TestStep{
105 | {
106 | Config: testAccResourceB2BucketFileVersionConfig_basic(bucketName, tempFile),
107 | },
108 | {
109 | Config: testAccResourceB2BucketFileVersionConfig_all(bucketName, tempFile),
110 | Check: resource.ComposeTestCheckFunc(
111 | resource.TestCheckResourceAttr(resourceName, "action", "upload"),
112 | resource.TestCheckResourceAttrPair(resourceName, "bucket_id", parentResourceName, "bucket_id"),
113 | resource.TestCheckResourceAttr(resourceName, "content_md5", "5d41402abc4b2a76b9719d911017c592"),
114 | resource.TestCheckResourceAttr(resourceName, "content_sha1", "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"),
115 | resource.TestCheckResourceAttr(resourceName, "content_type", "octet/stream"),
116 | resource.TestCheckResourceAttr(resourceName, "file_info.%", "1"),
117 | resource.TestCheckResourceAttr(resourceName, "file_info.description", "the file"),
118 | resource.TestCheckResourceAttr(resourceName, "file_name", "temp.bin"),
119 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.#", "1"),
120 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.mode", "SSE-B2"),
121 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.algorithm", "AES256"),
122 | resource.TestCheckResourceAttr(resourceName, "source", tempFile),
123 | resource.TestCheckResourceAttr(resourceName, "size", "5"),
124 | resource.TestMatchResourceAttr(resourceName, "upload_timestamp", regexp.MustCompile("^[0-9]{13}$")),
125 | ),
126 | },
127 | },
128 | })
129 | }
130 |
131 | func TestAccResourceB2BucketFileVersion_largeFile(t *testing.T) {
132 | parentResourceName := "b2_bucket.test"
133 | resourceName := "b2_bucket_file_version.test"
134 | var fileSize int64 = 105 * 1000 * 1000 // 105MB file is uploaded as a large file
135 |
136 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
137 | tempFile := createTempFileTruncate(t, fileSize)
138 | defer os.Remove(tempFile)
139 |
140 | resource.Test(t, resource.TestCase{
141 | PreCheck: func() { testAccPreCheck(t) },
142 | ProviderFactories: providerFactories,
143 | Steps: []resource.TestStep{
144 | {
145 | Config: testAccResourceB2BucketFileVersionConfig_basic(bucketName, tempFile),
146 | Check: resource.ComposeTestCheckFunc(
147 | resource.TestCheckResourceAttr(resourceName, "action", "upload"),
148 | resource.TestCheckResourceAttrPair(resourceName, "bucket_id", parentResourceName, "bucket_id"),
149 | resource.TestCheckResourceAttr(resourceName, "content_md5", ""), // empty for large files
150 | resource.TestCheckResourceAttr(resourceName, "content_sha1", "none"), // "none" for large files
151 | resource.TestCheckResourceAttr(resourceName, "content_type", "text/plain"),
152 | resource.TestCheckResourceAttr(resourceName, "file_info.%", "1"),
153 | resource.TestMatchResourceAttr(resourceName, "file_info.large_file_sha1", regexp.MustCompile("^[a-z0-9]{40}$")),
154 | resource.TestCheckResourceAttr(resourceName, "file_name", "temp.txt"),
155 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.#", "1"),
156 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.mode", "none"),
157 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.algorithm", ""),
158 | resource.TestCheckResourceAttr(resourceName, "size", strconv.Itoa(int(fileSize))),
159 | resource.TestCheckResourceAttr(resourceName, "source", tempFile),
160 | resource.TestMatchResourceAttr(resourceName, "upload_timestamp", regexp.MustCompile("^[0-9]{13}$")),
161 | ),
162 | },
163 | },
164 | })
165 | }
166 |
167 | func TestAccResourceB2BucketFileVersion_sse_c(t *testing.T) {
168 | parentResourceName := "b2_bucket.test"
169 | resourceName := "b2_bucket_file_version.test"
170 |
171 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
172 | tempFile := createTempFileString(t, "hello")
173 | defer os.Remove(tempFile)
174 |
175 | resource.Test(t, resource.TestCase{
176 | PreCheck: func() { testAccPreCheck(t) },
177 | ProviderFactories: providerFactories,
178 | Steps: []resource.TestStep{
179 | {
180 | Config: testAccResourceB2BucketFileVersionConfig_sse_c(bucketName, tempFile),
181 | Check: resource.ComposeTestCheckFunc(
182 | resource.TestCheckResourceAttr(resourceName, "action", "upload"),
183 | resource.TestCheckResourceAttrPair(resourceName, "bucket_id", parentResourceName, "bucket_id"),
184 | resource.TestCheckResourceAttr(resourceName, "content_md5", "5d41402abc4b2a76b9719d911017c592"),
185 | resource.TestCheckResourceAttr(resourceName, "content_sha1", "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"),
186 | resource.TestCheckResourceAttr(resourceName, "content_type", "octet/stream"),
187 | resource.TestCheckResourceAttr(resourceName, "file_info.%", "2"),
188 | resource.TestCheckResourceAttr(resourceName, "file_info.description", "the file"),
189 | resource.TestCheckResourceAttr(resourceName, "file_info.sse_c_key_id", "test_id"),
190 | resource.TestCheckResourceAttr(resourceName, "file_name", "temp.bin"),
191 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.#", "1"),
192 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.mode", "SSE-C"),
193 | resource.TestCheckResourceAttr(resourceName, "server_side_encryption.0.algorithm", "AES256"),
194 | resource.TestCheckResourceAttr(resourceName, "source", tempFile),
195 | resource.TestCheckResourceAttr(resourceName, "size", "5"),
196 | resource.TestMatchResourceAttr(resourceName, "upload_timestamp", regexp.MustCompile("^[0-9]{13}$")),
197 | ),
198 | },
199 | },
200 | })
201 | }
202 |
203 | func testAccResourceB2BucketFileVersionConfig_basic(bucketName string, tempFile string) string {
204 | return fmt.Sprintf(`
205 | resource "b2_bucket" "test" {
206 | bucket_name = "%s"
207 | bucket_type = "allPublic"
208 | }
209 |
210 | resource "b2_bucket_file_version" "test" {
211 | bucket_id = b2_bucket.test.id
212 | file_name = "temp.txt"
213 | source = "%s"
214 | }
215 | `, bucketName, tempFile)
216 | }
217 |
218 | func testAccResourceB2BucketFileVersionConfig_all(bucketName string, tempFile string) string {
219 | return fmt.Sprintf(`
220 | resource "b2_bucket" "test" {
221 | bucket_name = "%s"
222 | bucket_type = "allPublic"
223 | }
224 |
225 | resource "b2_bucket_file_version" "test" {
226 | bucket_id = b2_bucket.test.id
227 | file_name = "temp.bin"
228 | source = "%s"
229 | content_type = "octet/stream"
230 | file_info = {
231 | description = "the file"
232 | }
233 | server_side_encryption {
234 | mode = "SSE-B2"
235 | algorithm = "AES256"
236 | }
237 | }
238 | `, bucketName, tempFile)
239 | }
240 |
241 | func testAccResourceB2BucketFileVersionConfig_sse_c(bucketName string, tempFile string) string {
242 | return fmt.Sprintf(`
243 | resource "b2_bucket" "test" {
244 | bucket_name = "%s"
245 | bucket_type = "allPublic"
246 | }
247 |
248 | resource "b2_bucket_file_version" "test" {
249 | bucket_id = b2_bucket.test.id
250 | file_name = "temp.bin"
251 | source = "%s"
252 | content_type = "octet/stream"
253 | file_info = {
254 | description = "the file"
255 | }
256 | server_side_encryption {
257 | mode = "SSE-C"
258 | algorithm = "AES256"
259 | key {
260 | secret_b64 = "notarealkey11111111111111111111111111111111="
261 | key_id = "test_id"
262 | }
263 | }
264 | }
265 | `, bucketName, tempFile)
266 | }
267 |
--------------------------------------------------------------------------------
/b2/resource_b2_bucket_notification_rules.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/resource_b2_bucket_notification_rules.go
4 | //
5 | // Copyright 2024 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "context"
15 |
16 | "github.com/hashicorp/terraform-plugin-log/tflog"
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
19 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
20 | )
21 |
22 | func resourceB2BucketNotificationRules() *schema.Resource {
23 | return &schema.Resource{
24 | Description: "B2 bucket notification rules resource.",
25 |
26 | CreateContext: resourceB2BucketNotificationRulesCreate,
27 | ReadContext: resourceB2BucketNotificationRulesRead,
28 | UpdateContext: resourceB2BucketNotificationRulesUpdate,
29 | DeleteContext: resourceB2BucketNotificationRulesDelete,
30 |
31 | Schema: map[string]*schema.Schema{
32 | "bucket_id": {
33 | Description: "The ID of the bucket.",
34 | Type: schema.TypeString,
35 | Required: true,
36 | ForceNew: true,
37 | ValidateFunc: validation.NoZeroValues,
38 | },
39 | "notification_rules": {
40 | Description: "An array of Event Notification Rules.",
41 | Type: schema.TypeList,
42 | Elem: getNotificationRulesElem(false),
43 | Required: true,
44 | MinItems: 1,
45 | },
46 | },
47 | }
48 | }
49 |
50 | func resourceB2BucketNotificationRulesCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
51 | client := meta.(*Client)
52 | const name = "bucket_notification_rules"
53 | const op = RESOURCE_CREATE
54 |
55 | input := map[string]interface{}{
56 | "bucket_id": d.Get("bucket_id").(string),
57 | "notification_rules": d.Get("notification_rules").([]interface{}),
58 | }
59 |
60 | output, err := client.apply(ctx, name, op, input)
61 | if err != nil {
62 | return diag.FromErr(err)
63 | }
64 |
65 | d.SetId(output["bucket_id"].(string))
66 |
67 | err = client.populate(ctx, name, op, output, d)
68 | if err != nil {
69 | return diag.FromErr(err)
70 | }
71 |
72 | return nil
73 | }
74 |
75 | func resourceB2BucketNotificationRulesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
76 | client := meta.(*Client)
77 | const name = "bucket_notification_rules"
78 | const op = RESOURCE_READ
79 |
80 | input := map[string]interface{}{
81 | "bucket_id": d.Id(),
82 | }
83 |
84 | output, err := client.apply(ctx, name, op, input)
85 | if err != nil {
86 | return diag.FromErr(err)
87 | }
88 | if _, ok := output["bucket_id"]; !ok && !d.IsNewResource() {
89 | // deleted bucket, thus notification rules no longer exist
90 | tflog.Warn(ctx, "Bucket not found for Event Notifications, possible resource drift", map[string]interface{}{
91 | "bucket_id": d.Id(),
92 | })
93 | d.SetId("")
94 | return nil
95 | }
96 |
97 | err = client.populate(ctx, name, op, output, d)
98 | if err != nil {
99 | return diag.FromErr(err)
100 | }
101 |
102 | return nil
103 | }
104 |
105 | func resourceB2BucketNotificationRulesUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
106 | client := meta.(*Client)
107 | const name = "bucket_notification_rules"
108 | const op = RESOURCE_UPDATE
109 |
110 | input := map[string]interface{}{
111 | "bucket_id": d.Id(),
112 | "notification_rules": d.Get("notification_rules").([]interface{}),
113 | }
114 |
115 | output, err := client.apply(ctx, name, op, input)
116 | if err != nil {
117 | return diag.FromErr(err)
118 | }
119 |
120 | err = client.populate(ctx, name, op, output, d)
121 | if err != nil {
122 | return diag.FromErr(err)
123 | }
124 |
125 | return nil
126 | }
127 |
128 | func resourceB2BucketNotificationRulesDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
129 | client := meta.(*Client)
130 | const name = "bucket_notification_rules"
131 | const op = RESOURCE_DELETE
132 |
133 | input := map[string]interface{}{
134 | "bucket_id": d.Id(),
135 | }
136 |
137 | _, err := client.apply(ctx, name, op, input)
138 | if err != nil {
139 | return diag.FromErr(err)
140 | }
141 |
142 | d.SetId("")
143 |
144 | return nil
145 | }
146 |
--------------------------------------------------------------------------------
/b2/resource_b2_bucket_notification_rules_test.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/resource_b2_bucket_notification_rules_test.go
4 | //
5 | // Copyright 2024 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "fmt"
15 | "testing"
16 |
17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
19 | )
20 |
21 | func TestAccResourceB2BucketNotificationRules_basic(t *testing.T) {
22 | parentResourceName := "b2_bucket.test"
23 | resourceName := "b2_bucket_notification_rules.test"
24 |
25 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
26 | ruleName := acctest.RandomWithPrefix("test-b2-tfp")
27 |
28 | resource.Test(t, resource.TestCase{
29 | PreCheck: func() { testAccPreCheck(t) },
30 | ProviderFactories: providerFactories,
31 | Steps: []resource.TestStep{
32 | {
33 | Config: testAccResourceB2BucketNotificationRulesConfig_basic(bucketName, ruleName),
34 | Check: resource.ComposeTestCheckFunc(
35 | resource.TestCheckResourceAttrPair(resourceName, "bucket_id", parentResourceName, "bucket_id"),
36 | resource.TestCheckResourceAttr(resourceName, "notification_rules.#", "1"),
37 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.name", ruleName),
38 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.event_types.#", "1"),
39 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.event_types.0", "b2:ObjectCreated:*"),
40 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.is_enabled", "true"),
41 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.object_name_prefix", ""),
42 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.#", "1"),
43 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.target_type", "webhook"),
44 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.url", "https://example.com/webhook"),
45 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.hmac_sha256_signing_secret", ""),
46 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.#", "0"),
47 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.is_suspended", "false"),
48 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.suspension_reason", ""),
49 | ),
50 | },
51 | },
52 | })
53 | }
54 |
55 | func TestAccResourceB2BucketNotificationRules_all(t *testing.T) {
56 | parentResourceName := "b2_bucket.test"
57 | resourceName := "b2_bucket_notification_rules.test"
58 |
59 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
60 | ruleName := acctest.RandomWithPrefix("test-b2-tfp")
61 |
62 | resource.Test(t, resource.TestCase{
63 | PreCheck: func() { testAccPreCheck(t) },
64 | ProviderFactories: providerFactories,
65 | Steps: []resource.TestStep{
66 | {
67 | Config: testAccResourceB2BucketNotificationRulesConfig_all(bucketName, ruleName),
68 | Check: resource.ComposeTestCheckFunc(
69 | resource.TestCheckResourceAttrPair(resourceName, "bucket_id", parentResourceName, "bucket_id"),
70 | resource.TestCheckResourceAttr(resourceName, "notification_rules.#", "1"),
71 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.name", ruleName),
72 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.event_types.#", "2"),
73 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.event_types.0", "b2:ObjectCreated:*"),
74 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.event_types.1", "b2:ObjectDeleted:*"),
75 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.is_enabled", "false"),
76 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.object_name_prefix", "prefix/"),
77 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.#", "1"),
78 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.target_type", "webhook"),
79 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.url", "https://example.com/webhook"),
80 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.hmac_sha256_signing_secret", "sWhtNHMFntMPukWYacpMmJbrhsuylxTg"),
81 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.#", "2"),
82 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.0.name", "myCustomHeader1"),
83 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.0.value", "myCustomHeaderVal1"),
84 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.1.name", "myCustomHeader2"),
85 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.1.value", "myCustomHeaderVal2"),
86 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.is_suspended", "false"),
87 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.suspension_reason", ""),
88 | ),
89 | },
90 | },
91 | })
92 | }
93 |
94 | func TestAccResourceB2BucketNotificationRules_update(t *testing.T) {
95 | parentResourceName := "b2_bucket.test"
96 | resourceName := "b2_bucket_notification_rules.test"
97 |
98 | bucketName := acctest.RandomWithPrefix("test-b2-tfp")
99 | ruleName := acctest.RandomWithPrefix("test-b2-tfp")
100 |
101 | resource.Test(t, resource.TestCase{
102 | PreCheck: func() { testAccPreCheck(t) },
103 | ProviderFactories: providerFactories,
104 | Steps: []resource.TestStep{
105 | {
106 | Config: testAccResourceB2BucketNotificationRulesConfig_basic(bucketName, ruleName),
107 | },
108 | {
109 | Config: testAccResourceB2BucketNotificationRulesConfig_all(bucketName, ruleName),
110 | Check: resource.ComposeTestCheckFunc(
111 | resource.TestCheckResourceAttrPair(resourceName, "bucket_id", parentResourceName, "bucket_id"),
112 | resource.TestCheckResourceAttr(resourceName, "notification_rules.#", "1"),
113 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.name", ruleName),
114 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.event_types.#", "2"),
115 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.event_types.0", "b2:ObjectCreated:*"),
116 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.event_types.1", "b2:ObjectDeleted:*"),
117 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.is_enabled", "false"),
118 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.object_name_prefix", "prefix/"),
119 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.#", "1"),
120 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.target_type", "webhook"),
121 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.url", "https://example.com/webhook"),
122 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.hmac_sha256_signing_secret", "sWhtNHMFntMPukWYacpMmJbrhsuylxTg"),
123 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.#", "2"),
124 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.0.name", "myCustomHeader1"),
125 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.0.value", "myCustomHeaderVal1"),
126 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.1.name", "myCustomHeader2"),
127 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.target_configuration.0.custom_headers.1.value", "myCustomHeaderVal2"),
128 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.is_suspended", "false"),
129 | resource.TestCheckResourceAttr(resourceName, "notification_rules.0.suspension_reason", ""),
130 | ),
131 | },
132 | },
133 | })
134 | }
135 |
136 | func testAccResourceB2BucketNotificationRulesConfig_basic(bucketName string, ruleName string) string {
137 | return fmt.Sprintf(`
138 | resource "b2_bucket" "test" {
139 | bucket_name = "%s"
140 | bucket_type = "allPublic"
141 | }
142 |
143 | resource "b2_bucket_notification_rules" "test" {
144 | bucket_id = b2_bucket.test.id
145 | notification_rules {
146 | name = "%s"
147 | event_types = ["b2:ObjectCreated:*"]
148 | target_configuration {
149 | target_type = "webhook"
150 | url = "https://example.com/webhook"
151 | }
152 | }
153 | }
154 | `, bucketName, ruleName)
155 | }
156 |
157 | func testAccResourceB2BucketNotificationRulesConfig_all(bucketName string, ruleName string) string {
158 | return fmt.Sprintf(`
159 | resource "b2_bucket" "test" {
160 | bucket_name = "%s"
161 | bucket_type = "allPublic"
162 | }
163 |
164 | resource "b2_bucket_notification_rules" "test" {
165 | bucket_id = b2_bucket.test.id
166 | notification_rules {
167 | name = "%s"
168 | event_types = ["b2:ObjectCreated:*", "b2:ObjectDeleted:*"]
169 | is_enabled = false
170 | object_name_prefix = "prefix/"
171 | target_configuration {
172 | target_type = "webhook"
173 | url = "https://example.com/webhook"
174 | hmac_sha256_signing_secret = "sWhtNHMFntMPukWYacpMmJbrhsuylxTg"
175 | custom_headers {
176 | name = "myCustomHeader1"
177 | value = "myCustomHeaderVal1"
178 | }
179 | custom_headers { # optional
180 | name = "myCustomHeader2"
181 | value = "myCustomHeaderVal2"
182 | }
183 | }
184 | }
185 | }
186 | `, bucketName, ruleName)
187 | }
188 |
--------------------------------------------------------------------------------
/b2/utils.go:
--------------------------------------------------------------------------------
1 | // ####################################################################
2 | //
3 | // File: b2/utils.go
4 | //
5 | // Copyright 2024 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | // ####################################################################
10 |
11 | package b2
12 |
13 | func If[T any](cond bool, vtrue, vfalse T) T {
14 | if cond {
15 | return vtrue
16 | }
17 | return vfalse
18 | }
19 |
--------------------------------------------------------------------------------
/b2/validators.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: b2/validators.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package b2
12 |
13 | import (
14 | "encoding/base64"
15 | "fmt"
16 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
17 | )
18 |
19 | func validateBase64Key(i interface{}, k string) (warnings []string, errors []error) {
20 | v, ok := i.(string)
21 | if ok {
22 | decoded, err := base64.StdEncoding.DecodeString(v)
23 | if err == nil {
24 | // AES256 (which is the only supported algorithm for now) key should be 256 bits (32 bytes)
25 | if len(decoded) != 32 {
26 | errors = append(errors, fmt.Errorf("AES256 key should be 32 bytes, got %d bytes instead",
27 | len(decoded)))
28 | }
29 | } else {
30 | errors = append(errors, err)
31 | }
32 | } else {
33 | errors = append(errors, fmt.Errorf("expected type of %s to be string", k))
34 | }
35 |
36 | return warnings, errors
37 | }
38 |
39 | // StringLenExact returns a SchemaValidateFunc which tests if the provided value
40 | // is of type string and has given length
41 | func StringLenExact(length int) schema.SchemaValidateFunc {
42 | return func(i interface{}, k string) (warnings []string, errors []error) {
43 | v, ok := i.(string)
44 | if !ok {
45 | errors = append(errors, fmt.Errorf("expected type of %s to be string", k))
46 | return warnings, errors
47 | }
48 |
49 | if len(v) != length {
50 | errors = append(errors, fmt.Errorf("expected length of %s must be %d, got %s", k, length, v))
51 | }
52 |
53 | return warnings, errors
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/docs/data-sources/account_info.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_account_info Data Source - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 account info data source.
7 | ---
8 |
9 | # b2_account_info (Data Source)
10 |
11 | B2 account info data source.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Read-Only
19 |
20 | - `account_auth_token` (String, Sensitive) An authorization token to use with all calls, other than b2_authorize_account, that need an Authorization header. This authorization token is valid for at most 24 hours.
21 | - `account_id` (String) The identifier for the account.
22 | - `allowed` (List of Object) An object containing the capabilities of this auth token, and any restrictions on using it. (see [below for nested schema](#nestedatt--allowed))
23 | - `api_url` (String) The base URL to use for all API calls except for uploading and downloading files.
24 | - `download_url` (String) The base URL to use for downloading files.
25 | - `id` (String) The ID of this resource.
26 | - `s3_api_url` (String) The base URL to use for S3-compatible API calls.
27 |
28 |
29 | ### Nested Schema for `allowed`
30 |
31 | Read-Only:
32 |
33 | - `bucket_id` (String)
34 | - `bucket_name` (String)
35 | - `capabilities` (Set of String)
36 | - `name_prefix` (String)
37 |
--------------------------------------------------------------------------------
/docs/data-sources/application_key.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_application_key Data Source - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 application key data source.
7 | ---
8 |
9 | # b2_application_key (Data Source)
10 |
11 | B2 application key data source.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `key_name` (String) The name assigned when the key was created.
21 |
22 | ### Optional
23 |
24 | - `name_prefix` (String) When present, restricts access to files whose names start with the prefix.
25 |
26 | ### Read-Only
27 |
28 | - `application_key_id` (String) The ID of the key.
29 | - `bucket_id` (String) When present, restricts access to one bucket.
30 | - `capabilities` (Set of String) A set of strings, each one naming a capability the key has.
31 | - `id` (String) The ID of this resource.
32 | - `options` (Set of String) A list of application key options.
33 |
--------------------------------------------------------------------------------
/docs/data-sources/bucket.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_bucket Data Source - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 bucket data source.
7 | ---
8 |
9 | # b2_bucket (Data Source)
10 |
11 | B2 bucket data source.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `bucket_name` (String) The name of the bucket.
21 |
22 | ### Read-Only
23 |
24 | - `account_id` (String) Account ID that the bucket belongs to.
25 | - `bucket_id` (String) The ID of the bucket.
26 | - `bucket_info` (Map of String) User-defined information to be stored with the bucket.
27 | - `bucket_type` (String) The bucket type. Either 'allPublic', meaning that files in this bucket can be downloaded by anybody, or 'allPrivate'.
28 | - `cors_rules` (List of Object) The initial list of CORS rules for this bucket. (see [below for nested schema](#nestedatt--cors_rules))
29 | - `default_server_side_encryption` (List of Object) The default server-side encryption settings of this bucket. (see [below for nested schema](#nestedatt--default_server_side_encryption))
30 | - `file_lock_configuration` (List of Object) The default File Lock retention settings for this bucket. (see [below for nested schema](#nestedatt--file_lock_configuration))
31 | - `id` (String) The ID of this resource.
32 | - `lifecycle_rules` (List of Object) The initial list of lifecycle rules for this bucket. (see [below for nested schema](#nestedatt--lifecycle_rules))
33 | - `options` (Set of String) List of bucket options.
34 | - `revision` (Number) Bucket revision.
35 |
36 |
37 | ### Nested Schema for `cors_rules`
38 |
39 | Read-Only:
40 |
41 | - `allowed_headers` (List of String)
42 | - `allowed_operations` (List of String)
43 | - `allowed_origins` (List of String)
44 | - `cors_rule_name` (String)
45 | - `expose_headers` (List of String)
46 | - `max_age_seconds` (Number)
47 |
48 |
49 |
50 | ### Nested Schema for `default_server_side_encryption`
51 |
52 | Read-Only:
53 |
54 | - `algorithm` (String)
55 | - `mode` (String)
56 |
57 |
58 |
59 | ### Nested Schema for `file_lock_configuration`
60 |
61 | Read-Only:
62 |
63 | - `default_retention` (List of Object) (see [below for nested schema](#nestedobjatt--file_lock_configuration--default_retention))
64 | - `is_file_lock_enabled` (Boolean)
65 |
66 |
67 | ### Nested Schema for `file_lock_configuration.default_retention`
68 |
69 | Read-Only:
70 |
71 | - `mode` (String)
72 | - `period` (List of Object) (see [below for nested schema](#nestedobjatt--file_lock_configuration--default_retention--period))
73 |
74 |
75 | ### Nested Schema for `file_lock_configuration.default_retention.period`
76 |
77 | Read-Only:
78 |
79 | - `duration` (Number)
80 | - `unit` (String)
81 |
82 |
83 |
84 |
85 |
86 | ### Nested Schema for `lifecycle_rules`
87 |
88 | Read-Only:
89 |
90 | - `days_from_hiding_to_deleting` (Number)
91 | - `days_from_uploading_to_hiding` (Number)
92 | - `file_name_prefix` (String)
93 |
--------------------------------------------------------------------------------
/docs/data-sources/bucket_file.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_bucket_file Data Source - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 bucket file data source.
7 | ---
8 |
9 | # b2_bucket_file (Data Source)
10 |
11 | B2 bucket file data source.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `bucket_id` (String) The ID of the bucket.
21 | - `file_name` (String) The file name.
22 |
23 | ### Optional
24 |
25 | - `show_versions` (Boolean) Show all file versions.
26 |
27 | ### Read-Only
28 |
29 | - `file_versions` (List of Object) File versions. (see [below for nested schema](#nestedatt--file_versions))
30 | - `id` (String) The ID of this resource.
31 |
32 |
33 | ### Nested Schema for `file_versions`
34 |
35 | Read-Only:
36 |
37 | - `action` (String)
38 | - `bucket_id` (String)
39 | - `content_md5` (String)
40 | - `content_sha1` (String)
41 | - `content_type` (String)
42 | - `file_id` (String)
43 | - `file_info` (Map of String)
44 | - `file_name` (String)
45 | - `server_side_encryption` (List of Object) (see [below for nested schema](#nestedobjatt--file_versions--server_side_encryption))
46 | - `size` (Number)
47 | - `upload_timestamp` (Number)
48 |
49 |
50 | ### Nested Schema for `file_versions.server_side_encryption`
51 |
52 | Read-Only:
53 |
54 | - `algorithm` (String)
55 | - `mode` (String)
56 |
--------------------------------------------------------------------------------
/docs/data-sources/bucket_file_signed_url.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_bucket_file_signed_url Data Source - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 signed URL for a bucket file data source.
7 | ---
8 |
9 | # b2_bucket_file_signed_url (Data Source)
10 |
11 | B2 signed URL for a bucket file data source.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `bucket_id` (String) The ID of the bucket.
21 | - `file_name` (String) The file name.
22 |
23 | ### Optional
24 |
25 | - `duration` (Number) The duration for which the presigned URL is valid.
26 |
27 | ### Read-Only
28 |
29 | - `id` (String) The ID of this resource.
30 | - `signed_url` (String) The signed URL for the given file.
31 |
--------------------------------------------------------------------------------
/docs/data-sources/bucket_files.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_bucket_files Data Source - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 bucket files data source.
7 | ---
8 |
9 | # b2_bucket_files (Data Source)
10 |
11 | B2 bucket files data source.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `bucket_id` (String) The ID of the bucket.
21 |
22 | ### Optional
23 |
24 | - `folder_name` (String) The folder name (B2 file name prefix).
25 | - `recursive` (Boolean) Recursive mode.
26 | - `show_versions` (Boolean) Show all file versions.
27 |
28 | ### Read-Only
29 |
30 | - `file_versions` (List of Object) File versions in the folder. (see [below for nested schema](#nestedatt--file_versions))
31 | - `id` (String) The ID of this resource.
32 |
33 |
34 | ### Nested Schema for `file_versions`
35 |
36 | Read-Only:
37 |
38 | - `action` (String)
39 | - `bucket_id` (String)
40 | - `content_md5` (String)
41 | - `content_sha1` (String)
42 | - `content_type` (String)
43 | - `file_id` (String)
44 | - `file_info` (Map of String)
45 | - `file_name` (String)
46 | - `server_side_encryption` (List of Object) (see [below for nested schema](#nestedobjatt--file_versions--server_side_encryption))
47 | - `size` (Number)
48 | - `upload_timestamp` (Number)
49 |
50 |
51 | ### Nested Schema for `file_versions.server_side_encryption`
52 |
53 | Read-Only:
54 |
55 | - `algorithm` (String)
56 | - `mode` (String)
57 |
--------------------------------------------------------------------------------
/docs/data-sources/bucket_notification_rules.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_bucket_notification_rules Data Source - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 bucket notification rules data source.
7 | ---
8 |
9 | # b2_bucket_notification_rules (Data Source)
10 |
11 | B2 bucket notification rules data source.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `bucket_id` (String) The ID of the bucket.
21 |
22 | ### Read-Only
23 |
24 | - `id` (String) The ID of this resource.
25 | - `notification_rules` (List of Object) An array of Event Notification Rules. (see [below for nested schema](#nestedatt--notification_rules))
26 |
27 |
28 | ### Nested Schema for `notification_rules`
29 |
30 | Read-Only:
31 |
32 | - `event_types` (List of String)
33 | - `is_enabled` (Boolean)
34 | - `is_suspended` (Boolean)
35 | - `name` (String)
36 | - `object_name_prefix` (String)
37 | - `suspension_reason` (String)
38 | - `target_configuration` (List of Object) (see [below for nested schema](#nestedobjatt--notification_rules--target_configuration))
39 |
40 |
41 | ### Nested Schema for `notification_rules.target_configuration`
42 |
43 | Read-Only:
44 |
45 | - `custom_headers` (List of Object) (see [below for nested schema](#nestedobjatt--notification_rules--target_configuration--custom_headers))
46 | - `hmac_sha256_signing_secret` (String)
47 | - `target_type` (String)
48 | - `url` (String)
49 |
50 |
51 | ### Nested Schema for `notification_rules.target_configuration.custom_headers`
52 |
53 | Read-Only:
54 |
55 | - `name` (String)
56 | - `value` (String)
57 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | page_title: "B2 Provider"
3 | description: |-
4 |
5 | ---
6 | # B2 Provider
7 |
8 | Terraform provider for [Backblaze B2](https://www.backblaze.com/b2/).
9 |
10 | The provider is written in go, but it uses official [B2 python SDK](https://github.com/Backblaze/b2-sdk-python) embedded into the binary.
11 |
12 | ## Example Usage
13 | ```terraform
14 | terraform {
15 | required_version = ">= 1.0.0"
16 | required_providers {
17 | b2 = {
18 | source = "Backblaze/b2"
19 | }
20 | }
21 | }
22 |
23 | provider "b2" {
24 | }
25 |
26 | resource "b2_application_key" "example_key" {
27 | key_name = "my-key"
28 | capabilities = ["readFiles"]
29 | }
30 |
31 | resource "b2_bucket" "example_bucket" {
32 | bucket_name = "my-b2-bucket"
33 | bucket_type = "allPublic"
34 | }
35 | ```
36 |
37 |
38 | ## Schema
39 |
40 | ### Optional
41 |
42 | - `application_key` (String, Sensitive) B2 Application Key (B2_APPLICATION_KEY env).
43 | - `application_key_id` (String, Sensitive) B2 Application Key ID (B2_APPLICATION_KEY_ID env).
44 | - `endpoint` (String) B2 endpoint - the string 'production' or a custom B2 API URL (B2_ENDPOINT env). You should not need to set this unless you work at Backblaze. Defaults to `production`.
45 |
--------------------------------------------------------------------------------
/docs/resources/application_key.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_application_key Resource - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 application key resource.
7 | ---
8 |
9 | # b2_application_key (Resource)
10 |
11 | B2 application key resource.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `capabilities` (Set of String) A set of strings, each one naming a capability the key has. **Modifying this attribute will force creation of a new resource.**
21 | - `key_name` (String) The name of the key. **Modifying this attribute will force creation of a new resource.**
22 |
23 | ### Optional
24 |
25 | - `bucket_id` (String) When present, restricts access to one bucket. **Modifying this attribute will force creation of a new resource.**
26 | - `name_prefix` (String) When present, restricts access to files whose names start with the prefix. Required when using `bucket_id`. **Modifying this attribute will force creation of a new resource.**
27 |
28 | ### Read-Only
29 |
30 | - `application_key` (String, Sensitive) The key.
31 | - `application_key_id` (String) The ID of the newly created key.
32 | - `id` (String) The ID of this resource.
33 | - `options` (Set of String) List of application key options.
34 |
--------------------------------------------------------------------------------
/docs/resources/bucket.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_bucket Resource - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 bucket resource.
7 | ---
8 |
9 | # b2_bucket (Resource)
10 |
11 | B2 bucket resource.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `bucket_name` (String) The name of the bucket. **Modifying this attribute will force creation of a new resource.**
21 | - `bucket_type` (String) The bucket type. Either 'allPublic', meaning that files in this bucket can be downloaded by anybody, or 'allPrivate'.
22 |
23 | ### Optional
24 |
25 | - `bucket_info` (Map of String) User-defined information to be stored with the bucket.
26 | - `cors_rules` (Block List) The initial list of CORS rules for this bucket. (see [below for nested schema](#nestedblock--cors_rules))
27 | - `default_server_side_encryption` (Block List, Max: 1) The default server-side encryption settings for this bucket. (see [below for nested schema](#nestedblock--default_server_side_encryption))
28 | - `file_lock_configuration` (Block List) File lock enabled flag, and default retention settings. (see [below for nested schema](#nestedblock--file_lock_configuration))
29 | - `lifecycle_rules` (Block List) The initial list of lifecycle rules for this bucket. (see [below for nested schema](#nestedblock--lifecycle_rules))
30 |
31 | ### Read-Only
32 |
33 | - `account_id` (String) Account ID that the bucket belongs to.
34 | - `bucket_id` (String) The ID of the bucket.
35 | - `id` (String) The ID of this resource.
36 | - `options` (Set of String) List of bucket options.
37 | - `revision` (Number) Bucket revision.
38 |
39 |
40 | ### Nested Schema for `cors_rules`
41 |
42 | Required:
43 |
44 | - `allowed_operations` (List of String) A list specifying which operations the rule allows.
45 | - `allowed_origins` (List of String) A non-empty list specifying which origins the rule covers.
46 | - `cors_rule_name` (String) A name for humans to recognize the rule in a user interface.
47 | - `max_age_seconds` (Number) This specifies the maximum number of seconds that a browser may cache the response to a preflight request.
48 |
49 | Optional:
50 |
51 | - `allowed_headers` (List of String) If present, this is a list of headers that are allowed in a pre-flight OPTIONS's request's Access-Control-Request-Headers header value.
52 | - `expose_headers` (List of String) If present, this is a list of headers that may be exposed to an application inside the client.
53 |
54 |
55 |
56 | ### Nested Schema for `default_server_side_encryption`
57 |
58 | Optional:
59 |
60 | - `algorithm` (String) Server-side encryption algorithm. AES256 is the only one supported.
61 | - `mode` (String) Server-side encryption mode.
62 |
63 |
64 |
65 | ### Nested Schema for `file_lock_configuration`
66 |
67 | Optional:
68 |
69 | - `default_retention` (Block List, Max: 1) Default retention settings for files uploaded to this bucket. (see [below for nested schema](#nestedblock--file_lock_configuration--default_retention))
70 | - `is_file_lock_enabled` (Boolean) If present, the boolean value specifies whether bucket is File Lock-enabled. Defaults to `false`. **Modifying this attribute will force creation of a new resource.**
71 |
72 |
73 | ### Nested Schema for `file_lock_configuration.default_retention`
74 |
75 | Required:
76 |
77 | - `mode` (String) Default retention mode (compliance|governance|none).
78 |
79 | Optional:
80 |
81 | - `period` (Block List, Max: 1) How long for to make files immutable. (see [below for nested schema](#nestedblock--file_lock_configuration--default_retention--period))
82 |
83 |
84 | ### Nested Schema for `file_lock_configuration.default_retention.period`
85 |
86 | Required:
87 |
88 | - `duration` (Number) Duration.
89 | - `unit` (String) Unit for duration (days|years).
90 |
91 |
92 |
93 |
94 |
95 | ### Nested Schema for `lifecycle_rules`
96 |
97 | Required:
98 |
99 | - `file_name_prefix` (String) It specifies which files in the bucket it applies to.
100 |
101 | Optional:
102 |
103 | - `days_from_hiding_to_deleting` (Number) It says how long to keep file versions that are not the current version.
104 | - `days_from_uploading_to_hiding` (Number) It causes files to be hidden automatically after the given number of days.
105 |
--------------------------------------------------------------------------------
/docs/resources/bucket_file_version.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_bucket_file_version Resource - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 bucket file version resource.
7 | ---
8 |
9 | # b2_bucket_file_version (Resource)
10 |
11 | B2 bucket file version resource.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `bucket_id` (String) The ID of the bucket. **Modifying this attribute will force creation of a new resource.**
21 | - `file_name` (String) The name of the B2 file. **Modifying this attribute will force creation of a new resource.**
22 | - `source` (String) Path to the local file. **Modifying this attribute will force creation of a new resource.**
23 |
24 | ### Optional
25 |
26 | - `content_type` (String) Content type. If not set, it will be set based on the file extension. **Modifying this attribute will force creation of a new resource.**
27 | - `file_info` (Map of String) The custom information that is uploaded with the file. **Modifying this attribute will force creation of a new resource.**
28 | - `server_side_encryption` (Block List, Max: 1) Server-side encryption settings. **Modifying this attribute will force creation of a new resource.** (see [below for nested schema](#nestedblock--server_side_encryption))
29 |
30 | ### Read-Only
31 |
32 | - `action` (String) One of 'start', 'upload', 'hide', 'folder', or other values added in the future.
33 | - `content_md5` (String) MD5 sum of the content.
34 | - `content_sha1` (String) SHA1 hash of the content.
35 | - `file_id` (String) The unique identifier for this version of this file.
36 | - `id` (String) The ID of this resource.
37 | - `size` (Number) The file size.
38 | - `upload_timestamp` (Number) This is a UTC time when this file was uploaded.
39 |
40 |
41 | ### Nested Schema for `server_side_encryption`
42 |
43 | Optional:
44 |
45 | - `algorithm` (String) Server-side encryption algorithm. AES256 is the only one supported.
46 | - `key` (Block List, Max: 1) Key used in SSE-C mode. (see [below for nested schema](#nestedblock--server_side_encryption--key))
47 | - `mode` (String) Server-side encryption mode.
48 |
49 |
50 | ### Nested Schema for `server_side_encryption.key`
51 |
52 | Optional:
53 |
54 | - `key_id` (String) Key identifier stored in file info metadata.
55 | - `secret_b64` (String, Sensitive) Secret key value, in standard Base 64 encoding (RFC 4648).
56 |
--------------------------------------------------------------------------------
/docs/resources/bucket_notification_rules.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "b2_bucket_notification_rules Resource - terraform-provider-b2"
4 | subcategory: ""
5 | description: |-
6 | B2 bucket notification rules resource.
7 | ---
8 |
9 | # b2_bucket_notification_rules (Resource)
10 |
11 | B2 bucket notification rules resource.
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - `bucket_id` (String) The ID of the bucket. **Modifying this attribute will force creation of a new resource.**
21 | - `notification_rules` (Block List, Min: 1) An array of Event Notification Rules. (see [below for nested schema](#nestedblock--notification_rules))
22 |
23 | ### Read-Only
24 |
25 | - `id` (String) The ID of this resource.
26 |
27 |
28 | ### Nested Schema for `notification_rules`
29 |
30 | Required:
31 |
32 | - `event_types` (List of String) The list of event types for the event notification rule.
33 | - `name` (String) A name for the event notification rule. The name must be unique among the bucket's notification rules.
34 | - `target_configuration` (Block List, Min: 1, Max: 1) The target configuration for the event notification rule. (see [below for nested schema](#nestedblock--notification_rules--target_configuration))
35 |
36 | Optional:
37 |
38 | - `is_enabled` (Boolean) Whether the event notification rule is enabled. Defaults to `true`.
39 | - `object_name_prefix` (String) Specifies which object(s) in the bucket the event notification rule applies to.
40 |
41 | Read-Only:
42 |
43 | - `is_suspended` (Boolean) Whether the event notification rule is suspended.
44 | - `suspension_reason` (String) A brief description of why the event notification rule was suspended.
45 |
46 |
47 | ### Nested Schema for `notification_rules.target_configuration`
48 |
49 | Required:
50 |
51 | - `target_type` (String) The type of the target configuration, currently "webhook" only.
52 | - `url` (String) The URL for the webhook.
53 |
54 | Optional:
55 |
56 | - `custom_headers` (Block List, Max: 10) When present, additional header name/value pairs to be sent on the webhook invocation. (see [below for nested schema](#nestedblock--notification_rules--target_configuration--custom_headers))
57 | - `hmac_sha256_signing_secret` (String, Sensitive) The signing secret for use in verifying the X-Bz-Event-Notification-Signature.
58 |
59 |
60 | ### Nested Schema for `notification_rules.target_configuration.custom_headers`
61 |
62 | Required:
63 |
64 | - `name` (String) Name of the header.
65 | - `value` (String) Value of the header.
66 |
--------------------------------------------------------------------------------
/examples/application_key/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0.0"
3 | required_providers {
4 | b2 = {
5 | source = "Backblaze/b2"
6 | }
7 | }
8 | }
9 |
10 | provider "b2" {
11 | }
12 |
13 | resource "b2_application_key" "example" {
14 | key_name = "test-b2-tfp-0000000000000000000"
15 | capabilities = ["readFiles"]
16 | }
17 |
18 | data "b2_application_key" "example" {
19 | key_name = b2_application_key.example.key_name
20 | }
21 |
22 | output "application_key" {
23 | value = data.b2_application_key.example
24 | }
25 |
--------------------------------------------------------------------------------
/examples/bucket/example.txt:
--------------------------------------------------------------------------------
1 | hello
2 |
--------------------------------------------------------------------------------
/examples/bucket/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0.0"
3 | required_providers {
4 | b2 = {
5 | source = "Backblaze/b2"
6 | }
7 | }
8 | }
9 |
10 | provider "b2" {
11 | }
12 |
13 | resource "b2_bucket" "example" {
14 | bucket_name = "test-b2-tfp-0000000000000000000"
15 | bucket_type = "allPublic"
16 | }
17 |
18 | resource "b2_bucket_file_version" "example1" {
19 | bucket_id = b2_bucket.example.id
20 | file_name = "example.txt"
21 | source = "example.txt"
22 | }
23 |
24 | resource "b2_bucket_file_version" "example2" {
25 | bucket_id = b2_bucket_file_version.example1.bucket_id
26 | file_name = b2_bucket_file_version.example1.file_name
27 | source = b2_bucket_file_version.example1.source
28 | file_info = {
29 | description = "second version"
30 | }
31 | }
32 |
33 | resource "b2_bucket_file_version" "example3" {
34 | bucket_id = b2_bucket_file_version.example2.bucket_id
35 | file_name = "dir/example.txt"
36 | source = b2_bucket_file_version.example2.source
37 | server_side_encryption {
38 | mode = "SSE-B2"
39 | algorithm = "AES256"
40 | }
41 | }
42 |
43 | data "b2_bucket" "example" {
44 | bucket_name = b2_bucket.example.bucket_name
45 | depends_on = [
46 | b2_bucket.example,
47 | ]
48 | }
49 |
50 | data "b2_bucket_file" "example" {
51 | bucket_id = b2_bucket_file_version.example2.bucket_id
52 | file_name = b2_bucket_file_version.example2.file_name
53 | show_versions = true
54 | depends_on = [
55 | b2_bucket_file_version.example1,
56 | b2_bucket_file_version.example2,
57 | b2_bucket_file_version.example3,
58 | ]
59 | }
60 |
61 | data "b2_bucket_files" "example" {
62 | bucket_id = b2_bucket_file_version.example3.bucket_id
63 | depends_on = [
64 | b2_bucket_file_version.example1,
65 | b2_bucket_file_version.example2,
66 | b2_bucket_file_version.example3,
67 | ]
68 | }
69 |
70 | output "bucket_example" {
71 | value = data.b2_bucket.example
72 | }
73 |
74 | output "bucket_file_example" {
75 | value = data.b2_bucket_file.example
76 | }
77 |
78 | output "bucket_files_example" {
79 | value = data.b2_bucket_files.example
80 | }
81 |
--------------------------------------------------------------------------------
/examples/bucket_file_lock/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0.0"
3 | required_providers {
4 | b2 = {
5 | source = "Backblaze/b2"
6 | }
7 | }
8 | }
9 |
10 | provider "b2" {
11 |
12 | }
13 |
14 | resource "b2_bucket" "example" {
15 | bucket_name = "test-b2-lock"
16 | bucket_type = "allPublic"
17 | file_lock_configuration {
18 | is_file_lock_enabled = true
19 | default_retention {
20 | mode = "governance"
21 | period {
22 | duration = 7
23 | unit = "days"
24 | }
25 | }
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/examples/bucket_notification_rules/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0.0"
3 | required_providers {
4 | b2 = {
5 | source = "Backblaze/b2"
6 | }
7 | }
8 | }
9 |
10 | provider "b2" {
11 | }
12 |
13 | resource "b2_bucket" "example" {
14 | bucket_name = "test-b2-tfp-0000000000000000000"
15 | bucket_type = "allPublic"
16 | }
17 |
18 | resource "b2_bucket_notification_rules" "example" {
19 | bucket_id = b2_bucket.example.id
20 | notification_rules {
21 | name = "${b2_bucket.example.bucket_name}-rule-1"
22 | event_types = [
23 | "b2:ObjectCreated:*",
24 | "b2:ObjectDeleted:*",
25 | ]
26 | target_configuration {
27 | target_type = "webhook"
28 | url = "https://example.com/webhook"
29 | custom_headers {
30 | name = "myCustomHeader1"
31 | value = "myCustomHeaderVal1"
32 | }
33 | }
34 | }
35 | }
36 |
37 | data "b2_bucket" "example" {
38 | bucket_name = b2_bucket.example.bucket_name
39 | depends_on = [
40 | b2_bucket.example,
41 | ]
42 | }
43 |
44 | data "b2_bucket_notification_rules" "example" {
45 | bucket_id = b2_bucket.example.bucket_id
46 | depends_on = [
47 | b2_bucket_notification_rules.example,
48 | ]
49 | }
50 |
51 | output "bucket_example" {
52 | value = data.b2_bucket.example
53 | }
54 |
55 | output "bucket_notification_rules_example" {
56 | value = data.b2_bucket_notification_rules.example
57 | }
58 |
--------------------------------------------------------------------------------
/examples/file_version_sse_c/main.tf:
--------------------------------------------------------------------------------
1 |
2 | variable "encryption_key" {
3 | # value will be taken from env variable 'TF_VAR_encryption_key'
4 | description = "Encryption key"
5 | type = string
6 | sensitive = true
7 | }
8 |
9 | resource "b2_bucket" "test" {
10 | bucket_name = "!bucketname!"
11 | bucket_type = "allPublic"
12 | }
13 |
14 | resource "b2_bucket_file_version" "test" {
15 | bucket_id = b2_bucket.test.id
16 | file_name = "temp.bin"
17 | source = "!filename!"
18 | content_type = "octet/stream"
19 | file_info = {
20 | description = "the file"
21 | }
22 | server_side_encryption {
23 | mode = "SSE-C"
24 | algorithm = "AES256"
25 | key {
26 | secret_b64 = var.encryption_key
27 | key_id = "Identifier that will let client tools know which key to provide"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/provider/provider.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0.0"
3 | required_providers {
4 | b2 = {
5 | source = "Backblaze/b2"
6 | }
7 | }
8 | }
9 |
10 | provider "b2" {
11 | }
12 |
13 | resource "b2_application_key" "example_key" {
14 | key_name = "my-key"
15 | capabilities = ["readFiles"]
16 | }
17 |
18 | resource "b2_bucket" "example_bucket" {
19 | bucket_name = "my-b2-bucket"
20 | bucket_type = "allPublic"
21 | }
22 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/Backblaze/terraform-provider-b2
2 |
3 | go 1.24.0
4 |
5 | toolchain go1.24.1
6 |
7 | require github.com/hashicorp/terraform-plugin-log v0.9.0
8 |
9 | require github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1
10 |
11 | require (
12 | github.com/ProtonMail/go-crypto v1.1.3 // indirect
13 | github.com/agext/levenshtein v1.2.2 // indirect
14 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
15 | github.com/cloudflare/circl v1.3.7 // indirect
16 | github.com/fatih/color v1.16.0 // indirect
17 | github.com/golang/protobuf v1.5.4 // indirect
18 | github.com/google/go-cmp v0.6.0 // indirect
19 | github.com/hashicorp/errwrap v1.0.0 // indirect
20 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect
21 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
22 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect
23 | github.com/hashicorp/go-hclog v1.6.3 // indirect
24 | github.com/hashicorp/go-multierror v1.1.1 // indirect
25 | github.com/hashicorp/go-plugin v1.6.2 // indirect
26 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
27 | github.com/hashicorp/go-uuid v1.0.3 // indirect
28 | github.com/hashicorp/go-version v1.7.0 // indirect
29 | github.com/hashicorp/hc-install v0.9.1 // indirect
30 | github.com/hashicorp/hcl/v2 v2.23.0 // indirect
31 | github.com/hashicorp/logutils v1.0.0 // indirect
32 | github.com/hashicorp/terraform-exec v0.22.0 // indirect
33 | github.com/hashicorp/terraform-json v0.24.0 // indirect
34 | github.com/hashicorp/terraform-plugin-go v0.26.0 // indirect
35 | github.com/hashicorp/terraform-registry-address v0.2.4 // indirect
36 | github.com/hashicorp/terraform-svchost v0.1.1 // indirect
37 | github.com/hashicorp/yamux v0.1.1 // indirect
38 | github.com/mattn/go-colorable v0.1.13 // indirect
39 | github.com/mattn/go-isatty v0.0.20 // indirect
40 | github.com/mitchellh/copystructure v1.2.0 // indirect
41 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect
42 | github.com/mitchellh/go-wordwrap v1.0.0 // indirect
43 | github.com/mitchellh/mapstructure v1.5.0 // indirect
44 | github.com/mitchellh/reflectwalk v1.0.2 // indirect
45 | github.com/oklog/run v1.0.0 // indirect
46 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
47 | github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
48 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
49 | github.com/zclconf/go-cty v1.16.2 // indirect
50 | golang.org/x/crypto v0.36.0 // indirect
51 | golang.org/x/mod v0.22.0 // indirect
52 | golang.org/x/net v0.38.0 // indirect
53 | golang.org/x/sync v0.12.0 // indirect
54 | golang.org/x/sys v0.31.0 // indirect
55 | golang.org/x/text v0.23.0 // indirect
56 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
57 | google.golang.org/appengine v1.6.8 // indirect
58 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
59 | google.golang.org/grpc v1.69.4 // indirect
60 | google.golang.org/protobuf v1.36.3 // indirect
61 | )
62 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: main.go
4 | //
5 | // Copyright 2020 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | package main
12 |
13 | import (
14 | "flag"
15 | "log"
16 | "os"
17 |
18 | "github.com/Backblaze/terraform-provider-b2/b2"
19 | "github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
20 | )
21 |
22 | var (
23 | // these will be set by the goreleaser configuration
24 | // to appropriate values for the compiled binary
25 | version string = "dev"
26 | )
27 |
28 | func main() {
29 | var debugMode bool
30 |
31 | flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve")
32 | flag.Parse()
33 |
34 | pybindings, err := b2.GetBindings()
35 | if err != nil {
36 | log.Fatal(err.Error())
37 | return
38 | }
39 | defer os.Remove(pybindings)
40 |
41 | opts := &plugin.ServeOpts{ProviderFunc: b2.New(version, pybindings), Debug: debugMode}
42 | plugin.Serve(opts)
43 | }
44 |
--------------------------------------------------------------------------------
/python-bindings/GNUmakefile:
--------------------------------------------------------------------------------
1 | NAME=py-terraform-provider-b2
2 | DIR=b2_terraform
3 | EGG_INFO=${NAME}.egg-info
4 | SPEC=${NAME}.spec
5 | OS=$(shell python -c "import platform; print(platform.system())")
6 | STATICX?=1
7 |
8 | default: build
9 |
10 | .PHONY: deps format lint testacc clean build
11 |
12 | deps:
13 | @pip install -r requirements-dev.txt
14 |
15 | format:
16 | @black -Sl 100 ${DIR}
17 |
18 | lint:
19 | @black --check -Sl 100 ${DIR}
20 | @flake8 --ignore=E501,W503 ${DIR}
21 | @python ../scripts/check-headers.py '**/*.py'
22 |
23 | testacc: build
24 |
25 | clean:
26 | @rm -rf build dist ${EGG_INFO}
27 |
28 | build:
29 | @pyinstaller ${SPEC}
30 | ifeq ($(OS)$(STATICX), Linux1)
31 | @mv -f dist/py-terraform-provider-b2 dist/py-terraform-provider-b2-linked
32 | @staticx --strip --loglevel INFO dist/py-terraform-provider-b2-linked dist/py-terraform-provider-b2
33 | @rm -f dist/py-terraform-provider-b2-linked
34 | endif
35 | ifeq ($(OS), Windows)
36 | @mv -f dist/py-terraform-provider-b2.exe dist/py-terraform-provider-b2
37 | endif
38 | ifneq ($(origin CI), undefined)
39 | @echo "version=$(subst refs/tags/v,,${GITHUB_REF})" > "${GITHUB_OUTPUT}"
40 | endif
41 |
42 | install: build
43 |
44 | docs: build
45 |
46 | docs-lint: build
47 |
48 | all: deps lint build
49 |
--------------------------------------------------------------------------------
/python-bindings/b2_terraform/__init__.py:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | #
3 | # File: python-bindings/b2_terraform/__init__.py
4 | #
5 | # Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | #
7 | # License https://www.backblaze.com/using_b2_code.html
8 | #
9 | ######################################################################
10 |
--------------------------------------------------------------------------------
/python-bindings/b2_terraform/arg_parser.py:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | #
3 | # File: python-bindings/b2_terraform/arg_parser.py
4 | #
5 | # Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | #
7 | # License https://www.backblaze.com/using_b2_code.html
8 | #
9 | ######################################################################
10 |
11 | import argparse
12 |
13 |
14 | class ArgumentParser(argparse.ArgumentParser):
15 | def error(self, message):
16 | raise RuntimeError(message)
17 |
--------------------------------------------------------------------------------
/python-bindings/b2_terraform/json_encoder.py:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | #
3 | # File: python-bindings/b2_terraform/json_encoder.py
4 | #
5 | # Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | #
7 | # License https://www.backblaze.com/using_b2_code.html
8 | #
9 | ######################################################################
10 |
11 | import json
12 |
13 |
14 | class B2ProviderJsonEncoder(json.JSONEncoder):
15 | """
16 | Makes it possible to serialize sets to json.
17 | """
18 |
19 | def default(self, obj):
20 | if isinstance(obj, set):
21 | return list(obj)
22 | return super(B2ProviderJsonEncoder, self).default(obj)
23 |
--------------------------------------------------------------------------------
/python-bindings/b2_terraform/terraform_structures.py:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | #
3 | # File: python-bindings/b2_terraform/terraform_structures.py
4 | #
5 | # Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | #
7 | # License https://www.backblaze.com/using_b2_code.html
8 | #
9 | ######################################################################
10 |
11 | """
12 | Store information about which data keys returned by B2 SDK should be passed on to terraform provider.
13 | Prevents error in terraform provider when API is extended with new fields.
14 | """
15 |
16 | API_KEY_KEYS = {
17 | "application_key": None,
18 | "application_key_id": None,
19 | "bucket_id": "",
20 | "capabilities": None,
21 | "name_prefix": "",
22 | "options": None,
23 | "key_name": None,
24 | }
25 |
26 | BUCKET_SERVER_SIDE_ENCRYPTION = {
27 | "mode": None,
28 | "algorithm": None,
29 | }
30 |
31 | FILE_VERSION_SERVER_SIDE_ENCRYPTION = {
32 | "mode": None,
33 | "algorithm": None,
34 | # 'key' is not here as API does not return it
35 | }
36 |
37 | FILE_VERSION_KEYS = {
38 | "action": None,
39 | "content_md5": None,
40 | "content_sha1": None,
41 | "content_type": None,
42 | "bucket_id": None,
43 | "file_id": None,
44 | "file_info": None,
45 | "file_name": None,
46 | "size": None,
47 | "source": None,
48 | "server_side_encryption": FILE_VERSION_SERVER_SIDE_ENCRYPTION,
49 | "upload_timestamp": None,
50 | }
51 |
52 | FILE_KEYS = {
53 | "bucket_id": None,
54 | "file_name": None,
55 | "show_versions": None,
56 | "file_versions": FILE_VERSION_KEYS,
57 | }
58 |
59 | FILE_SIGNED_URL_KEYS = {
60 | "bucket_id": None,
61 | "file_name": None,
62 | "duration": None,
63 | "signed_url": None,
64 | }
65 |
66 | FILES_KEYS = {
67 | "bucket_id": None,
68 | "folder_name": None,
69 | "show_versions": None,
70 | "recursive": None,
71 | "file_versions": FILE_VERSION_KEYS,
72 | }
73 |
74 | NOTIFICATION_RULES = {
75 | "bucket_id": None,
76 | "notification_rules": {
77 | "event_types": None,
78 | "is_enabled": None,
79 | "name": None,
80 | "object_name_prefix": None,
81 | "target_configuration": {
82 | "target_type": None,
83 | "url": None,
84 | "custom_headers": None,
85 | "hmac_sha256_signing_secret": None,
86 | },
87 | },
88 | "is_suspended": None,
89 | "suspension_reason": None,
90 | }
91 |
92 | BUCKET_KEYS = {
93 | "bucket_id": None,
94 | "bucket_name": None,
95 | "bucket_type": None,
96 | "bucket_info": None,
97 | "cors_rules": {
98 | "cors_rule_name": None,
99 | "allowed_origins": None,
100 | "allowed_operations": None,
101 | "max_age_seconds": None,
102 | "allowed_headers": None,
103 | "expose_headers": None,
104 | },
105 | "file_lock_configuration": {
106 | "is_file_lock_enabled": None,
107 | "default_retention": {
108 | "mode": None,
109 | "period": {
110 | "duration": None,
111 | "unit": None,
112 | },
113 | },
114 | },
115 | "default_server_side_encryption": BUCKET_SERVER_SIDE_ENCRYPTION,
116 | "lifecycle_rules": {
117 | "file_name_prefix": None,
118 | "days_from_hiding_to_deleting": None,
119 | "days_from_uploading_to_hiding": None,
120 | },
121 | "account_id": None,
122 | "options": None,
123 | "revision": None,
124 | }
125 |
--------------------------------------------------------------------------------
/python-bindings/py-terraform-provider-b2.spec:
--------------------------------------------------------------------------------
1 | # -*- mode: python ; coding: utf-8 -*-
2 |
3 | block_cipher = None
4 |
5 | a = Analysis(['b2_terraform/provider_tool.py'],
6 | pathex=['.'],
7 | binaries=[],
8 | datas=[],
9 | hiddenimports=[],
10 | hookspath=[],
11 | runtime_hooks=[],
12 | excludes=[],
13 | win_no_prefer_redirects=False,
14 | win_private_assemblies=False,
15 | cipher=block_cipher,
16 | noarchive=False)
17 |
18 | pyz = PYZ(a.pure,
19 | a.zipped_data,
20 | cipher=block_cipher)
21 |
22 | exe = EXE(pyz,
23 | a.scripts,
24 | a.binaries,
25 | a.zipfiles,
26 | a.datas,
27 | [],
28 | name='py-terraform-provider-b2',
29 | debug=False,
30 | bootloader_ignore_signals=False,
31 | strip=False,
32 | upx=True,
33 | upx_exclude=[],
34 | runtime_tmpdir=None,
35 | console=True)
36 |
--------------------------------------------------------------------------------
/python-bindings/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | -r requirements.txt
2 |
3 | black~=24.8.0
4 | flake8~=7.1.1
5 | pyinstaller~=6.11.1
6 | patchelf-wrapper~=1.2 ; sys_platform == 'linux'
7 | staticx~=0.14.1 ; sys_platform == 'linux'
8 |
--------------------------------------------------------------------------------
/python-bindings/requirements.txt:
--------------------------------------------------------------------------------
1 | b2sdk==2.8.0
2 | phx-class-registry==3.0.5
3 | pyhumps==1.6.1
4 |
--------------------------------------------------------------------------------
/scripts/check-gofmt.py:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | #
3 | # File: scripts/check-gofmt.py
4 | #
5 | # Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | #
7 | # License https://www.backblaze.com/using_b2_code.html
8 | #
9 | ######################################################################
10 |
11 | import subprocess
12 | import sys
13 | from glob import glob
14 |
15 |
16 | try:
17 | pattern = sys.argv[1]
18 | except IndexError:
19 | print('usage: python check-gofmt.py GLOB_PATTERN')
20 | sys.exit(2)
21 |
22 | ignored = sys.argv[1:]
23 |
24 | for file in glob(pattern, recursive=True):
25 | if file in ignored:
26 | continue
27 | output = subprocess.run(['gofmt', '-l', file], capture_output=True).stdout.strip()
28 | if output:
29 | print('Go formatter needs running on the file: {}'.format(file), file=sys.stderr)
30 | sys.exit(1)
31 |
--------------------------------------------------------------------------------
/scripts/check-headers.py:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | #
3 | # File: scripts/check-headers.py
4 | #
5 | # Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | #
7 | # License https://www.backblaze.com/using_b2_code.html
8 | #
9 | ######################################################################
10 |
11 | import sys
12 | from glob import glob
13 | from itertools import islice
14 |
15 |
16 | try:
17 | pattern = sys.argv[1]
18 | except IndexError:
19 | print('usage: python check-headers.py GLOB_PATTERN')
20 | sys.exit(2)
21 |
22 | ignored = sys.argv[1:]
23 |
24 | for file in glob(pattern, recursive=True):
25 | if file in ignored:
26 | continue
27 | with open(file) as fd:
28 | head = ''.join(islice(fd, 9))
29 | if 'All Rights Reserved' not in head:
30 | print(
31 | 'Missing "All Rights Reserved" in the header in: {}'.format(file), file=sys.stderr
32 | )
33 | sys.exit(1)
34 | if file not in head:
35 | print('Wrong file name in the header in: {}'.format(file), file=sys.stderr)
36 | sys.exit(1)
37 |
--------------------------------------------------------------------------------
/templates/index.md.tmpl:
--------------------------------------------------------------------------------
1 | ---
2 | page_title: "B2 Provider"
3 | description: |-
4 | {{ .Description | plainmarkdown | trimspace | prefixlines " " }}
5 | ---
6 | # B2 Provider
7 |
8 | Terraform provider for [Backblaze B2](https://www.backblaze.com/b2/).
9 |
10 | The provider is written in go, but it uses official [B2 python SDK](https://github.com/Backblaze/b2-sdk-python) embedded into the binary.
11 |
12 | ## Example Usage
13 | {{tffile "examples/provider/provider.tf"}}
14 |
15 | {{ .SchemaMarkdown | trimspace }}
16 |
--------------------------------------------------------------------------------
/tools/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/Backblaze/terraform-provider-b2/tools
2 |
3 | go 1.24.1
4 |
5 | require github.com/hashicorp/terraform-plugin-docs v0.21.0
6 |
7 | require (
8 | github.com/BurntSushi/toml v1.2.1 // indirect
9 | github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect
10 | github.com/Masterminds/goutils v1.1.1 // indirect
11 | github.com/Masterminds/semver/v3 v3.2.0 // indirect
12 | github.com/Masterminds/sprig/v3 v3.2.3 // indirect
13 | github.com/ProtonMail/go-crypto v1.1.3 // indirect
14 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
15 | github.com/armon/go-radix v1.0.0 // indirect
16 | github.com/bgentry/speakeasy v0.1.0 // indirect
17 | github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
18 | github.com/cloudflare/circl v1.3.7 // indirect
19 | github.com/fatih/color v1.16.0 // indirect
20 | github.com/google/uuid v1.3.0 // indirect
21 | github.com/hashicorp/cli v1.1.7 // indirect
22 | github.com/hashicorp/errwrap v1.1.0 // indirect
23 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect
24 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
25 | github.com/hashicorp/go-multierror v1.1.1 // indirect
26 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
27 | github.com/hashicorp/go-uuid v1.0.3 // indirect
28 | github.com/hashicorp/go-version v1.7.0 // indirect
29 | github.com/hashicorp/hc-install v0.9.1 // indirect
30 | github.com/hashicorp/terraform-exec v0.22.0 // indirect
31 | github.com/hashicorp/terraform-json v0.24.0 // indirect
32 | github.com/huandu/xstrings v1.3.3 // indirect
33 | github.com/imdario/mergo v0.3.15 // indirect
34 | github.com/mattn/go-colorable v0.1.14 // indirect
35 | github.com/mattn/go-isatty v0.0.20 // indirect
36 | github.com/mattn/go-runewidth v0.0.9 // indirect
37 | github.com/mitchellh/copystructure v1.2.0 // indirect
38 | github.com/mitchellh/reflectwalk v1.0.2 // indirect
39 | github.com/posener/complete v1.2.3 // indirect
40 | github.com/shopspring/decimal v1.3.1 // indirect
41 | github.com/spf13/cast v1.5.0 // indirect
42 | github.com/yuin/goldmark v1.7.7 // indirect
43 | github.com/yuin/goldmark-meta v1.1.0 // indirect
44 | github.com/zclconf/go-cty v1.16.2 // indirect
45 | go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
46 | golang.org/x/crypto v0.35.0 // indirect
47 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
48 | golang.org/x/mod v0.22.0 // indirect
49 | golang.org/x/sys v0.30.0 // indirect
50 | golang.org/x/text v0.22.0 // indirect
51 | gopkg.in/yaml.v2 v2.3.0 // indirect
52 | gopkg.in/yaml.v3 v3.0.1 // indirect
53 | )
54 |
--------------------------------------------------------------------------------
/tools/main.go:
--------------------------------------------------------------------------------
1 | //####################################################################
2 | //
3 | // File: tools/main.go
4 | //
5 | // Copyright 2021 Backblaze Inc. All Rights Reserved.
6 | //
7 | // License https://www.backblaze.com/using_b2_code.html
8 | //
9 | //####################################################################
10 |
11 | //go:build tools
12 |
13 | package tools
14 |
15 | import (
16 | _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs"
17 | )
18 |
--------------------------------------------------------------------------------