mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
Copy Terraform provider from teleport-plugins repo (#40224)
* copy terraform provider * Make dependecy checker happy + use go 1.22 * Run terraform tests in the CI, add Makefile target * go mod tidy
This commit is contained in:
@@ -12,7 +12,10 @@ jobs:
|
||||
pull-requests: write
|
||||
with:
|
||||
base-ref: ${{ github.event.pull_request.base.sha || 'master' }}
|
||||
allow-ghsas: 'GHSA-xwh9-gc39-5298'
|
||||
# 'GHSA-6xf3-5hp7-xqqg' is a false positive. That's an old Teleport Vuln,
|
||||
# but because of the replace, the dependency cannot find the correct
|
||||
# Teleport version.
|
||||
allow-ghsas: 'GHSA-6xf3-5hp7-xqqg'
|
||||
allow-dependencies-licenses: >-
|
||||
pkg:cargo/curve25519-dalek-derive,
|
||||
pkg:cargo/ring,
|
||||
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
- name: Run base difftest
|
||||
uses: ./.github/actions/difftest
|
||||
with:
|
||||
flags: --skip="${{ steps.find_excluded.outputs.FLAKE_SKIP }}" -e "integrations/operator/**/*" -e "tool/tsh/**/*" -e "integration/**/*" -e "build.assets/**/*" -e "lib/auth/webauthncli/**/*" -e "lib/auth/touchid/**/*" -e "api/**/*" -e "examples/teleport-usage/**/*" -e "integrations/access/**" -e "integrations/lib/**" -e "integrations/lib/backoff/backoff_test.go" -e "e2e/**/*"
|
||||
flags: --skip="${{ steps.find_excluded.outputs.FLAKE_SKIP }}" -e "integrations/operator/**/*" -e "integrations/terraform/**/*" -e "tool/tsh/**/*" -e "integration/**/*" -e "build.assets/**/*" -e "lib/auth/webauthncli/**/*" -e "lib/auth/touchid/**/*" -e "api/**/*" -e "examples/teleport-usage/**/*" -e "integrations/access/**" -e "integrations/lib/**" -e "integrations/lib/backoff/backoff_test.go" -e "e2e/**/*"
|
||||
target: test-go-unit
|
||||
|
||||
- name: Run touch-id difftest
|
||||
|
||||
@@ -67,6 +67,10 @@ jobs:
|
||||
run: make test-integrations-lib
|
||||
timeout-minutes: 5
|
||||
|
||||
- name: Run terraform provider OSS tests
|
||||
run: make test-terraform-provider
|
||||
timeout-minutes: 15
|
||||
|
||||
- name: Run operator tests
|
||||
timeout-minutes: 40
|
||||
run: make test-operator
|
||||
|
||||
@@ -680,7 +680,7 @@ tooling: ensure-gotestsum $(DIFF_TEST)
|
||||
# Runs all Go/shell tests, called by CI/CD.
|
||||
#
|
||||
.PHONY: test
|
||||
test: test-helm test-sh test-api test-go test-rust test-operator
|
||||
test: test-helm test-sh test-api test-go test-rust test-operator test-terraform-provider
|
||||
|
||||
$(TEST_LOG_DIR):
|
||||
mkdir $(TEST_LOG_DIR)
|
||||
@@ -821,6 +821,12 @@ test-api:
|
||||
test-operator:
|
||||
make -C integrations/operator test
|
||||
#
|
||||
# Runs Teleport Terraform provider tests.
|
||||
#
|
||||
.PHONY: test-terraform-provider
|
||||
test-terraform-provider:
|
||||
make -C integrations/terraform test
|
||||
#
|
||||
# Runs Go tests on the integrations/kube-agent-updater module. These have to be run separately as the package name is different.
|
||||
#
|
||||
.PHONY: test-kube-agent-updater
|
||||
|
||||
@@ -44,7 +44,10 @@ import (
|
||||
// integration tests to build teleport clients for each user/plugin/bot.
|
||||
type OSSAuthHelper struct {
|
||||
server *libauth.TestTLSServer
|
||||
dir string
|
||||
// dir is where we put identity files, and start the auth server
|
||||
// (unless AuthConfig.Dir is manually set).
|
||||
dir string
|
||||
AuthConfig libauth.TestAuthServerConfig
|
||||
}
|
||||
|
||||
// StartServer implements the AuthHelper interface.
|
||||
@@ -52,9 +55,10 @@ type OSSAuthHelper struct {
|
||||
// on the t.Cleanup() stack.
|
||||
func (a *OSSAuthHelper) StartServer(t *testing.T) *client.Client {
|
||||
a.dir = t.TempDir()
|
||||
authServer, err := libauth.NewTestAuthServer(libauth.TestAuthServerConfig{
|
||||
Dir: a.dir,
|
||||
})
|
||||
if a.AuthConfig.Dir == "" {
|
||||
a.AuthConfig.Dir = a.dir
|
||||
}
|
||||
authServer, err := libauth.NewTestAuthServer(a.AuthConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Build artifacts
|
||||
build/*
|
||||
|
||||
# Terraform state
|
||||
.terraform
|
||||
*.tfstate*
|
||||
*.lock.hcl
|
||||
|
||||
example/vars.tfvars
|
||||
example/*.tf
|
||||
|
||||
tmp
|
||||
*.swp
|
||||
*.log
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,132 @@
|
||||
include install.mk
|
||||
|
||||
LOCALDIR := $(dir $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
|
||||
GENTERRAFORMPATH := $(shell go env GOPATH)/bin
|
||||
|
||||
BUILDDIR ?= build
|
||||
TFDIR ?= example
|
||||
|
||||
ADDFLAGS ?=
|
||||
BUILDFLAGS ?= $(ADDFLAGS) -ldflags '-w -s'
|
||||
CGOFLAG ?= CGO_ENABLED=0
|
||||
|
||||
RELEASE = terraform-provider-teleport-v$(VERSION)-$(OS)-$(ARCH)-bin
|
||||
|
||||
.PHONY: tfclean
|
||||
tfclean:
|
||||
rm -rf $(TFDIR)/terraform.tfstate
|
||||
rm -rf $(TFDIR)/terraform.tfstate.backup
|
||||
rm -rf $(TFDIR)/.terraform
|
||||
rm -rf $(TFDIR)/.terraform.lock.hcl
|
||||
|
||||
.PHONY: clean
|
||||
clean: tfclean
|
||||
rm -rf $(PROVIDER_PATH)*
|
||||
rm -rf $(BUILDDIR)/*
|
||||
rm -rf $(RELEASE).tar.gz
|
||||
go clean
|
||||
|
||||
.PHONY: build
|
||||
build: clean
|
||||
GOOS=$(OS) GOARCH=$(ARCH) $(CGOFLAG) go build -o $(BUILDDIR)/terraform-provider-teleport $(BUILDFLAGS)
|
||||
|
||||
build-darwin-universal: $(addprefix $(BUILDDIR)/terraform-provider-teleport_,arm64 amd64)
|
||||
lipo -create -output $(BUILDDIR)/terraform-provider-teleport $^
|
||||
rm -r $^ $(BUILDDIR)/$(OS)
|
||||
|
||||
# Architecture-specific binaries for the universal binary are extracted from
|
||||
# the release tarball. make will not automatically build this; you will need
|
||||
# to run "make ARCH=amd64 release" and "make ARCH=arm64 release" first as is
|
||||
# done in the build workflow.
|
||||
$(BUILDDIR)/terraform-provider-teleport_%: terraform-provider-teleport-v$(VERSION)-$(OS)-%-bin.tar.gz
|
||||
mkdir -p $(BUILDDIR)/$(OS)/$*
|
||||
tar -xzf $< -C $(BUILDDIR)/$(OS)/$*
|
||||
mv $(BUILDDIR)/$(OS)/$*/terraform-provider-teleport $@
|
||||
|
||||
CUSTOM_IMPORTS_TMP_DIR ?= /tmp/protoc-gen-terraform/custom-imports
|
||||
PROTOC_GEN_TERRAFORM_VERSION ?= v2.1.0
|
||||
PROTOC_GEN_TERRAFORM_EXISTS := $(shell protoc-gen-terraform version 2>&1 >/dev/null | grep 'protoc-gen-terraform $(PROTOC_GEN_TERRAFORM_VERSION)')
|
||||
|
||||
.PHONY: gen-tfschema
|
||||
gen-tfschema:
|
||||
ifndef PROTOC_GEN_TERRAFORM_EXISTS
|
||||
@echo "protoc-gen-terraform $(PROTOC_GEN_TERRAFORM_VERSION) is not installed. Please, refer to README.md for installation instructions."
|
||||
@exit -1
|
||||
endif
|
||||
|
||||
# The wrappers.proto file needed for this generator exist only inside the go mod cache,
|
||||
# so we retrieve the file path for the cached proto files with go mod tools.
|
||||
$(eval PROTOBUF_MOD_PATH := $(shell go mod download --json github.com/gogo/protobuf | jq .Dir))
|
||||
|
||||
@protoc \
|
||||
-I=../../api/proto \
|
||||
-I=$(PROTOBUF_MOD_PATH) \
|
||||
--plugin=$(GENTERRAFORMPATH)/protoc-gen-terraform \
|
||||
--terraform_out=config=protoc-gen-terraform-teleport.yaml:./tfschema \
|
||||
teleport/legacy/types/types.proto
|
||||
|
||||
@protoc \
|
||||
-I=../../api/proto \
|
||||
-I=$(PROTOBUF_MOD_PATH) \
|
||||
--plugin=$(GENTERRAFORMPATH)/protoc-gen-terraform \
|
||||
--terraform_out=config=protoc-gen-terraform-loginrule.yaml:./tfschema \
|
||||
teleport/loginrule/v1/loginrule.proto
|
||||
|
||||
@protoc \
|
||||
-I=../../api/proto \
|
||||
-I=$(PROTOBUF_MOD_PATH) \
|
||||
--plugin=$(GENTERRAFORMPATH)/protoc-gen-terraform \
|
||||
--terraform_out=config=protoc-gen-terraform-devicetrust.yaml:./tfschema \
|
||||
teleport/legacy/types/device.proto
|
||||
|
||||
@protoc \
|
||||
-I=../../api/proto \
|
||||
-I=$(PROTOBUF_MOD_PATH) \
|
||||
--plugin=$(GENTERRAFORMPATH)/protoc-gen-terraform \
|
||||
--terraform_out=config=protoc-gen-terraform-accesslist.yaml:./tfschema \
|
||||
teleport/accesslist/v1/accesslist.proto
|
||||
|
||||
mv ./tfschema/github.com/gravitational/teleport/api/types/types_terraform.go ./tfschema/
|
||||
mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1/loginrule_terraform.go ./tfschema/loginrule/v1/
|
||||
mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1/accesslist_terraform.go ./tfschema/accesslist/v1/
|
||||
mv ./tfschema/github.com/gravitational/teleport/api/types/device_terraform.go ./tfschema/devicetrust/v1/
|
||||
rm -r ./tfschema/github.com/
|
||||
@go run ./gen/main.go
|
||||
@go run ./gen/main.go docs
|
||||
|
||||
.PHONY: release
|
||||
ifeq ($(OS)-$(ARCH),darwin-universal)
|
||||
release: build-darwin-universal
|
||||
else
|
||||
release: build
|
||||
endif
|
||||
tar -C $(BUILDDIR) -czf $(RELEASE).tar.gz .
|
||||
|
||||
TERRAFORM_EXISTS := $(shell terraform -version 2>/dev/null | grep 'Terraform v1.')
|
||||
CURRENT_ULIMIT := $(shell ulimit -n)
|
||||
|
||||
.PHONY: test
|
||||
test: install
|
||||
ifndef TERRAFORM_EXISTS
|
||||
@echo "Terraform v1.4+ is not installed (tfenv install 1.5.6 && tfenv use 1.5.6)."
|
||||
terraform -version
|
||||
@exit -1
|
||||
endif
|
||||
# NOTE: This is related to an old bug in Terraform and will be fixed in future releases (possibly, by a workaround on our side)
|
||||
ifeq ($(shell expr $(CURRENT_ULIMIT) \< 1024), 1)
|
||||
@echo "ulimit -n is too low ($(CURRENT_ULIMIT)), please set ulimit -n 1024"
|
||||
@exit -1
|
||||
endif
|
||||
TF_ACC=true go test ./test -v
|
||||
|
||||
.PHONY: apply
|
||||
apply: install
|
||||
terraform -chdir=$(TFDIR) init && terraform -chdir=$(TFDIR) apply -auto-approve
|
||||
|
||||
.PHONY: reapply
|
||||
reapply:
|
||||
terraform -chdir=$(TFDIR) apply
|
||||
|
||||
.PHONY: destroy
|
||||
destroy:
|
||||
terraform -chdir=$(TFDIR) destroy -auto-approve
|
||||
@@ -0,0 +1,95 @@
|
||||
# Terraform Provider Plugin
|
||||
|
||||
## Usage
|
||||
|
||||
Please, refer to [official documentation](https://goteleport.com/docs/setup/guides/terraform-provider/).
|
||||
|
||||
## Development
|
||||
|
||||
1. Install [`protobuf`](https://grpc.io/docs/protoc-installation/).
|
||||
2. Install [`protoc-gen-terraform`](https://github.com/gravitational/protoc-gen-terraform).
|
||||
|
||||
```go install github.com/gravitational/protoc-gen-terraform@main```
|
||||
|
||||
3. Install [`Terraform`](https://learn.hashicorp.com/tutorials/terraform/install-cli) v1.1.0+. Alternatively, you can use [`tfenv`](https://github.com/tfutils/tfenv). Please note that on Mac M1 you need to specify `TFENV_ARCH` (ex: `TFENV_ARCH=arm64 tfenv install 1.1.6`).
|
||||
|
||||
4. Clone the plugin:
|
||||
|
||||
```bash
|
||||
git clone [email protected]:gravitational/teleport-plugins
|
||||
```
|
||||
|
||||
5. Build and install the plugin:
|
||||
|
||||
```bash
|
||||
cd teleport-plugins/terraform
|
||||
make install
|
||||
```
|
||||
|
||||
6. Run tests:
|
||||
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
|
||||
Note: Some tests won't pass without a valid `teleport` binary, enterprise license, etc.
|
||||
See [Testing](../TESTING.md) to see how to provide these values to the tests locally.
|
||||
|
||||
# Updating the provider
|
||||
|
||||
Run:
|
||||
|
||||
```
|
||||
make gen-tfschema
|
||||
```
|
||||
|
||||
This will generate `types_tfschema.go` from a current API `.proto` file, and regenerate the provider code.
|
||||
|
||||
# Playing with examples locally
|
||||
|
||||
1. Start Teleport.
|
||||
|
||||
```
|
||||
teleport start
|
||||
```
|
||||
|
||||
1. Create Terraform user and role:
|
||||
|
||||
```
|
||||
tctl create example/terraform.yaml
|
||||
tctl auth sign --format=file --user=terraform --out=/tmp/terraform-identity --ttl=10h
|
||||
```
|
||||
|
||||
1. Create `main.tf` file:
|
||||
|
||||
```
|
||||
cp example/main.tf.example example/main.tf
|
||||
```
|
||||
|
||||
Please note that target identity file was exported to `/tmp/terraform-identity` on previous step. If you used another location, please change in in `main.tf`.
|
||||
|
||||
1. Create sample resources:
|
||||
|
||||
```
|
||||
cp example/user.tf.example example/user.tf
|
||||
cp example/role.tf.example example/role.tf
|
||||
cp example/provision_token.tf.example example/provision_token.tf
|
||||
```
|
||||
|
||||
Please note that some resources require preliminary setup steps.
|
||||
|
||||
1. Apply changes:
|
||||
|
||||
```
|
||||
make apply
|
||||
```
|
||||
|
||||
1. Make changes to .tf files and run:
|
||||
```
|
||||
make reapply
|
||||
```
|
||||
|
||||
1. Clean up:
|
||||
```
|
||||
make destroy
|
||||
```
|
||||
@@ -0,0 +1,39 @@
|
||||
resource "teleport_access_list" "crane-operation" {
|
||||
header = {
|
||||
metadata = {
|
||||
name = "crane-operation"
|
||||
labels = {
|
||||
example = "yes"
|
||||
}
|
||||
}
|
||||
}
|
||||
spec = {
|
||||
description = "Used to grant access to the crane."
|
||||
owners = [
|
||||
{
|
||||
name = "gru"
|
||||
description = "The supervillain."
|
||||
}
|
||||
]
|
||||
membership_requires = {
|
||||
roles = ["minion"]
|
||||
}
|
||||
ownership_requires = {
|
||||
roles = ["supervillain"]
|
||||
}
|
||||
grants = {
|
||||
roles = ["crane-operator"]
|
||||
traits = [{
|
||||
key = "allowed-machines"
|
||||
values = ["crane", "forklift"]
|
||||
}]
|
||||
}
|
||||
title = "Crane operation"
|
||||
audit = {
|
||||
recurrence = {
|
||||
frequency = 3 # audit every 3 months
|
||||
day_of_month = 15 # audit happen 15's day of the month. Possible values are 1, 15, and 31.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Teleport App
|
||||
|
||||
resource "teleport_app" "example" {
|
||||
metadata = {
|
||||
name = "example"
|
||||
description = "Test app"
|
||||
labels = {
|
||||
"teleport.dev/origin" = "dynamic" // This label is added on Teleport side by default
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
uri = "localhost:3000"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# AuthPreference resource
|
||||
|
||||
resource "teleport_auth_preference" "example" {
|
||||
metadata = {
|
||||
description = "Auth preference"
|
||||
labels = {
|
||||
"example" = "yes"
|
||||
"teleport.dev/origin" = "dynamic" // This label is added on Teleport side by default
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
disconnect_expired_cert = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# Teleport Machine ID Bot creation example
|
||||
|
||||
locals {
|
||||
bot_name = "example"
|
||||
}
|
||||
|
||||
resource "random_password" "bot_token" {
|
||||
length = 32
|
||||
special = false
|
||||
}
|
||||
|
||||
resource "time_offset" "bot_example_token_expiry" {
|
||||
offset_hours = 1
|
||||
}
|
||||
|
||||
resource "teleport_provision_token" "bot_example" {
|
||||
metadata = {
|
||||
expires = time_offset.bot_example_token_expiry.rfc3339
|
||||
description = "Bot join token for ${local.bot_name} generated by Terraform"
|
||||
|
||||
name = random_password.bot_token.result
|
||||
}
|
||||
|
||||
spec = {
|
||||
roles = ["Bot"]
|
||||
bot_name = local.bot_name
|
||||
join_method = "token"
|
||||
}
|
||||
}
|
||||
|
||||
resource "teleport_bot" "example" {
|
||||
name = local.bot_name
|
||||
token_id = teleport_provision_token.bot_example.metadata.name
|
||||
roles = ["access"]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Teleport Cluster Networking config
|
||||
|
||||
resource "teleport_cluster_maintenance_config" "example" {
|
||||
metadata = {
|
||||
description = "Maintenance config"
|
||||
}
|
||||
|
||||
spec = {
|
||||
agent_upgrades = {
|
||||
utc_start_hour = 1
|
||||
weekdays = [ "monday" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Teleport Cluster Networking config
|
||||
|
||||
resource "teleport_cluster_networking_config" "example" {
|
||||
metadata = {
|
||||
description = "Networking config"
|
||||
labels = {
|
||||
"example" = "yes"
|
||||
"teleport.dev/origin" = "dynamic" // This label is added on Teleport side by default
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
client_idle_timeout = "1h"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# Teleport Database
|
||||
|
||||
resource "teleport_database" "example" {
|
||||
metadata = {
|
||||
name = "example"
|
||||
description = "Test database"
|
||||
labels = {
|
||||
"teleport.dev/origin" = "dynamic" // This label is added on Teleport side by default
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
protocol = "postgres"
|
||||
uri = "localhost"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# Terraform Github connector
|
||||
|
||||
variable "github_secret" {}
|
||||
|
||||
resource "teleport_github_connector" "github" {
|
||||
# This section tells Terraform that role example must be created before the GitHub connector
|
||||
depends_on = [
|
||||
teleport_role.example
|
||||
]
|
||||
|
||||
metadata = {
|
||||
name = "example"
|
||||
labels = {
|
||||
example = "yes"
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
client_id = "client"
|
||||
client_secret = var.github_secret
|
||||
|
||||
teams_to_roles = [{
|
||||
organization = "gravitational"
|
||||
team = "devs"
|
||||
roles = ["example"]
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# Teleport Login Rule resource
|
||||
|
||||
resource "teleport_login_rule" "example" {
|
||||
metadata = {
|
||||
description = "Example Login Rule"
|
||||
labels = {
|
||||
"example" = "yes"
|
||||
}
|
||||
}
|
||||
|
||||
version = "v1"
|
||||
priority = 0
|
||||
traits_map = {
|
||||
"logins" = {
|
||||
values = [
|
||||
"external.logins",
|
||||
"external.username",
|
||||
]
|
||||
}
|
||||
"groups" = {
|
||||
values = [
|
||||
"external.groups",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
teleport = {
|
||||
version = "9.1.3"
|
||||
source = "terraform.releases.teleport.dev/gravitational/teleport"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Terraform Provider configuration. See provider.go for available options
|
||||
provider "teleport" {
|
||||
# Path to Teleport identity file
|
||||
identity_file_path = "/tmp/terraform-identity"
|
||||
|
||||
# Teleport proxy addr
|
||||
addr = "teleport.example.com:443"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
# Teleport OIDC connector
|
||||
#
|
||||
# Please note that OIDC connector will work in Enterprise version only. Check the setup docs:
|
||||
# https://goteleport.com/docs/enterprise/sso/oidc/
|
||||
|
||||
variable "oidc_secret" {}
|
||||
|
||||
resource "teleport_oidc_connector" "example" {
|
||||
metadata = {
|
||||
name = "example"
|
||||
labels = {
|
||||
test = "yes"
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
client_id = "client"
|
||||
client_secret = var.oidc_secret
|
||||
|
||||
claims_to_roles = [{
|
||||
claim = "test"
|
||||
roles = ["terraform"]
|
||||
}]
|
||||
|
||||
redirect_url = ["https://example.com/redirect"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
# Teleport Okta Import Rule resource
|
||||
|
||||
resource "teleport_okta_import_rule" "example" {
|
||||
metadata = {
|
||||
description = "Example Okta Import Rule"
|
||||
labels = {
|
||||
"example" = "yes"
|
||||
}
|
||||
}
|
||||
|
||||
version = "v1"
|
||||
|
||||
spec = {
|
||||
priority = 100
|
||||
mappings = [
|
||||
{
|
||||
add_labels = {
|
||||
"label1": "value1"
|
||||
}
|
||||
match = [
|
||||
{
|
||||
app_ids = ["1", "2", "3"]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
add_labels = {
|
||||
"label2": "value2"
|
||||
}
|
||||
match = [
|
||||
{
|
||||
group_ids = ["1", "2", "3"]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
add_labels = {
|
||||
"label3" : "value3",
|
||||
}
|
||||
match = [
|
||||
{
|
||||
group_name_regexes = ["^.*$"]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
add_labels = {
|
||||
"label4" : "value4",
|
||||
}
|
||||
match = [
|
||||
{
|
||||
app_name_regexes = ["^.*$"]
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Teleport Provision Token resource
|
||||
|
||||
resource "teleport_provision_token" "example" {
|
||||
metadata = {
|
||||
expires = "2022-10-12T07:20:51Z"
|
||||
description = "Example token"
|
||||
|
||||
labels = {
|
||||
example = "yes"
|
||||
"teleport.dev/origin" = "dynamic" // This label is added on Teleport side by default
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
roles = ["Node", "Auth"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "teleport_provision_token" "iam-token" {
|
||||
metadata = {
|
||||
name = "iam-token"
|
||||
}
|
||||
spec = {
|
||||
roles = ["Bot"]
|
||||
bot_name = "mybot"
|
||||
join_method = "iam"
|
||||
allow = [{
|
||||
aws_account = "123456789012"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Teleport Role resource
|
||||
|
||||
resource "teleport_role" "example" {
|
||||
metadata = {
|
||||
name = "example"
|
||||
description = "Example Teleport Role"
|
||||
expires = "2022-10-12T07:20:51Z"
|
||||
labels = {
|
||||
example = "yes"
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
options = {
|
||||
forward_agent = false
|
||||
max_session_ttl = "7m"
|
||||
port_forwarding = false
|
||||
client_idle_timeout = "1h"
|
||||
disconnect_expired_cert = true
|
||||
permit_x11_forwarding = false
|
||||
request_access = "denied"
|
||||
}
|
||||
|
||||
allow = {
|
||||
logins = ["example"]
|
||||
|
||||
rules = [{
|
||||
resources = ["user", "role"]
|
||||
verbs = ["list"]
|
||||
}]
|
||||
|
||||
request = {
|
||||
roles = ["example"]
|
||||
claims_to_roles = [{
|
||||
claim = "example"
|
||||
value = "example"
|
||||
roles = ["example"]
|
||||
}]
|
||||
}
|
||||
|
||||
node_labels = {
|
||||
example = ["yes"]
|
||||
}
|
||||
}
|
||||
|
||||
deny = {
|
||||
logins = ["anonymous"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# Teleport SAML connector
|
||||
#
|
||||
# Please note that SAML connector will work in Enterprise version only. Check the setup docs:
|
||||
# https://goteleport.com/docs/enterprise/sso/okta/
|
||||
|
||||
resource "teleport_saml_connector" "example" {
|
||||
# This block will tell Terraform to never update private key from our side if a keys are managed
|
||||
# from an outside of Terraform.
|
||||
|
||||
# lifecycle {
|
||||
# ignore_changes = [
|
||||
# spec[0].signing_key_pair[0].cert,
|
||||
# spec[0].signing_key_pair[0].private_key,
|
||||
# spec[0].assertion_key_pair[0].cert,
|
||||
# spec[0].assertion_key_pair[0].private_key,
|
||||
# ]
|
||||
# }
|
||||
|
||||
# This section tells Terraform that role example must be created before the SAML connector
|
||||
depends_on = [
|
||||
teleport_role.example
|
||||
]
|
||||
|
||||
metadata = {
|
||||
name = "example"
|
||||
}
|
||||
|
||||
spec = {
|
||||
attributes_to_roles = [{
|
||||
name = "groups"
|
||||
roles = ["example"]
|
||||
value = "okta-admin"
|
||||
},
|
||||
{
|
||||
name = "groups"
|
||||
roles = ["example"]
|
||||
value = "okta-dev"
|
||||
}]
|
||||
|
||||
acs = "https://localhost:3025/v1/webapi/saml/acs"
|
||||
entity_descriptor = ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
resource "teleport_server" "ssh_agentless" {
|
||||
version = "v2"
|
||||
sub_kind = "openssh"
|
||||
// Name is not required for servers, this is a special case.
|
||||
// When a name is not set, an UUID will be generated by Teleport and
|
||||
// imported back into Terraform.
|
||||
// Giving unique IDs to servers allows UUID-based dialing (as opposed to
|
||||
// host-based dialing and IP-based dialing) which is more robust than its
|
||||
// counterparts as it can point to a specific server if multiple servers
|
||||
// share the same hostname/ip.
|
||||
spec = {
|
||||
addr = "127.0.0.1:22"
|
||||
hostname = "test.local"
|
||||
}
|
||||
}
|
||||
|
||||
resource "teleport_server" "ssh_agentless_eice" {
|
||||
version = "v2"
|
||||
sub_kind = "openssh-ec2-ice"
|
||||
metadata = {
|
||||
// It is recommended to put the account and instance ID as a name for EC2 Instance Connect
|
||||
// When dialing to this instance, teleport will detect that this is an
|
||||
// AWS instance ID an will contact this specific instance. This is more
|
||||
// robust than host-based and IP-based dialing (because several server
|
||||
// can have similar hostnames).
|
||||
name = "123456789012-i-0123456789abcdef"
|
||||
}
|
||||
spec = {
|
||||
addr = "127.0.0.1:22"
|
||||
hostname = "test.local"
|
||||
|
||||
cloud_metadata = {
|
||||
aws = {
|
||||
account_id = "123"
|
||||
instance_id = "123"
|
||||
region = "us-east-1"
|
||||
vpc_id = "123"
|
||||
integration = "foo"
|
||||
subnet_id = "123"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Teleport session recording config
|
||||
|
||||
resource "teleport_session_recording_config" "example" {
|
||||
metadata = {
|
||||
description = "Session recording config"
|
||||
labels = {
|
||||
"example" = "yes"
|
||||
"teleport.dev/origin" = "dynamic" // This label is added on Teleport side by default
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
proxy_checks_host_keys = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
kind: role
|
||||
metadata:
|
||||
name: terraform
|
||||
spec:
|
||||
allow:
|
||||
app_labels: # This gives Terraform access to all apps in the cluster. You might want to restrict it.
|
||||
'*': '*'
|
||||
db_labels: # This gives Terraform access to all databases in the cluster. You might want to restrict it.
|
||||
'*': '*'
|
||||
db_users: ['*']
|
||||
db_names: ['*']
|
||||
|
||||
rules:
|
||||
- resources:
|
||||
- user
|
||||
- role
|
||||
- token
|
||||
- trusted_cluster
|
||||
- github
|
||||
- oidc
|
||||
- saml
|
||||
- cluster_auth_preference
|
||||
- cluster_networking_config
|
||||
- cluster_maintenance_config
|
||||
- session_recording_config
|
||||
- app
|
||||
- db
|
||||
- login_rule
|
||||
- device
|
||||
- okta_import_rule
|
||||
- access_list
|
||||
verbs: ['list','create','read','update','delete']
|
||||
version: v6
|
||||
---
|
||||
kind: user
|
||||
metadata:
|
||||
name: terraform
|
||||
spec:
|
||||
roles: ['terraform']
|
||||
version: v2
|
||||
@@ -0,0 +1,22 @@
|
||||
# Teleport trusted cluster
|
||||
#
|
||||
# https://goteleport.com/docs/setup/admin/trustedclusters/
|
||||
|
||||
resource "teleport_trusted_cluster" "cluster" {
|
||||
metadata = {
|
||||
name = "primary"
|
||||
labels = {
|
||||
test = "yes"
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
enabled = false
|
||||
role_map = [{
|
||||
remote = "test"
|
||||
local = ["admin"]
|
||||
}]
|
||||
proxy_addr = "localhost:3080"
|
||||
token = "salami"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Trusted device resource
|
||||
|
||||
resource "teleport_trusted_device" "TESTDEVICE1" {
|
||||
spec = {
|
||||
asset_tag = "TESTDEVICE1"
|
||||
os_type = "macos"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# Teleport User resource
|
||||
|
||||
resource "teleport_user" "example" {
|
||||
# Tells Terraform that the role could not be destroyed while this user exists
|
||||
depends_on = [
|
||||
teleport_role.example
|
||||
]
|
||||
|
||||
metadata = {
|
||||
name = "example"
|
||||
description = "Example Teleport User"
|
||||
|
||||
expires = "2022-10-12T07:20:50Z"
|
||||
|
||||
labels = {
|
||||
example = "yes"
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
roles = ["example"]
|
||||
|
||||
oidc_identities = [{
|
||||
connector_id = "oidc1"
|
||||
username = "example"
|
||||
}]
|
||||
|
||||
traits = {
|
||||
"logins1" = ["example"]
|
||||
"logins2" = ["example"]
|
||||
}
|
||||
|
||||
github_identities = [{
|
||||
connector_id = "github"
|
||||
username = "example"
|
||||
}]
|
||||
|
||||
saml_identities = [{
|
||||
connector_id = "example-saml"
|
||||
username = "example"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,732 @@
|
||||
// Copyright 2023 Gravitational, Inc
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/provider"
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
accesslistSchema "github.com/gravitational/teleport/integrations/terraform/tfschema/accesslist/v1"
|
||||
devicetrustSchema "github.com/gravitational/teleport/integrations/terraform/tfschema/devicetrust/v1"
|
||||
loginruleSchema "github.com/gravitational/teleport/integrations/terraform/tfschema/loginrule/v1"
|
||||
)
|
||||
|
||||
// payload represents template payload
|
||||
type payload struct {
|
||||
// Name represents resource name (capitalized)
|
||||
Name string
|
||||
// VarName represents resource variable name (underscored)
|
||||
VarName string
|
||||
// TypeName represents api/types resource type name
|
||||
TypeName string
|
||||
// IfaceName represents api/types interface for the (usually this is the same as Name)
|
||||
IfaceName string
|
||||
// GetMethod represents API get method name
|
||||
GetMethod string
|
||||
// CreateMethod represents API create method name
|
||||
CreateMethod string
|
||||
// CreateMethod represents API update method name
|
||||
UpdateMethod string
|
||||
// DeleteMethod represents API reset method used in singular resources
|
||||
DeleteMethod string
|
||||
// UpsertMethodArity represents Create/Update method arity, if it's 2, then the call signature would be "_, err :="
|
||||
UpsertMethodArity int
|
||||
// WithSecrets value for a withSecrets param of Get method (empty means no param used)
|
||||
WithSecrets string
|
||||
// ID id value on create and import
|
||||
ID string
|
||||
// RandomMetadataName indicates that Metadata.Name must be generated (supported by plural resources only)
|
||||
RandomMetadataName bool
|
||||
// UUIDMetadataName functions similar to RandomMetadataName but generates UUID instead of
|
||||
// generating 32 bit crypto random value
|
||||
UUIDMetadataName bool
|
||||
// Kind Teleport kind for a resource
|
||||
Kind string
|
||||
// DefaultVersion represents the default resource version on create
|
||||
DefaultVersion string
|
||||
// HasStaticID states whether this particular resource has a static (usually 0) Metadata.ID
|
||||
// This is relevant to cache enabled clusters: we use Metadata.ID to check if the resource was updated
|
||||
// Currently, the resources that don't have a dynamic Metadata.ID are strong consistent: oidc, github and saml connectors
|
||||
HasStaticID bool
|
||||
// ProtoPackagePath is the path of the package where the protobuf type of
|
||||
// the resource is defined.
|
||||
ProtoPackagePath string
|
||||
// ProtoPackagePath is the name of the package where the protobuf type of
|
||||
// the resource is defined.
|
||||
ProtoPackage string
|
||||
// SchemaPackagePath is the path of the package where the resource schema
|
||||
// definitions are defined.
|
||||
SchemaPackagePath string
|
||||
// SchemaPackagePath is the name of the package where the resource schema
|
||||
// definitions are defined.
|
||||
SchemaPackage string
|
||||
// IsPlainStruct states whether the resource type used by the API methods
|
||||
// for this resource is a plain struct, rather than an interface.
|
||||
IsPlainStruct bool
|
||||
// HasCheckAndSetDefaults indicates whether the resource type has the CheckAndSetDefaults method
|
||||
HasCheckAndSetDefaults bool
|
||||
// ExtraImports contains a list of imports that are being used.
|
||||
ExtraImports []string
|
||||
// TerraformResourceType represents the resource type in Terraform code.
|
||||
// e.g. `terraform import <resource_type>.<resource_name> identifier`.
|
||||
// This is also used to name the generated files.
|
||||
TerraformResourceType string
|
||||
// WithNonce is used to force upsert behavior for nonce protected values.
|
||||
WithNonce bool
|
||||
// ConvertPackagePath is the path of the package doing the conversion between protobuf and the go types.
|
||||
ConvertPackagePath string
|
||||
// PropagatedFields is a list of fields that must be copied from the
|
||||
// existing resource when we're updating it. For example:
|
||||
// "Spec.Audit.NextAuditDate" in AccessList resource
|
||||
PropagatedFields []string
|
||||
// Namespaced indicates that the resource get and delete methods need the
|
||||
// deprecated namespace parameter (always the default namespace).
|
||||
Namespaced bool
|
||||
// ForceSetKind indicates that the resource kind must be forcefully set by the provider.
|
||||
// This is required for some special resources (ServerV2) that support multiple kinds.
|
||||
// For those resources, we must set the kind, and don't want to have the user do it.
|
||||
ForceSetKind string
|
||||
// GetCanReturnNil is used to check for nil returned value when doing a Get<Resource>.
|
||||
GetCanReturnNil bool
|
||||
}
|
||||
|
||||
func (p *payload) CheckAndSetDefaults() error {
|
||||
if p.ProtoPackage == "" {
|
||||
p.ProtoPackage = "apitypes"
|
||||
}
|
||||
if p.ProtoPackagePath == "" {
|
||||
p.ProtoPackagePath = "github.com/gravitational/teleport/api/types"
|
||||
}
|
||||
if p.SchemaPackage == "" {
|
||||
p.SchemaPackage = "tfschema"
|
||||
}
|
||||
if p.SchemaPackagePath == "" {
|
||||
p.SchemaPackagePath = "github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
pluralResource = "plural_resource.go.tpl"
|
||||
pluralDataSource = "plural_data_source.go.tpl"
|
||||
singularResource = "singular_resource.go.tpl"
|
||||
singularDataSource = "singular_data_source.go.tpl"
|
||||
outFileResourceFormat = "provider/resource_%s.go"
|
||||
outFileDataSourceFormat = "provider/data_source_%s.go"
|
||||
|
||||
referenceDocsTemplate = "referencedocs.go.tpl"
|
||||
)
|
||||
|
||||
var (
|
||||
app = payload{
|
||||
Name: "App",
|
||||
TypeName: "AppV3",
|
||||
VarName: "app",
|
||||
IfaceName: "Application",
|
||||
GetMethod: "GetApp",
|
||||
CreateMethod: "CreateApp",
|
||||
UpdateMethod: "UpdateApp",
|
||||
DeleteMethod: "DeleteApp",
|
||||
ID: `app.Metadata.Name`,
|
||||
Kind: "app",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_app",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
authPreference = payload{
|
||||
Name: "AuthPreference",
|
||||
TypeName: "AuthPreferenceV2",
|
||||
VarName: "authPreference",
|
||||
GetMethod: "GetAuthPreference",
|
||||
CreateMethod: "SetAuthPreference",
|
||||
UpdateMethod: "SetAuthPreference",
|
||||
DeleteMethod: "ResetAuthPreference",
|
||||
ID: `"auth_preference"`,
|
||||
Kind: "cluster_auth_preference",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_auth_preference",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
clusterMaintenance = payload{
|
||||
Name: "ClusterMaintenanceConfig",
|
||||
TypeName: "ClusterMaintenanceConfigV1",
|
||||
VarName: "clusterMaintenanceConfig",
|
||||
GetMethod: "GetClusterMaintenanceConfig",
|
||||
CreateMethod: "UpdateClusterMaintenanceConfig",
|
||||
UpdateMethod: "UpdateClusterMaintenanceConfig",
|
||||
DeleteMethod: "DeleteClusterMaintenanceConfig",
|
||||
ID: `"cluster_maintenance_config"`,
|
||||
Kind: "cluster_maintenance_config",
|
||||
HasStaticID: true,
|
||||
TerraformResourceType: "teleport_cluster_maintenance_config",
|
||||
WithNonce: true,
|
||||
GetCanReturnNil: true,
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
clusterNetworking = payload{
|
||||
Name: "ClusterNetworkingConfig",
|
||||
TypeName: "ClusterNetworkingConfigV2",
|
||||
VarName: "clusterNetworkingConfig",
|
||||
GetMethod: "GetClusterNetworkingConfig",
|
||||
CreateMethod: "SetClusterNetworkingConfig",
|
||||
UpdateMethod: "SetClusterNetworkingConfig",
|
||||
DeleteMethod: "ResetClusterNetworkingConfig",
|
||||
ID: `"cluster_networking_config"`,
|
||||
Kind: "cluster_networking_config",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_cluster_networking_config",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
database = payload{
|
||||
Name: "Database",
|
||||
TypeName: "DatabaseV3",
|
||||
VarName: "database",
|
||||
GetMethod: "GetDatabase",
|
||||
CreateMethod: "CreateDatabase",
|
||||
UpdateMethod: "UpdateDatabase",
|
||||
DeleteMethod: "DeleteDatabase",
|
||||
ID: `database.Metadata.Name`,
|
||||
Kind: "db",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_database",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
githubConnector = payload{
|
||||
Name: "GithubConnector",
|
||||
TypeName: "GithubConnectorV3",
|
||||
VarName: "githubConnector",
|
||||
GetMethod: "GetGithubConnector",
|
||||
CreateMethod: "CreateGithubConnector",
|
||||
UpdateMethod: "UpsertGithubConnector",
|
||||
UpsertMethodArity: 2,
|
||||
DeleteMethod: "DeleteGithubConnector",
|
||||
WithSecrets: "true",
|
||||
ID: "githubConnector.Metadata.Name",
|
||||
Kind: "github",
|
||||
HasStaticID: true,
|
||||
TerraformResourceType: "teleport_github_connector",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
oidcConnector = payload{
|
||||
Name: "OIDCConnector",
|
||||
TypeName: "OIDCConnectorV3",
|
||||
VarName: "oidcConnector",
|
||||
GetMethod: "GetOIDCConnector",
|
||||
CreateMethod: "CreateOIDCConnector",
|
||||
UpdateMethod: "UpsertOIDCConnector",
|
||||
UpsertMethodArity: 2,
|
||||
DeleteMethod: "DeleteOIDCConnector",
|
||||
WithSecrets: "true",
|
||||
ID: "oidcConnector.Metadata.Name",
|
||||
Kind: "oidc",
|
||||
HasStaticID: true,
|
||||
TerraformResourceType: "teleport_oidc_connector",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
samlConnector = payload{
|
||||
Name: "SAMLConnector",
|
||||
TypeName: "SAMLConnectorV2",
|
||||
VarName: "samlConnector",
|
||||
GetMethod: "GetSAMLConnector",
|
||||
CreateMethod: "CreateSAMLConnector",
|
||||
UpdateMethod: "UpsertSAMLConnector",
|
||||
UpsertMethodArity: 2,
|
||||
DeleteMethod: "DeleteSAMLConnector",
|
||||
WithSecrets: "true",
|
||||
ID: "samlConnector.Metadata.Name",
|
||||
Kind: "saml",
|
||||
HasStaticID: true,
|
||||
TerraformResourceType: "teleport_saml_connector",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
provisionToken = payload{
|
||||
Name: "ProvisionToken",
|
||||
TypeName: "ProvisionTokenV2",
|
||||
VarName: "provisionToken",
|
||||
GetMethod: "GetToken",
|
||||
CreateMethod: "UpsertToken",
|
||||
UpdateMethod: "UpsertToken",
|
||||
DeleteMethod: "DeleteToken",
|
||||
ID: "strconv.FormatInt(provisionToken.Metadata.ID, 10)", // must be a string
|
||||
RandomMetadataName: true,
|
||||
Kind: "token",
|
||||
HasStaticID: false,
|
||||
ExtraImports: []string{"strconv"},
|
||||
TerraformResourceType: "teleport_provision_token",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
role = payload{
|
||||
Name: "Role",
|
||||
TypeName: "RoleV6",
|
||||
VarName: "role",
|
||||
GetMethod: "GetRole",
|
||||
CreateMethod: "CreateRole",
|
||||
UpdateMethod: "UpsertRole",
|
||||
UpsertMethodArity: 2,
|
||||
DeleteMethod: "DeleteRole",
|
||||
ID: "role.Metadata.Name",
|
||||
Kind: "role",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_role",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
sessionRecording = payload{
|
||||
Name: "SessionRecordingConfig",
|
||||
TypeName: "SessionRecordingConfigV2",
|
||||
VarName: "sessionRecordingConfig",
|
||||
GetMethod: "GetSessionRecordingConfig",
|
||||
CreateMethod: "SetSessionRecordingConfig",
|
||||
UpdateMethod: "SetSessionRecordingConfig",
|
||||
DeleteMethod: "ResetSessionRecordingConfig",
|
||||
ID: `"session_recording_config"`,
|
||||
Kind: "session_recording_config",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_session_recording_config",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
trustedCluster = payload{
|
||||
Name: "TrustedCluster",
|
||||
TypeName: "TrustedClusterV2",
|
||||
VarName: "trustedCluster",
|
||||
GetMethod: "GetTrustedCluster",
|
||||
CreateMethod: "UpsertTrustedCluster",
|
||||
UpdateMethod: "UpsertTrustedCluster",
|
||||
DeleteMethod: "DeleteTrustedCluster",
|
||||
UpsertMethodArity: 2,
|
||||
ID: "trustedCluster.Metadata.Name",
|
||||
Kind: "trusted_cluster",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_trusted_cluster",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
user = payload{
|
||||
Name: "User",
|
||||
TypeName: "UserV2",
|
||||
VarName: "user",
|
||||
GetMethod: "GetUser",
|
||||
CreateMethod: "CreateUser",
|
||||
UpdateMethod: "UpsertUser",
|
||||
UpsertMethodArity: 2,
|
||||
DeleteMethod: "DeleteUser",
|
||||
WithSecrets: "false",
|
||||
ID: "user.Metadata.Name",
|
||||
Kind: "user",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_user",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
loginRule = payload{
|
||||
Name: "LoginRule",
|
||||
TypeName: "LoginRule",
|
||||
VarName: "loginRule",
|
||||
GetMethod: "GetLoginRule",
|
||||
CreateMethod: "UpsertLoginRule",
|
||||
UpsertMethodArity: 2,
|
||||
UpdateMethod: "UpsertLoginRule",
|
||||
DeleteMethod: "DeleteLoginRule",
|
||||
ID: "loginRule.Metadata.Name",
|
||||
Kind: "login_rule",
|
||||
HasStaticID: true,
|
||||
ProtoPackage: "loginrulev1",
|
||||
ProtoPackagePath: "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1",
|
||||
SchemaPackage: "schemav1",
|
||||
SchemaPackagePath: "github.com/gravitational/teleport/integrations/terraform/tfschema/loginrule/v1",
|
||||
IsPlainStruct: true,
|
||||
TerraformResourceType: "teleport_login_rule",
|
||||
}
|
||||
|
||||
deviceTrust = payload{
|
||||
Name: "DeviceV1",
|
||||
VarName: "trustedDevice",
|
||||
TypeName: "DeviceV1",
|
||||
GetMethod: "GetDeviceResource",
|
||||
CreateMethod: "UpsertDeviceResource",
|
||||
UpsertMethodArity: 2,
|
||||
UpdateMethod: "UpsertDeviceResource",
|
||||
DeleteMethod: "DeleteDeviceResource",
|
||||
Kind: "device",
|
||||
ID: "trustedDevice.Metadata.Name",
|
||||
HasStaticID: true,
|
||||
SchemaPackage: "schemav1",
|
||||
SchemaPackagePath: "github.com/gravitational/teleport/integrations/terraform/tfschema/devicetrust/v1",
|
||||
IsPlainStruct: true,
|
||||
UUIDMetadataName: true,
|
||||
TerraformResourceType: "teleport_device_trust",
|
||||
}
|
||||
|
||||
oktaImportRule = payload{
|
||||
Name: "OktaImportRule",
|
||||
TypeName: "OktaImportRuleV1",
|
||||
VarName: "oktaImportRule",
|
||||
IfaceName: "OktaImportRule",
|
||||
GetMethod: "OktaClient().GetOktaImportRule",
|
||||
CreateMethod: "OktaClient().CreateOktaImportRule",
|
||||
UpdateMethod: "OktaClient().UpdateOktaImportRule",
|
||||
DeleteMethod: "OktaClient().DeleteOktaImportRule",
|
||||
UpsertMethodArity: 2,
|
||||
ID: "oktaImportRule.Metadata.Name",
|
||||
Kind: "okta_import_rule",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_okta_import_rule",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
accessList = payload{
|
||||
Name: "AccessList",
|
||||
TypeName: "AccessList",
|
||||
VarName: "accessList",
|
||||
GetMethod: "AccessListClient().GetAccessList",
|
||||
CreateMethod: "AccessListClient().UpsertAccessList",
|
||||
UpsertMethodArity: 2,
|
||||
UpdateMethod: "AccessListClient().UpsertAccessList",
|
||||
DeleteMethod: "AccessListClient().DeleteAccessList",
|
||||
ID: "accessList.Header.Metadata.Name",
|
||||
Kind: "access_list",
|
||||
HasStaticID: false,
|
||||
SchemaPackage: "schemav1",
|
||||
SchemaPackagePath: "github.com/gravitational/teleport/integrations/terraform/tfschema/accesslist/v1",
|
||||
ProtoPackage: "accesslist",
|
||||
ProtoPackagePath: "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1",
|
||||
TerraformResourceType: "teleport_access_list",
|
||||
ConvertPackagePath: "github.com/gravitational/teleport/api/types/accesslist/convert/v1",
|
||||
HasCheckAndSetDefaults: true,
|
||||
PropagatedFields: []string{"Spec.Audit.NextAuditDate"},
|
||||
}
|
||||
|
||||
server = payload{
|
||||
Name: "Server",
|
||||
TypeName: "ServerV2",
|
||||
VarName: "server",
|
||||
GetMethod: "GetNode",
|
||||
CreateMethod: "UpsertNode",
|
||||
UpdateMethod: "UpsertNode",
|
||||
UpsertMethodArity: 2,
|
||||
DeleteMethod: "DeleteNode",
|
||||
ID: "server.Metadata.Name",
|
||||
Kind: "node",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_server",
|
||||
HasCheckAndSetDefaults: true,
|
||||
Namespaced: true,
|
||||
ForceSetKind: "apitypes.KindNode",
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 2 && os.Args[1] == "docs" {
|
||||
genReferenceDocs()
|
||||
} else {
|
||||
genTFSchema()
|
||||
}
|
||||
}
|
||||
|
||||
func genTFSchema() {
|
||||
generateResource(app, pluralResource)
|
||||
generateDataSource(app, pluralDataSource)
|
||||
generateResource(authPreference, singularResource)
|
||||
generateDataSource(authPreference, singularDataSource)
|
||||
generateResource(clusterMaintenance, singularResource)
|
||||
generateDataSource(clusterMaintenance, singularDataSource)
|
||||
generateResource(clusterNetworking, singularResource)
|
||||
generateDataSource(clusterNetworking, singularDataSource)
|
||||
generateResource(database, pluralResource)
|
||||
generateDataSource(database, pluralDataSource)
|
||||
generateResource(githubConnector, pluralResource)
|
||||
generateDataSource(githubConnector, pluralDataSource)
|
||||
generateResource(oidcConnector, pluralResource)
|
||||
generateDataSource(oidcConnector, pluralDataSource)
|
||||
generateResource(samlConnector, pluralResource)
|
||||
generateDataSource(samlConnector, pluralDataSource)
|
||||
generateResource(provisionToken, pluralResource)
|
||||
generateDataSource(provisionToken, pluralDataSource)
|
||||
generateResource(role, pluralResource)
|
||||
generateDataSource(role, pluralDataSource)
|
||||
generateResource(trustedCluster, pluralResource)
|
||||
generateDataSource(trustedCluster, pluralDataSource)
|
||||
generateResource(sessionRecording, singularResource)
|
||||
generateDataSource(sessionRecording, singularDataSource)
|
||||
generateResource(user, pluralResource)
|
||||
generateDataSource(user, pluralDataSource)
|
||||
generateResource(loginRule, pluralResource)
|
||||
generateDataSource(loginRule, pluralDataSource)
|
||||
generateResource(deviceTrust, pluralResource)
|
||||
generateDataSource(deviceTrust, pluralDataSource)
|
||||
generateResource(oktaImportRule, pluralResource)
|
||||
generateDataSource(oktaImportRule, pluralDataSource)
|
||||
generateResource(accessList, pluralResource)
|
||||
generateDataSource(accessList, pluralDataSource)
|
||||
generateResource(server, pluralResource)
|
||||
generateDataSource(server, pluralDataSource)
|
||||
}
|
||||
|
||||
func generateResource(p payload, tpl string) {
|
||||
outFile := fmt.Sprintf(outFileResourceFormat, p.TerraformResourceType)
|
||||
generate(p, tpl, outFile)
|
||||
}
|
||||
func generateDataSource(p payload, tpl string) {
|
||||
outFile := fmt.Sprintf(outFileDataSourceFormat, p.TerraformResourceType)
|
||||
generate(p, tpl, outFile)
|
||||
}
|
||||
|
||||
func generate(p payload, tpl, outFile string) {
|
||||
if err := p.CheckAndSetDefaults(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
funcs := template.FuncMap{
|
||||
"schemaImport": func(p payload) string {
|
||||
if p.SchemaPackage == "tfschema" {
|
||||
return `"` + p.SchemaPackagePath + `"`
|
||||
}
|
||||
|
||||
return p.SchemaPackage + ` "` + p.SchemaPackagePath + `"`
|
||||
},
|
||||
"protoImport": func(p payload) string {
|
||||
if p.ConvertPackagePath != "" {
|
||||
return "convert" + ` "` + p.ConvertPackagePath + `"`
|
||||
}
|
||||
|
||||
return p.ProtoPackage + ` "` + p.ProtoPackagePath + `"`
|
||||
},
|
||||
}
|
||||
|
||||
t, err := template.New(p.Name).Funcs(funcs).ParseFiles(path.Join("gen", tpl))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
err = t.ExecuteTemplate(&b, tpl, p)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(outFile, b.Bytes(), 0777)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create Docs Markdown
|
||||
var (
|
||||
mapResourceSchema = map[string]func(context.Context) (tfsdk.Schema, diag.Diagnostics){
|
||||
"access_list": accesslistSchema.GenSchemaAccessList,
|
||||
"app": tfschema.GenSchemaAppV3,
|
||||
"auth_preference": tfschema.GenSchemaAuthPreferenceV2,
|
||||
"bot": provider.GenSchemaBot,
|
||||
"cluster_maintenance_config": tfschema.GenSchemaClusterMaintenanceConfigV1,
|
||||
"cluster_networking_config": tfschema.GenSchemaClusterNetworkingConfigV2,
|
||||
"database": tfschema.GenSchemaDatabaseV3,
|
||||
"trusted_device": devicetrustSchema.GenSchemaDeviceV1,
|
||||
"github_connector": tfschema.GenSchemaGithubConnectorV3,
|
||||
"login_rule": loginruleSchema.GenSchemaLoginRule,
|
||||
"okta_import_rule": tfschema.GenSchemaOktaImportRuleV1,
|
||||
"oidc_connector": tfschema.GenSchemaOIDCConnectorV3,
|
||||
"provision_token": tfschema.GenSchemaProvisionTokenV2,
|
||||
"role": tfschema.GenSchemaRoleV6,
|
||||
"saml_connector": tfschema.GenSchemaSAMLConnectorV2,
|
||||
"session_recording_config": tfschema.GenSchemaSessionRecordingConfigV2,
|
||||
"trusted_cluster": tfschema.GenSchemaTrustedClusterV2,
|
||||
"user": tfschema.GenSchemaUserV2,
|
||||
"server": tfschema.GenSchemaServerV2,
|
||||
}
|
||||
|
||||
// hiddenFields are fields that are not outputted to the reference doc.
|
||||
// It supports non-top level fields by adding its prefix. Eg: metadata.namespace
|
||||
hiddenFields = []string{
|
||||
"id", // read only field
|
||||
"kind", // each resource already defines its kind so this is redundant
|
||||
}
|
||||
|
||||
// fieldComments is used to define specific descriptions for the given fields.
|
||||
// Typical usage is for enums which we don't have comments yet.
|
||||
fieldComments = map[string]string{
|
||||
"teleport_auth_preference.spec.require_session_mfa": "RequireMFAType is the type of MFA requirement enforced for this cluster: 0:Off, 1:Session, 2:SessionAndHardwareKey, 3:HardwareKeyTouch",
|
||||
"teleport_role.spec.options.require_session_mfa": "RequireMFAType is the type of MFA requirement enforced for this role: 0:Off, 1:Session, 2:SessionAndHardwareKey, 3:HardwareKeyTouch",
|
||||
}
|
||||
)
|
||||
|
||||
func genReferenceDocs() {
|
||||
sortedNames := make([]string, 0, len(mapResourceSchema))
|
||||
for k := range mapResourceSchema {
|
||||
sortedNames = append(sortedNames, k)
|
||||
}
|
||||
sort.Strings(sortedNames)
|
||||
|
||||
t, err := template.ParseFiles(path.Join("gen", referenceDocsTemplate))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
referenceDocsResource := make([]referenceDocResource, 0, len(mapResourceSchema))
|
||||
for _, name := range sortedNames {
|
||||
resourceName := "teleport_" + name
|
||||
|
||||
schemaFn := mapResourceSchema[name]
|
||||
schema, diags := schemaFn(context.Background())
|
||||
if diags.HasError() {
|
||||
log.Fatalf("%v", diags)
|
||||
}
|
||||
|
||||
fieldDescBuilder := strings.Builder{}
|
||||
dumpAttributes(&fieldDescBuilder, 0, resourceName, "", schema.Attributes)
|
||||
|
||||
exampleFileName := fmt.Sprintf("example/%s.tf.example", name)
|
||||
exampleBytes, err := os.ReadFile(exampleFileName)
|
||||
if err != nil {
|
||||
log.Fatalf("error loading %q file: %v", exampleFileName, err)
|
||||
}
|
||||
|
||||
referenceDocsResource = append(referenceDocsResource, referenceDocResource{
|
||||
Name: resourceName,
|
||||
FieldsDesc: fieldDescBuilder.String(),
|
||||
Example: string(exampleBytes),
|
||||
})
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
err = t.ExecuteTemplate(&b, referenceDocsTemplate, map[string]any{
|
||||
"resourceList": sortedNames,
|
||||
"resourcesDoc": referenceDocsResource,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.WriteFile("reference.mdx", b.Bytes(), 0777)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type referenceDocResource struct {
|
||||
Name string
|
||||
FieldsDesc string
|
||||
Example string
|
||||
}
|
||||
|
||||
func dumpAttributes(fp io.Writer, level int, resourceName string, prefix string, attrs map[string]tfsdk.Attribute) {
|
||||
sortedAttrKeys := make([]string, 0, len(attrs))
|
||||
for k := range attrs {
|
||||
sortedAttrKeys = append(sortedAttrKeys, k)
|
||||
}
|
||||
sort.Strings(sortedAttrKeys)
|
||||
|
||||
table := tablewriter.NewWriter(fp)
|
||||
table.SetHeader([]string{"Name", "Type", "Required", "Description"})
|
||||
table.SetAutoWrapText(false)
|
||||
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
|
||||
table.SetCenterSeparator("|")
|
||||
table.SetAutoFormatHeaders(false)
|
||||
|
||||
for _, name := range sortedAttrKeys {
|
||||
fullFieldPath := resourceName + "." + prefix + name
|
||||
attr := attrs[name]
|
||||
|
||||
if slices.Contains(hiddenFields, prefix+name) {
|
||||
continue
|
||||
}
|
||||
|
||||
description := attr.Description
|
||||
if d, found := fieldComments[fullFieldPath]; found {
|
||||
description = d
|
||||
}
|
||||
// Using html.EscapeString also escapes `'`` (into `'`)
|
||||
// This generates a lint error when running cspell because the word `doesn't` becomes `doesn't`
|
||||
// and `doesn` isn't a valid word.
|
||||
// This lint error happens when running the `Lint (docs)` CI step in the teleport repo.
|
||||
// The mdx format supports `'"&` (the other chars that html.EscapeString escapes) without escaping.
|
||||
descriptionMDXEscaped := strings.ReplaceAll(strings.ReplaceAll(description, "<", "<"), ">", ">")
|
||||
table.Append([]string{name, typ(attr.Type), requiredString(attr.Required), descriptionMDXEscaped})
|
||||
}
|
||||
table.Render()
|
||||
fmt.Fprintln(fp)
|
||||
|
||||
for _, name := range sortedAttrKeys {
|
||||
attr := attrs[name]
|
||||
if attr.Attributes != nil {
|
||||
fmt.Fprintf(fp, "%s %s\n", strings.Repeat("#", 3+level), prefix+name)
|
||||
fmt.Fprintln(fp)
|
||||
fmt.Fprintln(fp, attr.Description)
|
||||
fmt.Fprintln(fp)
|
||||
|
||||
dumpAttributes(fp, level+1, resourceName, prefix+name+".", attr.Attributes.GetAttributes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func typ(typ attr.Type) string {
|
||||
if typ == nil {
|
||||
return "object"
|
||||
}
|
||||
|
||||
switch typ.String() {
|
||||
case "types.StringType":
|
||||
return "string"
|
||||
case "TimeType(2006-01-02T15:04:05Z07:00)":
|
||||
return "RFC3339 time"
|
||||
case "DurationType":
|
||||
return "duration"
|
||||
case "types.BoolType":
|
||||
return "bool"
|
||||
case "types.MapType[types.StringType]":
|
||||
return "map of strings"
|
||||
case "types.ListType[types.StringType]":
|
||||
return "array of strings"
|
||||
case "types.MapType[types.ListType[types.StringType]]":
|
||||
return "map of string arrays"
|
||||
case "types.Int64Type":
|
||||
return "number"
|
||||
default:
|
||||
return typ.String()
|
||||
}
|
||||
}
|
||||
|
||||
func requiredString(r bool) string {
|
||||
if r {
|
||||
return "*"
|
||||
}
|
||||
return " "
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
{{ if not .IsPlainStruct }}
|
||||
{{- protoImport . }}
|
||||
{{- end }}
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
{{- if .Namespaced }}
|
||||
"github.com/gravitational/teleport/api/defaults"
|
||||
{{- end }}
|
||||
|
||||
{{ schemaImport . }}
|
||||
)
|
||||
|
||||
// dataSourceTeleport{{.Name}}Type is the data source metadata type
|
||||
type dataSourceTeleport{{.Name}}Type struct{}
|
||||
|
||||
// dataSourceTeleport{{.Name}} is the resource
|
||||
type dataSourceTeleport{{.Name}} struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleport{{.Name}}Type) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return {{.SchemaPackage}}.GenSchema{{.TypeName}}(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleport{{.Name}}Type) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleport{{.Name}}{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport {{.Name}}
|
||||
func (r dataSourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
{{- if .ConvertPackagePath}}
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id)
|
||||
{{- else }}
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
{{- end}}
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}}I, err := r.p.Client.{{.GetMethod}}(ctx, {{if .Namespaced}}defaults.Namespace, {{end}}id.Value{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
{{if .IsPlainStruct -}}
|
||||
{{.VarName}} := {{.VarName}}I
|
||||
{{else if .ConvertPackagePath -}}
|
||||
{{.VarName}} := convert.ToProto({{.VarName}}I)
|
||||
{{else}}
|
||||
{{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
{{- end}}
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
{{- if .RandomMetadataName }}
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
{{- end }}
|
||||
"fmt"
|
||||
{{- range $i, $a := .ExtraImports }}
|
||||
"{{$a}}"
|
||||
{{- end }}
|
||||
{{ if .UUIDMetadataName }}
|
||||
"github.com/google/uuid"
|
||||
{{- end }}
|
||||
{{.ProtoPackage}} "{{.ProtoPackagePath}}"
|
||||
{{ if .ConvertPackagePath -}}
|
||||
convert "{{.ConvertPackagePath}}"
|
||||
{{- end}}
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
{{- if .Namespaced }}
|
||||
"github.com/gravitational/teleport/api/defaults"
|
||||
{{- end }}
|
||||
|
||||
{{ schemaImport . }}
|
||||
)
|
||||
|
||||
// resourceTeleport{{.Name}}Type is the resource metadata type
|
||||
type resourceTeleport{{.Name}}Type struct{}
|
||||
|
||||
// resourceTeleport{{.Name}} is the resource
|
||||
type resourceTeleport{{.Name}} struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleport{{.Name}}Type) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return {{.SchemaPackage}}.GenSchema{{.TypeName}}(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleport{{.Name}}Type) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleport{{.Name}}{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the {{.Name}}
|
||||
func (r resourceTeleport{{.Name}}) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}} := &{{.ProtoPackage}}.{{.TypeName}}{}
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}FromTerraform(ctx, plan, {{.VarName}})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{if .RandomMetadataName -}}
|
||||
if {{.VarName}}.Metadata.Name == "" {
|
||||
b := make([]byte, 32)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to generate random token", err.Error())
|
||||
return
|
||||
}
|
||||
{{.VarName}}.Metadata.Name = hex.EncodeToString(b)
|
||||
}
|
||||
{{end -}}
|
||||
{{if .UUIDMetadataName -}}
|
||||
if {{.VarName}}.Metadata.Name == "" {
|
||||
{{.VarName}}.Metadata.Name = uuid.NewString()
|
||||
}
|
||||
{{end -}}
|
||||
{{if .DefaultVersion -}}
|
||||
if {{.VarName}}.Version == "" {
|
||||
{{.VarName}}.Version = "{{.DefaultVersion}}"
|
||||
}
|
||||
{{- end}}
|
||||
{{- if .ConvertPackagePath}}
|
||||
{{.VarName}}Resource, err := convert.FromProto({{.VarName}})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Errorf("Can not convert %T to {{.TypeName}}: %s", {{.VarName}}Resource, err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{- else }}
|
||||
{{.VarName}}Resource := {{.VarName}}
|
||||
{{end}}
|
||||
|
||||
{{- if .ForceSetKind }}
|
||||
{{.VarName}}Resource.Kind = {{.ForceSetKind}}
|
||||
{{- end}}
|
||||
|
||||
{{- if .HasCheckAndSetDefaults }}
|
||||
err = {{.VarName}}Resource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting {{.Name}} defaults", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
id := {{.VarName}}Resource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.{{.GetMethod}}(ctx, {{if .Namespaced}}defaults.Namespace, {{end}}id{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}})
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("{{.Name}} exists in Teleport. Either remove it (tctl rm {{.Kind}}/%v)"+
|
||||
" or import it to the existing state (terraform import {{.TerraformResourceType}}.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("{{.Name}} exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{if eq .UpsertMethodArity 2}}_, {{end}}err = r.p.Client.{{.CreateMethod}}(ctx, {{.VarName}}Resource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{- if .IsPlainStruct }}
|
||||
var {{.VarName}}I *{{.ProtoPackage}}.{{.Name}}
|
||||
{{- else }}
|
||||
{{if .ConvertPackagePath -}}
|
||||
var {{.VarName}}I = {{.VarName}}Resource
|
||||
{{- else }}
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var {{.VarName}}I {{.ProtoPackage}}.{{ if ne .IfaceName ""}}{{.IfaceName}}{{else}}{{.Name}}{{end}}
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
{{.VarName}}I, err = r.p.Client.{{.GetMethod}}(ctx, {{if .Namespaced}}defaults.Namespace, {{end}}id{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}})
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(bErr), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading {{.Name}} (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "{{.Kind}}")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{if or .IsPlainStruct .ConvertPackagePath -}}
|
||||
{{.VarName}}Resource = {{.VarName}}I
|
||||
{{else -}}
|
||||
{{.VarName}}Resource, ok := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Errorf("Can not convert %T to {{.TypeName}}", {{.VarName}}I), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
{{- if .ConvertPackagePath}}
|
||||
{{.VarName}} = convert.ToProto({{.VarName}}Resource)
|
||||
{{else}}
|
||||
{{.VarName}} = {{.VarName}}Resource
|
||||
{{- end }}
|
||||
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: {{.ID}}}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport {{.Name}}
|
||||
func (r resourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
{{- if .ConvertPackagePath}}
|
||||
diags = req.State.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id)
|
||||
{{- else }}
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
{{- end}}
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}}I, err := r.p.Client.{{.GetMethod}}(ctx, {{if .Namespaced}}defaults.Namespace, {{end}}id.Value{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}})
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{if .IsPlainStruct -}}
|
||||
{{.VarName}} := {{.VarName}}I
|
||||
{{else if .ConvertPackagePath -}}
|
||||
{{.VarName}} := convert.ToProto({{.VarName}}I)
|
||||
{{ else }}
|
||||
{{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
{{end -}}
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport {{.Name}}
|
||||
func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}} := &{{.ProtoPackage}}.{{.TypeName}}{}
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}FromTerraform(ctx, plan, {{.VarName}})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{- if .ConvertPackagePath}}
|
||||
{{.VarName}}Resource, err := convert.FromProto({{.VarName}})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Errorf("Can not convert %T to {{.TypeName}}: %s", {{.VarName}}Resource, err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{- else }}
|
||||
{{.VarName}}Resource := {{.VarName}}
|
||||
{{end}}
|
||||
|
||||
{{if .HasCheckAndSetDefaults -}}
|
||||
if err := {{.VarName}}Resource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating {{.Name}}", err, "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
name := {{.VarName}}Resource.Metadata.Name
|
||||
|
||||
{{.VarName}}Before, err := r.p.Client.{{.GetMethod}}(ctx, {{if .Namespaced}}defaults.Namespace, {{end}}name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", err, "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{- $VarName := .VarName }}
|
||||
{{- range $field := .PropagatedFields }}
|
||||
{{ $VarName }}Resource.{{ $field }} = {{ $VarName }}Before.{{ $field }}
|
||||
{{- end }}
|
||||
|
||||
{{if eq .UpsertMethodArity 2}}_, {{end}}err = r.p.Client.{{.UpdateMethod}}(ctx, {{.VarName}}Resource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating {{.Name}}", err, "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{- if .IsPlainStruct }}
|
||||
var {{.VarName}}I *{{.ProtoPackage}}.{{.Name}}
|
||||
{{- else }}
|
||||
{{if .ConvertPackagePath -}}
|
||||
var {{.VarName}}I = {{.VarName}}Resource
|
||||
{{- else }}
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var {{.VarName}}I {{.ProtoPackage}}.{{ if ne .IfaceName ""}}{{.IfaceName}}{{else}}{{.Name}}{{end}}
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
{{.VarName}}I, err = r.p.Client.{{.GetMethod}}(ctx, {{if .Namespaced}}defaults.Namespace, {{end}}name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", err, "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
if {{.VarName}}Before.GetMetadata().Revision != {{.VarName}}I.GetMetadata().Revision || {{.HasStaticID}} {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading {{.Name}} (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "{{.Kind}}")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
{{if or .IsPlainStruct .ConvertPackagePath -}}
|
||||
{{.VarName}}Resource = {{.VarName}}I
|
||||
{{else -}}
|
||||
{{.VarName}}Resource, ok := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Errorf("Can not convert %T to {{.TypeName}}", {{.VarName}}I), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport {{.Name}}
|
||||
func (r resourceTeleport{{.Name}}) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
{{- if .ConvertPackagePath}}
|
||||
diags := req.State.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id)
|
||||
{{- else }}
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
{{- end}}
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.{{.DeleteMethod}}(ctx, {{if .Namespaced}}defaults.Namespace, {{end}}id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting {{.TypeName}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports {{.Name}} state
|
||||
func (r resourceTeleport{{.Name}}) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
{{.VarName}}, err := r.p.Client.{{.GetMethod}}(ctx, {{if .Namespaced}}defaults.Namespace, {{end}}req.ID{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{if .IsPlainStruct -}}
|
||||
{{.VarName}}Resource := {{.VarName}}
|
||||
{{else if .ConvertPackagePath -}}
|
||||
{{.VarName}}Resource := convert.ToProto({{.VarName}})
|
||||
{{else}}
|
||||
{{.VarName}}Resource := {{.VarName}}.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
{{- end}}
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}Resource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{- if or .IsPlainStruct .ConvertPackagePath}}
|
||||
id := {{.VarName}}.Metadata.Name
|
||||
{{- else }}
|
||||
id := {{.VarName}}Resource.GetName()
|
||||
{{- end}}
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: Terraform provider resources
|
||||
description: Terraform provider resources reference
|
||||
---
|
||||
|
||||
{/* Content generated by teleport-plugins/terraform/gen/main.go DO NOT EDIT */}
|
||||
|
||||
Supported resources:
|
||||
{{range $_, $resource := .resourceList}}
|
||||
- [teleport_{{$resource}}](#teleport_{{$resource}})
|
||||
{{- end}}
|
||||
|
||||
## Provider configuration
|
||||
|
||||
Ensure your Terraform version is v(=terraform.version=) or higher.
|
||||
|
||||
Add the following configuration section to your `terraform` configuration block:
|
||||
|
||||
```
|
||||
terraform {
|
||||
required_providers {
|
||||
teleport = {
|
||||
version = "~> (=teleport.major_version=).0"
|
||||
source = "terraform.releases.teleport.dev/gravitational/teleport"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The provider supports the following options:
|
||||
|
||||
| Name | Type | Description | Environment Variable |
|
||||
| ---------------------- | ------ | ------------------------------------------------------------------------------ | ---------------------------------- |
|
||||
| `addr` | string | Teleport auth or proxy address in "host:port" format. | `TF_TELEPORT_ADDR` |
|
||||
| `cert_path` | string | Path to Teleport certificate file. | `TF_TELEPORT_CERT` |
|
||||
| `cert_base64` | string | Teleport certificate as base64. | `TF_TELEPORT_CERT_BASE64` |
|
||||
| `identity_file_path` | string | Path to Teleport identity file. | `TF_TELEPORT_IDENTITY_FILE_PATH` |
|
||||
| `identity_file_base64` | string | Teleport identity file as base64. | `TF_TELEPORT_IDENTITY_FILE_BASE64` |
|
||||
| `key_path` | string | Path to Teleport key file. | `TF_TELEPORT_KEY` |
|
||||
| `key_base64` | string | Teleport key as base64. | `TF_TELEPORT_KEY_BASE64` |
|
||||
| `profile_dir` | string | Teleport profile path. | `TF_TELEPORT_PROFILE_PATH` |
|
||||
| `profile_name` | string | Teleport profile name. | `TF_TELEPORT_PROFILE_NAME` |
|
||||
| `root_ca_path` | string | Path to Teleport CA file. | `TF_TELEPORT_ROOT_CA` |
|
||||
| `root_ca_base64` | string | Teleport CA as base64. | `TF_TELEPORT_ROOT_CA_BASE64` |
|
||||
| `retry_base_duration` | string | Base duration between retries. [Format](https://pkg.go.dev/time#ParseDuration) | `TF_TELEPORT_RETRY_BASE_DURATION` |
|
||||
| `retry_cap_duration` | string | Max duration between retries. [Format](https://pkg.go.dev/time#ParseDuration) | `TF_TELEPORT_RETRY_CAP_DURATION` |
|
||||
| `retry_max_tries` | string | Max number of retries. | `TF_TELEPORT_RETRY_MAX_TRIES` |
|
||||
|
||||
You need to specify at least one of:
|
||||
|
||||
- `cert_path`, `key_path`,`root_ca_path` and `addr` to connect using key files.
|
||||
- `cert_base64`, `key_base64`,`root_ca_base64` and `addr` to connect using a base64-encoded key.
|
||||
- `identity_file_path` or `identity_file_base64` and `addr` to connect using an identity file.
|
||||
- `profile_name`, `profile_dir` (both can be empty) and `addr` to connect using current profile from `~/.tsh`
|
||||
|
||||
The `retry_*` values are used to retry the API calls to Teleport when the cache is stale.
|
||||
|
||||
If more than one are provided, they will be tried in the order above until one succeeds.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
provider "teleport" {
|
||||
addr = "localhost:3025"
|
||||
cert_path = "tf.crt"
|
||||
key_path = "tf.key"
|
||||
root_ca_path = "tf.ca"
|
||||
}
|
||||
```
|
||||
|
||||
## Provider resource versioning
|
||||
|
||||
Since Teleport 15, you must set the version on each resource, and version cannot
|
||||
be changed in-place. Terraform will delete the resource and create a new one if
|
||||
a version change is required.
|
||||
|
||||
This is not enforced on previous Teleport provider versions, but we recommend doing
|
||||
so. When the version is not specified, Terraform will pick the latest one by default.
|
||||
However, version upgrades don't re-apply the resource defaults. This could lead
|
||||
to different results if you create a new resource or upgrade an existing one.
|
||||
To mitigate this, you should explicitly set the resource version.
|
||||
|
||||
<Admonition type="warning">
|
||||
Upgrading the Terraform Provider to a new version with `teleport_role`
|
||||
resources without a specified version can change the role behavior and access
|
||||
rules. You must set the role version before upgrading to ensure the role
|
||||
access rules don't change.
|
||||
|
||||
The default role version is the highest supported:
|
||||
|
||||
- v12 default role version is `v5`
|
||||
- v13 default role version is `v6`
|
||||
- v14 default role version is `v7`
|
||||
|
||||
For example, before upgrading from v12 to v13, edit every unversioned role
|
||||
to pin the `v5` version:
|
||||
|
||||
```hcl
|
||||
resource "teleport_role" "test" {
|
||||
version = "v5"
|
||||
metadata = {
|
||||
name = "my-role"
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
</Admonition>
|
||||
|
||||
{{range $_, $resource := .resourcesDoc}}
|
||||
## {{$resource.Name}}
|
||||
|
||||
{{$resource.FieldsDesc -}}
|
||||
Example:
|
||||
|
||||
```
|
||||
{{$resource.Example}}
|
||||
```
|
||||
{{end}}
|
||||
@@ -0,0 +1,79 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
{{ if not .IsPlainStruct }}
|
||||
{{- protoImport . }}
|
||||
{{- end }}
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
{{ schemaImport . }}
|
||||
)
|
||||
|
||||
// dataSourceTeleport{{.Name}}Type is the data source metadata type
|
||||
type dataSourceTeleport{{.Name}}Type struct{}
|
||||
|
||||
// dataSourceTeleport{{.Name}} is the resource
|
||||
type dataSourceTeleport{{.Name}} struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleport{{.Name}}Type) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return {{.SchemaPackage}}.GenSchema{{.TypeName}}(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleport{{.Name}}Type) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleport{{.Name}}{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport {{.Name}}
|
||||
func (r dataSourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
{{.VarName}}I, err := r.p.Client.{{.GetMethod}}(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
{{if .IsPlainStruct -}}
|
||||
{{.VarName}} := {{.VarName}}I
|
||||
{{else -}}
|
||||
{{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
{{end -}}
|
||||
diags := {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
{{- if .WithNonce}}
|
||||
"math"
|
||||
{{- end}}
|
||||
|
||||
{{.ProtoPackage}} "{{.ProtoPackagePath}}"
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
{{ schemaImport . }}
|
||||
)
|
||||
|
||||
// resourceTeleport{{.Name}}Type is the resource metadata type
|
||||
type resourceTeleport{{.Name}}Type struct{}
|
||||
|
||||
// resourceTeleport{{.Name}} is the resource
|
||||
type resourceTeleport{{.Name}} struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleport{{.Name}}Type) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return {{.SchemaPackage}}.GenSchema{{.TypeName}}(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleport{{.Name}}Type) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleport{{.Name}}{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the {{.Name}}
|
||||
func (r resourceTeleport{{.Name}}) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}} := &{{.ProtoPackage}}.{{.TypeName}}{}
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}FromTerraform(ctx, plan, {{.VarName}})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := {{.VarName}}.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting {{.Name}} defaults", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{if .DefaultVersion -}}
|
||||
if {{.VarName}}.Version == "" {
|
||||
{{.VarName}}.Version = "{{.DefaultVersion}}"
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
{{.VarName}}Before, err := r.p.Client.Get{{.Name}}(ctx)
|
||||
if err != nil && !trace.IsNotFound(err) {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{- if .GetCanReturnNil }}
|
||||
|
||||
if {{.VarName}}Before == nil {
|
||||
{{.VarName}}Before = &{{.ProtoPackage}}.{{.TypeName}}{}
|
||||
}
|
||||
|
||||
{{- end}}
|
||||
|
||||
{{- if .WithNonce}}
|
||||
{{.VarName}} = {{.VarName}}.WithNonce(math.MaxUint64).(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
{{- end}}
|
||||
|
||||
err = r.p.Client.{{.CreateMethod}}(ctx, {{.VarName}})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{if .IsPlainStruct -}}
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var {{.VarName}}I *{{.ProtoPackage}}.{{.Name}}
|
||||
{{else -}}
|
||||
var {{.VarName}}I {{.ProtoPackage}}.{{ if ne .IfaceName ""}}{{.IfaceName}}{{else}}{{.Name}}{{end}}
|
||||
{{- end}}
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
{{.VarName}}I, err = r.p.Client.{{.GetMethod}}(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
if {{.VarName}}Before.GetMetadata().Revision != {{.VarName}}I.GetMetadata().Revision || {{.HasStaticID}} {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(bErr), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading {{.Name}} (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "{{.Kind}}")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{if .IsPlainStruct -}}
|
||||
{{.VarName}} = {{.VarName}}I
|
||||
{{else -}}
|
||||
{{.VarName}}, ok := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(
|
||||
diagFromWrappedErr("Error reading {{.Name}}", trace.Errorf("Can not convert %T to {{.TypeName}}", {{.VarName}}I), "{{.Kind}}"),
|
||||
)
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: {{.ID}}}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport {{.Name}}
|
||||
func (r resourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}}I, err := r.p.Client.Get{{.Name}}(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport {{.Name}}
|
||||
func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}} := &{{.ProtoPackage}}.{{.TypeName}}{}
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}FromTerraform(ctx, plan, {{.VarName}})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := {{.VarName}}.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}}Before, err := r.p.Client.Get{{.Name}}(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
{{- $VarName := .VarName }}
|
||||
{{- range $field := .PropagatedFields }}
|
||||
{{ $VarName }}.{{ $field }} = {{ $VarName }}Before.{{ $field }}
|
||||
{{- end }}
|
||||
|
||||
{{- if .WithNonce}}
|
||||
{{.VarName}} = {{.VarName}}.WithNonce(math.MaxUint64).(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
{{- end}}
|
||||
|
||||
err = r.p.Client.{{.UpdateMethod}}(ctx, {{.VarName}})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
var {{.VarName}}I {{.ProtoPackage}}.{{ if ne .IfaceName ""}}{{.IfaceName}}{{else}}{{.Name}}{{end}}
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
{{.VarName}}I, err = r.p.Client.{{.GetMethod}}(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
if {{.VarName}}Before.GetMetadata().Revision != {{.VarName}}I.GetMetadata().Revision || {{.HasStaticID}} {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(bErr), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading {{.Name}} (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "{{.Kind}}")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}} = {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport {{.Name}}
|
||||
func (r resourceTeleport{{.Name}}) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
err := r.p.Client.{{.DeleteMethod}}(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports {{.Name}} state
|
||||
func (r resourceTeleport{{.Name}}) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
{{.VarName}}I, err := r.p.Client.Get{{.Name}}(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating {{.Name}}", trace.Wrap(err), "{{.Kind}}"))
|
||||
return
|
||||
}
|
||||
|
||||
{{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}})
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
state.Attrs["id"] = types.String{Value: {{.VarName}}.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
module github.com/gravitational/teleport/integrations/terraform
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.2
|
||||
|
||||
require (
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gravitational/teleport v0.0.0 // replaced
|
||||
github.com/gravitational/teleport/api v0.0.0 // replaced
|
||||
github.com/gravitational/trace v1.4.0
|
||||
github.com/hashicorp/terraform-plugin-framework v0.10.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.12.0
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1
|
||||
github.com/jonboulle/clockwork v0.4.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
|
||||
google.golang.org/grpc v1.63.0
|
||||
google.golang.org/protobuf v1.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.25.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/container v1.35.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.7 // indirect
|
||||
cloud.google.com/go/kms v1.15.8 // indirect
|
||||
connectrpc.com/connect v1.16.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.4.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysql v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysqlflexibleservers v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresql v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresqlflexibleservers v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis/v2 v2.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redisenterprise/armredisenterprise v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.4 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.4 // indirect
|
||||
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
|
||||
github.com/agext/levenshtein v1.2.2 // indirect
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/apparentlymart/go-textseg v1.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go v1.51.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/athena v1.40.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.156.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.23.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ecs v1.41.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/eks v1.42.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/glue v1.78.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.31.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/rds v1.76.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
|
||||
github.com/aws/smithy-go v1.20.2 // indirect
|
||||
github.com/beevik/etree v1.3.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chai2010/gettext-go v1.0.2 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.4 // indirect
|
||||
github.com/containerd/containerd v1.7.12 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf // indirect
|
||||
github.com/crewjam/httperr v0.2.0 // indirect
|
||||
github.com/crewjam/saml v0.4.14 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/di-wu/parser v0.3.0 // indirect
|
||||
github.com/di-wu/xsd-datetime v1.0.0 // indirect
|
||||
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/docker/cli v25.0.1+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker v25.0.5+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.1 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/elimity-com/scim v0.0.0-20240320110924-172bf2aee9c8 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.3 // indirect
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/flynn/hid v0.0.0-20190502022136-f1b9b6cc019a // indirect
|
||||
github.com/flynn/u2f v0.0.0-20180613185708-15554eb68e5d // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.22.9 // indirect
|
||||
github.com/go-piv/piv-go v1.11.0 // indirect
|
||||
github.com/go-webauthn/webauthn v0.10.2 // indirect
|
||||
github.com/go-webauthn/x v0.1.9 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.3.2 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.7 // indirect
|
||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
|
||||
github.com/google/go-attestation v0.5.1 // indirect
|
||||
github.com/google/go-tpm v0.9.0 // indirect
|
||||
github.com/google/go-tpm-tools v0.4.4 // indirect
|
||||
github.com/google/go-tspi v0.3.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/gosuri/uitable v0.0.4 // indirect
|
||||
github.com/gravitational/license v0.0.0-20240313232707-8312e719d624 // indirect
|
||||
github.com/gravitational/oxy v0.0.0-20231219172753-f855322f2a6c // indirect
|
||||
github.com/gravitational/roundtrip v1.0.2 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect
|
||||
github.com/hashicorp/go-hclog v1.2.1 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.4.4 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hc-install v0.3.1 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.3.0 // indirect
|
||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.15.0 // indirect
|
||||
github.com/hashicorp/terraform-json v0.13.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-log v0.6.0 // indirect
|
||||
github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c // indirect
|
||||
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
||||
github.com/keys-pub/go-libfido2 v1.5.3-0.20220306005615-8ab03fb1ec27 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/mailgun/holster/v3 v3.16.2 // indirect
|
||||
github.com/mailgun/minheap v0.0.0-20170619185613-3dbe6c6bf55f // indirect
|
||||
github.com/mailgun/timetools v0.0.0-20170619190023-f3a7b8ffff47 // indirect
|
||||
github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/oklog/run v1.0.0 // indirect
|
||||
github.com/okta/okta-sdk-golang/v2 v2.20.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc6 // indirect
|
||||
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 // indirect
|
||||
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.6 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/pquerna/cachecontrol v0.1.0 // indirect
|
||||
github.com/pquerna/otp v1.4.0 // indirect
|
||||
github.com/prometheus/client_golang v1.19.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.51.1 // indirect
|
||||
github.com/prometheus/procfs v0.13.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/rubenv/sql-migrate v1.5.2 // indirect
|
||||
github.com/russellhaering/gosaml2 v0.9.1 // indirect
|
||||
github.com/russellhaering/goxmldsig v1.4.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sashabaranov/go-openai v1.20.5 // indirect
|
||||
github.com/schollz/progressbar/v3 v3.14.2 // indirect
|
||||
github.com/scim2/filter-parser/v2 v2.2.0 // indirect
|
||||
github.com/sethvargo/go-diceware v0.3.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sijms/go-ora/v2 v2.8.10 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/cobra v1.8.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/thales-e-security/pool v0.0.2 // indirect
|
||||
github.com/tiktoken-go/tokenizer v0.1.0 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
|
||||
github.com/vmihailenco/tagparser v0.1.1 // indirect
|
||||
github.com/vulcand/predicate v1.2.0 // indirect
|
||||
github.com/weppos/publicsuffix-go v0.30.1-0.20230620154423-38c92ad2d5c6 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
github.com/zclconf/go-cty v1.9.1 // indirect
|
||||
github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300 // indirect
|
||||
github.com/zmap/zlint/v3 v3.5.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect
|
||||
go.opentelemetry.io/otel v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.25.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/oauth2 v0.19.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/term v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.19.0 // indirect
|
||||
google.golang.org/api v0.172.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240325164216-beb30f47624b // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240325164216-beb30f47624b // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
helm.sh/helm/v3 v3.14.3 // indirect
|
||||
k8s.io/api v0.29.3 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.29.3 // indirect
|
||||
k8s.io/apimachinery v0.29.3 // indirect
|
||||
k8s.io/apiserver v0.29.3 // indirect
|
||||
k8s.io/cli-runtime v0.29.3 // indirect
|
||||
k8s.io/client-go v0.29.3 // indirect
|
||||
k8s.io/component-base v0.29.3 // indirect
|
||||
k8s.io/klog/v2 v2.120.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240224005224-582cce78233b // indirect
|
||||
k8s.io/kubectl v0.29.3 // indirect
|
||||
k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect
|
||||
oras.land/oras-go v1.2.5 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.17.2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.16.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
software.sslmate.com/src/go-pkcs12 v0.4.0 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/alecthomas/kingpin/v2 => github.com/gravitational/kingpin/v2 v2.1.11-0.20230515143221-4ec6b70ecd33
|
||||
github.com/coreos/go-oidc => github.com/gravitational/go-oidc v0.1.1
|
||||
github.com/gogo/protobuf => github.com/gravitational/protobuf v1.3.2-0.20201123192827-2b9fcfaffcbf
|
||||
github.com/gravitational/teleport => ../../
|
||||
github.com/gravitational/teleport/api => ../../api
|
||||
github.com/julienschmidt/httprouter => github.com/gravitational/httprouter v1.3.1-0.20220408074523-c876c5e705a5
|
||||
github.com/microsoft/go-mssqldb => github.com/gravitational/go-mssqldb v0.11.1-0.20230331180905-0f76f1751cd3
|
||||
github.com/vulcand/predicate => github.com/gravitational/predicate v1.3.1
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
VERSION?=15.1.10
|
||||
|
||||
OS ?= $(shell go env GOOS)
|
||||
ARCH ?= $(shell go env GOARCH)
|
||||
TERRAFORM_ARCH=$(OS)_$(ARCH)
|
||||
|
||||
PROVIDER_PATH = ~/.terraform.d/plugins/terraform.releases.teleport.dev/gravitational/teleport/$(VERSION)/$(TERRAFORM_ARCH)/
|
||||
|
||||
.PHONY: install
|
||||
install: build
|
||||
mkdir -p $(PROVIDER_PATH)
|
||||
mv $(BUILDDIR)/terraform-provider-teleport $(PROVIDER_PATH)
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/providerserver"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/provider"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := providerserver.Serve(context.Background(), provider.New, providerserver.ServeOpts{
|
||||
Address: "terraform.releases.teleport.dev/gravitational/teleport",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
target_package_name: "v1"
|
||||
default_package_name: "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1"
|
||||
duration_custom_type: Duration
|
||||
use_state_for_unknown_by_default: true
|
||||
|
||||
# Top-level type names to export
|
||||
types:
|
||||
- "AccessList"
|
||||
|
||||
# These import paths were not being automatically picked up by
|
||||
# protoc-gen-terraform without these overrides
|
||||
import_path_overrides:
|
||||
"types": "github.com/gravitational/teleport/api/types"
|
||||
"wrappers": "github.com/gravitational/teleport/api/types/wrappers"
|
||||
"durationpb": "google.golang.org/protobuf/types/known/durationpb"
|
||||
"timestamppb": "google.golang.org/protobuf/types/known/timestamppb"
|
||||
"v1": "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1"
|
||||
"v11": "github.com/gravitational/teleport/api/gen/proto/go/teleport/trait/v1"
|
||||
|
||||
# id field is required for integration tests. It is not used by provider.
|
||||
# We have to add it manually (might be removed in the future versions).
|
||||
injected_fields:
|
||||
AccessList:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
|
||||
# These fields will be excluded
|
||||
exclude_fields:
|
||||
# Metadata (we id resources by name on our side)
|
||||
- "AccessList.header.metadata.id"
|
||||
# Read only field
|
||||
- "AccessList.spec.owners.ineligible_status"
|
||||
- "AccessList.status"
|
||||
|
||||
# These fields will be marked as Computed: true
|
||||
computed_fields:
|
||||
# Metadata
|
||||
- "AccessList.header.metadata.namespace"
|
||||
- "AccessList.spec.audit.next_audit_date"
|
||||
|
||||
# These fields will be marked as Required: true
|
||||
required_fields:
|
||||
- "Metadata.name"
|
||||
- "AccessList.header.version"
|
||||
- "AccessList.spec.owners"
|
||||
- "AccessList.spec.grants"
|
||||
- "AccessList.spec.audit"
|
||||
- "AccessList.spec.audit.recurrence"
|
||||
- "AccessList.spec.audit.recurrence.frequency"
|
||||
|
||||
plan_modifiers:
|
||||
# Force to recreate resource if it's name changes
|
||||
Metadata.name:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()"
|
||||
|
||||
# This must be defined for the generator to be happy, but in reality all time
|
||||
# fields are overridden (because the protobuf timestamps contain locks and the
|
||||
# linter gets mad if we use raw structs instead of pointers).
|
||||
time_type:
|
||||
type: "PlaceholderType"
|
||||
duration_type:
|
||||
type: "PlaceholderType"
|
||||
|
||||
validators:
|
||||
# Expires must be in the future
|
||||
Metadata.expires:
|
||||
- github_com_gravitational_teleport_integrations_terraform_tfschema.MustTimeBeInFuture()
|
||||
|
||||
custom_types:
|
||||
"AccessList.header.metadata.expires": Timestamp
|
||||
"AccessList.spec.audit.next_audit_date": Timestamp
|
||||
"AccessList.spec.audit.frequency": Duration
|
||||
"AccessList.spec.audit.notifications.start": Duration
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
target_package_name: "v1"
|
||||
default_package_name: "github.com/gravitational/teleport/api/types"
|
||||
use_state_for_unknown_by_default: true
|
||||
|
||||
# Top-level type names to export
|
||||
types:
|
||||
- "DeviceV1"
|
||||
|
||||
# These import paths were not being automatically picked up by
|
||||
# protoc-gen-terraform without these overrides
|
||||
import_path_overrides:
|
||||
"types": "github.com/gravitational/teleport/api/types"
|
||||
"wrappers": "github.com/gravitational/teleport/api/types/wrappers"
|
||||
|
||||
|
||||
injected_fields:
|
||||
DeviceV1:
|
||||
# id field is required for integration tests. It is also used by this provider
|
||||
# to hold Metadata.name value returned by Teleport.
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
|
||||
|
||||
|
||||
# These fields will be marked as Computed: true
|
||||
computed_fields:
|
||||
# Metadata
|
||||
- "DeviceV1.Metadata"
|
||||
- "DeviceV1.Kind"
|
||||
# DeviceV1
|
||||
- "DeviceV1.spec.enroll_status"
|
||||
|
||||
|
||||
# These fields will be marked as Required: true
|
||||
required_fields:
|
||||
- "DeviceV1.spec.asset_tag"
|
||||
- "DeviceV1.spec.os_type"
|
||||
- "DeviceV1.Version"
|
||||
|
||||
|
||||
exclude_fields:
|
||||
# Below fields are either managed or not used on Teleport side
|
||||
- "DeviceV1.SubKind"
|
||||
- "DeviceV1.Metadata.ID"
|
||||
- "DeviceV1.Metadata.Namespace"
|
||||
- "DeviceV1.Metadata.Description"
|
||||
- "DeviceV1.Metadata.Expires"
|
||||
- "DeviceV1.spec.create_time"
|
||||
- "DeviceV1.spec.update_time"
|
||||
- "DeviceV1.spec.credential"
|
||||
- "DeviceV1.spec.collected_data"
|
||||
- "DeviceV1.spec.profile"
|
||||
|
||||
plan_modifiers:
|
||||
# Force to recreate resource if asset tag changes
|
||||
"DeviceV1.spec.asset_tag":
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()"
|
||||
|
||||
|
||||
time_type:
|
||||
type: "github.com/gravitational/teleport/integrations/terraform/tfschema.TimeType"
|
||||
value_type: "github.com/gravitational/teleport/integrations/terraform/tfschema.TimeValue"
|
||||
cast_to_type: "time.Time"
|
||||
cast_from_type: "time.Time"
|
||||
type_constructor: "github.com/gravitational/teleport/integrations/terraform/tfschema.UseRFC3339Time()"
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
target_package_name: "v1"
|
||||
default_package_name: "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1"
|
||||
duration_custom_type: Duration
|
||||
use_state_for_unknown_by_default: true
|
||||
|
||||
# Top-level type names to export
|
||||
types:
|
||||
- "LoginRule"
|
||||
|
||||
# These import paths were not being automatically picked up by
|
||||
# protoc-gen-terraform without these overrides
|
||||
import_path_overrides:
|
||||
"types": "github.com/gravitational/teleport/api/types"
|
||||
"wrappers": "github.com/gravitational/teleport/api/types/wrappers"
|
||||
|
||||
# id field is required for integration tests. It is not used by provider.
|
||||
# We have to add it manually (might be removed in the future versions).
|
||||
injected_fields:
|
||||
LoginRule:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
|
||||
# These fields will be excluded
|
||||
exclude_fields:
|
||||
# Metadata (we id resources by name on our side)
|
||||
- "Metadata.ID"
|
||||
|
||||
# These fields will be marked as Computed: true
|
||||
computed_fields:
|
||||
# Metadata
|
||||
- "Metadata.Namespace"
|
||||
|
||||
# These fields will be marked as Required: true
|
||||
required_fields:
|
||||
# LoginRule
|
||||
- "Metadata.Name"
|
||||
- "LoginRule.version"
|
||||
- "LoginRule.priority"
|
||||
|
||||
plan_modifiers:
|
||||
# Force to recreate resource if it's name changes
|
||||
Metadata.Name:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()"
|
||||
|
||||
validators:
|
||||
Metadata.Expires:
|
||||
- github_com_gravitational_teleport_integrations_terraform_tfschema.MustTimeBeInFuture()
|
||||
LoginRule:
|
||||
- github_com_gravitational_teleport_integrations_terraform_tfschema.UseAnyOfValidator("traits_map", "traits_expression")
|
||||
LoginRule.TraitsExpression:
|
||||
- github_com_gravitational_teleport_integrations_terraform_tfschema.UseAnyOfValidator("traits_map", "traits_expression")
|
||||
TraitsExpression:
|
||||
- github_com_gravitational_teleport_integrations_terraform_tfschema.UseAnyOfValidator("traits_map", "traits_expression")
|
||||
|
||||
time_type:
|
||||
type: "github.com/gravitational/teleport/integrations/terraform/tfschema.TimeType"
|
||||
value_type: "github.com/gravitational/teleport/integrations/terraform/tfschema.TimeValue"
|
||||
cast_to_type: "time.Time"
|
||||
cast_from_type: "time.Time"
|
||||
type_constructor: "github.com/gravitational/teleport/integrations/terraform/tfschema.UseRFC3339Time()"
|
||||
@@ -0,0 +1,434 @@
|
||||
---
|
||||
target_package_name: "tfschema"
|
||||
default_package_name: "github.com/gravitational/teleport/api/types"
|
||||
duration_custom_type: Duration
|
||||
use_state_for_unknown_by_default: true
|
||||
|
||||
# Top-level type names to export
|
||||
types:
|
||||
- "AppV3"
|
||||
- "AuthPreferenceV2"
|
||||
- "ClusterMaintenanceConfigV1"
|
||||
- "ClusterNetworkingConfigV2"
|
||||
- "DatabaseV3"
|
||||
- "GithubConnectorV3"
|
||||
- "OIDCConnectorV3"
|
||||
- "OktaImportRuleV1"
|
||||
- "ProvisionTokenV2"
|
||||
- "RoleV6"
|
||||
- "SAMLConnectorV2"
|
||||
- "ServerV2"
|
||||
- "SessionRecordingConfigV2"
|
||||
- "TrustedClusterV2"
|
||||
- "UserV2"
|
||||
|
||||
# id field is required for integration tests. It is not used by provider.
|
||||
# We have to add it manually (might be removed in the future versions).
|
||||
injected_fields:
|
||||
AppV3:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
AuthPreferenceV2:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
ClusterMaintenanceConfigV1:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
ClusterNetworkingConfigV2:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
DatabaseV3:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
GithubConnectorV3:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
OIDCConnectorV3:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
OktaImportRuleV1:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
ProvisionTokenV2:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
RoleV6:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
SAMLConnectorV2:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
ServerV2:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
SessionRecordingConfigV2:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
TrustedClusterV2:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
UserV2:
|
||||
-
|
||||
name: id
|
||||
type: github.com/hashicorp/terraform-plugin-framework/types.StringType
|
||||
computed: true
|
||||
plan_modifiers:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
|
||||
# These fields will be excluded
|
||||
exclude_fields:
|
||||
# Metadata (we id resources by name on our side)
|
||||
- "Metadata.ID"
|
||||
|
||||
# AuthPreference
|
||||
- "AuthPreferenceV2.Metadata.Name" # It's a singleton resource
|
||||
|
||||
# ClusterMaintenanceConfig
|
||||
- "ClusterMaintenanceConfigV1.Metadata.Name" # It's a singleton resource
|
||||
|
||||
# NetworkingConfig
|
||||
- "ClusterNetworkingConfigV2.Metadata.Name" # It's a singleton resource
|
||||
|
||||
# Status
|
||||
- "DatabaseV3.Status"
|
||||
|
||||
# Role
|
||||
- "RoleV6.Spec.Allow.Namespaces" # These fields are not settable via API
|
||||
- "RoleV6.Spec.Deny.Namespaces"
|
||||
|
||||
# Server
|
||||
- "ServerSpecV2.CmdLabels"
|
||||
|
||||
# SessionRecordingConfig
|
||||
- "SessionRecordingConfigV2.Metadata.Name" # It's a singleton resource
|
||||
|
||||
# User
|
||||
- "UserSpecV2.LocalAuth" # We do not need to set user auth data
|
||||
- "UserSpecV2.Expires" # Those fields are Teleport side meta, we do not need to change them via Terraform
|
||||
- "UserSpecV2.CreatedBy"
|
||||
- "UserSpecV2.Status"
|
||||
|
||||
name_overrides:
|
||||
|
||||
# These fields will be marked as Computed: true
|
||||
computed_fields:
|
||||
# Metadata
|
||||
- "Metadata.Namespace"
|
||||
|
||||
# App
|
||||
- "AppV3.Metadata.Labels"
|
||||
- "AppV3.Kind"
|
||||
|
||||
# Auth preference
|
||||
- "AuthPreferenceV2.Spec.SecondFactor"
|
||||
- "AuthPreferenceV2.Spec.Type"
|
||||
- "AuthPreferenceV2.Spec.LockingMode"
|
||||
- "AuthPreferenceV2.Metadata.Namespace"
|
||||
- "AuthPreferenceV2.Metadata.Labels"
|
||||
- "AuthPreferenceV2.Kind"
|
||||
|
||||
# ClusterMaintenanceConfig
|
||||
- "ClusterMaintenanceConfigV1.Kind"
|
||||
|
||||
# Cluster networking
|
||||
- "ClusterNetworkingConfigV2.Spec.KeepAliveCountMax"
|
||||
- "ClusterNetworkingConfigV2.Spec.KeepAliveInterval"
|
||||
- "ClusterNetworkingConfigV2.Kind"
|
||||
- "ClusterNetworkingConfigV2.Version"
|
||||
|
||||
# Database
|
||||
- "DatabaseV3.Kind"
|
||||
|
||||
# Github connector
|
||||
- "GithubConnectorV3.Kind"
|
||||
|
||||
# Provision token
|
||||
- "ProvisionTokenV2.Spec.AWSIIDTTL"
|
||||
- "ProvisionTokenV2.Kind"
|
||||
- "ProvisionTokenV2.Metadata.Name"
|
||||
|
||||
# OIDC connector
|
||||
- "OIDCConnectorV3.Kind"
|
||||
|
||||
# Okta import rule
|
||||
- "OktaImportRuleV1.Kind"
|
||||
|
||||
# Role
|
||||
- "RoleV6.Spec.Options.MaxSessionTTL"
|
||||
- "RoleV6.Spec.Options.CertificateFormat"
|
||||
# KubernetesResources was added in v12
|
||||
# When not set, the api returns kind:Pod/name:*/namespace:*
|
||||
# We must include this as computed, otherwise terraform would complain about an unexpected field:
|
||||
# https://developer.hashicorp.com/terraform/plugin/sdkv2/schemas/schema-behaviors#computed
|
||||
# Computed is often used to represent values that are not user configurable or can not be known at time of terraform plan or apply,
|
||||
#------------------------------------
|
||||
# role_test.go:244: Step 1/8 error: Error running apply: exit status 1
|
||||
#
|
||||
# Error: Provider produced inconsistent result after apply
|
||||
#
|
||||
# When applying changes to teleport_role.upgrade, provider
|
||||
# "provider[\"registry.terraform.io/hashicorp/teleport\"]" produced an
|
||||
# unexpected new value: .spec.allow.kubernetes_resources: was null, but now
|
||||
# cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"kind":cty.StringVal("pod"),
|
||||
# "name":cty.StringVal("*"), "namespace":cty.StringVal("*")})}).
|
||||
#
|
||||
# This is a bug in the provider, which should be reported in the provider's own
|
||||
# issue tracker.
|
||||
#------------------------------------
|
||||
# The following tests were added to validate the behavior:
|
||||
# - TestTerraform/TestRoleVersionUpgrade
|
||||
# - TestTerraform/TestRoleWithKubernetesResources
|
||||
- "RoleV6.Spec.Allow.KubernetesResources"
|
||||
- "RoleV6.Spec.Allow.KubernetesResources.Verbs"
|
||||
- "RoleV6.Spec.Options.BPF"
|
||||
- "RoleV6.Kind"
|
||||
|
||||
# SAML connector
|
||||
- "SAMLConnectorV2.Spec.Audience"
|
||||
- "SAMLConnectorV2.Spec.Issuer"
|
||||
- "SAMLConnectorV2.Spec.ServiceProviderIssuer"
|
||||
- "SAMLConnectorV2.Spec.SSO"
|
||||
- "SAMLConnectorV2.Spec.SigningKeyPair"
|
||||
- "SAMLConnectorV2.Spec.SigningKeyPair.PrivateKey"
|
||||
- "SAMLConnectorV2.Spec.SigningKeyPair.Cert"
|
||||
- "SAMLConnectorV2.Spec.EncryprionKeyPair"
|
||||
- "SAMLConnectorV2.Spec.EncryptionKeyPair.PrivateKey"
|
||||
- "SAMLConnectorV2.Spec.EncryptionKeyPair.Cert"
|
||||
- "SAMLConnectorV2.Kind"
|
||||
|
||||
# Server
|
||||
- "ServerV2.Kind"
|
||||
# Name is not required for servers, this is a special case.
|
||||
# When a name is not set, an UUID will be generated by Teleport and
|
||||
# imported back into Terraform.
|
||||
# Giving unique IDs to servers allows UUID-based dialing (as opposed to
|
||||
# host-based dialing and IP-based dialing) which is more robust than its
|
||||
# counterparts as it can point to a specific server if multiple servers
|
||||
# share the same hostname/ip.
|
||||
- "ServerV2.Metadata.Name"
|
||||
# Metadata must be marked computed as well, because we ccan potentially compute Metadata.Name.
|
||||
# If there's no metadata and the attribute is not marked as comnputed, it will
|
||||
# keep its values.Null = true field, which means its content won't be imported
|
||||
# back into the state.
|
||||
- "ServerV2.Metadata"
|
||||
|
||||
# Session recording
|
||||
- "SessionRecordingConfigV2.Spec.Mode"
|
||||
- "SessionRecordingConfigV2.Kind"
|
||||
|
||||
# Trusted cluster
|
||||
- "TrustedClusterV2.Kind"
|
||||
|
||||
# User
|
||||
- "UserV2.Kind"
|
||||
|
||||
# These fields will be marked as Required: true
|
||||
required_fields:
|
||||
# App
|
||||
- "AppV3.Metadata.Name"
|
||||
- "AppV3.Version"
|
||||
|
||||
# Auth preference
|
||||
- "AuthPreferenceV2.Spec"
|
||||
- "AuthPreferenceV2.Metadata.Name"
|
||||
|
||||
# Database
|
||||
- "DatabaseV3.Spec.Protocol"
|
||||
- "DatabaseV3.Spec.URI"
|
||||
- "DatabaseV3.Metadata.Name"
|
||||
- "DatabaseV3.Version"
|
||||
|
||||
# Github connector
|
||||
- "GithubConnectorV3.Spec"
|
||||
- "GithubConnectorV3.Spec.ClientID"
|
||||
- "GithubConnectorV3.Spec.ClientSecret"
|
||||
- "GithubConnectorV3.Metadata.Name"
|
||||
- "GithubConnectorV3.Version"
|
||||
|
||||
# OIDC connector
|
||||
- "OIDCConnectorV3.Spec"
|
||||
- "OIDCConnectorV3.Metadata.Name"
|
||||
- "OIDCConnectorV3.Version"
|
||||
|
||||
# Okta import rule
|
||||
- "OktaImportRuleV1.Spec"
|
||||
- "OktaImportRuleV1.Metadata.Name"
|
||||
- "OktaImportRuleV1.Version"
|
||||
|
||||
# Provision token
|
||||
- "ProvisionTokenV2.Spec"
|
||||
- "ProvisionTokenV2.Spec.Options"
|
||||
- "ProvisionTokenV2.Spec.Roles"
|
||||
- "ProvisionTokenV2.Version"
|
||||
|
||||
# Role
|
||||
- "RoleV6.Metadata.Name"
|
||||
- "RoleV6.Version"
|
||||
|
||||
# SAML connector
|
||||
- "SAMLConnectorV2.Spec"
|
||||
- "SAMLConnectorV2.Spec.AssertionConsumerService"
|
||||
- "SAMLConnectorV2.Spec.AttributesToRoles"
|
||||
- "SAMLConnectorV2.Metadata.Name"
|
||||
- "SAMLConnectorV2.Version"
|
||||
|
||||
# Server
|
||||
- "ServerV2.Version"
|
||||
- "ServerV2.SubKind"
|
||||
|
||||
# Trusted cluster
|
||||
- "TrustedClusterV2.Metadata.Name"
|
||||
- "TrustedClusterV2.Version"
|
||||
- "TrustedClusterV2.Spec"
|
||||
|
||||
# User
|
||||
- "UserV2.Metadata.Name"
|
||||
- "UserV2.Version"
|
||||
|
||||
- "SessionRecordingConfigV2.Version"
|
||||
- "ClusterMaintenanceConfigV1.Version"
|
||||
- "AuthPreferenceV2.Version"
|
||||
|
||||
# These fields must be marked as sensitive
|
||||
sensitive_fields:
|
||||
- "ProvisionTokenV2.Metadata.Name"
|
||||
- "SAMLConnectorV2.Spec.Cert"
|
||||
- "SAMLConnectorV2.Spec.SigningKeyPair.PrivateKey"
|
||||
- "SAMLConnectorV2.Spec.EncryptionKeyPair.PrivateKey"
|
||||
- "SAMLConnectorV2.Spec.EntityDescriptor"
|
||||
- "GithubConnectorV3.Spec.ClientSecret"
|
||||
- "OIDCConnectorV3.Spec.ClientSecret"
|
||||
- "OIDCConnectorV3.Spec.GoogleServiceAccount"
|
||||
- "TrustedClusterV2.Spec.Token"
|
||||
|
||||
# These suffixes for custom methods called when field has custom_type flag. By default they might be weird.
|
||||
suffixes:
|
||||
"BoolOption": "BoolOption" # This is listed here for convenience. Just to have all custom method names in a single place.
|
||||
"Labels": "Labels"
|
||||
"github.com/gravitational/teleport/api/types/wrappers.Traits": "Traits"
|
||||
"github.com/gravitational/teleport/api/types/wrappers.Strings": "Strings"
|
||||
|
||||
plan_modifiers:
|
||||
# Force to recreate resource if it's name changes
|
||||
Metadata.Name:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()"
|
||||
ProvisionTokenV2.Metadata.Name:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()"
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
ServerV2.Metadata.Name:
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()"
|
||||
- "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()"
|
||||
|
||||
validators:
|
||||
# Expires must be in the future
|
||||
Metadata.Expires:
|
||||
- MustTimeBeInFuture()
|
||||
AppV3.Version:
|
||||
- UseVersionBetween(3,3)
|
||||
AuthPreferenceV2.Version:
|
||||
- UseVersionBetween(2,2)
|
||||
AuthPreferenceV2.Metadata.Labels:
|
||||
- UseMapKeysPresentValidator("teleport.dev/origin")
|
||||
ClusterNetworkingConfigV2.Version:
|
||||
- UseVersionBetween(2,2)
|
||||
ClusterNetworkingConfigV2.Metadata.Labels:
|
||||
- UseMapKeysPresentValidator("teleport.dev/origin")
|
||||
DatabaseV3.Version:
|
||||
- UseVersionBetween(3,3)
|
||||
DatabaseV3.Metadata.Labels:
|
||||
- UseMapKeysPresentValidator("teleport.dev/origin")
|
||||
GithubConnectorV3.Version:
|
||||
- UseVersionBetween(3,3)
|
||||
OIDCConnectorV3.Version:
|
||||
- UseVersionBetween(3,3)
|
||||
OktaImportRuleV1.Version:
|
||||
- UseVersionBetween(1,1)
|
||||
ProvisionTokenV2.Version:
|
||||
- UseVersionBetween(2,2)
|
||||
RoleV6.Version:
|
||||
- UseVersionBetween(3,7)
|
||||
SAMLConnectorV2.Version:
|
||||
- UseVersionBetween(2,2)
|
||||
SAMLConnectorV2.Spec:
|
||||
- UseAnyOfValidator("entity_descriptor", "entity_descriptor_url")
|
||||
ServerV2.Version:
|
||||
- UseVersionBetween(2,2)
|
||||
ServerV2.SubKind:
|
||||
- UseValueIn("openssh", "openssh-ec2-ice")
|
||||
SessionRecordingConfigV2.Version:
|
||||
- UseVersionBetween(2,2)
|
||||
SessionRecordingConfigV2.Metadata.Labels:
|
||||
- UseMapKeysPresentValidator("teleport.dev/origin")
|
||||
UserV2.Version:
|
||||
- UseVersionBetween(2,2)
|
||||
ClusterMaintenanceConfigV1.Version:
|
||||
- UseVersionBetween(1,1)
|
||||
|
||||
time_type:
|
||||
type: "TimeType"
|
||||
value_type: "TimeValue"
|
||||
cast_to_type: "time.Time"
|
||||
cast_from_type: "time.Time"
|
||||
type_constructor: UseRFC3339Time()
|
||||
|
||||
duration_type:
|
||||
type: "DurationType"
|
||||
value_type: "DurationValue"
|
||||
cast_to_type: "time.Duration"
|
||||
cast_from_type: "time.Duration"
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
convert "github.com/gravitational/teleport/api/types/accesslist/convert/v1"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/accesslist/v1"
|
||||
)
|
||||
|
||||
// dataSourceTeleportAccessListType is the data source metadata type
|
||||
type dataSourceTeleportAccessListType struct{}
|
||||
|
||||
// dataSourceTeleportAccessList is the resource
|
||||
type dataSourceTeleportAccessList struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportAccessListType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return schemav1.GenSchemaAccessList(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportAccessListType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportAccessList{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport AccessList
|
||||
func (r dataSourceTeleportAccessList) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
accessListI, err := r.p.Client.AccessListClient().GetAccessList(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
accessList := convert.ToProto(accessListI)
|
||||
|
||||
diags = schemav1.CopyAccessListToTerraform(ctx, accessList, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportAppType is the data source metadata type
|
||||
type dataSourceTeleportAppType struct{}
|
||||
|
||||
// dataSourceTeleportApp is the resource
|
||||
type dataSourceTeleportApp struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportAppType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaAppV3(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportAppType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportApp{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport App
|
||||
func (r dataSourceTeleportApp) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
appI, err := r.p.Client.GetApp(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(err), "app"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
app := appI.(*apitypes.AppV3)
|
||||
diags = tfschema.CopyAppV3ToTerraform(ctx, app, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportAuthPreferenceType is the data source metadata type
|
||||
type dataSourceTeleportAuthPreferenceType struct{}
|
||||
|
||||
// dataSourceTeleportAuthPreference is the resource
|
||||
type dataSourceTeleportAuthPreference struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportAuthPreferenceType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaAuthPreferenceV2(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportAuthPreferenceType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportAuthPreference{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport AuthPreference
|
||||
func (r dataSourceTeleportAuthPreference) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
authPreferenceI, err := r.p.Client.GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
authPreference := authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
diags := tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportClusterMaintenanceConfigType is the data source metadata type
|
||||
type dataSourceTeleportClusterMaintenanceConfigType struct{}
|
||||
|
||||
// dataSourceTeleportClusterMaintenanceConfig is the resource
|
||||
type dataSourceTeleportClusterMaintenanceConfig struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportClusterMaintenanceConfigType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaClusterMaintenanceConfigV1(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportClusterMaintenanceConfigType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportClusterMaintenanceConfig{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport ClusterMaintenanceConfig
|
||||
func (r dataSourceTeleportClusterMaintenanceConfig) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
clusterMaintenanceConfigI, err := r.p.Client.GetClusterMaintenanceConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
clusterMaintenanceConfig := clusterMaintenanceConfigI.(*apitypes.ClusterMaintenanceConfigV1)
|
||||
diags := tfschema.CopyClusterMaintenanceConfigV1ToTerraform(ctx, clusterMaintenanceConfig, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportClusterNetworkingConfigType is the data source metadata type
|
||||
type dataSourceTeleportClusterNetworkingConfigType struct{}
|
||||
|
||||
// dataSourceTeleportClusterNetworkingConfig is the resource
|
||||
type dataSourceTeleportClusterNetworkingConfig struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportClusterNetworkingConfigType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaClusterNetworkingConfigV2(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportClusterNetworkingConfigType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportClusterNetworkingConfig{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport ClusterNetworkingConfig
|
||||
func (r dataSourceTeleportClusterNetworkingConfig) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
clusterNetworkingConfigI, err := r.p.Client.GetClusterNetworkingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
clusterNetworkingConfig := clusterNetworkingConfigI.(*apitypes.ClusterNetworkingConfigV2)
|
||||
diags := tfschema.CopyClusterNetworkingConfigV2ToTerraform(ctx, clusterNetworkingConfig, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportDatabaseType is the data source metadata type
|
||||
type dataSourceTeleportDatabaseType struct{}
|
||||
|
||||
// dataSourceTeleportDatabase is the resource
|
||||
type dataSourceTeleportDatabase struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportDatabaseType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaDatabaseV3(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportDatabaseType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportDatabase{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport Database
|
||||
func (r dataSourceTeleportDatabase) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
databaseI, err := r.p.Client.GetDatabase(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(err), "db"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
database := databaseI.(*apitypes.DatabaseV3)
|
||||
diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/devicetrust/v1"
|
||||
)
|
||||
|
||||
// dataSourceTeleportDeviceV1Type is the data source metadata type
|
||||
type dataSourceTeleportDeviceV1Type struct{}
|
||||
|
||||
// dataSourceTeleportDeviceV1 is the resource
|
||||
type dataSourceTeleportDeviceV1 struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportDeviceV1Type) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return schemav1.GenSchemaDeviceV1(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportDeviceV1Type) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportDeviceV1{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport DeviceV1
|
||||
func (r dataSourceTeleportDeviceV1) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
trustedDeviceI, err := r.p.Client.GetDeviceResource(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
trustedDevice := trustedDeviceI
|
||||
|
||||
diags = schemav1.CopyDeviceV1ToTerraform(ctx, trustedDevice, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportGithubConnectorType is the data source metadata type
|
||||
type dataSourceTeleportGithubConnectorType struct{}
|
||||
|
||||
// dataSourceTeleportGithubConnector is the resource
|
||||
type dataSourceTeleportGithubConnector struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportGithubConnectorType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaGithubConnectorV3(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportGithubConnectorType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportGithubConnector{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport GithubConnector
|
||||
func (r dataSourceTeleportGithubConnector) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
githubConnectorI, err := r.p.Client.GetGithubConnector(ctx, id.Value, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Wrap(err), "github"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
githubConnector := githubConnectorI.(*apitypes.GithubConnectorV3)
|
||||
diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/loginrule/v1"
|
||||
)
|
||||
|
||||
// dataSourceTeleportLoginRuleType is the data source metadata type
|
||||
type dataSourceTeleportLoginRuleType struct{}
|
||||
|
||||
// dataSourceTeleportLoginRule is the resource
|
||||
type dataSourceTeleportLoginRule struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportLoginRuleType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return schemav1.GenSchemaLoginRule(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportLoginRuleType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportLoginRule{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport LoginRule
|
||||
func (r dataSourceTeleportLoginRule) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
loginRuleI, err := r.p.Client.GetLoginRule(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(err), "login_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
loginRule := loginRuleI
|
||||
|
||||
diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportOIDCConnectorType is the data source metadata type
|
||||
type dataSourceTeleportOIDCConnectorType struct{}
|
||||
|
||||
// dataSourceTeleportOIDCConnector is the resource
|
||||
type dataSourceTeleportOIDCConnector struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportOIDCConnectorType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaOIDCConnectorV3(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportOIDCConnectorType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportOIDCConnector{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport OIDCConnector
|
||||
func (r dataSourceTeleportOIDCConnector) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
oidcConnectorI, err := r.p.Client.GetOIDCConnector(ctx, id.Value, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Wrap(err), "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
oidcConnector := oidcConnectorI.(*apitypes.OIDCConnectorV3)
|
||||
diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportOktaImportRuleType is the data source metadata type
|
||||
type dataSourceTeleportOktaImportRuleType struct{}
|
||||
|
||||
// dataSourceTeleportOktaImportRule is the resource
|
||||
type dataSourceTeleportOktaImportRule struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportOktaImportRuleType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaOktaImportRuleV1(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportOktaImportRuleType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportOktaImportRule{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport OktaImportRule
|
||||
func (r dataSourceTeleportOktaImportRule) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
oktaImportRuleI, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Wrap(err), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
oktaImportRule := oktaImportRuleI.(*apitypes.OktaImportRuleV1)
|
||||
diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportProvisionTokenType is the data source metadata type
|
||||
type dataSourceTeleportProvisionTokenType struct{}
|
||||
|
||||
// dataSourceTeleportProvisionToken is the resource
|
||||
type dataSourceTeleportProvisionToken struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportProvisionTokenType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaProvisionTokenV2(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportProvisionTokenType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportProvisionToken{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport ProvisionToken
|
||||
func (r dataSourceTeleportProvisionToken) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
provisionTokenI, err := r.p.Client.GetToken(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(err), "token"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
provisionToken := provisionTokenI.(*apitypes.ProvisionTokenV2)
|
||||
diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportRoleType is the data source metadata type
|
||||
type dataSourceTeleportRoleType struct{}
|
||||
|
||||
// dataSourceTeleportRole is the resource
|
||||
type dataSourceTeleportRole struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportRoleType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaRoleV6(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportRoleType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportRole{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport Role
|
||||
func (r dataSourceTeleportRole) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
roleI, err := r.p.Client.GetRole(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(err), "role"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
role := roleI.(*apitypes.RoleV6)
|
||||
diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportSAMLConnectorType is the data source metadata type
|
||||
type dataSourceTeleportSAMLConnectorType struct{}
|
||||
|
||||
// dataSourceTeleportSAMLConnector is the resource
|
||||
type dataSourceTeleportSAMLConnector struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportSAMLConnectorType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaSAMLConnectorV2(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportSAMLConnectorType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportSAMLConnector{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport SAMLConnector
|
||||
func (r dataSourceTeleportSAMLConnector) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
samlConnectorI, err := r.p.Client.GetSAMLConnector(ctx, id.Value, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Wrap(err), "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
samlConnector := samlConnectorI.(*apitypes.SAMLConnectorV2)
|
||||
diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/gravitational/teleport/api/defaults"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportServerType is the data source metadata type
|
||||
type dataSourceTeleportServerType struct{}
|
||||
|
||||
// dataSourceTeleportServer is the resource
|
||||
type dataSourceTeleportServer struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportServerType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaServerV2(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportServerType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportServer{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport Server
|
||||
func (r dataSourceTeleportServer) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
serverI, err := r.p.Client.GetNode(ctx, defaults.Namespace, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", trace.Wrap(err), "node"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
server := serverI.(*apitypes.ServerV2)
|
||||
diags = tfschema.CopyServerV2ToTerraform(ctx, server, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportSessionRecordingConfigType is the data source metadata type
|
||||
type dataSourceTeleportSessionRecordingConfigType struct{}
|
||||
|
||||
// dataSourceTeleportSessionRecordingConfig is the resource
|
||||
type dataSourceTeleportSessionRecordingConfig struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportSessionRecordingConfigType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaSessionRecordingConfigV2(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportSessionRecordingConfigType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportSessionRecordingConfig{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport SessionRecordingConfig
|
||||
func (r dataSourceTeleportSessionRecordingConfig) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
sessionRecordingConfigI, err := r.p.Client.GetSessionRecordingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
sessionRecordingConfig := sessionRecordingConfigI.(*apitypes.SessionRecordingConfigV2)
|
||||
diags := tfschema.CopySessionRecordingConfigV2ToTerraform(ctx, sessionRecordingConfig, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportTrustedClusterType is the data source metadata type
|
||||
type dataSourceTeleportTrustedClusterType struct{}
|
||||
|
||||
// dataSourceTeleportTrustedCluster is the resource
|
||||
type dataSourceTeleportTrustedCluster struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportTrustedClusterType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaTrustedClusterV2(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportTrustedClusterType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportTrustedCluster{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport TrustedCluster
|
||||
func (r dataSourceTeleportTrustedCluster) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
trustedClusterI, err := r.p.Client.GetTrustedCluster(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Wrap(err), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
trustedCluster := trustedClusterI.(*apitypes.TrustedClusterV2)
|
||||
diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// dataSourceTeleportUserType is the data source metadata type
|
||||
type dataSourceTeleportUserType struct{}
|
||||
|
||||
// dataSourceTeleportUser is the resource
|
||||
type dataSourceTeleportUser struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportUserType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaUserV2(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportUserType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportUser{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport User
|
||||
func (r dataSourceTeleportUser) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
var id types.String
|
||||
diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
userI, err := r.p.Client.GetUser(ctx, id.Value, false)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(err), "user"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
|
||||
user := userI.(*apitypes.UserV2)
|
||||
diags = tfschema.CopyUserV2ToTerraform(ctx, user, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// diagFromWrappedErr wraps error with additional information
|
||||
func diagFromWrappedErr(summary string, err error, kind string) diag.Diagnostic {
|
||||
var notFoundErr = "Terraform user has no rights to perform this action. Check that Terraform user role has " +
|
||||
"['list','create','read','update','delete'] verbs for '" + kind + "' resource."
|
||||
|
||||
var accessDeniedErr = "Terraform user is missing on the Teleport side. Check that your auth credentials (certs) " +
|
||||
"specified in provider configuration belong to existing user and are not expired."
|
||||
|
||||
if trace.IsNotFound(err) {
|
||||
return diagFromErr(summary, trace.WrapWithMessage(err, notFoundErr))
|
||||
}
|
||||
|
||||
if trace.IsAccessDenied(err) {
|
||||
return diagFromErr(summary, trace.WrapWithMessage(err, accessDeniedErr))
|
||||
}
|
||||
|
||||
return diagFromErr(summary, trace.Wrap(err))
|
||||
}
|
||||
|
||||
// diagFromErr converts error to diag.Diagnostics. If logging level is debug, provides trace.DebugReport instead of short text.
|
||||
func diagFromErr(summary string, err error) diag.Diagnostic {
|
||||
if log.GetLevel() >= log.DebugLevel {
|
||||
return diag.NewErrorDiagnostic(err.Error(), trace.DebugReport(err))
|
||||
}
|
||||
|
||||
return diag.NewErrorDiagnostic(summary, err.Error())
|
||||
}
|
||||
@@ -0,0 +1,609 @@
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport/api/client"
|
||||
"github.com/gravitational/teleport/integrations/lib"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
|
||||
const (
|
||||
// minServerVersion is the minimal teleport version the plugin supports.
|
||||
minServerVersion = "15.0.0-0"
|
||||
)
|
||||
|
||||
type RetryConfig struct {
|
||||
Base time.Duration
|
||||
Cap time.Duration
|
||||
MaxTries int
|
||||
}
|
||||
|
||||
// Provider Teleport Provider
|
||||
type Provider struct {
|
||||
configured bool
|
||||
Client *client.Client
|
||||
RetryConfig RetryConfig
|
||||
}
|
||||
|
||||
// providerData provider schema struct
|
||||
type providerData struct {
|
||||
// Addr Teleport address
|
||||
Addr types.String `tfsdk:"addr"`
|
||||
// CertPath path to TLS certificate file
|
||||
CertPath types.String `tfsdk:"cert_path"`
|
||||
// CertBase64 base64 encoded TLS certificate file
|
||||
CertBase64 types.String `tfsdk:"cert_base64"`
|
||||
// KeyPath path to TLS private key file
|
||||
KeyPath types.String `tfsdk:"key_path"`
|
||||
// KeyBase64 base64 encoded TLS private key
|
||||
KeyBase64 types.String `tfsdk:"key_base64"`
|
||||
// RootCAPath path to TLS root CA certificate file
|
||||
RootCaPath types.String `tfsdk:"root_ca_path"`
|
||||
// RootCaPath base64 encoded root CA certificate
|
||||
RootCaBase64 types.String `tfsdk:"root_ca_base64"`
|
||||
// ProfileName Teleport profile name
|
||||
ProfileName types.String `tfsdk:"profile_name"`
|
||||
// ProfileDir Teleport profile dir
|
||||
ProfileDir types.String `tfsdk:"profile_dir"`
|
||||
// IdentityFilePath path to identity file
|
||||
IdentityFilePath types.String `tfsdk:"identity_file_path"`
|
||||
// IdentityFile identity file content
|
||||
IdentityFile types.String `tfsdk:"identity_file"`
|
||||
// IdentityFile identity file content encoded in base64
|
||||
IdentityFileBase64 types.String `tfsdk:"identity_file_base64"`
|
||||
// RetryBaseDuration is used to setup the retry algorithm when the API returns 'not found'
|
||||
RetryBaseDuration types.String `tfsdk:"retry_base_duration"`
|
||||
// RetryCapDuration is used to setup the retry algorithm when the API returns 'not found'
|
||||
RetryCapDuration types.String `tfsdk:"retry_cap_duration"`
|
||||
// RetryMaxTries sets the max number of tries when retrying
|
||||
RetryMaxTries types.String `tfsdk:"retry_max_tries"`
|
||||
// DialTimeout sets timeout when trying to connect to the server.
|
||||
DialTimeoutDuration types.String `tfsdk:"dial_timeout_duration"`
|
||||
}
|
||||
|
||||
// New returns an empty provider struct
|
||||
func New() tfsdk.Provider {
|
||||
return &Provider{}
|
||||
}
|
||||
|
||||
// GetSchema returns the Terraform provider schema
|
||||
func (p *Provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfsdk.Schema{
|
||||
Attributes: map[string]tfsdk.Attribute{
|
||||
"addr": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Description: "host:port where Teleport Auth server is running.",
|
||||
},
|
||||
"cert_path": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Description: "Path to Teleport auth certificate file.",
|
||||
},
|
||||
"cert_base64": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Description: "Base64 encoded TLS auth certificate.",
|
||||
},
|
||||
"key_path": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Description: "Path to Teleport auth key file.",
|
||||
},
|
||||
"key_base64": {
|
||||
Type: types.StringType,
|
||||
Sensitive: true,
|
||||
Optional: true,
|
||||
Description: "Base64 encoded TLS auth key.",
|
||||
},
|
||||
"root_ca_path": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Description: "Path to Teleport Root CA.",
|
||||
},
|
||||
"root_ca_base64": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Description: "Base64 encoded Root CA.",
|
||||
},
|
||||
"profile_name": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Description: "Teleport profile name.",
|
||||
},
|
||||
"profile_dir": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Description: "Teleport profile path.",
|
||||
},
|
||||
"identity_file_path": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Description: "Teleport identity file path.",
|
||||
},
|
||||
"identity_file": {
|
||||
Type: types.StringType,
|
||||
Sensitive: true,
|
||||
Optional: true,
|
||||
Description: "Teleport identity file content.",
|
||||
},
|
||||
"identity_file_base64": {
|
||||
Type: types.StringType,
|
||||
Sensitive: true,
|
||||
Optional: true,
|
||||
Description: "Teleport identity file content base64 encoded.",
|
||||
},
|
||||
"retry_base_duration": {
|
||||
Type: types.StringType,
|
||||
Sensitive: false,
|
||||
Optional: true,
|
||||
Description: "Retry algorithm when the API returns 'not found': base duration between retries (https://pkg.go.dev/time#ParseDuration).",
|
||||
},
|
||||
"retry_cap_duration": {
|
||||
Type: types.StringType,
|
||||
Sensitive: false,
|
||||
Optional: true,
|
||||
Description: "Retry algorithm when the API returns 'not found': max duration between retries (https://pkg.go.dev/time#ParseDuration).",
|
||||
},
|
||||
"retry_max_tries": {
|
||||
Type: types.StringType,
|
||||
Sensitive: false,
|
||||
Optional: true,
|
||||
Description: "Retry algorithm when the API returns 'not found': max tries.",
|
||||
},
|
||||
"dial_timeout_duration": {
|
||||
Type: types.StringType,
|
||||
Sensitive: false,
|
||||
Optional: true,
|
||||
Description: "DialTimeout sets timeout when trying to connect to the server.",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsConfigured checks if provider is configured, adds diagnostics if not
|
||||
func (p *Provider) IsConfigured(diags diag.Diagnostics) bool {
|
||||
if !p.configured {
|
||||
diags.AddError(
|
||||
"Provider not configured",
|
||||
"The provider hasn't been configured before apply, likely because it depends on an unknown value from another resource. This leads to weird stuff happening, so we'd prefer if you didn't do that. Thanks!",
|
||||
)
|
||||
}
|
||||
|
||||
return p.configured
|
||||
}
|
||||
|
||||
// Configure configures the Teleport client
|
||||
func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderRequest, resp *tfsdk.ConfigureProviderResponse) {
|
||||
var creds []client.Credentials
|
||||
|
||||
p.configureLog()
|
||||
|
||||
var config providerData
|
||||
diags := req.Config.Get(ctx, &config)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
addr := p.stringFromConfigOrEnv(config.Addr, "TF_TELEPORT_ADDR", "")
|
||||
certPath := p.stringFromConfigOrEnv(config.CertPath, "TF_TELEPORT_CERT", "")
|
||||
certBase64 := p.stringFromConfigOrEnv(config.CertBase64, "TF_TELEPORT_CERT_BASE64", "")
|
||||
keyPath := p.stringFromConfigOrEnv(config.KeyPath, "TF_TELEPORT_KEY", "")
|
||||
keyBase64 := p.stringFromConfigOrEnv(config.KeyBase64, "TF_TELEPORT_KEY_BASE64", "")
|
||||
caPath := p.stringFromConfigOrEnv(config.RootCaPath, "TF_TELEPORT_ROOT_CA", "")
|
||||
caBase64 := p.stringFromConfigOrEnv(config.RootCaBase64, "TF_TELEPORT_CA_BASE64", "")
|
||||
profileName := p.stringFromConfigOrEnv(config.ProfileName, "TF_TELEPORT_PROFILE_NAME", "")
|
||||
profileDir := p.stringFromConfigOrEnv(config.ProfileDir, "TF_TELEPORT_PROFILE_PATH", "")
|
||||
identityFilePath := p.stringFromConfigOrEnv(config.IdentityFilePath, "TF_TELEPORT_IDENTITY_FILE_PATH", "")
|
||||
identityFile := p.stringFromConfigOrEnv(config.IdentityFile, "TF_TELEPORT_IDENTITY_FILE", "")
|
||||
identityFileBase64 := p.stringFromConfigOrEnv(config.IdentityFileBase64, "TF_TELEPORT_IDENTITY_FILE_BASE64", "")
|
||||
retryBaseDurationStr := p.stringFromConfigOrEnv(config.RetryBaseDuration, "TF_TELEPORT_RETRY_BASE_DURATION", "1s")
|
||||
retryCapDurationStr := p.stringFromConfigOrEnv(config.RetryCapDuration, "TF_TELEPORT_RETRY_CAP_DURATION", "5s")
|
||||
maxTriesStr := p.stringFromConfigOrEnv(config.RetryMaxTries, "TF_TELEPORT_RETRY_MAX_TRIES", "10")
|
||||
dialTimeoutDurationStr := p.stringFromConfigOrEnv(config.DialTimeoutDuration, "TF_TELEPORT_DIAL_TIMEOUT_DURATION", "30s")
|
||||
|
||||
if !p.validateAddr(addr, resp) {
|
||||
return
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{"addr": addr}).Debug("Using Teleport address")
|
||||
|
||||
if certPath != "" && keyPath != "" {
|
||||
l := log.WithField("cert_path", certPath).WithField("key_path", keyPath).WithField("root_ca_path", caPath)
|
||||
l.Debug("Using auth with certificate, private key and (optionally) CA read from files")
|
||||
|
||||
cred, ok := p.getCredentialsFromKeyPair(certPath, keyPath, caPath, resp)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
creds = append(creds, cred)
|
||||
}
|
||||
|
||||
if certBase64 != "" && keyBase64 != "" {
|
||||
log.Debug("Using auth with certificate, private key and (optionally) CA read from base64 encoded vars")
|
||||
cred, ok := p.getCredentialsFromBase64(certBase64, keyBase64, caBase64, resp)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
creds = append(creds, cred)
|
||||
}
|
||||
|
||||
if identityFilePath != "" {
|
||||
log.WithField("identity_file_path", identityFilePath).Debug("Using auth with identity file")
|
||||
|
||||
if !p.fileExists(identityFilePath) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Identity file not found",
|
||||
fmt.Sprintf(
|
||||
"File %v not found! Use `tctl auth sign [email protected] --format=file --out=%v` to generate identity file",
|
||||
identityFilePath,
|
||||
identityFilePath,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
creds = append(creds, client.LoadIdentityFile(identityFilePath))
|
||||
}
|
||||
|
||||
if identityFile != "" {
|
||||
log.Debug("Using auth from identity file provided with environment variable TF_TELEPORT_IDENTITY_FILE")
|
||||
creds = append(creds, client.LoadIdentityFileFromString(identityFile))
|
||||
}
|
||||
|
||||
if identityFileBase64 != "" {
|
||||
log.Debug("Using auth from base64 encoded identity file provided with environment variable TF_TELEPORT_IDENTITY_FILE_BASE64")
|
||||
decoded, err := base64.StdEncoding.DecodeString(identityFileBase64)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Failed to decode Identity file using base 64",
|
||||
fmt.Sprintf("Error when trying to decode: %v", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
creds = append(creds, client.LoadIdentityFileFromString(string(decoded)))
|
||||
}
|
||||
|
||||
if profileDir != "" || len(creds) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"dir": profileDir,
|
||||
"name": profileName,
|
||||
}).Debug("Using profile as the default auth method")
|
||||
creds = append(creds, client.LoadProfile(profileDir, profileName))
|
||||
}
|
||||
|
||||
dialTimeoutDuration, err := time.ParseDuration(dialTimeoutDurationStr)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Failed to parse Dial Timeout Duration Cap Duration",
|
||||
fmt.Sprintf("Please check if dial_timeout_duration (or TF_TELEPORT_DIAL_TIMEOUT_DURATION) is set correctly. Error: %s", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
client, err := client.New(ctx, client.Config{
|
||||
Addrs: []string{addr},
|
||||
Credentials: creds,
|
||||
DialTimeout: dialTimeoutDuration,
|
||||
DialOpts: []grpc.DialOption{
|
||||
grpc.WithReturnConnectionError(),
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.WaitForReady(true),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Error connecting to Teleport!")
|
||||
resp.Diagnostics.AddError("Error connecting to Teleport!", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !p.checkTeleportVersion(ctx, client, resp) {
|
||||
return
|
||||
}
|
||||
|
||||
retryBaseDuration, err := time.ParseDuration(retryBaseDurationStr)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Failed to parse Retry Base Duration",
|
||||
fmt.Sprintf("Please check if retry_cap_duration (or TF_TELEPORT_RETRY_BASE_DURATION) is set correctly. Error: %s", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
retryCapDuration, err := time.ParseDuration(retryCapDurationStr)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Failed to parse Retry Cap Duration",
|
||||
fmt.Sprintf("Please check if retry_cap_duration (or TF_TELEPORT_RETRY_CAP_DURATION) is set correctly. Error: %s", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
maxTries, err := strconv.ParseUint(maxTriesStr, 10, 32)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Failed to parse Retry Max Tries",
|
||||
fmt.Sprintf("Please check if retry_max_tries (or TF_TELEPORT_RETRY_MAX_TRIES) is set correctly. Error: %s", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
p.RetryConfig = RetryConfig{
|
||||
Base: retryBaseDuration,
|
||||
Cap: retryCapDuration,
|
||||
MaxTries: int(maxTries),
|
||||
}
|
||||
p.Client = client
|
||||
p.configured = true
|
||||
}
|
||||
|
||||
// checkTeleportVersion ensures that Teleport version is at least minServerVersion
|
||||
func (p *Provider) checkTeleportVersion(ctx context.Context, client *client.Client, resp *tfsdk.ConfigureProviderResponse) bool {
|
||||
log.Debug("Checking Teleport server version")
|
||||
pong, err := client.Ping(ctx)
|
||||
if err != nil {
|
||||
if trace.IsNotImplemented(err) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Teleport version is too old!",
|
||||
fmt.Sprintf("Server version must be at least %s", minServerVersion),
|
||||
)
|
||||
return false
|
||||
}
|
||||
log.WithError(err).Debug("Teleport version check error!")
|
||||
resp.Diagnostics.AddError("Unable to get Teleport server version!", "Unable to get Teleport server version!")
|
||||
return false
|
||||
}
|
||||
err = lib.AssertServerVersion(pong, minServerVersion)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Teleport version check error!")
|
||||
resp.Diagnostics.AddError("Teleport version check error!", err.Error())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// stringFromConfigOrEnv returns value from config or from env var if config value is empty, default otherwise
|
||||
func (p *Provider) stringFromConfigOrEnv(value types.String, env string, def string) string {
|
||||
if value.Unknown || value.Null {
|
||||
value := os.Getenv(env)
|
||||
if value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
configValue := strings.TrimSpace(value.Value)
|
||||
|
||||
if configValue == "" {
|
||||
return def
|
||||
}
|
||||
|
||||
return configValue
|
||||
}
|
||||
|
||||
// validateAddr validates passed addr
|
||||
func (p *Provider) validateAddr(addr string, resp *tfsdk.ConfigureProviderResponse) bool {
|
||||
if addr == "" {
|
||||
resp.Diagnostics.AddError(
|
||||
"Teleport address is empty",
|
||||
"Please, specify either TF_TELEPORT_ADDR or addr in provider configuration",
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
_, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
log.WithField("addr", addr).WithError(err).Debug("Teleport addr format error!")
|
||||
resp.Diagnostics.AddError(
|
||||
"Invalid Teleport addr format",
|
||||
"Teleport addr must be specified as host:port",
|
||||
)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// getCredentialsFromBase64 returns client.Credentials built from base64 encoded keys
|
||||
func (p *Provider) getCredentialsFromBase64(certBase64, keyBase64, caBase64 string, resp *tfsdk.ConfigureProviderResponse) (client.Credentials, bool) {
|
||||
cert, err := base64.StdEncoding.DecodeString(certBase64)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Failed to base64 decode cert",
|
||||
fmt.Sprintf("Please check if cert_base64 (or TF_TELEPORT_CERT_BASE64) is set correctly. Error: %s", err),
|
||||
)
|
||||
return nil, false
|
||||
}
|
||||
key, err := base64.StdEncoding.DecodeString(keyBase64)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Failed to base64 decode key",
|
||||
fmt.Sprintf("Please check if key_base64 (or TF_TELEPORT_KEY_BASE64) is set correctly. Error: %s", err),
|
||||
)
|
||||
return nil, false
|
||||
}
|
||||
rootCa, err := base64.StdEncoding.DecodeString(caBase64)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Failed to base64 decode root ca",
|
||||
fmt.Sprintf("Please check if root_ca_base64 (or TF_TELEPORT_CA_BASE64) is set correctly. Error: %s", err),
|
||||
)
|
||||
return nil, false
|
||||
}
|
||||
tlsConfig, err := createTLSConfig(cert, key, rootCa)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Failed to create TLS config",
|
||||
fmt.Sprintf("Error: %s", err),
|
||||
)
|
||||
return nil, false
|
||||
}
|
||||
return client.LoadTLS(tlsConfig), true
|
||||
}
|
||||
|
||||
// getCredentialsFromKeyPair returns client.Credentials built from path to key files
|
||||
func (p *Provider) getCredentialsFromKeyPair(certPath string, keyPath string, caPath string, resp *tfsdk.ConfigureProviderResponse) (client.Credentials, bool) {
|
||||
if !p.fileExists(certPath) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Certificate file not found",
|
||||
fmt.Sprintf("File %v not found! Use 'tctl auth sign [email protected] --format=tls --out=%v' to generate keys",
|
||||
certPath,
|
||||
filepath.Dir(certPath),
|
||||
),
|
||||
)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if !p.fileExists(keyPath) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Private key file not found",
|
||||
fmt.Sprintf("File %v not found! Use 'tctl auth sign [email protected] --format=tls --out=%v' to generate keys",
|
||||
keyPath,
|
||||
filepath.Dir(keyPath),
|
||||
),
|
||||
)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if !p.fileExists(caPath) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Root CA certificate file not found",
|
||||
fmt.Sprintf("File %v not found! Use 'tctl auth sign [email protected] --format=tls --out=%v' to generate keys",
|
||||
caPath,
|
||||
filepath.Dir(caPath),
|
||||
),
|
||||
)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return client.LoadKeyPair(certPath, keyPath, caPath), true
|
||||
}
|
||||
|
||||
// fileExists returns true if file exists
|
||||
func (p *Provider) fileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// configureLog configures logging
|
||||
func (p *Provider) configureLog() {
|
||||
// Get Terraform log level
|
||||
level, err := log.ParseLevel(os.Getenv("TF_LOG"))
|
||||
if err != nil {
|
||||
log.SetLevel(log.ErrorLevel)
|
||||
} else {
|
||||
log.SetLevel(level)
|
||||
}
|
||||
|
||||
log.SetFormatter(&log.TextFormatter{})
|
||||
|
||||
// Show GRPC debug logs only if TF_LOG=DEBUG
|
||||
if log.GetLevel() >= log.DebugLevel {
|
||||
l := grpclog.NewLoggerV2(log.StandardLogger().Out, log.StandardLogger().Out, log.StandardLogger().Out)
|
||||
grpclog.SetLoggerV2(l)
|
||||
}
|
||||
}
|
||||
|
||||
// createTLSConfig returns tls.Config build from keys
|
||||
func createTLSConfig(cert, key, rootCa []byte) (*tls.Config, error) {
|
||||
keyPair, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(rootCa)
|
||||
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{keyPair},
|
||||
RootCAs: caCertPool,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetResources returns the map of provider resources
|
||||
func (p *Provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceType, diag.Diagnostics) {
|
||||
return map[string]tfsdk.ResourceType{
|
||||
"teleport_app": resourceTeleportAppType{},
|
||||
"teleport_auth_preference": resourceTeleportAuthPreferenceType{},
|
||||
"teleport_cluster_maintenance_config": resourceTeleportClusterMaintenanceConfigType{},
|
||||
"teleport_cluster_networking_config": resourceTeleportClusterNetworkingConfigType{},
|
||||
"teleport_database": resourceTeleportDatabaseType{},
|
||||
"teleport_github_connector": resourceTeleportGithubConnectorType{},
|
||||
"teleport_provision_token": resourceTeleportProvisionTokenType{},
|
||||
"teleport_oidc_connector": resourceTeleportOIDCConnectorType{},
|
||||
"teleport_role": resourceTeleportRoleType{},
|
||||
"teleport_saml_connector": resourceTeleportSAMLConnectorType{},
|
||||
"teleport_session_recording_config": resourceTeleportSessionRecordingConfigType{},
|
||||
"teleport_trusted_cluster": resourceTeleportTrustedClusterType{},
|
||||
"teleport_user": resourceTeleportUserType{},
|
||||
"teleport_bot": resourceTeleportBotType{},
|
||||
"teleport_login_rule": resourceTeleportLoginRuleType{},
|
||||
"teleport_trusted_device": resourceTeleportDeviceV1Type{},
|
||||
"teleport_okta_import_rule": resourceTeleportOktaImportRuleType{},
|
||||
"teleport_access_list": resourceTeleportAccessListType{},
|
||||
"teleport_server": resourceTeleportServerType{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetDataSources returns the map of provider data sources
|
||||
func (p *Provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) {
|
||||
return map[string]tfsdk.DataSourceType{
|
||||
"teleport_app": dataSourceTeleportAppType{},
|
||||
"teleport_auth_preference": dataSourceTeleportAuthPreferenceType{},
|
||||
"teleport_cluster_maintenance_config": dataSourceTeleportClusterMaintenanceConfigType{},
|
||||
"teleport_cluster_networking_config": dataSourceTeleportClusterNetworkingConfigType{},
|
||||
"teleport_database": dataSourceTeleportDatabaseType{},
|
||||
"teleport_github_connector": dataSourceTeleportGithubConnectorType{},
|
||||
"teleport_provision_token": dataSourceTeleportProvisionTokenType{},
|
||||
"teleport_oidc_connector": dataSourceTeleportOIDCConnectorType{},
|
||||
"teleport_role": dataSourceTeleportRoleType{},
|
||||
"teleport_saml_connector": dataSourceTeleportSAMLConnectorType{},
|
||||
"teleport_session_recording_config": dataSourceTeleportSessionRecordingConfigType{},
|
||||
"teleport_trusted_cluster": dataSourceTeleportTrustedClusterType{},
|
||||
"teleport_user": dataSourceTeleportUserType{},
|
||||
"teleport_login_rule": dataSourceTeleportLoginRuleType{},
|
||||
"teleport_trusted_device": dataSourceTeleportDeviceV1Type{},
|
||||
"teleport_okta_import_rule": dataSourceTeleportOktaImportRuleType{},
|
||||
"teleport_access_list": dataSourceTeleportAccessListType{},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
accesslist "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1"
|
||||
convert "github.com/gravitational/teleport/api/types/accesslist/convert/v1"
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/accesslist/v1"
|
||||
)
|
||||
|
||||
// resourceTeleportAccessListType is the resource metadata type
|
||||
type resourceTeleportAccessListType struct{}
|
||||
|
||||
// resourceTeleportAccessList is the resource
|
||||
type resourceTeleportAccessList struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportAccessListType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return schemav1.GenSchemaAccessList(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportAccessListType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportAccessList{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the AccessList
|
||||
func (r resourceTeleportAccessList) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
accessList := &accesslist.AccessList{}
|
||||
diags = schemav1.CopyAccessListFromTerraform(ctx, plan, accessList)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
accessListResource, err := convert.FromProto(accessList)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Errorf("Can not convert %T to AccessList: %s", accessListResource, err), "access_list"))
|
||||
return
|
||||
}
|
||||
err = accessListResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting AccessList defaults", trace.Wrap(err), "access_list"))
|
||||
return
|
||||
}
|
||||
|
||||
id := accessListResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.AccessListClient().GetAccessList(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("AccessList exists in Teleport. Either remove it (tctl rm access_list/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_access_list.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("AccessList exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.AccessListClient().UpsertAccessList(ctx, accessListResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating AccessList", trace.Wrap(err), "access_list"))
|
||||
return
|
||||
}
|
||||
var accessListI = accessListResource
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
accessListI, err = r.p.Client.AccessListClient().GetAccessList(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(bErr), "access_list"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading AccessList (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "access_list")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list"))
|
||||
return
|
||||
}
|
||||
|
||||
accessListResource = accessListI
|
||||
|
||||
accessList = convert.ToProto(accessListResource)
|
||||
|
||||
|
||||
diags = schemav1.CopyAccessListToTerraform(ctx, accessList, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: accessList.Header.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport AccessList
|
||||
func (r resourceTeleportAccessList) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
accessListI, err := r.p.Client.AccessListClient().GetAccessList(ctx, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list"))
|
||||
return
|
||||
}
|
||||
accessList := convert.ToProto(accessListI)
|
||||
diags = schemav1.CopyAccessListToTerraform(ctx, accessList, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport AccessList
|
||||
func (r resourceTeleportAccessList) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
accessList := &accesslist.AccessList{}
|
||||
diags = schemav1.CopyAccessListFromTerraform(ctx, plan, accessList)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
accessListResource, err := convert.FromProto(accessList)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Errorf("Can not convert %T to AccessList: %s", accessListResource, err), "access_list"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := accessListResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating AccessList", err, "access_list"))
|
||||
return
|
||||
}
|
||||
name := accessListResource.Metadata.Name
|
||||
|
||||
accessListBefore, err := r.p.Client.AccessListClient().GetAccessList(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", err, "access_list"))
|
||||
return
|
||||
}
|
||||
accessListResource.Spec.Audit.NextAuditDate = accessListBefore.Spec.Audit.NextAuditDate
|
||||
|
||||
_, err = r.p.Client.AccessListClient().UpsertAccessList(ctx, accessListResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating AccessList", err, "access_list"))
|
||||
return
|
||||
}
|
||||
var accessListI = accessListResource
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
accessListI, err = r.p.Client.AccessListClient().GetAccessList(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", err, "access_list"))
|
||||
return
|
||||
}
|
||||
if accessListBefore.GetMetadata().Revision != accessListI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading AccessList (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "access_list")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
accessListResource = accessListI
|
||||
|
||||
diags = schemav1.CopyAccessListToTerraform(ctx, accessList, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport AccessList
|
||||
func (r resourceTeleportAccessList) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("header").AtName("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.AccessListClient().DeleteAccessList(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting AccessList", trace.Wrap(err), "access_list"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports AccessList state
|
||||
func (r resourceTeleportAccessList) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
accessList, err := r.p.Client.AccessListClient().GetAccessList(ctx, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AccessList", trace.Wrap(err), "access_list"))
|
||||
return
|
||||
}
|
||||
|
||||
accessListResource := convert.ToProto(accessList)
|
||||
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = schemav1.CopyAccessListToTerraform(ctx, accessListResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := accessList.Metadata.Name
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportAppType is the resource metadata type
|
||||
type resourceTeleportAppType struct{}
|
||||
|
||||
// resourceTeleportApp is the resource
|
||||
type resourceTeleportApp struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportAppType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaAppV3(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportAppType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportApp{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the App
|
||||
func (r resourceTeleportApp) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
app := &apitypes.AppV3{}
|
||||
diags = tfschema.CopyAppV3FromTerraform(ctx, plan, app)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
appResource := app
|
||||
|
||||
err = appResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting App defaults", trace.Wrap(err), "app"))
|
||||
return
|
||||
}
|
||||
|
||||
id := appResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetApp(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("App exists in Teleport. Either remove it (tctl rm app/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_app.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("App exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(err), "app"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.CreateApp(ctx, appResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating App", trace.Wrap(err), "app"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var appI apitypes.Application
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
appI, err = r.p.Client.GetApp(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(bErr), "app"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading App (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "app")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(err), "app"))
|
||||
return
|
||||
}
|
||||
|
||||
appResource, ok := appI.(*apitypes.AppV3)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Errorf("Can not convert %T to AppV3", appI), "app"))
|
||||
return
|
||||
}
|
||||
app = appResource
|
||||
|
||||
diags = tfschema.CopyAppV3ToTerraform(ctx, app, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: app.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport App
|
||||
func (r resourceTeleportApp) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
appI, err := r.p.Client.GetApp(ctx, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(err), "app"))
|
||||
return
|
||||
}
|
||||
|
||||
app := appI.(*apitypes.AppV3)
|
||||
diags = tfschema.CopyAppV3ToTerraform(ctx, app, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport App
|
||||
func (r resourceTeleportApp) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
app := &apitypes.AppV3{}
|
||||
diags = tfschema.CopyAppV3FromTerraform(ctx, plan, app)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
appResource := app
|
||||
|
||||
|
||||
if err := appResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating App", err, "app"))
|
||||
return
|
||||
}
|
||||
name := appResource.Metadata.Name
|
||||
|
||||
appBefore, err := r.p.Client.GetApp(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", err, "app"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.UpdateApp(ctx, appResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating App", err, "app"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var appI apitypes.Application
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
appI, err = r.p.Client.GetApp(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", err, "app"))
|
||||
return
|
||||
}
|
||||
if appBefore.GetMetadata().Revision != appI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(err), "app"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading App (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "app")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
appResource, ok := appI.(*apitypes.AppV3)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Errorf("Can not convert %T to AppV3", appI), "app"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyAppV3ToTerraform(ctx, app, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport App
|
||||
func (r resourceTeleportApp) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteApp(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting AppV3", trace.Wrap(err), "app"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports App state
|
||||
func (r resourceTeleportApp) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
app, err := r.p.Client.GetApp(ctx, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(err), "app"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
appResource := app.(*apitypes.AppV3)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyAppV3ToTerraform(ctx, appResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := appResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportAuthPreferenceType is the resource metadata type
|
||||
type resourceTeleportAuthPreferenceType struct{}
|
||||
|
||||
// resourceTeleportAuthPreference is the resource
|
||||
type resourceTeleportAuthPreference struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportAuthPreferenceType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaAuthPreferenceV2(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportAuthPreferenceType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportAuthPreference{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the AuthPreference
|
||||
func (r resourceTeleportAuthPreference) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
authPreference := &apitypes.AuthPreferenceV2{}
|
||||
diags = tfschema.CopyAuthPreferenceV2FromTerraform(ctx, plan, authPreference)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := authPreference.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting AuthPreference defaults", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
authPreferenceBefore, err := r.p.Client.GetAuthPreference(ctx)
|
||||
if err != nil && !trace.IsNotFound(err) {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.SetAuthPreference(ctx, authPreference)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
var authPreferenceI apitypes.AuthPreference
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
authPreferenceI, err = r.p.Client.GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
if authPreferenceBefore.GetMetadata().Revision != authPreferenceI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(bErr), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading AuthPreference (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "cluster_auth_preference")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
authPreference, ok := authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(
|
||||
diagFromWrappedErr("Error reading AuthPreference", trace.Errorf("Can not convert %T to AuthPreferenceV2", authPreferenceI), "cluster_auth_preference"),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: "auth_preference"}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport AuthPreference
|
||||
func (r resourceTeleportAuthPreference) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
authPreferenceI, err := r.p.Client.GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
authPreference := authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
diags = tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport AuthPreference
|
||||
func (r resourceTeleportAuthPreference) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
authPreference := &apitypes.AuthPreferenceV2{}
|
||||
diags = tfschema.CopyAuthPreferenceV2FromTerraform(ctx, plan, authPreference)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := authPreference.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
authPreferenceBefore, err := r.p.Client.GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.SetAuthPreference(ctx, authPreference)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
var authPreferenceI apitypes.AuthPreference
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
authPreferenceI, err = r.p.Client.GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
if authPreferenceBefore.GetMetadata().Revision != authPreferenceI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(bErr), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading AuthPreference (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "cluster_auth_preference")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
authPreference = authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
diags = tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport AuthPreference
|
||||
func (r resourceTeleportAuthPreference) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
err := r.p.Client.ResetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports AuthPreference state
|
||||
func (r resourceTeleportAuthPreference) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
authPreferenceI, err := r.p.Client.GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
authPreference := authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
state.Attrs["id"] = types.String{Value: authPreference.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
Copyright 2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1"
|
||||
machineidv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1"
|
||||
"github.com/gravitational/teleport/api/utils"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
func GenSchemaBot(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfsdk.Schema{
|
||||
Attributes: map[string]tfsdk.Attribute{
|
||||
"id": {
|
||||
Type: types.StringType,
|
||||
Computed: true,
|
||||
},
|
||||
"name": {
|
||||
Type: types.StringType,
|
||||
Required: true,
|
||||
Description: "The name of the bot, i.e. the unprefixed User name",
|
||||
PlanModifiers: []tfsdk.AttributePlanModifier{
|
||||
tfsdk.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"user_name": {
|
||||
Type: types.StringType,
|
||||
Computed: true,
|
||||
Description: "The name of the generated bot user",
|
||||
},
|
||||
"role_name": {
|
||||
Type: types.StringType,
|
||||
Computed: true,
|
||||
Description: "The name of the generated bot role",
|
||||
},
|
||||
"token_ttl": {
|
||||
Type: types.StringType,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "Deprecated. This field is not required anymore and has no effect.",
|
||||
},
|
||||
"token_id": {
|
||||
Type: types.StringType,
|
||||
// Implementation note: this is not used anymore, we can skip this
|
||||
// This will go away eventually when we'll generate the bot provider instead
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
Description: "Deprecated. This field is not required anymore and has no effect.",
|
||||
},
|
||||
"roles": {
|
||||
Type: types.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
Required: true,
|
||||
Description: "A list of roles the created bot should be allowed to assume via role impersonation.",
|
||||
|
||||
// TODO: Consider dropping RequiresReplace() in the future if a
|
||||
// UpdateBotRoles() API becomes available that can modify the
|
||||
// underlying bot user.
|
||||
PlanModifiers: []tfsdk.AttributePlanModifier{
|
||||
tfsdk.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
// Implementation note: This needs RequiresReplace() to handle
|
||||
// updates properly but we aren't able to attach plan modifiers to
|
||||
// fields from schema methods here. See ModifyPlan below.
|
||||
"traits": tfschema.GenSchemaTraits(ctx),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Bot is a deserializes representation of the terraform state for this
|
||||
// resource.
|
||||
type Bot struct {
|
||||
ID types.String `tfsdk:"id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Roles []types.String `tfsdk:"roles"`
|
||||
TokenID types.String `tfsdk:"token_id"`
|
||||
Traits types.Map `tfsdk:"traits"`
|
||||
TTL types.String `tfsdk:"token_ttl"`
|
||||
|
||||
UserName types.String `tfsdk:"user_name"`
|
||||
RoleName types.String `tfsdk:"role_name"`
|
||||
}
|
||||
|
||||
// resourceTeleportBotType is the resource metadata type
|
||||
type resourceTeleportBotType struct{}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportBotType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
// It's unusual for this provider, but we'll hand-write the schema here as
|
||||
// bots do not have any server-side resources of their own.
|
||||
return GenSchemaBot(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportBotType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportBot{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// resourceTeleportBot is the resource
|
||||
type resourceTeleportBot struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
func (r resourceTeleportBot) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan Bot
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var roles []string
|
||||
for _, role := range plan.Roles {
|
||||
roles = append(roles, role.Value)
|
||||
}
|
||||
|
||||
traits := make([]*machineidv1.Trait, 0, len(plan.Traits.Elems))
|
||||
for name, e := range plan.Traits.Elems {
|
||||
l, ok := e.(types.List)
|
||||
if !ok {
|
||||
diags.AddError("Error reading from Terraform object", fmt.Sprintf("Can not convert %T to types.List", l))
|
||||
return
|
||||
}
|
||||
|
||||
values := make(utils.Strings, len(l.Elems))
|
||||
|
||||
for i, v := range l.Elems {
|
||||
s, ok := v.(types.String)
|
||||
if !ok {
|
||||
diags.AddError("Error reading from Terraform object", fmt.Sprintf("Can not convert %T to types.String", s))
|
||||
return
|
||||
}
|
||||
|
||||
values[i] = s.Value
|
||||
}
|
||||
traits = append(traits, &machineidv1.Trait{
|
||||
Name: name,
|
||||
Values: values,
|
||||
})
|
||||
}
|
||||
|
||||
// This is a temporary workaround to fix the provider compilation in v16 (the legacy RPC got removed).
|
||||
// We must do a breaking change in v16 and rely on the new bot schema and the tf code generator.
|
||||
response, err := r.p.Client.BotServiceClient().CreateBot(ctx, &machineidv1.CreateBotRequest{Bot: &machineidv1.Bot{
|
||||
Metadata: &headerv1.Metadata{
|
||||
Name: plan.Name.Value,
|
||||
},
|
||||
Spec: &machineidv1.BotSpec{
|
||||
Roles: roles,
|
||||
Traits: traits,
|
||||
},
|
||||
}})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating Bot", trace.Wrap(err), "bot"))
|
||||
return
|
||||
}
|
||||
|
||||
plan.TTL = types.String{Value: ""}
|
||||
plan.UserName = types.String{Value: response.Status.UserName}
|
||||
plan.RoleName = types.String{Value: response.Status.RoleName}
|
||||
|
||||
// ID is for terraform-plugin-framework's acctests
|
||||
plan.ID = types.String{Value: plan.Name.Value}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r resourceTeleportBot) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
// Not much to do here: bots are currently immutable. We'll just check for
|
||||
// deletion.
|
||||
|
||||
var plan Bot
|
||||
diags := req.State.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := r.p.Client.GetUser(ctx, plan.UserName.Value, false)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Bot", trace.Wrap(err), "bot"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r resourceTeleportBot) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
// Nothing to do here: bots are currently immutable. In the future we'd
|
||||
// ideally want to add specific RPCs for desired mutable attributes, e.g.
|
||||
// UpdateBotRoles(), UpdateBotToken(), etc.
|
||||
}
|
||||
|
||||
func (r resourceTeleportBot) ModifyPlan(ctx context.Context, req tfsdk.ModifyResourcePlanRequest, resp *tfsdk.ModifyResourcePlanResponse) {
|
||||
// Add .traits to RequiresReplace to ensure changes to this field trigger a
|
||||
// replacement. We can't set it in the schema as the attribute is generated
|
||||
// by a helper method.
|
||||
resp.RequiresReplace = append(resp.RequiresReplace, path.Root("traits"))
|
||||
}
|
||||
|
||||
func (r resourceTeleportBot) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var name types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("name"), &name)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := r.p.Client.BotServiceClient().DeleteBot(ctx, &machineidv1.DeleteBotRequest{BotName: name.Value})
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting Bot", trace.Wrap(err), "bot"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportClusterMaintenanceConfigType is the resource metadata type
|
||||
type resourceTeleportClusterMaintenanceConfigType struct{}
|
||||
|
||||
// resourceTeleportClusterMaintenanceConfig is the resource
|
||||
type resourceTeleportClusterMaintenanceConfig struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportClusterMaintenanceConfigType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaClusterMaintenanceConfigV1(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportClusterMaintenanceConfigType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportClusterMaintenanceConfig{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the ClusterMaintenanceConfig
|
||||
func (r resourceTeleportClusterMaintenanceConfig) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
clusterMaintenanceConfig := &apitypes.ClusterMaintenanceConfigV1{}
|
||||
diags = tfschema.CopyClusterMaintenanceConfigV1FromTerraform(ctx, plan, clusterMaintenanceConfig)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := clusterMaintenanceConfig.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting ClusterMaintenanceConfig defaults", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
clusterMaintenanceConfigBefore, err := r.p.Client.GetClusterMaintenanceConfig(ctx)
|
||||
if err != nil && !trace.IsNotFound(err) {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
if clusterMaintenanceConfigBefore == nil {
|
||||
clusterMaintenanceConfigBefore = &apitypes.ClusterMaintenanceConfigV1{}
|
||||
}
|
||||
clusterMaintenanceConfig = clusterMaintenanceConfig.WithNonce(math.MaxUint64).(*apitypes.ClusterMaintenanceConfigV1)
|
||||
|
||||
err = r.p.Client.UpdateClusterMaintenanceConfig(ctx, clusterMaintenanceConfig)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
var clusterMaintenanceConfigI apitypes.ClusterMaintenanceConfig
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
clusterMaintenanceConfigI, err = r.p.Client.GetClusterMaintenanceConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
if clusterMaintenanceConfigBefore.GetMetadata().Revision != clusterMaintenanceConfigI.GetMetadata().Revision || true {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(bErr), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading ClusterMaintenanceConfig (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "cluster_maintenance_config")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterMaintenanceConfig, ok := clusterMaintenanceConfigI.(*apitypes.ClusterMaintenanceConfigV1)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(
|
||||
diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Errorf("Can not convert %T to ClusterMaintenanceConfigV1", clusterMaintenanceConfigI), "cluster_maintenance_config"),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyClusterMaintenanceConfigV1ToTerraform(ctx, clusterMaintenanceConfig, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: "cluster_maintenance_config"}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport ClusterMaintenanceConfig
|
||||
func (r resourceTeleportClusterMaintenanceConfig) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
clusterMaintenanceConfigI, err := r.p.Client.GetClusterMaintenanceConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterMaintenanceConfig := clusterMaintenanceConfigI.(*apitypes.ClusterMaintenanceConfigV1)
|
||||
diags = tfschema.CopyClusterMaintenanceConfigV1ToTerraform(ctx, clusterMaintenanceConfig, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport ClusterMaintenanceConfig
|
||||
func (r resourceTeleportClusterMaintenanceConfig) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
clusterMaintenanceConfig := &apitypes.ClusterMaintenanceConfigV1{}
|
||||
diags = tfschema.CopyClusterMaintenanceConfigV1FromTerraform(ctx, plan, clusterMaintenanceConfig)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := clusterMaintenanceConfig.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterMaintenanceConfigBefore, err := r.p.Client.GetClusterMaintenanceConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
clusterMaintenanceConfig = clusterMaintenanceConfig.WithNonce(math.MaxUint64).(*apitypes.ClusterMaintenanceConfigV1)
|
||||
|
||||
err = r.p.Client.UpdateClusterMaintenanceConfig(ctx, clusterMaintenanceConfig)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
var clusterMaintenanceConfigI apitypes.ClusterMaintenanceConfig
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
clusterMaintenanceConfigI, err = r.p.Client.GetClusterMaintenanceConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
if clusterMaintenanceConfigBefore.GetMetadata().Revision != clusterMaintenanceConfigI.GetMetadata().Revision || true {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(bErr), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading ClusterMaintenanceConfig (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "cluster_maintenance_config")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterMaintenanceConfig = clusterMaintenanceConfigI.(*apitypes.ClusterMaintenanceConfigV1)
|
||||
diags = tfschema.CopyClusterMaintenanceConfigV1ToTerraform(ctx, clusterMaintenanceConfig, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport ClusterMaintenanceConfig
|
||||
func (r resourceTeleportClusterMaintenanceConfig) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
err := r.p.Client.DeleteClusterMaintenanceConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports ClusterMaintenanceConfig state
|
||||
func (r resourceTeleportClusterMaintenanceConfig) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
clusterMaintenanceConfigI, err := r.p.Client.GetClusterMaintenanceConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating ClusterMaintenanceConfig", trace.Wrap(err), "cluster_maintenance_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterMaintenanceConfig := clusterMaintenanceConfigI.(*apitypes.ClusterMaintenanceConfigV1)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyClusterMaintenanceConfigV1ToTerraform(ctx, clusterMaintenanceConfig, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
state.Attrs["id"] = types.String{Value: clusterMaintenanceConfig.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportClusterNetworkingConfigType is the resource metadata type
|
||||
type resourceTeleportClusterNetworkingConfigType struct{}
|
||||
|
||||
// resourceTeleportClusterNetworkingConfig is the resource
|
||||
type resourceTeleportClusterNetworkingConfig struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportClusterNetworkingConfigType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaClusterNetworkingConfigV2(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportClusterNetworkingConfigType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportClusterNetworkingConfig{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the ClusterNetworkingConfig
|
||||
func (r resourceTeleportClusterNetworkingConfig) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
clusterNetworkingConfig := &apitypes.ClusterNetworkingConfigV2{}
|
||||
diags = tfschema.CopyClusterNetworkingConfigV2FromTerraform(ctx, plan, clusterNetworkingConfig)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := clusterNetworkingConfig.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting ClusterNetworkingConfig defaults", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
clusterNetworkingConfigBefore, err := r.p.Client.GetClusterNetworkingConfig(ctx)
|
||||
if err != nil && !trace.IsNotFound(err) {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.SetClusterNetworkingConfig(ctx, clusterNetworkingConfig)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
var clusterNetworkingConfigI apitypes.ClusterNetworkingConfig
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
clusterNetworkingConfigI, err = r.p.Client.GetClusterNetworkingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
if clusterNetworkingConfigBefore.GetMetadata().Revision != clusterNetworkingConfigI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(bErr), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading ClusterNetworkingConfig (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "cluster_networking_config")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterNetworkingConfig, ok := clusterNetworkingConfigI.(*apitypes.ClusterNetworkingConfigV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(
|
||||
diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Errorf("Can not convert %T to ClusterNetworkingConfigV2", clusterNetworkingConfigI), "cluster_networking_config"),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyClusterNetworkingConfigV2ToTerraform(ctx, clusterNetworkingConfig, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: "cluster_networking_config"}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport ClusterNetworkingConfig
|
||||
func (r resourceTeleportClusterNetworkingConfig) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
clusterNetworkingConfigI, err := r.p.Client.GetClusterNetworkingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterNetworkingConfig := clusterNetworkingConfigI.(*apitypes.ClusterNetworkingConfigV2)
|
||||
diags = tfschema.CopyClusterNetworkingConfigV2ToTerraform(ctx, clusterNetworkingConfig, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport ClusterNetworkingConfig
|
||||
func (r resourceTeleportClusterNetworkingConfig) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
clusterNetworkingConfig := &apitypes.ClusterNetworkingConfigV2{}
|
||||
diags = tfschema.CopyClusterNetworkingConfigV2FromTerraform(ctx, plan, clusterNetworkingConfig)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := clusterNetworkingConfig.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterNetworkingConfigBefore, err := r.p.Client.GetClusterNetworkingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.SetClusterNetworkingConfig(ctx, clusterNetworkingConfig)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
var clusterNetworkingConfigI apitypes.ClusterNetworkingConfig
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
clusterNetworkingConfigI, err = r.p.Client.GetClusterNetworkingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
if clusterNetworkingConfigBefore.GetMetadata().Revision != clusterNetworkingConfigI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(bErr), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading ClusterNetworkingConfig (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "cluster_networking_config")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterNetworkingConfig = clusterNetworkingConfigI.(*apitypes.ClusterNetworkingConfigV2)
|
||||
diags = tfschema.CopyClusterNetworkingConfigV2ToTerraform(ctx, clusterNetworkingConfig, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport ClusterNetworkingConfig
|
||||
func (r resourceTeleportClusterNetworkingConfig) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
err := r.p.Client.ResetClusterNetworkingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports ClusterNetworkingConfig state
|
||||
func (r resourceTeleportClusterNetworkingConfig) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
clusterNetworkingConfigI, err := r.p.Client.GetClusterNetworkingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating ClusterNetworkingConfig", trace.Wrap(err), "cluster_networking_config"))
|
||||
return
|
||||
}
|
||||
|
||||
clusterNetworkingConfig := clusterNetworkingConfigI.(*apitypes.ClusterNetworkingConfigV2)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyClusterNetworkingConfigV2ToTerraform(ctx, clusterNetworkingConfig, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
state.Attrs["id"] = types.String{Value: clusterNetworkingConfig.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportDatabaseType is the resource metadata type
|
||||
type resourceTeleportDatabaseType struct{}
|
||||
|
||||
// resourceTeleportDatabase is the resource
|
||||
type resourceTeleportDatabase struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportDatabaseType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaDatabaseV3(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportDatabaseType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportDatabase{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the Database
|
||||
func (r resourceTeleportDatabase) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
database := &apitypes.DatabaseV3{}
|
||||
diags = tfschema.CopyDatabaseV3FromTerraform(ctx, plan, database)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
databaseResource := database
|
||||
|
||||
err = databaseResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting Database defaults", trace.Wrap(err), "db"))
|
||||
return
|
||||
}
|
||||
|
||||
id := databaseResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetDatabase(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("Database exists in Teleport. Either remove it (tctl rm db/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_database.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("Database exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(err), "db"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.CreateDatabase(ctx, databaseResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating Database", trace.Wrap(err), "db"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var databaseI apitypes.Database
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
databaseI, err = r.p.Client.GetDatabase(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(bErr), "db"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading Database (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "db")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(err), "db"))
|
||||
return
|
||||
}
|
||||
|
||||
databaseResource, ok := databaseI.(*apitypes.DatabaseV3)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Errorf("Can not convert %T to DatabaseV3", databaseI), "db"))
|
||||
return
|
||||
}
|
||||
database = databaseResource
|
||||
|
||||
diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: database.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport Database
|
||||
func (r resourceTeleportDatabase) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
databaseI, err := r.p.Client.GetDatabase(ctx, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(err), "db"))
|
||||
return
|
||||
}
|
||||
|
||||
database := databaseI.(*apitypes.DatabaseV3)
|
||||
diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport Database
|
||||
func (r resourceTeleportDatabase) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
database := &apitypes.DatabaseV3{}
|
||||
diags = tfschema.CopyDatabaseV3FromTerraform(ctx, plan, database)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
databaseResource := database
|
||||
|
||||
|
||||
if err := databaseResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating Database", err, "db"))
|
||||
return
|
||||
}
|
||||
name := databaseResource.Metadata.Name
|
||||
|
||||
databaseBefore, err := r.p.Client.GetDatabase(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", err, "db"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.UpdateDatabase(ctx, databaseResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating Database", err, "db"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var databaseI apitypes.Database
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
databaseI, err = r.p.Client.GetDatabase(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", err, "db"))
|
||||
return
|
||||
}
|
||||
if databaseBefore.GetMetadata().Revision != databaseI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(err), "db"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading Database (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "db")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
databaseResource, ok := databaseI.(*apitypes.DatabaseV3)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Errorf("Can not convert %T to DatabaseV3", databaseI), "db"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport Database
|
||||
func (r resourceTeleportDatabase) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteDatabase(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting DatabaseV3", trace.Wrap(err), "db"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports Database state
|
||||
func (r resourceTeleportDatabase) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
database, err := r.p.Client.GetDatabase(ctx, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(err), "db"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
databaseResource := database.(*apitypes.DatabaseV3)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyDatabaseV3ToTerraform(ctx, databaseResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := databaseResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/devicetrust/v1"
|
||||
)
|
||||
|
||||
// resourceTeleportDeviceV1Type is the resource metadata type
|
||||
type resourceTeleportDeviceV1Type struct{}
|
||||
|
||||
// resourceTeleportDeviceV1 is the resource
|
||||
type resourceTeleportDeviceV1 struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportDeviceV1Type) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return schemav1.GenSchemaDeviceV1(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportDeviceV1Type) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportDeviceV1{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the DeviceV1
|
||||
func (r resourceTeleportDeviceV1) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
trustedDevice := &apitypes.DeviceV1{}
|
||||
diags = schemav1.CopyDeviceV1FromTerraform(ctx, plan, trustedDevice)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if trustedDevice.Metadata.Name == "" {
|
||||
trustedDevice.Metadata.Name = uuid.NewString()
|
||||
}
|
||||
|
||||
trustedDeviceResource := trustedDevice
|
||||
|
||||
|
||||
id := trustedDeviceResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetDeviceResource(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("DeviceV1 exists in Teleport. Either remove it (tctl rm device/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_device_trust.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("DeviceV1 exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertDeviceResource(ctx, trustedDeviceResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating DeviceV1", trace.Wrap(err), "device"))
|
||||
return
|
||||
}
|
||||
var trustedDeviceI *apitypes.DeviceV1
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
trustedDeviceI, err = r.p.Client.GetDeviceResource(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(bErr), "device"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading DeviceV1 (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "device")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device"))
|
||||
return
|
||||
}
|
||||
|
||||
trustedDeviceResource = trustedDeviceI
|
||||
|
||||
trustedDevice = trustedDeviceResource
|
||||
|
||||
diags = schemav1.CopyDeviceV1ToTerraform(ctx, trustedDevice, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: trustedDevice.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport DeviceV1
|
||||
func (r resourceTeleportDeviceV1) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
trustedDeviceI, err := r.p.Client.GetDeviceResource(ctx, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device"))
|
||||
return
|
||||
}
|
||||
trustedDevice := trustedDeviceI
|
||||
diags = schemav1.CopyDeviceV1ToTerraform(ctx, trustedDevice, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport DeviceV1
|
||||
func (r resourceTeleportDeviceV1) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
trustedDevice := &apitypes.DeviceV1{}
|
||||
diags = schemav1.CopyDeviceV1FromTerraform(ctx, plan, trustedDevice)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
trustedDeviceResource := trustedDevice
|
||||
|
||||
|
||||
|
||||
name := trustedDeviceResource.Metadata.Name
|
||||
|
||||
trustedDeviceBefore, err := r.p.Client.GetDeviceResource(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", err, "device"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertDeviceResource(ctx, trustedDeviceResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating DeviceV1", err, "device"))
|
||||
return
|
||||
}
|
||||
var trustedDeviceI *apitypes.DeviceV1
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
trustedDeviceI, err = r.p.Client.GetDeviceResource(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", err, "device"))
|
||||
return
|
||||
}
|
||||
if trustedDeviceBefore.GetMetadata().Revision != trustedDeviceI.GetMetadata().Revision || true {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading DeviceV1 (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "device")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
trustedDeviceResource = trustedDeviceI
|
||||
|
||||
diags = schemav1.CopyDeviceV1ToTerraform(ctx, trustedDevice, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport DeviceV1
|
||||
func (r resourceTeleportDeviceV1) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteDeviceResource(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting DeviceV1", trace.Wrap(err), "device"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports DeviceV1 state
|
||||
func (r resourceTeleportDeviceV1) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
trustedDevice, err := r.p.Client.GetDeviceResource(ctx, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device"))
|
||||
return
|
||||
}
|
||||
|
||||
trustedDeviceResource := trustedDevice
|
||||
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = schemav1.CopyDeviceV1ToTerraform(ctx, trustedDeviceResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := trustedDevice.Metadata.Name
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportGithubConnectorType is the resource metadata type
|
||||
type resourceTeleportGithubConnectorType struct{}
|
||||
|
||||
// resourceTeleportGithubConnector is the resource
|
||||
type resourceTeleportGithubConnector struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportGithubConnectorType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaGithubConnectorV3(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportGithubConnectorType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportGithubConnector{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the GithubConnector
|
||||
func (r resourceTeleportGithubConnector) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
githubConnector := &apitypes.GithubConnectorV3{}
|
||||
diags = tfschema.CopyGithubConnectorV3FromTerraform(ctx, plan, githubConnector)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
githubConnectorResource := githubConnector
|
||||
|
||||
err = githubConnectorResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting GithubConnector defaults", trace.Wrap(err), "github"))
|
||||
return
|
||||
}
|
||||
|
||||
id := githubConnectorResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetGithubConnector(ctx, id, true)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("GithubConnector exists in Teleport. Either remove it (tctl rm github/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_github_connector.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("GithubConnector exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Wrap(err), "github"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.CreateGithubConnector(ctx, githubConnectorResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating GithubConnector", trace.Wrap(err), "github"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var githubConnectorI apitypes.GithubConnector
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
githubConnectorI, err = r.p.Client.GetGithubConnector(ctx, id, true)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Wrap(bErr), "github"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading GithubConnector (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "github")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Wrap(err), "github"))
|
||||
return
|
||||
}
|
||||
|
||||
githubConnectorResource, ok := githubConnectorI.(*apitypes.GithubConnectorV3)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Errorf("Can not convert %T to GithubConnectorV3", githubConnectorI), "github"))
|
||||
return
|
||||
}
|
||||
githubConnector = githubConnectorResource
|
||||
|
||||
diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: githubConnector.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport GithubConnector
|
||||
func (r resourceTeleportGithubConnector) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
githubConnectorI, err := r.p.Client.GetGithubConnector(ctx, id.Value, true)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Wrap(err), "github"))
|
||||
return
|
||||
}
|
||||
|
||||
githubConnector := githubConnectorI.(*apitypes.GithubConnectorV3)
|
||||
diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport GithubConnector
|
||||
func (r resourceTeleportGithubConnector) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
githubConnector := &apitypes.GithubConnectorV3{}
|
||||
diags = tfschema.CopyGithubConnectorV3FromTerraform(ctx, plan, githubConnector)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
githubConnectorResource := githubConnector
|
||||
|
||||
|
||||
if err := githubConnectorResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating GithubConnector", err, "github"))
|
||||
return
|
||||
}
|
||||
name := githubConnectorResource.Metadata.Name
|
||||
|
||||
githubConnectorBefore, err := r.p.Client.GetGithubConnector(ctx, name, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", err, "github"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertGithubConnector(ctx, githubConnectorResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating GithubConnector", err, "github"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var githubConnectorI apitypes.GithubConnector
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
githubConnectorI, err = r.p.Client.GetGithubConnector(ctx, name, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", err, "github"))
|
||||
return
|
||||
}
|
||||
if githubConnectorBefore.GetMetadata().Revision != githubConnectorI.GetMetadata().Revision || true {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Wrap(err), "github"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading GithubConnector (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "github")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
githubConnectorResource, ok := githubConnectorI.(*apitypes.GithubConnectorV3)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Errorf("Can not convert %T to GithubConnectorV3", githubConnectorI), "github"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport GithubConnector
|
||||
func (r resourceTeleportGithubConnector) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteGithubConnector(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting GithubConnectorV3", trace.Wrap(err), "github"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports GithubConnector state
|
||||
func (r resourceTeleportGithubConnector) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
githubConnector, err := r.p.Client.GetGithubConnector(ctx, req.ID, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading GithubConnector", trace.Wrap(err), "github"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
githubConnectorResource := githubConnector.(*apitypes.GithubConnectorV3)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnectorResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := githubConnectorResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
loginrulev1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/loginrule/v1"
|
||||
)
|
||||
|
||||
// resourceTeleportLoginRuleType is the resource metadata type
|
||||
type resourceTeleportLoginRuleType struct{}
|
||||
|
||||
// resourceTeleportLoginRule is the resource
|
||||
type resourceTeleportLoginRule struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportLoginRuleType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return schemav1.GenSchemaLoginRule(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportLoginRuleType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportLoginRule{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the LoginRule
|
||||
func (r resourceTeleportLoginRule) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
loginRule := &loginrulev1.LoginRule{}
|
||||
diags = schemav1.CopyLoginRuleFromTerraform(ctx, plan, loginRule)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
loginRuleResource := loginRule
|
||||
|
||||
|
||||
id := loginRuleResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetLoginRule(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("LoginRule exists in Teleport. Either remove it (tctl rm login_rule/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_login_rule.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("LoginRule exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(err), "login_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertLoginRule(ctx, loginRuleResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating LoginRule", trace.Wrap(err), "login_rule"))
|
||||
return
|
||||
}
|
||||
var loginRuleI *loginrulev1.LoginRule
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
loginRuleI, err = r.p.Client.GetLoginRule(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(bErr), "login_rule"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading LoginRule (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "login_rule")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(err), "login_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
loginRuleResource = loginRuleI
|
||||
|
||||
loginRule = loginRuleResource
|
||||
|
||||
diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: loginRule.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport LoginRule
|
||||
func (r resourceTeleportLoginRule) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
loginRuleI, err := r.p.Client.GetLoginRule(ctx, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(err), "login_rule"))
|
||||
return
|
||||
}
|
||||
loginRule := loginRuleI
|
||||
diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport LoginRule
|
||||
func (r resourceTeleportLoginRule) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
loginRule := &loginrulev1.LoginRule{}
|
||||
diags = schemav1.CopyLoginRuleFromTerraform(ctx, plan, loginRule)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
loginRuleResource := loginRule
|
||||
|
||||
|
||||
|
||||
name := loginRuleResource.Metadata.Name
|
||||
|
||||
loginRuleBefore, err := r.p.Client.GetLoginRule(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", err, "login_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertLoginRule(ctx, loginRuleResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating LoginRule", err, "login_rule"))
|
||||
return
|
||||
}
|
||||
var loginRuleI *loginrulev1.LoginRule
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
loginRuleI, err = r.p.Client.GetLoginRule(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", err, "login_rule"))
|
||||
return
|
||||
}
|
||||
if loginRuleBefore.GetMetadata().Revision != loginRuleI.GetMetadata().Revision || true {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(err), "login_rule"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading LoginRule (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "login_rule")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
loginRuleResource = loginRuleI
|
||||
|
||||
diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport LoginRule
|
||||
func (r resourceTeleportLoginRule) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteLoginRule(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting LoginRule", trace.Wrap(err), "login_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports LoginRule state
|
||||
func (r resourceTeleportLoginRule) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
loginRule, err := r.p.Client.GetLoginRule(ctx, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(err), "login_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
loginRuleResource := loginRule
|
||||
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRuleResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := loginRule.Metadata.Name
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportOIDCConnectorType is the resource metadata type
|
||||
type resourceTeleportOIDCConnectorType struct{}
|
||||
|
||||
// resourceTeleportOIDCConnector is the resource
|
||||
type resourceTeleportOIDCConnector struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportOIDCConnectorType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaOIDCConnectorV3(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportOIDCConnectorType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportOIDCConnector{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the OIDCConnector
|
||||
func (r resourceTeleportOIDCConnector) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
oidcConnector := &apitypes.OIDCConnectorV3{}
|
||||
diags = tfschema.CopyOIDCConnectorV3FromTerraform(ctx, plan, oidcConnector)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
oidcConnectorResource := oidcConnector
|
||||
|
||||
err = oidcConnectorResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting OIDCConnector defaults", trace.Wrap(err), "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
id := oidcConnectorResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetOIDCConnector(ctx, id, true)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("OIDCConnector exists in Teleport. Either remove it (tctl rm oidc/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_oidc_connector.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("OIDCConnector exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Wrap(err), "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.CreateOIDCConnector(ctx, oidcConnectorResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating OIDCConnector", trace.Wrap(err), "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var oidcConnectorI apitypes.OIDCConnector
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
oidcConnectorI, err = r.p.Client.GetOIDCConnector(ctx, id, true)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Wrap(bErr), "oidc"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading OIDCConnector (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "oidc")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Wrap(err), "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
oidcConnectorResource, ok := oidcConnectorI.(*apitypes.OIDCConnectorV3)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Errorf("Can not convert %T to OIDCConnectorV3", oidcConnectorI), "oidc"))
|
||||
return
|
||||
}
|
||||
oidcConnector = oidcConnectorResource
|
||||
|
||||
diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: oidcConnector.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport OIDCConnector
|
||||
func (r resourceTeleportOIDCConnector) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
oidcConnectorI, err := r.p.Client.GetOIDCConnector(ctx, id.Value, true)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Wrap(err), "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
oidcConnector := oidcConnectorI.(*apitypes.OIDCConnectorV3)
|
||||
diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport OIDCConnector
|
||||
func (r resourceTeleportOIDCConnector) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
oidcConnector := &apitypes.OIDCConnectorV3{}
|
||||
diags = tfschema.CopyOIDCConnectorV3FromTerraform(ctx, plan, oidcConnector)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
oidcConnectorResource := oidcConnector
|
||||
|
||||
|
||||
if err := oidcConnectorResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating OIDCConnector", err, "oidc"))
|
||||
return
|
||||
}
|
||||
name := oidcConnectorResource.Metadata.Name
|
||||
|
||||
oidcConnectorBefore, err := r.p.Client.GetOIDCConnector(ctx, name, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", err, "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertOIDCConnector(ctx, oidcConnectorResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating OIDCConnector", err, "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var oidcConnectorI apitypes.OIDCConnector
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
oidcConnectorI, err = r.p.Client.GetOIDCConnector(ctx, name, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", err, "oidc"))
|
||||
return
|
||||
}
|
||||
if oidcConnectorBefore.GetMetadata().Revision != oidcConnectorI.GetMetadata().Revision || true {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Wrap(err), "oidc"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading OIDCConnector (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "oidc")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
oidcConnectorResource, ok := oidcConnectorI.(*apitypes.OIDCConnectorV3)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Errorf("Can not convert %T to OIDCConnectorV3", oidcConnectorI), "oidc"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport OIDCConnector
|
||||
func (r resourceTeleportOIDCConnector) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteOIDCConnector(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting OIDCConnectorV3", trace.Wrap(err), "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports OIDCConnector state
|
||||
func (r resourceTeleportOIDCConnector) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
oidcConnector, err := r.p.Client.GetOIDCConnector(ctx, req.ID, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OIDCConnector", trace.Wrap(err), "oidc"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
oidcConnectorResource := oidcConnector.(*apitypes.OIDCConnectorV3)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnectorResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := oidcConnectorResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportOktaImportRuleType is the resource metadata type
|
||||
type resourceTeleportOktaImportRuleType struct{}
|
||||
|
||||
// resourceTeleportOktaImportRule is the resource
|
||||
type resourceTeleportOktaImportRule struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportOktaImportRuleType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaOktaImportRuleV1(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportOktaImportRuleType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportOktaImportRule{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the OktaImportRule
|
||||
func (r resourceTeleportOktaImportRule) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
oktaImportRule := &apitypes.OktaImportRuleV1{}
|
||||
diags = tfschema.CopyOktaImportRuleV1FromTerraform(ctx, plan, oktaImportRule)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
oktaImportRuleResource := oktaImportRule
|
||||
|
||||
err = oktaImportRuleResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting OktaImportRule defaults", trace.Wrap(err), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
id := oktaImportRuleResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.OktaClient().GetOktaImportRule(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("OktaImportRule exists in Teleport. Either remove it (tctl rm okta_import_rule/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_okta_import_rule.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("OktaImportRule exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Wrap(err), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.OktaClient().CreateOktaImportRule(ctx, oktaImportRuleResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating OktaImportRule", trace.Wrap(err), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var oktaImportRuleI apitypes.OktaImportRule
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
oktaImportRuleI, err = r.p.Client.OktaClient().GetOktaImportRule(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Wrap(bErr), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading OktaImportRule (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "okta_import_rule")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Wrap(err), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
oktaImportRuleResource, ok := oktaImportRuleI.(*apitypes.OktaImportRuleV1)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Errorf("Can not convert %T to OktaImportRuleV1", oktaImportRuleI), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
oktaImportRule = oktaImportRuleResource
|
||||
|
||||
diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: oktaImportRule.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport OktaImportRule
|
||||
func (r resourceTeleportOktaImportRule) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
oktaImportRuleI, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Wrap(err), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
oktaImportRule := oktaImportRuleI.(*apitypes.OktaImportRuleV1)
|
||||
diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport OktaImportRule
|
||||
func (r resourceTeleportOktaImportRule) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
oktaImportRule := &apitypes.OktaImportRuleV1{}
|
||||
diags = tfschema.CopyOktaImportRuleV1FromTerraform(ctx, plan, oktaImportRule)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
oktaImportRuleResource := oktaImportRule
|
||||
|
||||
|
||||
if err := oktaImportRuleResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating OktaImportRule", err, "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
name := oktaImportRuleResource.Metadata.Name
|
||||
|
||||
oktaImportRuleBefore, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", err, "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.OktaClient().UpdateOktaImportRule(ctx, oktaImportRuleResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating OktaImportRule", err, "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var oktaImportRuleI apitypes.OktaImportRule
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
oktaImportRuleI, err = r.p.Client.OktaClient().GetOktaImportRule(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", err, "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
if oktaImportRuleBefore.GetMetadata().Revision != oktaImportRuleI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Wrap(err), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading OktaImportRule (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "okta_import_rule")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
oktaImportRuleResource, ok := oktaImportRuleI.(*apitypes.OktaImportRuleV1)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Errorf("Can not convert %T to OktaImportRuleV1", oktaImportRuleI), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport OktaImportRule
|
||||
func (r resourceTeleportOktaImportRule) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.OktaClient().DeleteOktaImportRule(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting OktaImportRuleV1", trace.Wrap(err), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports OktaImportRule state
|
||||
func (r resourceTeleportOktaImportRule) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
oktaImportRule, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading OktaImportRule", trace.Wrap(err), "okta_import_rule"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
oktaImportRuleResource := oktaImportRule.(*apitypes.OktaImportRuleV1)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRuleResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := oktaImportRuleResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportProvisionTokenType is the resource metadata type
|
||||
type resourceTeleportProvisionTokenType struct{}
|
||||
|
||||
// resourceTeleportProvisionToken is the resource
|
||||
type resourceTeleportProvisionToken struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportProvisionTokenType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaProvisionTokenV2(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportProvisionTokenType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportProvisionToken{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the ProvisionToken
|
||||
func (r resourceTeleportProvisionToken) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
provisionToken := &apitypes.ProvisionTokenV2{}
|
||||
diags = tfschema.CopyProvisionTokenV2FromTerraform(ctx, plan, provisionToken)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if provisionToken.Metadata.Name == "" {
|
||||
b := make([]byte, 32)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to generate random token", err.Error())
|
||||
return
|
||||
}
|
||||
provisionToken.Metadata.Name = hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
provisionTokenResource := provisionToken
|
||||
|
||||
err = provisionTokenResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting ProvisionToken defaults", trace.Wrap(err), "token"))
|
||||
return
|
||||
}
|
||||
|
||||
id := provisionTokenResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetToken(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("ProvisionToken exists in Teleport. Either remove it (tctl rm token/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_provision_token.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("ProvisionToken exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(err), "token"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.UpsertToken(ctx, provisionTokenResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating ProvisionToken", trace.Wrap(err), "token"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var provisionTokenI apitypes.ProvisionToken
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
provisionTokenI, err = r.p.Client.GetToken(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(bErr), "token"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading ProvisionToken (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "token")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(err), "token"))
|
||||
return
|
||||
}
|
||||
|
||||
provisionTokenResource, ok := provisionTokenI.(*apitypes.ProvisionTokenV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Errorf("Can not convert %T to ProvisionTokenV2", provisionTokenI), "token"))
|
||||
return
|
||||
}
|
||||
provisionToken = provisionTokenResource
|
||||
|
||||
diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: strconv.FormatInt(provisionToken.Metadata.ID, 10)}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport ProvisionToken
|
||||
func (r resourceTeleportProvisionToken) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
provisionTokenI, err := r.p.Client.GetToken(ctx, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(err), "token"))
|
||||
return
|
||||
}
|
||||
|
||||
provisionToken := provisionTokenI.(*apitypes.ProvisionTokenV2)
|
||||
diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport ProvisionToken
|
||||
func (r resourceTeleportProvisionToken) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
provisionToken := &apitypes.ProvisionTokenV2{}
|
||||
diags = tfschema.CopyProvisionTokenV2FromTerraform(ctx, plan, provisionToken)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
provisionTokenResource := provisionToken
|
||||
|
||||
|
||||
if err := provisionTokenResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating ProvisionToken", err, "token"))
|
||||
return
|
||||
}
|
||||
name := provisionTokenResource.Metadata.Name
|
||||
|
||||
provisionTokenBefore, err := r.p.Client.GetToken(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", err, "token"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.UpsertToken(ctx, provisionTokenResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating ProvisionToken", err, "token"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var provisionTokenI apitypes.ProvisionToken
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
provisionTokenI, err = r.p.Client.GetToken(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", err, "token"))
|
||||
return
|
||||
}
|
||||
if provisionTokenBefore.GetMetadata().Revision != provisionTokenI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(err), "token"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading ProvisionToken (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "token")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
provisionTokenResource, ok := provisionTokenI.(*apitypes.ProvisionTokenV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Errorf("Can not convert %T to ProvisionTokenV2", provisionTokenI), "token"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport ProvisionToken
|
||||
func (r resourceTeleportProvisionToken) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteToken(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting ProvisionTokenV2", trace.Wrap(err), "token"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports ProvisionToken state
|
||||
func (r resourceTeleportProvisionToken) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
provisionToken, err := r.p.Client.GetToken(ctx, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(err), "token"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
provisionTokenResource := provisionToken.(*apitypes.ProvisionTokenV2)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionTokenResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := provisionTokenResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportRoleType is the resource metadata type
|
||||
type resourceTeleportRoleType struct{}
|
||||
|
||||
// resourceTeleportRole is the resource
|
||||
type resourceTeleportRole struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportRoleType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaRoleV6(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportRoleType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportRole{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the Role
|
||||
func (r resourceTeleportRole) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
role := &apitypes.RoleV6{}
|
||||
diags = tfschema.CopyRoleV6FromTerraform(ctx, plan, role)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
roleResource := role
|
||||
|
||||
err = roleResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting Role defaults", trace.Wrap(err), "role"))
|
||||
return
|
||||
}
|
||||
|
||||
id := roleResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetRole(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("Role exists in Teleport. Either remove it (tctl rm role/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_role.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("Role exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(err), "role"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.CreateRole(ctx, roleResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating Role", trace.Wrap(err), "role"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var roleI apitypes.Role
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
roleI, err = r.p.Client.GetRole(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(bErr), "role"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading Role (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "role")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(err), "role"))
|
||||
return
|
||||
}
|
||||
|
||||
roleResource, ok := roleI.(*apitypes.RoleV6)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Errorf("Can not convert %T to RoleV6", roleI), "role"))
|
||||
return
|
||||
}
|
||||
role = roleResource
|
||||
|
||||
diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: role.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport Role
|
||||
func (r resourceTeleportRole) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
roleI, err := r.p.Client.GetRole(ctx, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(err), "role"))
|
||||
return
|
||||
}
|
||||
|
||||
role := roleI.(*apitypes.RoleV6)
|
||||
diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport Role
|
||||
func (r resourceTeleportRole) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
role := &apitypes.RoleV6{}
|
||||
diags = tfschema.CopyRoleV6FromTerraform(ctx, plan, role)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
roleResource := role
|
||||
|
||||
|
||||
if err := roleResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating Role", err, "role"))
|
||||
return
|
||||
}
|
||||
name := roleResource.Metadata.Name
|
||||
|
||||
roleBefore, err := r.p.Client.GetRole(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", err, "role"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertRole(ctx, roleResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating Role", err, "role"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var roleI apitypes.Role
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
roleI, err = r.p.Client.GetRole(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", err, "role"))
|
||||
return
|
||||
}
|
||||
if roleBefore.GetMetadata().Revision != roleI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(err), "role"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading Role (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "role")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
roleResource, ok := roleI.(*apitypes.RoleV6)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Errorf("Can not convert %T to RoleV6", roleI), "role"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport Role
|
||||
func (r resourceTeleportRole) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteRole(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting RoleV6", trace.Wrap(err), "role"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports Role state
|
||||
func (r resourceTeleportRole) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
role, err := r.p.Client.GetRole(ctx, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(err), "role"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
roleResource := role.(*apitypes.RoleV6)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyRoleV6ToTerraform(ctx, roleResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := roleResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportSAMLConnectorType is the resource metadata type
|
||||
type resourceTeleportSAMLConnectorType struct{}
|
||||
|
||||
// resourceTeleportSAMLConnector is the resource
|
||||
type resourceTeleportSAMLConnector struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportSAMLConnectorType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaSAMLConnectorV2(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportSAMLConnectorType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportSAMLConnector{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the SAMLConnector
|
||||
func (r resourceTeleportSAMLConnector) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
samlConnector := &apitypes.SAMLConnectorV2{}
|
||||
diags = tfschema.CopySAMLConnectorV2FromTerraform(ctx, plan, samlConnector)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
samlConnectorResource := samlConnector
|
||||
|
||||
err = samlConnectorResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting SAMLConnector defaults", trace.Wrap(err), "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
id := samlConnectorResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetSAMLConnector(ctx, id, true)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("SAMLConnector exists in Teleport. Either remove it (tctl rm saml/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_saml_connector.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("SAMLConnector exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Wrap(err), "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.CreateSAMLConnector(ctx, samlConnectorResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating SAMLConnector", trace.Wrap(err), "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var samlConnectorI apitypes.SAMLConnector
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
samlConnectorI, err = r.p.Client.GetSAMLConnector(ctx, id, true)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Wrap(bErr), "saml"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading SAMLConnector (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "saml")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Wrap(err), "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
samlConnectorResource, ok := samlConnectorI.(*apitypes.SAMLConnectorV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Errorf("Can not convert %T to SAMLConnectorV2", samlConnectorI), "saml"))
|
||||
return
|
||||
}
|
||||
samlConnector = samlConnectorResource
|
||||
|
||||
diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: samlConnector.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport SAMLConnector
|
||||
func (r resourceTeleportSAMLConnector) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
samlConnectorI, err := r.p.Client.GetSAMLConnector(ctx, id.Value, true)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Wrap(err), "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
samlConnector := samlConnectorI.(*apitypes.SAMLConnectorV2)
|
||||
diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport SAMLConnector
|
||||
func (r resourceTeleportSAMLConnector) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
samlConnector := &apitypes.SAMLConnectorV2{}
|
||||
diags = tfschema.CopySAMLConnectorV2FromTerraform(ctx, plan, samlConnector)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
samlConnectorResource := samlConnector
|
||||
|
||||
|
||||
if err := samlConnectorResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating SAMLConnector", err, "saml"))
|
||||
return
|
||||
}
|
||||
name := samlConnectorResource.Metadata.Name
|
||||
|
||||
samlConnectorBefore, err := r.p.Client.GetSAMLConnector(ctx, name, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", err, "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertSAMLConnector(ctx, samlConnectorResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating SAMLConnector", err, "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var samlConnectorI apitypes.SAMLConnector
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
samlConnectorI, err = r.p.Client.GetSAMLConnector(ctx, name, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", err, "saml"))
|
||||
return
|
||||
}
|
||||
if samlConnectorBefore.GetMetadata().Revision != samlConnectorI.GetMetadata().Revision || true {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Wrap(err), "saml"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading SAMLConnector (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "saml")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
samlConnectorResource, ok := samlConnectorI.(*apitypes.SAMLConnectorV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Errorf("Can not convert %T to SAMLConnectorV2", samlConnectorI), "saml"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport SAMLConnector
|
||||
func (r resourceTeleportSAMLConnector) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteSAMLConnector(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting SAMLConnectorV2", trace.Wrap(err), "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports SAMLConnector state
|
||||
func (r resourceTeleportSAMLConnector) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
samlConnector, err := r.p.Client.GetSAMLConnector(ctx, req.ID, true)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SAMLConnector", trace.Wrap(err), "saml"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
samlConnectorResource := samlConnector.(*apitypes.SAMLConnectorV2)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnectorResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := samlConnectorResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/gravitational/teleport/api/defaults"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportServerType is the resource metadata type
|
||||
type resourceTeleportServerType struct{}
|
||||
|
||||
// resourceTeleportServer is the resource
|
||||
type resourceTeleportServer struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportServerType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaServerV2(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportServerType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportServer{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the Server
|
||||
func (r resourceTeleportServer) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
server := &apitypes.ServerV2{}
|
||||
diags = tfschema.CopyServerV2FromTerraform(ctx, plan, server)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
serverResource := server
|
||||
|
||||
serverResource.Kind = apitypes.KindNode
|
||||
err = serverResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting Server defaults", trace.Wrap(err), "node"))
|
||||
return
|
||||
}
|
||||
|
||||
id := serverResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetNode(ctx, defaults.Namespace, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("Server exists in Teleport. Either remove it (tctl rm node/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_server.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("Server exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", trace.Wrap(err), "node"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertNode(ctx, serverResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating Server", trace.Wrap(err), "node"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var serverI apitypes.Server
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
serverI, err = r.p.Client.GetNode(ctx, defaults.Namespace, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", trace.Wrap(bErr), "node"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading Server (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "node")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", trace.Wrap(err), "node"))
|
||||
return
|
||||
}
|
||||
|
||||
serverResource, ok := serverI.(*apitypes.ServerV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", trace.Errorf("Can not convert %T to ServerV2", serverI), "node"))
|
||||
return
|
||||
}
|
||||
server = serverResource
|
||||
|
||||
diags = tfschema.CopyServerV2ToTerraform(ctx, server, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: server.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport Server
|
||||
func (r resourceTeleportServer) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
serverI, err := r.p.Client.GetNode(ctx, defaults.Namespace, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", trace.Wrap(err), "node"))
|
||||
return
|
||||
}
|
||||
|
||||
server := serverI.(*apitypes.ServerV2)
|
||||
diags = tfschema.CopyServerV2ToTerraform(ctx, server, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport Server
|
||||
func (r resourceTeleportServer) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
server := &apitypes.ServerV2{}
|
||||
diags = tfschema.CopyServerV2FromTerraform(ctx, plan, server)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
serverResource := server
|
||||
|
||||
|
||||
if err := serverResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating Server", err, "node"))
|
||||
return
|
||||
}
|
||||
name := serverResource.Metadata.Name
|
||||
|
||||
serverBefore, err := r.p.Client.GetNode(ctx, defaults.Namespace, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", err, "node"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertNode(ctx, serverResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating Server", err, "node"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var serverI apitypes.Server
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
serverI, err = r.p.Client.GetNode(ctx, defaults.Namespace, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", err, "node"))
|
||||
return
|
||||
}
|
||||
if serverBefore.GetMetadata().Revision != serverI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", trace.Wrap(err), "node"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading Server (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "node")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
serverResource, ok := serverI.(*apitypes.ServerV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", trace.Errorf("Can not convert %T to ServerV2", serverI), "node"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyServerV2ToTerraform(ctx, server, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport Server
|
||||
func (r resourceTeleportServer) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteNode(ctx, defaults.Namespace, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting ServerV2", trace.Wrap(err), "node"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports Server state
|
||||
func (r resourceTeleportServer) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
server, err := r.p.Client.GetNode(ctx, defaults.Namespace, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading Server", trace.Wrap(err), "node"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
serverResource := server.(*apitypes.ServerV2)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyServerV2ToTerraform(ctx, serverResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := serverResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportSessionRecordingConfigType is the resource metadata type
|
||||
type resourceTeleportSessionRecordingConfigType struct{}
|
||||
|
||||
// resourceTeleportSessionRecordingConfig is the resource
|
||||
type resourceTeleportSessionRecordingConfig struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportSessionRecordingConfigType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaSessionRecordingConfigV2(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportSessionRecordingConfigType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportSessionRecordingConfig{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the SessionRecordingConfig
|
||||
func (r resourceTeleportSessionRecordingConfig) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
sessionRecordingConfig := &apitypes.SessionRecordingConfigV2{}
|
||||
diags = tfschema.CopySessionRecordingConfigV2FromTerraform(ctx, plan, sessionRecordingConfig)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := sessionRecordingConfig.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting SessionRecordingConfig defaults", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
sessionRecordingConfigBefore, err := r.p.Client.GetSessionRecordingConfig(ctx)
|
||||
if err != nil && !trace.IsNotFound(err) {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.SetSessionRecordingConfig(ctx, sessionRecordingConfig)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
var sessionRecordingConfigI apitypes.SessionRecordingConfig
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
sessionRecordingConfigI, err = r.p.Client.GetSessionRecordingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
if sessionRecordingConfigBefore.GetMetadata().Revision != sessionRecordingConfigI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(bErr), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading SessionRecordingConfig (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "session_recording_config")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
sessionRecordingConfig, ok := sessionRecordingConfigI.(*apitypes.SessionRecordingConfigV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(
|
||||
diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Errorf("Can not convert %T to SessionRecordingConfigV2", sessionRecordingConfigI), "session_recording_config"),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopySessionRecordingConfigV2ToTerraform(ctx, sessionRecordingConfig, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: "session_recording_config"}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport SessionRecordingConfig
|
||||
func (r resourceTeleportSessionRecordingConfig) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
sessionRecordingConfigI, err := r.p.Client.GetSessionRecordingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
sessionRecordingConfig := sessionRecordingConfigI.(*apitypes.SessionRecordingConfigV2)
|
||||
diags = tfschema.CopySessionRecordingConfigV2ToTerraform(ctx, sessionRecordingConfig, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport SessionRecordingConfig
|
||||
func (r resourceTeleportSessionRecordingConfig) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
sessionRecordingConfig := &apitypes.SessionRecordingConfigV2{}
|
||||
diags = tfschema.CopySessionRecordingConfigV2FromTerraform(ctx, plan, sessionRecordingConfig)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := sessionRecordingConfig.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
sessionRecordingConfigBefore, err := r.p.Client.GetSessionRecordingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
err = r.p.Client.SetSessionRecordingConfig(ctx, sessionRecordingConfig)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
var sessionRecordingConfigI apitypes.SessionRecordingConfig
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
sessionRecordingConfigI, err = r.p.Client.GetSessionRecordingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
if sessionRecordingConfigBefore.GetMetadata().Revision != sessionRecordingConfigI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(bErr), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading SessionRecordingConfig (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "session_recording_config")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
sessionRecordingConfig = sessionRecordingConfigI.(*apitypes.SessionRecordingConfigV2)
|
||||
diags = tfschema.CopySessionRecordingConfigV2ToTerraform(ctx, sessionRecordingConfig, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport SessionRecordingConfig
|
||||
func (r resourceTeleportSessionRecordingConfig) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
err := r.p.Client.ResetSessionRecordingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports SessionRecordingConfig state
|
||||
func (r resourceTeleportSessionRecordingConfig) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
sessionRecordingConfigI, err := r.p.Client.GetSessionRecordingConfig(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating SessionRecordingConfig", trace.Wrap(err), "session_recording_config"))
|
||||
return
|
||||
}
|
||||
|
||||
sessionRecordingConfig := sessionRecordingConfigI.(*apitypes.SessionRecordingConfigV2)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopySessionRecordingConfigV2ToTerraform(ctx, sessionRecordingConfig, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
state.Attrs["id"] = types.String{Value: sessionRecordingConfig.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportTrustedClusterType is the resource metadata type
|
||||
type resourceTeleportTrustedClusterType struct{}
|
||||
|
||||
// resourceTeleportTrustedCluster is the resource
|
||||
type resourceTeleportTrustedCluster struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportTrustedClusterType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaTrustedClusterV2(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportTrustedClusterType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportTrustedCluster{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the TrustedCluster
|
||||
func (r resourceTeleportTrustedCluster) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
trustedCluster := &apitypes.TrustedClusterV2{}
|
||||
diags = tfschema.CopyTrustedClusterV2FromTerraform(ctx, plan, trustedCluster)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
trustedClusterResource := trustedCluster
|
||||
|
||||
err = trustedClusterResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting TrustedCluster defaults", trace.Wrap(err), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
id := trustedClusterResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetTrustedCluster(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("TrustedCluster exists in Teleport. Either remove it (tctl rm trusted_cluster/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_trusted_cluster.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("TrustedCluster exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Wrap(err), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertTrustedCluster(ctx, trustedClusterResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating TrustedCluster", trace.Wrap(err), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var trustedClusterI apitypes.TrustedCluster
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
trustedClusterI, err = r.p.Client.GetTrustedCluster(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Wrap(bErr), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading TrustedCluster (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "trusted_cluster")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Wrap(err), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
trustedClusterResource, ok := trustedClusterI.(*apitypes.TrustedClusterV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Errorf("Can not convert %T to TrustedClusterV2", trustedClusterI), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
trustedCluster = trustedClusterResource
|
||||
|
||||
diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: trustedCluster.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport TrustedCluster
|
||||
func (r resourceTeleportTrustedCluster) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
trustedClusterI, err := r.p.Client.GetTrustedCluster(ctx, id.Value)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Wrap(err), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
trustedCluster := trustedClusterI.(*apitypes.TrustedClusterV2)
|
||||
diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport TrustedCluster
|
||||
func (r resourceTeleportTrustedCluster) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
trustedCluster := &apitypes.TrustedClusterV2{}
|
||||
diags = tfschema.CopyTrustedClusterV2FromTerraform(ctx, plan, trustedCluster)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
trustedClusterResource := trustedCluster
|
||||
|
||||
|
||||
if err := trustedClusterResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating TrustedCluster", err, "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
name := trustedClusterResource.Metadata.Name
|
||||
|
||||
trustedClusterBefore, err := r.p.Client.GetTrustedCluster(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", err, "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertTrustedCluster(ctx, trustedClusterResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating TrustedCluster", err, "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var trustedClusterI apitypes.TrustedCluster
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
trustedClusterI, err = r.p.Client.GetTrustedCluster(ctx, name)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", err, "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
if trustedClusterBefore.GetMetadata().Revision != trustedClusterI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Wrap(err), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading TrustedCluster (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "trusted_cluster")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
trustedClusterResource, ok := trustedClusterI.(*apitypes.TrustedClusterV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Errorf("Can not convert %T to TrustedClusterV2", trustedClusterI), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport TrustedCluster
|
||||
func (r resourceTeleportTrustedCluster) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteTrustedCluster(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting TrustedClusterV2", trace.Wrap(err), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports TrustedCluster state
|
||||
func (r resourceTeleportTrustedCluster) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
trustedCluster, err := r.p.Client.GetTrustedCluster(ctx, req.ID)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading TrustedCluster", trace.Wrap(err), "trusted_cluster"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
trustedClusterResource := trustedCluster.(*apitypes.TrustedClusterV2)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedClusterResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := trustedClusterResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/lib/backoff"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// resourceTeleportUserType is the resource metadata type
|
||||
type resourceTeleportUserType struct{}
|
||||
|
||||
// resourceTeleportUser is the resource
|
||||
type resourceTeleportUser struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportUserType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaUserV2(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportUserType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportUser{
|
||||
p: *(p.(*Provider)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the User
|
||||
func (r resourceTeleportUser) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
var err error
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
user := &apitypes.UserV2{}
|
||||
diags = tfschema.CopyUserV2FromTerraform(ctx, plan, user)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
userResource := user
|
||||
|
||||
err = userResource.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error setting User defaults", trace.Wrap(err), "user"))
|
||||
return
|
||||
}
|
||||
|
||||
id := userResource.Metadata.Name
|
||||
|
||||
_, err = r.p.Client.GetUser(ctx, id, false)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
existErr := fmt.Sprintf("User exists in Teleport. Either remove it (tctl rm user/%v)"+
|
||||
" or import it to the existing state (terraform import teleport_user.%v %v)", id, id, id)
|
||||
|
||||
resp.Diagnostics.Append(diagFromErr("User exists in Teleport", trace.Errorf(existErr)))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(err), "user"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.CreateUser(ctx, userResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error creating User", trace.Wrap(err), "user"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var userI apitypes.User
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
userI, err = r.p.Client.GetUser(ctx, id, false)
|
||||
if trace.IsNotFound(err) {
|
||||
if bErr := backoff.Do(ctx); bErr != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(bErr), "user"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading User (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "user")
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(err), "user"))
|
||||
return
|
||||
}
|
||||
|
||||
userResource, ok := userI.(*apitypes.UserV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Errorf("Can not convert %T to UserV2", userI), "user"))
|
||||
return
|
||||
}
|
||||
user = userResource
|
||||
|
||||
diags = tfschema.CopyUserV2ToTerraform(ctx, user, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: user.Metadata.Name}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport User
|
||||
func (r resourceTeleportUser) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var id types.String
|
||||
diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
userI, err := r.p.Client.GetUser(ctx, id.Value, false)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(err), "user"))
|
||||
return
|
||||
}
|
||||
|
||||
user := userI.(*apitypes.UserV2)
|
||||
diags = tfschema.CopyUserV2ToTerraform(ctx, user, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport User
|
||||
func (r resourceTeleportUser) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
user := &apitypes.UserV2{}
|
||||
diags = tfschema.CopyUserV2FromTerraform(ctx, plan, user)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
userResource := user
|
||||
|
||||
|
||||
if err := userResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating User", err, "user"))
|
||||
return
|
||||
}
|
||||
name := userResource.Metadata.Name
|
||||
|
||||
userBefore, err := r.p.Client.GetUser(ctx, name, false)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", err, "user"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client.UpsertUser(ctx, userResource)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error updating User", err, "user"))
|
||||
return
|
||||
}
|
||||
|
||||
// Not really an inferface, just using the same name for easier templating.
|
||||
var userI apitypes.User
|
||||
|
||||
tries := 0
|
||||
backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock())
|
||||
for {
|
||||
tries = tries + 1
|
||||
userI, err = r.p.Client.GetUser(ctx, name, false)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", err, "user"))
|
||||
return
|
||||
}
|
||||
if userBefore.GetMetadata().Revision != userI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
|
||||
if err := backoff.Do(ctx); err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(err), "user"))
|
||||
return
|
||||
}
|
||||
if tries >= r.p.RetryConfig.MaxTries {
|
||||
diagMessage := fmt.Sprintf("Error reading User (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "user")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
userResource, ok := userI.(*apitypes.UserV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Errorf("Can not convert %T to UserV2", userI), "user"))
|
||||
return
|
||||
}
|
||||
diags = tfschema.CopyUserV2ToTerraform(ctx, user, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport User
|
||||
func (r resourceTeleportUser) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
var id types.String
|
||||
diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := r.p.Client.DeleteUser(ctx, id.Value)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error deleting UserV2", trace.Wrap(err), "user"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports User state
|
||||
func (r resourceTeleportUser) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
user, err := r.p.Client.GetUser(ctx, req.ID, false)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(err), "user"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
userResource := user.(*apitypes.UserV2)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyUserV2ToTerraform(ctx, userResource, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := userResource.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
Executable
+2670
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
Copyright 2015-2021 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gravitational/teleport/api/client"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
|
||||
)
|
||||
|
||||
type nextAuditDateComparer struct {
|
||||
client *client.Client
|
||||
nextAuditDate time.Time
|
||||
}
|
||||
|
||||
func (c *nextAuditDateComparer) CaptureNextAuditDate(name string) resource.TestCheckFunc {
|
||||
return func(state *terraform.State) error {
|
||||
al, err := c.client.AccessListClient().GetAccessList(context.TODO(), name)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
c.nextAuditDate = al.Spec.Audit.NextAuditDate
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *nextAuditDateComparer) TestNextAuditDateUnchanged(name string) resource.TestCheckFunc {
|
||||
return func(state *terraform.State) error {
|
||||
al, err := c.client.AccessListClient().GetAccessList(context.TODO(), name)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
diff := cmp.Diff(c.nextAuditDate, al.Spec.Audit.NextAuditDate, cmpopts.EquateApproxTime(2*time.Millisecond))
|
||||
if diff != "" {
|
||||
return trace.CompareFailed("NextAuditDate should not have changed, was %s, is now %s", c.nextAuditDate, al.Spec.Audit.NextAuditDate)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestAccessList() {
|
||||
if !s.teleportFeatures.GetAdvancedAccessWorkflows() {
|
||||
s.T().Skip("Doesn't work in OSS version, requires AdvancedWorkflow")
|
||||
}
|
||||
checkAccessListDestroyed := func(state *terraform.State) error {
|
||||
_, err := s.client.AccessListClient().GetAccessList(context.TODO(), "test")
|
||||
if trace.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
name := "teleport_access_list.test"
|
||||
auditDateChecker := nextAuditDateComparer{client: s.client}
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
CheckDestroy: checkAccessListDestroyed,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("access_list_0_create.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "header.metadata.name", "test"),
|
||||
resource.TestCheckResourceAttr(name, "spec.description", "test description"),
|
||||
resource.TestCheckResourceAttr(name, "spec.owners.0.name", "gru"),
|
||||
resource.TestCheckResourceAttr(name, "spec.membership_requires.roles.0", "minion"),
|
||||
resource.TestCheckResourceAttr(name, "spec.grants.roles.0", "crane-operator"),
|
||||
resource.TestCheckResourceAttr(name, "spec.audit.recurrence.frequency", "3"),
|
||||
auditDateChecker.CaptureNextAuditDate("test"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("access_list_0_create.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("access_list_1_update.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "spec.grants.traits.0.key", "allowed-machines"),
|
||||
resource.TestCheckResourceAttr(name, "spec.grants.traits.0.values.0", "crane"),
|
||||
resource.TestCheckResourceAttr(name, "spec.grants.traits.0.values.1", "forklift"),
|
||||
auditDateChecker.TestNextAuditDateUnchanged("test"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("access_list_1_update.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("access_list_2_expiring.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "header.metadata.expires", "2038-01-01T00:00:00Z"),
|
||||
auditDateChecker.TestNextAuditDateUnchanged("test"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("access_list_2_expiring.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
Copyright 2015-2021 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (s *TerraformSuite) TestApp() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
|
||||
checkDestroyed := func(state *terraform.State) error {
|
||||
_, err := s.client.GetApp(ctx, "test")
|
||||
if trace.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
name := "teleport_app.test"
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
CheckDestroy: checkDestroyed,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("app_0_create.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "app"),
|
||||
resource.TestCheckResourceAttr(name, "spec.uri", "localhost:3000"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("app_0_create.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("app_1_update.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "app"),
|
||||
resource.TestCheckResourceAttr(name, "spec.uri", "localhost:3000"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("app_1_update.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestImportApp() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
|
||||
r := "teleport_app"
|
||||
id := "test_import"
|
||||
name := r + "." + id
|
||||
|
||||
app := &types.AppV3{
|
||||
Metadata: types.Metadata{
|
||||
Name: id,
|
||||
},
|
||||
Spec: types.AppSpecV3{
|
||||
URI: "localhost:3000/test",
|
||||
},
|
||||
}
|
||||
err := app.CheckAndSetDefaults()
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
err = s.client.CreateApp(ctx, app)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
require.Eventually(s.T(), func() bool {
|
||||
_, err := s.client.GetApp(ctx, app.GetName())
|
||||
if trace.IsNotFound(err) {
|
||||
return false
|
||||
}
|
||||
require.NoError(s.T(), err)
|
||||
return true
|
||||
}, 5*time.Second, time.Second)
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.terraformConfig + "\n" + `resource "` + r + `" "` + id + `" { }`,
|
||||
ResourceName: name,
|
||||
ImportState: true,
|
||||
ImportStateId: id,
|
||||
ImportStateCheck: func(state []*terraform.InstanceState) error {
|
||||
require.Equal(s.T(), state[0].Attributes["kind"], "app")
|
||||
require.Equal(s.T(), state[0].Attributes["spec.uri"], "localhost:3000/test")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuiteWithCache) TestAppWithCache() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
checkDestroyed := func(state *terraform.State) error {
|
||||
_, err := s.client.GetApp(ctx, "test")
|
||||
if trace.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
name := "teleport_app.test_with_cache"
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
CheckDestroy: checkDestroyed,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("app_0_create_with_cache.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "app"),
|
||||
resource.TestCheckResourceAttr(name, "spec.uri", "localhost:3000"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("app_0_create_with_cache.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright 2015-2021 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (s *TerraformSuite) TestAuthPreference() {
|
||||
name := "teleport_auth_preference.test"
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
PreventPostDestroyRefresh: true,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("auth_preference_0_set.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "cluster_auth_preference"),
|
||||
resource.TestCheckResourceAttr(name, "metadata.labels.example", "yes"),
|
||||
resource.TestCheckResourceAttr(name, "spec.disconnect_expired_cert", "true"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("auth_preference_0_set.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("auth_preference_1_update.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "cluster_auth_preference"),
|
||||
resource.TestCheckResourceAttr(name, "spec.disconnect_expired_cert", "false"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("auth_preference_1_update.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestImportAuthPreference() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
|
||||
r := "teleport_auth_preference"
|
||||
id := "test_import"
|
||||
name := r + "." + id
|
||||
|
||||
authPreference := &types.AuthPreferenceV2{
|
||||
Metadata: types.Metadata{},
|
||||
Spec: types.AuthPreferenceSpecV2{
|
||||
DisconnectExpiredCert: types.NewBoolOption(true),
|
||||
},
|
||||
}
|
||||
err := authPreference.CheckAndSetDefaults()
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
authPreferencesBefore, err := s.client.GetAuthPreference(ctx)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
err = s.client.SetAuthPreference(ctx, authPreference)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
require.Eventually(s.T(), func() bool {
|
||||
authPreferencesCurrent, err := s.client.GetAuthPreference(ctx)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
return authPreferencesBefore.GetMetadata().ID != authPreferencesCurrent.GetMetadata().ID
|
||||
}, 5*time.Second, time.Second)
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.terraformConfig + "\n" + `resource "` + r + `" "` + id + `" { }`,
|
||||
ResourceName: name,
|
||||
ImportState: true,
|
||||
ImportStateId: id,
|
||||
ImportStateCheck: func(state []*terraform.InstanceState) error {
|
||||
require.Equal(s.T(), state[0].Attributes["kind"], "cluster_auth_preference")
|
||||
require.Equal(s.T(), state[0].Attributes["spec.disconnect_expired_cert"], "true")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuiteWithCache) TestAuthPreferenceAddLabel() {
|
||||
name := "teleport_auth_preference.cluster_auth_preference"
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
PreventPostDestroyRefresh: true,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("auth_preference_0_cluster.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "cluster_auth_preference"),
|
||||
resource.TestCheckNoResourceAttr(name, "metadata.labels"),
|
||||
resource.TestCheckResourceAttr(name, "spec.type", "oidc"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("auth_preference_0_cluster.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("auth_preference_1_cluster.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "cluster_auth_preference"),
|
||||
resource.TestCheckResourceAttr(name, "metadata.labels.provisioner", "terraform"),
|
||||
resource.TestCheckResourceAttr(name, "spec.type", "oidc"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("auth_preference_1_cluster.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright 2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
|
||||
)
|
||||
|
||||
func (s *TerraformSuite) TestBot() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
|
||||
checkResourcesDestroyed := func(state *terraform.State) error {
|
||||
var errs []error
|
||||
if _, err := s.client.GetToken(ctx, "bot-test"); err != nil {
|
||||
if !trace.IsNotFound(err) {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := s.client.GetUser(ctx, "bot-test", false); err != nil {
|
||||
if !trace.IsNotFound(err) {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return trace.NewAggregate(errs...)
|
||||
}
|
||||
|
||||
tokenName := "teleport_provision_token.bot_test"
|
||||
botName := "teleport_bot.test"
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
CheckDestroy: checkResourcesDestroyed,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("bot_0_create.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(tokenName, "kind", "token"),
|
||||
resource.TestCheckResourceAttr(tokenName, "metadata.name", "bot-test"),
|
||||
resource.TestCheckResourceAttr(tokenName, "spec.roles.0", "Bot"),
|
||||
resource.TestCheckResourceAttr(botName, "name", "test"),
|
||||
resource.TestCheckResourceAttr(botName, "user_name", "bot-test"),
|
||||
resource.TestCheckResourceAttr(botName, "role_name", "bot-test"),
|
||||
resource.TestCheckResourceAttr(botName, "token_id", "bot-test"),
|
||||
resource.TestCheckResourceAttr(botName, "roles.0", "terraform"),
|
||||
resource.TestCheckNoResourceAttr(botName, "spec.traits.logins1"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("bot_0_create.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("bot_1_update.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(tokenName, "kind", "token"),
|
||||
resource.TestCheckResourceAttr(tokenName, "metadata.name", "bot-test"),
|
||||
resource.TestCheckResourceAttr(tokenName, "spec.roles.0", "Bot"),
|
||||
resource.TestCheckResourceAttr(botName, "name", "test"),
|
||||
resource.TestCheckResourceAttr(botName, "user_name", "bot-test"),
|
||||
resource.TestCheckResourceAttr(botName, "role_name", "bot-test"),
|
||||
resource.TestCheckResourceAttr(botName, "token_id", "bot-test"),
|
||||
resource.TestCheckResourceAttr(botName, "roles.0", "terraform"),
|
||||
|
||||
// Note: traits are immutable and the plan will not converge
|
||||
// if the resource is not recreated when traits are
|
||||
// modified.
|
||||
resource.TestCheckResourceAttr(botName, "traits.logins1.0", "example"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("bot_1_update.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2015-2021 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
)
|
||||
|
||||
func (s *TerraformSuite) TestClusterMaintenanceConfig() {
|
||||
name := "teleport_cluster_maintenance_config.test"
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
PreventPostDestroyRefresh: true,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("maintenance_config_0_set.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "cluster_maintenance_config"),
|
||||
resource.TestCheckResourceAttr(name, "spec.agent_upgrades.utc_start_hour", "1"),
|
||||
resource.TestCheckResourceAttr(name, "spec.agent_upgrades.weekdays.0", "monday"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("maintenance_config_0_set.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("maintenance_config_1_update.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "cluster_maintenance_config"),
|
||||
resource.TestCheckResourceAttr(name, "spec.agent_upgrades.utc_start_hour", "12"),
|
||||
resource.TestCheckResourceAttr(name, "spec.agent_upgrades.weekdays.0", "tuesday"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("maintenance_config_1_update.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2023 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (s *TerraformSuite) TestClusterNetworkingConfig() {
|
||||
name := "teleport_cluster_networking_config.test"
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
PreventPostDestroyRefresh: true,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("networking_config_0_set.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "cluster_networking_config"),
|
||||
resource.TestCheckResourceAttr(name, "metadata.labels.example", "yes"),
|
||||
resource.TestCheckResourceAttr(name, "spec.client_idle_timeout", "30m"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("networking_config_0_set.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("networking_config_1_update.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "cluster_networking_config"),
|
||||
resource.TestCheckResourceAttr(name, "metadata.labels.example", "no"),
|
||||
resource.TestCheckResourceAttr(name, "spec.client_idle_timeout", "1h"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("networking_config_1_update.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestImportClusterNetworkingConfig() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
|
||||
r := "teleport_cluster_networking_config"
|
||||
id := "test_import"
|
||||
name := r + "." + id
|
||||
|
||||
clusterNetworkingConfig := &types.ClusterNetworkingConfigV2{
|
||||
Metadata: types.Metadata{},
|
||||
Spec: types.ClusterNetworkingConfigSpecV2{
|
||||
ClientIdleTimeout: types.Duration(30 * time.Second),
|
||||
},
|
||||
}
|
||||
err := clusterNetworkingConfig.CheckAndSetDefaults()
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
clusterNetworkConfigBefore, err := s.client.GetClusterNetworkingConfig(ctx)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
err = s.client.SetClusterNetworkingConfig(ctx, clusterNetworkingConfig)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
require.Eventually(s.T(), func() bool {
|
||||
clusterNetworkConfigCurrent, err := s.client.GetClusterNetworkingConfig(ctx)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
return clusterNetworkConfigBefore.GetMetadata().ID != clusterNetworkConfigCurrent.GetMetadata().ID
|
||||
}, 5*time.Second, time.Second)
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.terraformConfig + "\n" + `resource "` + r + `" "` + id + `" { }`,
|
||||
ResourceName: name,
|
||||
ImportState: true,
|
||||
ImportStateId: id,
|
||||
ImportStateCheck: func(state []*terraform.InstanceState) error {
|
||||
require.Equal(s.T(), state[0].Attributes["kind"], "cluster_networking_config")
|
||||
require.Equal(s.T(), state[0].Attributes["spec.client_idle_timeout"], "30s")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright 2022 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (s *TerraformSuite) getTLSCerts() {
|
||||
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestConfigureAuthBase64() {
|
||||
name := "teleport_app.test_auth_b64"
|
||||
|
||||
key, err := os.ReadFile(s.teleportConfig.ClientKey)
|
||||
require.NoError(s.T(), err)
|
||||
keyBase64 := base64.StdEncoding.EncodeToString(key)
|
||||
|
||||
cert, err := os.ReadFile(s.teleportConfig.ClientCrt)
|
||||
require.NoError(s.T(), err)
|
||||
certBase64 := base64.StdEncoding.EncodeToString(cert)
|
||||
|
||||
rootCA, err := os.ReadFile(s.teleportConfig.RootCAs)
|
||||
require.NoError(s.T(), err)
|
||||
rootCABase64 := base64.StdEncoding.EncodeToString(rootCA)
|
||||
|
||||
providerConfigUsingB64Auth := `
|
||||
provider "teleport" {
|
||||
addr = "` + s.teleportConfig.Addr + `"
|
||||
key_base64 = "` + keyBase64 + `"
|
||||
cert_base64 = "` + certBase64 + `"
|
||||
root_ca_base64 = "` + rootCABase64 + `"
|
||||
}
|
||||
`
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixtureWithCustomConfig("app_0_create_auth_b64.tf", providerConfigUsingB64Auth),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "app"),
|
||||
resource.TestCheckResourceAttr(name, "spec.uri", "localhost:3000"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestConfigureAuthFiles() {
|
||||
name := "teleport_app.test_auth_files"
|
||||
|
||||
providerConfigUsingAuthFiles := `
|
||||
provider "teleport" {
|
||||
addr = "` + s.teleportConfig.Addr + `"
|
||||
key_path = "` + s.teleportConfig.ClientKey + `"
|
||||
cert_path = "` + s.teleportConfig.ClientCrt + `"
|
||||
root_ca_path = "` + s.teleportConfig.RootCAs + `"
|
||||
}
|
||||
`
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixtureWithCustomConfig("app_0_create_auth_files.tf", providerConfigUsingAuthFiles),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "app"),
|
||||
resource.TestCheckResourceAttr(name, "spec.uri", "localhost:3000"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestConfigureIdentityFilePath() {
|
||||
name := "teleport_app.test"
|
||||
|
||||
providerConfigUsingAuthFiles := `
|
||||
provider "teleport" {
|
||||
addr = "` + s.teleportConfig.Addr + `"
|
||||
identity_file_path = "` + s.teleportConfig.Identity + `"
|
||||
}
|
||||
`
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixtureWithCustomConfig("app_0_create.tf", providerConfigUsingAuthFiles),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "app"),
|
||||
resource.TestCheckResourceAttr(name, "spec.uri", "localhost:3000"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestConfigureIdentityFileBase64() {
|
||||
name := "teleport_app.test"
|
||||
|
||||
identity, err := os.ReadFile(s.teleportConfig.Identity)
|
||||
require.NoError(s.T(), err)
|
||||
identityAsB64 := base64.StdEncoding.EncodeToString(identity)
|
||||
|
||||
providerConfigUsingAuthFiles := `
|
||||
provider "teleport" {
|
||||
addr = "` + s.teleportConfig.Addr + `"
|
||||
identity_file_base64 = "` + identityAsB64 + `"
|
||||
}
|
||||
`
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixtureWithCustomConfig("app_0_create.tf", providerConfigUsingAuthFiles),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "app"),
|
||||
resource.TestCheckResourceAttr(name, "spec.uri", "localhost:3000"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestConfigureIdentityFileBase64_InvalidBase64() {
|
||||
identityAsB64 := base64.StdEncoding.EncodeToString([]byte("invalid"))
|
||||
|
||||
providerConfigUsingAuthFiles := `
|
||||
provider "teleport" {
|
||||
addr = "` + s.teleportConfig.Addr + `"
|
||||
identity_file_base64 = "` + identityAsB64 + `"
|
||||
dial_timeout_duration = "1s"
|
||||
}
|
||||
`
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixtureWithCustomConfig("app_0_create.tf", providerConfigUsingAuthFiles),
|
||||
ExpectError: regexp.MustCompile("identity file could not be decoded"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright 2015-2021 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func (s *TerraformSuite) TestDatabase() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
|
||||
checkDestroyed := func(state *terraform.State) error {
|
||||
_, err := s.client.GetDatabase(ctx, "test")
|
||||
if trace.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
name := "teleport_database.test"
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
CheckDestroy: checkDestroyed,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("database_0_create.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "db"),
|
||||
resource.TestCheckResourceAttr(name, "metadata.expires", "2032-10-12T07:20:50Z"),
|
||||
resource.TestCheckResourceAttr(name, "spec.protocol", "postgres"),
|
||||
resource.TestCheckResourceAttr(name, "spec.uri", "localhost:5432"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("database_0_create.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("database_1_update.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "kind", "db"),
|
||||
resource.TestCheckResourceAttr(name, "metadata.expires", "2032-10-12T07:20:50Z"),
|
||||
resource.TestCheckResourceAttr(name, "spec.protocol", "postgres"),
|
||||
resource.TestCheckResourceAttr(name, "spec.uri", "example.com:5432"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("database_1_update.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestImportDatabase() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
|
||||
r := "teleport_database"
|
||||
id := "test-import"
|
||||
name := r + "." + id
|
||||
|
||||
database := &types.DatabaseV3{
|
||||
Metadata: types.Metadata{
|
||||
Name: id,
|
||||
},
|
||||
Spec: types.DatabaseSpecV3{
|
||||
Protocol: "postgres",
|
||||
URI: "localhost:3000/test",
|
||||
},
|
||||
}
|
||||
err := database.CheckAndSetDefaults()
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
err = s.client.CreateDatabase(ctx, database)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
require.Eventually(s.T(), func() bool {
|
||||
_, err := s.client.GetDatabase(ctx, database.GetName())
|
||||
if trace.IsNotFound(err) {
|
||||
return false
|
||||
}
|
||||
require.NoError(s.T(), err)
|
||||
return true
|
||||
}, 5*time.Second, time.Second)
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.terraformConfig + "\n" + `resource "` + r + `" "` + id + `" { }`,
|
||||
ResourceName: name,
|
||||
ImportState: true,
|
||||
ImportStateId: id,
|
||||
ImportStateCheck: func(state []*terraform.InstanceState) error {
|
||||
require.Equal(s.T(), state[0].Attributes["kind"], "db")
|
||||
require.Equal(s.T(), state[0].Attributes["spec.uri"], "localhost:3000/test")
|
||||
require.Equal(s.T(), state[0].Attributes["spec.protocol"], "postgres")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright 2015-2023 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
// devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
|
||||
)
|
||||
|
||||
func (s *TerraformSuite) TestTrustedDevices() {
|
||||
if !s.teleportFeatures.GetDeviceTrust().GetEnabled() {
|
||||
s.T().Skip("Doesn't work in OSS version, requires Device Trust")
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
|
||||
device1 := "teleport_trusted_device.TESTDEVICE1"
|
||||
device2 := "teleport_trusted_device.TESTDEVICE2"
|
||||
|
||||
allDevices := []string{device1, device2}
|
||||
|
||||
checkDeviceDestroyed := func(state *terraform.State) error {
|
||||
for _, deviceName := range allDevices {
|
||||
_, err := s.client.GetDeviceResource(ctx, deviceName)
|
||||
switch {
|
||||
case err == nil:
|
||||
return fmt.Errorf("Device %s was not deleted", deviceName)
|
||||
case trace.IsNotFound(err):
|
||||
continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
CheckDestroy: checkDeviceDestroyed,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.getFixture("device_trust_0_create.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(device1, "spec.asset_tag", "TESTDEVICE1"),
|
||||
resource.TestCheckResourceAttr(device1, "spec.os_type", "macos"),
|
||||
resource.TestCheckResourceAttr(device1, "spec.enroll_status", "enrolled"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("device_trust_0_create.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("device_trust_1_update.tf"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(device1, "spec.enroll_status", "not_enrolled"),
|
||||
resource.TestCheckResourceAttr(device2, "spec.asset_tag", "TESTDEVICE2"),
|
||||
resource.TestCheckResourceAttr(device2, "spec.os_type", "linux"),
|
||||
resource.TestCheckResourceAttr(device2, "spec.enroll_status", "not_enrolled"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: s.getFixture("device_trust_1_update.tf"),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TerraformSuite) TestImportTrustedDevices() {
|
||||
if !s.teleportFeatures.GetDeviceTrust().GetEnabled() {
|
||||
s.T().Skip("Doesn't work in OSS version, requires Device Trust")
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
r := "teleport_trusted_device"
|
||||
id := "test_device"
|
||||
deviceID := "1a6d1c46-cccf-4f58-8f67-85e6272ebef1"
|
||||
name := r + "." + id
|
||||
|
||||
device := &types.DeviceV1{
|
||||
ResourceHeader: types.ResourceHeader{
|
||||
Kind: "device",
|
||||
Metadata: types.Metadata{
|
||||
Name: deviceID,
|
||||
},
|
||||
},
|
||||
Spec: &types.DeviceSpec{
|
||||
AssetTag: "DEVICE1",
|
||||
OsType: "macos",
|
||||
EnrollStatus: "not_enrolled",
|
||||
},
|
||||
}
|
||||
|
||||
_, err := s.client.CreateDeviceResource(ctx, device)
|
||||
s.Require().NoError(err)
|
||||
|
||||
resource.Test(s.T(), resource.TestCase{
|
||||
ProtoV6ProviderFactories: s.terraformProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.terraformConfig + "\n" + `resource "` + r + `" "` + id + `" { }`,
|
||||
ResourceName: name,
|
||||
ImportState: true,
|
||||
ImportStateId: deviceID,
|
||||
ImportStateCheck: func(state []*terraform.InstanceState) error {
|
||||
s.Require().Equal(state[0].Attributes["metadata.name"], deviceID)
|
||||
s.Require().Equal(state[0].Attributes["kind"], "device")
|
||||
s.Require().Equal(state[0].Attributes["spec.asset_tag"], "DEVICE1")
|
||||
s.Require().Equal(state[0].Attributes["spec.os_type"], "macos")
|
||||
s.Require().Equal(state[0].Attributes["spec.enroll_status"], "not_enrolled")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
resource "teleport_access_list" "test" {
|
||||
header = {
|
||||
version = "v1"
|
||||
metadata = {
|
||||
name = "test"
|
||||
labels = {
|
||||
example = "yes"
|
||||
}
|
||||
}
|
||||
}
|
||||
spec = {
|
||||
description = "test description"
|
||||
owners = [
|
||||
{
|
||||
name = "gru"
|
||||
description = "The supervillain."
|
||||
}
|
||||
]
|
||||
membership_requires = {
|
||||
roles = ["minion"]
|
||||
}
|
||||
ownership_requires = {
|
||||
roles = ["supervillain"]
|
||||
}
|
||||
grants = {
|
||||
roles = ["crane-operator"]
|
||||
}
|
||||
title = "Hello"
|
||||
audit = {
|
||||
recurrence = {
|
||||
frequency = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
resource "teleport_access_list" "test" {
|
||||
header = {
|
||||
version = "v1"
|
||||
metadata = {
|
||||
name = "test"
|
||||
labels = {
|
||||
example = "yes"
|
||||
}
|
||||
}
|
||||
}
|
||||
spec = {
|
||||
description = "test description"
|
||||
owners = [
|
||||
{
|
||||
name = "gru"
|
||||
description = "The supervillain."
|
||||
}
|
||||
]
|
||||
membership_requires = {
|
||||
roles = ["minion"]
|
||||
}
|
||||
ownership_requires = {
|
||||
roles = ["supervillain"]
|
||||
}
|
||||
grants = {
|
||||
roles = ["crane-operator"]
|
||||
traits = [{
|
||||
key = "allowed-machines"
|
||||
values = ["crane", "forklift"]
|
||||
}]
|
||||
}
|
||||
title = "Hello"
|
||||
audit = {
|
||||
recurrence = {
|
||||
frequency = 3
|
||||
// changing day of the month should not change the next audit date
|
||||
// it should take effect after the next review
|
||||
day_of_month = 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
resource "teleport_access_list" "test" {
|
||||
header = {
|
||||
version = "v1"
|
||||
metadata = {
|
||||
name = "test"
|
||||
labels = {
|
||||
example = "yes"
|
||||
}
|
||||
expires = "2038-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
spec = {
|
||||
description = "test description"
|
||||
owners = [
|
||||
{
|
||||
name = "gru"
|
||||
description = "The supervillain."
|
||||
}
|
||||
]
|
||||
membership_requires = {
|
||||
roles = ["minion"]
|
||||
}
|
||||
ownership_requires = {
|
||||
roles = ["supervillain"]
|
||||
}
|
||||
grants = {
|
||||
roles = ["crane-operator"]
|
||||
traits = [{
|
||||
key = "allowed-machines"
|
||||
values = ["crane", "forklift"]
|
||||
}]
|
||||
}
|
||||
title = "Hello"
|
||||
audit = {
|
||||
recurrence = {
|
||||
frequency = 3
|
||||
day_of_month = 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
resource "teleport_app" "test" {
|
||||
version = "v3"
|
||||
metadata = {
|
||||
name = "example"
|
||||
description = "Test app"
|
||||
labels = {
|
||||
example = "yes"
|
||||
"teleport.dev/origin" = "dynamic"
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
uri = "localhost:3000"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
resource "teleport_app" "test_auth_b64" {
|
||||
version = "v3"
|
||||
metadata = {
|
||||
name = "test_auth_b64"
|
||||
description = "Test app"
|
||||
labels = {
|
||||
example = "yes"
|
||||
"teleport.dev/origin" = "dynamic"
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
uri = "localhost:3000"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
resource "teleport_app" "test_auth_files" {
|
||||
version = "v3"
|
||||
metadata = {
|
||||
name = "test_auth_files"
|
||||
description = "Test app"
|
||||
labels = {
|
||||
example = "yes"
|
||||
"teleport.dev/origin" = "dynamic"
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
uri = "localhost:3000"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
resource "teleport_app" "test_with_cache" {
|
||||
version = "v3"
|
||||
metadata = {
|
||||
name = "example"
|
||||
description = "Test app"
|
||||
labels = {
|
||||
example = "yes"
|
||||
"teleport.dev/origin" = "dynamic"
|
||||
}
|
||||
}
|
||||
|
||||
spec = {
|
||||
uri = "localhost:3000"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user