From 0a5793d1c204e5beb337d2076c8b47458fd1ca71 Mon Sep 17 00:00:00 2001 From: Hugo Shaka Date: Fri, 15 Sep 2023 11:33:49 -0400 Subject: [PATCH] terraform: access list support This PR adds access list support to the Terraform provider. Since we stopped using gogoproto, generating new resources became increasingly complex. The access list terraform schema was generated from the protobuf types and not the ones in `api/types`, which causes all kind of small oddities from a user PoV, and made this PR way more complex. User-facing limitations: - the `metadata` field is nested under the `header` one because of the protobuf structure - the traits is not a map but a list of structs, each struct with a key and a value attribute Non user-facing limitations the PR had to work around: - the protobuf time is different from go's time, I initially wanted to create `ProtoTimeType`/`ProtoTimeValue` but we cannot embbed a protobuf timestamp directly (because it contains a lock, the terraform value must be a reference, which is not supported by the protoc generator). The workaround is to use custom types (`Timestamp` and `Duration`). - the provider has to do the conversion between the proto type and the api/type. This required writing a new provider template for new non-gogo resources (`gen/plural_data_source_new.go.tpl` and `gen/plural_resources_new.go.tpl`) - The time type difference caused conversion issues fixed by https://github.com/gravitational/teleport/pull/32135 --- terraform/Makefile | 10 +- terraform/example/access_list.tf.example | 36 + terraform/example/terraform.yaml | 1 + terraform/gen/main.go | 375 +-- terraform/gen/plural_data_source.go.tpl | 14 +- terraform/gen/plural_resource.go.tpl | 136 +- .../protoc-gen-terraform-accesslist.yaml | 75 + .../data_source_teleport_access_list.go | 84 + .../provider/data_source_teleport_app.go | 2 + .../provider/data_source_teleport_database.go | 2 + .../data_source_teleport_device_trust.go | 1 + .../data_source_teleport_github_connector.go | 2 + .../data_source_teleport_login_rule.go | 1 + .../data_source_teleport_oidc_connector.go | 2 + .../data_source_teleport_okta_import_rule.go | 2 + .../data_source_teleport_provision_token.go | 2 + .../provider/data_source_teleport_role.go | 2 + .../data_source_teleport_saml_connector.go | 2 + .../data_source_teleport_trusted_cluster.go | 2 + .../provider/data_source_teleport_user.go | 2 + terraform/provider/provider.go | 2 + .../provider/resource_teleport_access_list.go | 332 +++ terraform/provider/resource_teleport_app.go | 50 +- .../provider/resource_teleport_database.go | 50 +- .../resource_teleport_device_trust.go | 43 +- .../resource_teleport_github_connector.go | 50 +- .../provider/resource_teleport_login_rule.go | 43 +- .../resource_teleport_oidc_connector.go | 50 +- .../resource_teleport_okta_import_rule.go | 50 +- .../resource_teleport_provision_token.go | 50 +- terraform/provider/resource_teleport_role.go | 50 +- .../resource_teleport_saml_connector.go | 50 +- .../resource_teleport_trusted_cluster.go | 50 +- terraform/provider/resource_teleport_user.go | 50 +- terraform/reference.mdx | 160 ++ terraform/test/access_list_test.go | 81 + .../test/fixtures/access_list_0_create.tf | 32 + .../test/fixtures/access_list_1_update.tf | 36 + .../test/fixtures/access_list_2_expiring.tf | 37 + terraform/test/main_test.go | 1 + .../accesslist/v1/accesslist_terraform.go | 2208 +++++++++++++++++ .../tfschema/accesslist/v1/custom_types.go | 100 + 42 files changed, 3874 insertions(+), 454 deletions(-) create mode 100644 terraform/example/access_list.tf.example create mode 100644 terraform/protoc-gen-terraform-accesslist.yaml create mode 100755 terraform/provider/data_source_teleport_access_list.go create mode 100755 terraform/provider/resource_teleport_access_list.go create mode 100644 terraform/test/access_list_test.go create mode 100644 terraform/test/fixtures/access_list_0_create.tf create mode 100644 terraform/test/fixtures/access_list_1_update.tf create mode 100644 terraform/test/fixtures/access_list_2_expiring.tf create mode 100644 terraform/tfschema/accesslist/v1/accesslist_terraform.go create mode 100644 terraform/tfschema/accesslist/v1/custom_types.go diff --git a/terraform/Makefile b/terraform/Makefile index f604802b5..f9940f214 100644 --- a/terraform/Makefile +++ b/terraform/Makefile @@ -79,11 +79,19 @@ endif --terraform_out=config=protoc-gen-terraform-devicetrust.yaml:./tfschema \ teleport/legacy/types/device.proto - @go run ./gen/main.go + @protoc \ + -I$(API_MOD_PATH)/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 diff --git a/terraform/example/access_list.tf.example b/terraform/example/access_list.tf.example new file mode 100644 index 000000000..0fc91a3ab --- /dev/null +++ b/terraform/example/access_list.tf.example @@ -0,0 +1,36 @@ +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 = { + frequency = "3600h" // 150 days + } + } +} diff --git a/terraform/example/terraform.yaml b/terraform/example/terraform.yaml index 4aec1933e..608d274bc 100644 --- a/terraform/example/terraform.yaml +++ b/terraform/example/terraform.yaml @@ -28,6 +28,7 @@ spec: - login_rule - device - okta_import_rule + - access_list verbs: ['list','create','read','update','delete'] version: v6 --- diff --git a/terraform/gen/main.go b/terraform/gen/main.go index 887e7a35c..291c9aac1 100644 --- a/terraform/gen/main.go +++ b/terraform/gen/main.go @@ -35,6 +35,7 @@ import ( "github.com/gravitational/teleport-plugins/terraform/provider" "github.com/gravitational/teleport-plugins/terraform/tfschema" + accesslistSchema "github.com/gravitational/teleport-plugins/terraform/tfschema/accesslist/v1" devicetrustSchema "github.com/gravitational/teleport-plugins/terraform/tfschema/devicetrust/v1" loginruleSchema "github.com/gravitational/teleport-plugins/terraform/tfschema/loginrule/v1" ) @@ -93,6 +94,8 @@ type payload struct { // 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. @@ -101,6 +104,8 @@ type payload struct { 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 } func (p *payload) CheckAndSetDefaults() error { @@ -132,195 +137,208 @@ const ( 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", + 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", + 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, + 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, + 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", + 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", + 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: "UpsertGithubConnector", - UpdateMethod: "UpsertGithubConnector", - DeleteMethod: "DeleteGithubConnector", - WithSecrets: "true", - ID: "githubConnector.Metadata.Name", - Kind: "github", - HasStaticID: true, - TerraformResourceType: "teleport_github_connector", + Name: "GithubConnector", + TypeName: "GithubConnectorV3", + VarName: "githubConnector", + GetMethod: "GetGithubConnector", + CreateMethod: "UpsertGithubConnector", + UpdateMethod: "UpsertGithubConnector", + 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: "UpsertOIDCConnector", - UpdateMethod: "UpsertOIDCConnector", - DeleteMethod: "DeleteOIDCConnector", - WithSecrets: "true", - ID: "oidcConnector.Metadata.Name", - Kind: "oidc", - HasStaticID: true, - TerraformResourceType: "teleport_oidc_connector", + Name: "OIDCConnector", + TypeName: "OIDCConnectorV3", + VarName: "oidcConnector", + GetMethod: "GetOIDCConnector", + CreateMethod: "UpsertOIDCConnector", + UpdateMethod: "UpsertOIDCConnector", + 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: "UpsertSAMLConnector", - UpdateMethod: "UpsertSAMLConnector", - DeleteMethod: "DeleteSAMLConnector", - WithSecrets: "true", - ID: "samlConnector.Metadata.Name", - Kind: "saml", - HasStaticID: true, - TerraformResourceType: "teleport_saml_connector", + Name: "SAMLConnector", + TypeName: "SAMLConnectorV2", + VarName: "samlConnector", + GetMethod: "GetSAMLConnector", + CreateMethod: "UpsertSAMLConnector", + UpdateMethod: "UpsertSAMLConnector", + 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", + 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: "UpsertRole", - UpdateMethod: "UpsertRole", - DeleteMethod: "DeleteRole", - ID: "role.Metadata.Name", - Kind: "role", - HasStaticID: false, - TerraformResourceType: "teleport_role", + Name: "Role", + TypeName: "RoleV6", + VarName: "role", + GetMethod: "GetRole", + CreateMethod: "UpsertRole", + UpdateMethod: "UpsertRole", + 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", + 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", + 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: "UpdateUser", - DeleteMethod: "DeleteUser", - WithSecrets: "false", - GetWithoutContext: true, - ID: "user.Metadata.Name", - Kind: "user", - HasStaticID: false, - TerraformResourceType: "teleport_user", + Name: "User", + TypeName: "UserV2", + VarName: "user", + GetMethod: "GetUser", + CreateMethod: "CreateUser", + UpdateMethod: "UpdateUser", + DeleteMethod: "DeleteUser", + WithSecrets: "false", + GetWithoutContext: true, + ID: "user.Metadata.Name", + Kind: "user", + HasStaticID: false, + TerraformResourceType: "teleport_user", + HasCheckAndSetDefaults: true, } loginRule = payload{ @@ -362,19 +380,41 @@ var ( } 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", + 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-plugins/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, } ) @@ -419,6 +459,8 @@ func genTFSchema() { generateDataSource(deviceTrust, pluralDataSource) generateResource(oktaImportRule, pluralResource) generateDataSource(oktaImportRule, pluralDataSource) + generateResource(accessList, pluralResource) + generateDataSource(accessList, pluralDataSource) } func generateResource(p payload, tpl string) { @@ -455,6 +497,7 @@ func generate(p payload, tpl, outFile string) { // 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, diff --git a/terraform/gen/plural_data_source.go.tpl b/terraform/gen/plural_data_source.go.tpl index 00e421365..8ce988456 100644 --- a/terraform/gen/plural_data_source.go.tpl +++ b/terraform/gen/plural_data_source.go.tpl @@ -27,7 +27,11 @@ import ( {{.SchemaPackage}} "{{.SchemaPackagePath}}" {{if not .IsPlainStruct -}} + {{- if .ConvertPackagePath}} + convert "{{.ConvertPackagePath}}" + {{ else }} {{.ProtoPackage}} "{{.ProtoPackagePath}}" + {{- end}} {{end -}} "github.com/gravitational/trace" ) @@ -55,7 +59,11 @@ func (r dataSourceTeleport{{.Name}}Type) NewDataSource(_ context.Context, p tfsd // 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 @@ -70,9 +78,11 @@ func (r dataSourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadDat var state types.Object {{if .IsPlainStruct -}} {{.VarName}} := {{.VarName}}I - {{else -}} + {{else if .ConvertPackagePath -}} + {{.VarName}} := convert.ToProto({{.VarName}}I) + {{else}} {{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) - {{end -}} + {{- end}} diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/terraform/gen/plural_resource.go.tpl b/terraform/gen/plural_resource.go.tpl index 718875b6a..6ae017591 100644 --- a/terraform/gen/plural_resource.go.tpl +++ b/terraform/gen/plural_resource.go.tpl @@ -38,6 +38,9 @@ import ( {{.ProtoPackage}} "{{.ProtoPackagePath}}" {{.SchemaPackage}} "{{.SchemaPackagePath}}" +{{- if .ConvertPackagePath}} + convert "{{.ConvertPackagePath}}" +{{- end}} "github.com/gravitational/teleport/integrations/lib/backoff" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" @@ -65,6 +68,7 @@ func (r resourceTeleport{{.Name}}Type) NewResource(_ context.Context, p tfsdk.Pr // 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 } @@ -104,13 +108,22 @@ func (r resourceTeleport{{.Name}}) Create(ctx context.Context, req tfsdk.CreateR {{.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}} + id := {{.VarName}}Resource.Metadata.Name - _, err := r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}{{.VarName}}.Metadata.Name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) + _, err = r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}id{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) if !trace.IsNotFound(err) { if err == nil { - n := {{.VarName}}.Metadata.Name 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)", n, n, n) + " 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 @@ -120,28 +133,29 @@ func (r resourceTeleport{{.Name}}) Create(ctx context.Context, req tfsdk.CreateR return } - {{if not .IsPlainStruct -}} - err = {{.VarName}}.CheckAndSetDefaults() + {{if .HasCheckAndSetDefaults -}} + err = {{.VarName}}Resource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting {{.Name}} defaults", trace.Wrap(err), "{{.Kind}}")) return } {{- end}} - {{if eq .UpsertMethodArity 2}}_, {{end}}err = r.p.Client.{{.CreateMethod}}(ctx, {{.VarName}}) + {{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 } - - id := {{.VarName}}.Metadata.Name - {{if .IsPlainStruct -}} + {{- 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}}.{{.Name}} - {{else -}} var {{.VarName}}I {{.ProtoPackage}}.{{ if ne .IfaceName ""}}{{.IfaceName}}{{else}}{{.Name}}{{end}} - {{- end}} - + {{- end}} + {{- end }} tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -167,16 +181,22 @@ func (r resourceTeleport{{.Name}}) Create(ctx context.Context, req tfsdk.CreateR return } - {{if .IsPlainStruct -}} - {{.VarName}} = {{.VarName}}I + {{if or .IsPlainStruct .ConvertPackagePath -}} + {{.VarName}}Resource = {{.VarName}}I {{else -}} - {{.VarName}}, ok := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) + {{.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() { @@ -202,7 +222,11 @@ func (r resourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadResou } 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 @@ -218,10 +242,11 @@ func (r resourceTeleport{{.Name}}) Read(ctx context.Context, req tfsdk.ReadResou resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}")) return } - {{if .IsPlainStruct -}} {{.VarName}} := {{.VarName}}I - {{else -}} + {{else if .ConvertPackagePath -}} + {{.VarName}} := convert.ToProto({{.VarName}}I) + {{ else }} {{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) {{end -}} diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state) @@ -258,15 +283,23 @@ func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateR return } - name := {{.VarName}}.Metadata.Name - - {{if not .IsPlainStruct -}} - err := {{.VarName}}.CheckAndSetDefaults() +{{- 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}}({{if not .GetWithoutContext}}ctx, {{end}}name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) if err != nil { @@ -274,38 +307,35 @@ func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateR return } - {{if eq .UpsertMethodArity 2}}_, {{end}}err = r.p.Client.{{.UpdateMethod}}(ctx, {{.VarName}}) + {{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 not .IsPlainStruct -}} + {{- 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}} + {{- end }} tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { tries = tries + 1 - {{if .IsPlainStruct -}} - {{.VarName}}, err = r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) - {{else -}} {{.VarName}}I, err = r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}name{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) - {{end -}} if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", err, "{{.Kind}}")) return } - {{if .IsPlainStruct -}} - if {{.VarName}}Before.GetMetadata().ID != {{.VarName}}.GetMetadata().ID || {{.HasStaticID}} { - break - } - {{else -}} if {{.VarName}}Before.GetMetadata().ID != {{.VarName}}I.GetMetadata().ID || {{.HasStaticID}} { break } - {{- end}} if err := backoff.Do(ctx); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}")) @@ -318,9 +348,15 @@ func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateR } } - {{if not .IsPlainStruct -}} - {{.VarName}} = {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) - {{end -}} + {{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() { @@ -337,7 +373,11 @@ func (r resourceTeleport{{.Name}}) Update(ctx context.Context, req tfsdk.UpdateR // 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 @@ -354,14 +394,18 @@ func (r resourceTeleport{{.Name}}) Delete(ctx context.Context, req tfsdk.DeleteR // ImportState imports {{.Name}} state func (r resourceTeleport{{.Name}}) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - {{.VarName}}{{if not .IsPlainStruct}}I{{end}}, err := r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}req.ID{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) + {{.VarName}}, err := r.p.Client.{{.GetMethod}}({{if not .GetWithoutContext}}ctx, {{end}}req.ID{{if ne .WithSecrets ""}}, {{.WithSecrets}}{{end}}) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading {{.Name}}", trace.Wrap(err), "{{.Kind}}")) return } - {{if not .IsPlainStruct -}} - {{.VarName}} := {{.VarName}}I.(*{{.ProtoPackage}}.{{.TypeName}}) + {{if .IsPlainStruct -}} + {{.VarName}}Resource := {{.VarName}} + {{else if .ConvertPackagePath -}} + {{.VarName}}Resource := convert.ToProto({{.VarName}}) + {{else}} + {{.VarName}}Resource := {{.VarName}}.(*{{.ProtoPackage}}.{{.TypeName}}) {{- end}} var state types.Object @@ -372,13 +416,19 @@ func (r resourceTeleport{{.Name}}) ImportState(ctx context.Context, req tfsdk.Im return } - diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}, &state) + diags = {{.SchemaPackage}}.Copy{{.TypeName}}ToTerraform(ctx, {{.VarName}}Resource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - state.Attrs["id"] = types.String{Value: {{.VarName}}.Metadata.Name} +{{- 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...) diff --git a/terraform/protoc-gen-terraform-accesslist.yaml b/terraform/protoc-gen-terraform-accesslist.yaml new file mode 100644 index 000000000..bb907536e --- /dev/null +++ b/terraform/protoc-gen-terraform-accesslist.yaml @@ -0,0 +1,75 @@ +--- +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" + github_com_gravitational_teleport_plugins_terraform_tfschema: "github.com/gravitational/teleport-plugins/terraform/tfschema" + +# 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" + +# 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.spec.owners" + - "AccessList.spec.grants" + - "AccessList.spec.membership_requires" + - "AccessList.spec.ownership_requires" + +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_plugins_terraform_tfschema.MustTimeBeInFuture() + +custom_types: + "AccessList.header.metadata.expires": Timestamp + "AccessList.spec.audit.next_audit_date": Timestamp + "AccessList.spec.audit.frequency": Duration diff --git a/terraform/provider/data_source_teleport_access_list.go b/terraform/provider/data_source_teleport_access_list.go new file mode 100755 index 000000000..0cc6aca02 --- /dev/null +++ b/terraform/provider/data_source_teleport_access_list.go @@ -0,0 +1,84 @@ +// 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/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/path" + + schemav1 "github.com/gravitational/teleport-plugins/terraform/tfschema/accesslist/v1" + + convert "github.com/gravitational/teleport/api/types/accesslist/convert/v1" + + "github.com/gravitational/trace" +) + +// 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 + } +} diff --git a/terraform/provider/data_source_teleport_app.go b/terraform/provider/data_source_teleport_app.go index caf59c20d..976313a16 100755 --- a/terraform/provider/data_source_teleport_app.go +++ b/terraform/provider/data_source_teleport_app.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportApp) Read(ctx context.Context, req tfsdk.ReadDataSourc } var state types.Object + app := appI.(*apitypes.AppV3) diags = tfschema.CopyAppV3ToTerraform(ctx, app, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_database.go b/terraform/provider/data_source_teleport_database.go index 179b9df34..415c489aa 100755 --- a/terraform/provider/data_source_teleport_database.go +++ b/terraform/provider/data_source_teleport_database.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportDatabase) Read(ctx context.Context, req tfsdk.ReadData } var state types.Object + database := databaseI.(*apitypes.DatabaseV3) diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_device_trust.go b/terraform/provider/data_source_teleport_device_trust.go index bee591464..75bd0a857 100755 --- a/terraform/provider/data_source_teleport_device_trust.go +++ b/terraform/provider/data_source_teleport_device_trust.go @@ -66,6 +66,7 @@ func (r dataSourceTeleportDeviceV1) Read(ctx context.Context, req tfsdk.ReadData var state types.Object trustedDevice := trustedDeviceI + diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/terraform/provider/data_source_teleport_github_connector.go b/terraform/provider/data_source_teleport_github_connector.go index 2fb0f9c09..19bd2880a 100755 --- a/terraform/provider/data_source_teleport_github_connector.go +++ b/terraform/provider/data_source_teleport_github_connector.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportGithubConnector) Read(ctx context.Context, req tfsdk.R } var state types.Object + githubConnector := githubConnectorI.(*apitypes.GithubConnectorV3) diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_login_rule.go b/terraform/provider/data_source_teleport_login_rule.go index dacfc88ee..b2c645aed 100755 --- a/terraform/provider/data_source_teleport_login_rule.go +++ b/terraform/provider/data_source_teleport_login_rule.go @@ -66,6 +66,7 @@ func (r dataSourceTeleportLoginRule) Read(ctx context.Context, req tfsdk.ReadDat var state types.Object loginRule := loginRuleI + diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/terraform/provider/data_source_teleport_oidc_connector.go b/terraform/provider/data_source_teleport_oidc_connector.go index 7d2ec6385..b8c864cba 100755 --- a/terraform/provider/data_source_teleport_oidc_connector.go +++ b/terraform/provider/data_source_teleport_oidc_connector.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportOIDCConnector) Read(ctx context.Context, req tfsdk.Rea } var state types.Object + oidcConnector := oidcConnectorI.(*apitypes.OIDCConnectorV3) diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_okta_import_rule.go b/terraform/provider/data_source_teleport_okta_import_rule.go index 84e75a039..51e21bc98 100755 --- a/terraform/provider/data_source_teleport_okta_import_rule.go +++ b/terraform/provider/data_source_teleport_okta_import_rule.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportOktaImportRule) Read(ctx context.Context, req tfsdk.Re } var state types.Object + oktaImportRule := oktaImportRuleI.(*apitypes.OktaImportRuleV1) diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_provision_token.go b/terraform/provider/data_source_teleport_provision_token.go index 19a3f03fd..d2d3c84d5 100755 --- a/terraform/provider/data_source_teleport_provision_token.go +++ b/terraform/provider/data_source_teleport_provision_token.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportProvisionToken) Read(ctx context.Context, req tfsdk.Re } var state types.Object + provisionToken := provisionTokenI.(*apitypes.ProvisionTokenV2) diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_role.go b/terraform/provider/data_source_teleport_role.go index 9546422a5..e7f789555 100755 --- a/terraform/provider/data_source_teleport_role.go +++ b/terraform/provider/data_source_teleport_role.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportRole) Read(ctx context.Context, req tfsdk.ReadDataSour } var state types.Object + role := roleI.(*apitypes.RoleV6) diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_saml_connector.go b/terraform/provider/data_source_teleport_saml_connector.go index 6b5dd619d..04190864c 100755 --- a/terraform/provider/data_source_teleport_saml_connector.go +++ b/terraform/provider/data_source_teleport_saml_connector.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportSAMLConnector) Read(ctx context.Context, req tfsdk.Rea } var state types.Object + samlConnector := samlConnectorI.(*apitypes.SAMLConnectorV2) diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_trusted_cluster.go b/terraform/provider/data_source_teleport_trusted_cluster.go index 5f1a37242..5136243d5 100755 --- a/terraform/provider/data_source_teleport_trusted_cluster.go +++ b/terraform/provider/data_source_teleport_trusted_cluster.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportTrustedCluster) Read(ctx context.Context, req tfsdk.Re } var state types.Object + trustedCluster := trustedClusterI.(*apitypes.TrustedClusterV2) diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/data_source_teleport_user.go b/terraform/provider/data_source_teleport_user.go index 2d0f82b5a..8ca3d0617 100755 --- a/terraform/provider/data_source_teleport_user.go +++ b/terraform/provider/data_source_teleport_user.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" tfschema "github.com/gravitational/teleport-plugins/terraform/tfschema" + apitypes "github.com/gravitational/teleport/api/types" "github.com/gravitational/trace" ) @@ -66,6 +67,7 @@ func (r dataSourceTeleportUser) Read(ctx context.Context, req tfsdk.ReadDataSour } var state types.Object + user := userI.(*apitypes.UserV2) diags = tfschema.CopyUserV2ToTerraform(ctx, user, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/provider.go b/terraform/provider/provider.go index 740f274fc..e542803b6 100644 --- a/terraform/provider/provider.go +++ b/terraform/provider/provider.go @@ -575,6 +575,7 @@ func (p *Provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceTyp "teleport_login_rule": resourceTeleportLoginRuleType{}, "teleport_trusted_device": resourceTeleportDeviceV1Type{}, "teleport_okta_import_rule": resourceTeleportOktaImportRuleType{}, + "teleport_access_list": resourceTeleportAccessListType{}, }, nil } @@ -597,5 +598,6 @@ func (p *Provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourc "teleport_login_rule": dataSourceTeleportLoginRuleType{}, "teleport_trusted_device": dataSourceTeleportDeviceV1Type{}, "teleport_okta_import_rule": dataSourceTeleportOktaImportRuleType{}, + "teleport_access_list": dataSourceTeleportAccessListType{}, }, nil } diff --git a/terraform/provider/resource_teleport_access_list.go b/terraform/provider/resource_teleport_access_list.go new file mode 100755 index 000000000..42b037b74 --- /dev/null +++ b/terraform/provider/resource_teleport_access_list.go @@ -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" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/path" + + accesslist "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1" + schemav1 "github.com/gravitational/teleport-plugins/terraform/tfschema/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/jonboulle/clockwork" +) + +// 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 + } + 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 = accessListResource.CheckAndSetDefaults() + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error setting AccessList defaults", 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(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.Append(diagFromWrappedErr(diagMessage, trace.Wrap(err), "access_list")) + return + } + 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 + } + + _, 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().ID != accessListI.GetMetadata().ID || 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 + } +} diff --git a/terraform/provider/resource_teleport_app.go b/terraform/provider/resource_teleport_app.go index c3d7ffcd6..358c7cba9 100755 --- a/terraform/provider/resource_teleport_app.go +++ b/terraform/provider/resource_teleport_app.go @@ -55,6 +55,7 @@ func (r resourceTeleportAppType) NewResource(_ context.Context, p tfsdk.Provider // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportApp) Create(ctx context.Context, req tfsdk.CreateResourc } + appResource := app - _, err := r.p.Client.GetApp(ctx, app.Metadata.Name) + id := appResource.Metadata.Name + + _, err = r.p.Client.GetApp(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := app.Metadata.Name 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)", n, n, n) + " 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 @@ -90,21 +93,20 @@ func (r resourceTeleportApp) Create(ctx context.Context, req tfsdk.CreateResourc return } - err = app.CheckAndSetDefaults() + err = appResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting App defaults", trace.Wrap(err), "app")) return } - err = r.p.Client.CreateApp(ctx, app) + err = r.p.Client.CreateApp(ctx, appResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating App", trace.Wrap(err), "app")) return } - - id := app.Metadata.Name + + // 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 { @@ -130,11 +132,12 @@ func (r resourceTeleportApp) Create(ctx context.Context, req tfsdk.CreateResourc return } - app, ok := appI.(*apitypes.AppV3) + 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...) @@ -177,7 +180,7 @@ func (r resourceTeleportApp) Read(ctx context.Context, req tfsdk.ReadResourceReq 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...) @@ -212,14 +215,14 @@ func (r resourceTeleportApp) Update(ctx context.Context, req tfsdk.UpdateResourc if resp.Diagnostics.HasError() { return } + appResource := app - name := app.Metadata.Name - err := app.CheckAndSetDefaults() - if err != nil { + 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 { @@ -227,12 +230,13 @@ func (r resourceTeleportApp) Update(ctx context.Context, req tfsdk.UpdateResourc return } - err = r.p.Client.UpdateApp(ctx, app) + 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 @@ -259,7 +263,11 @@ func (r resourceTeleportApp) Update(ctx context.Context, req tfsdk.UpdateResourc } } - app = appI.(*apitypes.AppV3) + 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() { @@ -293,13 +301,14 @@ func (r resourceTeleportApp) Delete(ctx context.Context, req tfsdk.DeleteResourc // ImportState imports App state func (r resourceTeleportApp) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - appI, err := r.p.Client.GetApp(ctx, req.ID) + app, err := r.p.Client.GetApp(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading App", trace.Wrap(err), "app")) return } - app := appI.(*apitypes.AppV3) + + appResource := app.(*apitypes.AppV3) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportApp) ImportState(ctx context.Context, req tfsdk.ImportRe return } - diags = tfschema.CopyAppV3ToTerraform(ctx, app, &state) + diags = tfschema.CopyAppV3ToTerraform(ctx, appResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := appResource.GetName() - state.Attrs["id"] = types.String{Value: app.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_database.go b/terraform/provider/resource_teleport_database.go index 71f13358e..82df15ef9 100755 --- a/terraform/provider/resource_teleport_database.go +++ b/terraform/provider/resource_teleport_database.go @@ -55,6 +55,7 @@ func (r resourceTeleportDatabaseType) NewResource(_ context.Context, p tfsdk.Pro // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportDatabase) Create(ctx context.Context, req tfsdk.CreateRe } + databaseResource := database - _, err := r.p.Client.GetDatabase(ctx, database.Metadata.Name) + id := databaseResource.Metadata.Name + + _, err = r.p.Client.GetDatabase(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := database.Metadata.Name 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)", n, n, n) + " 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 @@ -90,21 +93,20 @@ func (r resourceTeleportDatabase) Create(ctx context.Context, req tfsdk.CreateRe return } - err = database.CheckAndSetDefaults() + err = databaseResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting Database defaults", trace.Wrap(err), "db")) return } - err = r.p.Client.CreateDatabase(ctx, database) + err = r.p.Client.CreateDatabase(ctx, databaseResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating Database", trace.Wrap(err), "db")) return } - - id := database.Metadata.Name + + // 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 { @@ -130,11 +132,12 @@ func (r resourceTeleportDatabase) Create(ctx context.Context, req tfsdk.CreateRe return } - database, ok := databaseI.(*apitypes.DatabaseV3) + 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...) @@ -177,7 +180,7 @@ func (r resourceTeleportDatabase) Read(ctx context.Context, req tfsdk.ReadResour 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...) @@ -212,14 +215,14 @@ func (r resourceTeleportDatabase) Update(ctx context.Context, req tfsdk.UpdateRe if resp.Diagnostics.HasError() { return } + databaseResource := database - name := database.Metadata.Name - err := database.CheckAndSetDefaults() - if err != nil { + 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 { @@ -227,12 +230,13 @@ func (r resourceTeleportDatabase) Update(ctx context.Context, req tfsdk.UpdateRe return } - err = r.p.Client.UpdateDatabase(ctx, database) + 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 @@ -259,7 +263,11 @@ func (r resourceTeleportDatabase) Update(ctx context.Context, req tfsdk.UpdateRe } } - database = databaseI.(*apitypes.DatabaseV3) + 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() { @@ -293,13 +301,14 @@ func (r resourceTeleportDatabase) Delete(ctx context.Context, req tfsdk.DeleteRe // ImportState imports Database state func (r resourceTeleportDatabase) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - databaseI, err := r.p.Client.GetDatabase(ctx, req.ID) + database, err := r.p.Client.GetDatabase(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading Database", trace.Wrap(err), "db")) return } - database := databaseI.(*apitypes.DatabaseV3) + + databaseResource := database.(*apitypes.DatabaseV3) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportDatabase) ImportState(ctx context.Context, req tfsdk.Imp return } - diags = tfschema.CopyDatabaseV3ToTerraform(ctx, database, &state) + diags = tfschema.CopyDatabaseV3ToTerraform(ctx, databaseResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := databaseResource.GetName() - state.Attrs["id"] = types.String{Value: database.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_device_trust.go b/terraform/provider/resource_teleport_device_trust.go index ef32ebab8..47c38c7fc 100755 --- a/terraform/provider/resource_teleport_device_trust.go +++ b/terraform/provider/resource_teleport_device_trust.go @@ -56,6 +56,7 @@ func (r resourceTeleportDeviceV1Type) NewResource(_ context.Context, p tfsdk.Pro // 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 } @@ -78,13 +79,15 @@ func (r resourceTeleportDeviceV1) Create(ctx context.Context, req tfsdk.CreateRe trustedDevice.Metadata.Name = uuid.NewString() } + trustedDeviceResource := trustedDevice - _, err := r.p.Client.GetDeviceResource(ctx, trustedDevice.Metadata.Name) + id := trustedDeviceResource.Metadata.Name + + _, err = r.p.Client.GetDeviceResource(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := trustedDevice.Metadata.Name 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)", n, n, n) + " 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 @@ -96,17 +99,12 @@ func (r resourceTeleportDeviceV1) Create(ctx context.Context, req tfsdk.CreateRe - _, err = r.p.Client.UpsertDeviceResource(ctx, trustedDevice) + _, err = r.p.Client.UpsertDeviceResource(ctx, trustedDeviceResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating DeviceV1", trace.Wrap(err), "device")) return } - - id := trustedDevice.Metadata.Name - // Not really an inferface, just using the same name for easier templating. - var trustedDeviceI *apitypes.DeviceV1 - - + var trustedDeviceI *apitypes.DeviceV1 tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -132,8 +130,9 @@ func (r resourceTeleportDeviceV1) Create(ctx context.Context, req tfsdk.CreateRe return } - trustedDevice = trustedDeviceI + trustedDeviceResource = trustedDeviceI + trustedDevice = trustedDeviceResource diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &plan) resp.Diagnostics.Append(diags...) @@ -176,7 +175,6 @@ func (r resourceTeleportDeviceV1) Read(ctx context.Context, req tfsdk.ReadResour resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device")) return } - trustedDevice := trustedDeviceI diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &state) resp.Diagnostics.Append(diags...) @@ -211,10 +209,11 @@ func (r resourceTeleportDeviceV1) Update(ctx context.Context, req tfsdk.UpdateRe if resp.Diagnostics.HasError() { return } + trustedDeviceResource := trustedDevice - name := trustedDevice.Metadata.Name + name := trustedDeviceResource.Metadata.Name trustedDeviceBefore, err := r.p.Client.GetDeviceResource(ctx, name) if err != nil { @@ -222,27 +221,25 @@ func (r resourceTeleportDeviceV1) Update(ctx context.Context, req tfsdk.UpdateRe return } - _, err = r.p.Client.UpsertDeviceResource(ctx, trustedDevice) + _, 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 - trustedDevice, err = r.p.Client.GetDeviceResource(ctx, name) + trustedDeviceI, err = r.p.Client.GetDeviceResource(ctx, name) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", err, "device")) return } - if trustedDeviceBefore.GetMetadata().ID != trustedDevice.GetMetadata().ID || true { + if trustedDeviceBefore.GetMetadata().ID != trustedDeviceI.GetMetadata().ID || true { break } - if err := backoff.Do(ctx); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading DeviceV1", trace.Wrap(err), "device")) @@ -255,6 +252,8 @@ func (r resourceTeleportDeviceV1) Update(ctx context.Context, req tfsdk.UpdateRe } } + trustedDeviceResource = trustedDeviceI + diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -294,6 +293,7 @@ func (r resourceTeleportDeviceV1) ImportState(ctx context.Context, req tfsdk.Imp return } + trustedDeviceResource := trustedDevice var state types.Object @@ -304,13 +304,14 @@ func (r resourceTeleportDeviceV1) ImportState(ctx context.Context, req tfsdk.Imp return } - diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDevice, &state) + diags = tfschema.CopyDeviceV1ToTerraform(ctx, trustedDeviceResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := trustedDevice.Metadata.Name - state.Attrs["id"] = types.String{Value: trustedDevice.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_github_connector.go b/terraform/provider/resource_teleport_github_connector.go index e89afcb56..ca5e17a39 100755 --- a/terraform/provider/resource_teleport_github_connector.go +++ b/terraform/provider/resource_teleport_github_connector.go @@ -55,6 +55,7 @@ func (r resourceTeleportGithubConnectorType) NewResource(_ context.Context, p tf // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportGithubConnector) Create(ctx context.Context, req tfsdk.C } + githubConnectorResource := githubConnector - _, err := r.p.Client.GetGithubConnector(ctx, githubConnector.Metadata.Name, true) + id := githubConnectorResource.Metadata.Name + + _, err = r.p.Client.GetGithubConnector(ctx, id, true) if !trace.IsNotFound(err) { if err == nil { - n := githubConnector.Metadata.Name 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)", n, n, n) + " 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 @@ -90,21 +93,20 @@ func (r resourceTeleportGithubConnector) Create(ctx context.Context, req tfsdk.C return } - err = githubConnector.CheckAndSetDefaults() + err = githubConnectorResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting GithubConnector defaults", trace.Wrap(err), "github")) return } - err = r.p.Client.UpsertGithubConnector(ctx, githubConnector) + err = r.p.Client.UpsertGithubConnector(ctx, githubConnectorResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating GithubConnector", trace.Wrap(err), "github")) return } - - id := githubConnector.Metadata.Name + + // 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 { @@ -130,11 +132,12 @@ func (r resourceTeleportGithubConnector) Create(ctx context.Context, req tfsdk.C return } - githubConnector, ok := githubConnectorI.(*apitypes.GithubConnectorV3) + 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...) @@ -177,7 +180,7 @@ func (r resourceTeleportGithubConnector) Read(ctx context.Context, req tfsdk.Rea 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...) @@ -212,14 +215,14 @@ func (r resourceTeleportGithubConnector) Update(ctx context.Context, req tfsdk.U if resp.Diagnostics.HasError() { return } + githubConnectorResource := githubConnector - name := githubConnector.Metadata.Name - err := githubConnector.CheckAndSetDefaults() - if err != nil { + 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 { @@ -227,12 +230,13 @@ func (r resourceTeleportGithubConnector) Update(ctx context.Context, req tfsdk.U return } - err = r.p.Client.UpsertGithubConnector(ctx, githubConnector) + 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 @@ -259,7 +263,11 @@ func (r resourceTeleportGithubConnector) Update(ctx context.Context, req tfsdk.U } } - githubConnector = githubConnectorI.(*apitypes.GithubConnectorV3) + 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() { @@ -293,13 +301,14 @@ func (r resourceTeleportGithubConnector) Delete(ctx context.Context, req tfsdk.D // ImportState imports GithubConnector state func (r resourceTeleportGithubConnector) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - githubConnectorI, err := r.p.Client.GetGithubConnector(ctx, req.ID, true) + 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 } - githubConnector := githubConnectorI.(*apitypes.GithubConnectorV3) + + githubConnectorResource := githubConnector.(*apitypes.GithubConnectorV3) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportGithubConnector) ImportState(ctx context.Context, req tf return } - diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnector, &state) + diags = tfschema.CopyGithubConnectorV3ToTerraform(ctx, githubConnectorResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := githubConnectorResource.GetName() - state.Attrs["id"] = types.String{Value: githubConnector.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_login_rule.go b/terraform/provider/resource_teleport_login_rule.go index bd91b6933..4ca5c2d1d 100755 --- a/terraform/provider/resource_teleport_login_rule.go +++ b/terraform/provider/resource_teleport_login_rule.go @@ -55,6 +55,7 @@ func (r resourceTeleportLoginRuleType) NewResource(_ context.Context, p tfsdk.Pr // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportLoginRule) Create(ctx context.Context, req tfsdk.CreateR } + loginRuleResource := loginRule - _, err := r.p.Client.GetLoginRule(ctx, loginRule.Metadata.Name) + id := loginRuleResource.Metadata.Name + + _, err = r.p.Client.GetLoginRule(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := loginRule.Metadata.Name 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)", n, n, n) + " 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 @@ -92,17 +95,12 @@ func (r resourceTeleportLoginRule) Create(ctx context.Context, req tfsdk.CreateR - _, err = r.p.Client.UpsertLoginRule(ctx, loginRule) + _, err = r.p.Client.UpsertLoginRule(ctx, loginRuleResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating LoginRule", trace.Wrap(err), "login_rule")) return } - - id := loginRule.Metadata.Name - // Not really an inferface, just using the same name for easier templating. - var loginRuleI *loginrulev1.LoginRule - - + var loginRuleI *loginrulev1.LoginRule tries := 0 backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) for { @@ -128,8 +126,9 @@ func (r resourceTeleportLoginRule) Create(ctx context.Context, req tfsdk.CreateR return } - loginRule = loginRuleI + loginRuleResource = loginRuleI + loginRule = loginRuleResource diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &plan) resp.Diagnostics.Append(diags...) @@ -172,7 +171,6 @@ func (r resourceTeleportLoginRule) Read(ctx context.Context, req tfsdk.ReadResou 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...) @@ -207,10 +205,11 @@ func (r resourceTeleportLoginRule) Update(ctx context.Context, req tfsdk.UpdateR if resp.Diagnostics.HasError() { return } + loginRuleResource := loginRule - name := loginRule.Metadata.Name + name := loginRuleResource.Metadata.Name loginRuleBefore, err := r.p.Client.GetLoginRule(ctx, name) if err != nil { @@ -218,27 +217,25 @@ func (r resourceTeleportLoginRule) Update(ctx context.Context, req tfsdk.UpdateR return } - _, err = r.p.Client.UpsertLoginRule(ctx, loginRule) + _, 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 - loginRule, err = r.p.Client.GetLoginRule(ctx, name) + 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().ID != loginRule.GetMetadata().ID || false { + if loginRuleBefore.GetMetadata().ID != loginRuleI.GetMetadata().ID || false { break } - if err := backoff.Do(ctx); err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading LoginRule", trace.Wrap(err), "login_rule")) @@ -251,6 +248,8 @@ func (r resourceTeleportLoginRule) Update(ctx context.Context, req tfsdk.UpdateR } } + loginRuleResource = loginRuleI + diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -290,6 +289,7 @@ func (r resourceTeleportLoginRule) ImportState(ctx context.Context, req tfsdk.Im return } + loginRuleResource := loginRule var state types.Object @@ -300,13 +300,14 @@ func (r resourceTeleportLoginRule) ImportState(ctx context.Context, req tfsdk.Im return } - diags = schemav1.CopyLoginRuleToTerraform(ctx, loginRule, &state) + 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: loginRule.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_oidc_connector.go b/terraform/provider/resource_teleport_oidc_connector.go index 8a41f5901..6253f45c7 100755 --- a/terraform/provider/resource_teleport_oidc_connector.go +++ b/terraform/provider/resource_teleport_oidc_connector.go @@ -55,6 +55,7 @@ func (r resourceTeleportOIDCConnectorType) NewResource(_ context.Context, p tfsd // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportOIDCConnector) Create(ctx context.Context, req tfsdk.Cre } + oidcConnectorResource := oidcConnector - _, err := r.p.Client.GetOIDCConnector(ctx, oidcConnector.Metadata.Name, true) + id := oidcConnectorResource.Metadata.Name + + _, err = r.p.Client.GetOIDCConnector(ctx, id, true) if !trace.IsNotFound(err) { if err == nil { - n := oidcConnector.Metadata.Name 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)", n, n, n) + " 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 @@ -90,21 +93,20 @@ func (r resourceTeleportOIDCConnector) Create(ctx context.Context, req tfsdk.Cre return } - err = oidcConnector.CheckAndSetDefaults() + err = oidcConnectorResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting OIDCConnector defaults", trace.Wrap(err), "oidc")) return } - err = r.p.Client.UpsertOIDCConnector(ctx, oidcConnector) + err = r.p.Client.UpsertOIDCConnector(ctx, oidcConnectorResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating OIDCConnector", trace.Wrap(err), "oidc")) return } - - id := oidcConnector.Metadata.Name + + // 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 { @@ -130,11 +132,12 @@ func (r resourceTeleportOIDCConnector) Create(ctx context.Context, req tfsdk.Cre return } - oidcConnector, ok := oidcConnectorI.(*apitypes.OIDCConnectorV3) + 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...) @@ -177,7 +180,7 @@ func (r resourceTeleportOIDCConnector) Read(ctx context.Context, req tfsdk.ReadR 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...) @@ -212,14 +215,14 @@ func (r resourceTeleportOIDCConnector) Update(ctx context.Context, req tfsdk.Upd if resp.Diagnostics.HasError() { return } + oidcConnectorResource := oidcConnector - name := oidcConnector.Metadata.Name - err := oidcConnector.CheckAndSetDefaults() - if err != nil { + 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 { @@ -227,12 +230,13 @@ func (r resourceTeleportOIDCConnector) Update(ctx context.Context, req tfsdk.Upd return } - err = r.p.Client.UpsertOIDCConnector(ctx, oidcConnector) + 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 @@ -259,7 +263,11 @@ func (r resourceTeleportOIDCConnector) Update(ctx context.Context, req tfsdk.Upd } } - oidcConnector = oidcConnectorI.(*apitypes.OIDCConnectorV3) + 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() { @@ -293,13 +301,14 @@ func (r resourceTeleportOIDCConnector) Delete(ctx context.Context, req tfsdk.Del // ImportState imports OIDCConnector state func (r resourceTeleportOIDCConnector) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - oidcConnectorI, err := r.p.Client.GetOIDCConnector(ctx, req.ID, true) + 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 } - oidcConnector := oidcConnectorI.(*apitypes.OIDCConnectorV3) + + oidcConnectorResource := oidcConnector.(*apitypes.OIDCConnectorV3) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportOIDCConnector) ImportState(ctx context.Context, req tfsd return } - diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnector, &state) + diags = tfschema.CopyOIDCConnectorV3ToTerraform(ctx, oidcConnectorResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := oidcConnectorResource.GetName() - state.Attrs["id"] = types.String{Value: oidcConnector.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_okta_import_rule.go b/terraform/provider/resource_teleport_okta_import_rule.go index 7897a7a2c..0b0ab51a2 100755 --- a/terraform/provider/resource_teleport_okta_import_rule.go +++ b/terraform/provider/resource_teleport_okta_import_rule.go @@ -55,6 +55,7 @@ func (r resourceTeleportOktaImportRuleType) NewResource(_ context.Context, p tfs // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportOktaImportRule) Create(ctx context.Context, req tfsdk.Cr } + oktaImportRuleResource := oktaImportRule - _, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, oktaImportRule.Metadata.Name) + id := oktaImportRuleResource.Metadata.Name + + _, err = r.p.Client.OktaClient().GetOktaImportRule(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := oktaImportRule.Metadata.Name 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)", n, n, n) + " 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 @@ -90,21 +93,20 @@ func (r resourceTeleportOktaImportRule) Create(ctx context.Context, req tfsdk.Cr return } - err = oktaImportRule.CheckAndSetDefaults() + err = oktaImportRuleResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting OktaImportRule defaults", trace.Wrap(err), "okta_import_rule")) return } - _, err = r.p.Client.OktaClient().CreateOktaImportRule(ctx, oktaImportRule) + _, 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 } - - id := oktaImportRule.Metadata.Name + + // 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 { @@ -130,11 +132,12 @@ func (r resourceTeleportOktaImportRule) Create(ctx context.Context, req tfsdk.Cr return } - oktaImportRule, ok := oktaImportRuleI.(*apitypes.OktaImportRuleV1) + 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...) @@ -177,7 +180,7 @@ func (r resourceTeleportOktaImportRule) Read(ctx context.Context, req tfsdk.Read 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...) @@ -212,14 +215,14 @@ func (r resourceTeleportOktaImportRule) Update(ctx context.Context, req tfsdk.Up if resp.Diagnostics.HasError() { return } + oktaImportRuleResource := oktaImportRule - name := oktaImportRule.Metadata.Name - err := oktaImportRule.CheckAndSetDefaults() - if err != nil { + 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 { @@ -227,12 +230,13 @@ func (r resourceTeleportOktaImportRule) Update(ctx context.Context, req tfsdk.Up return } - _, err = r.p.Client.OktaClient().UpdateOktaImportRule(ctx, oktaImportRule) + _, 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 @@ -259,7 +263,11 @@ func (r resourceTeleportOktaImportRule) Update(ctx context.Context, req tfsdk.Up } } - oktaImportRule = oktaImportRuleI.(*apitypes.OktaImportRuleV1) + 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() { @@ -293,13 +301,14 @@ func (r resourceTeleportOktaImportRule) Delete(ctx context.Context, req tfsdk.De // ImportState imports OktaImportRule state func (r resourceTeleportOktaImportRule) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - oktaImportRuleI, err := r.p.Client.OktaClient().GetOktaImportRule(ctx, req.ID) + 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 } - oktaImportRule := oktaImportRuleI.(*apitypes.OktaImportRuleV1) + + oktaImportRuleResource := oktaImportRule.(*apitypes.OktaImportRuleV1) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportOktaImportRule) ImportState(ctx context.Context, req tfs return } - diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRule, &state) + diags = tfschema.CopyOktaImportRuleV1ToTerraform(ctx, oktaImportRuleResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := oktaImportRuleResource.GetName() - state.Attrs["id"] = types.String{Value: oktaImportRule.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_provision_token.go b/terraform/provider/resource_teleport_provision_token.go index 6d58de281..e599ff4b7 100755 --- a/terraform/provider/resource_teleport_provision_token.go +++ b/terraform/provider/resource_teleport_provision_token.go @@ -58,6 +58,7 @@ func (r resourceTeleportProvisionTokenType) NewResource(_ context.Context, p tfs // 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 } @@ -86,13 +87,15 @@ func (r resourceTeleportProvisionToken) Create(ctx context.Context, req tfsdk.Cr provisionToken.Metadata.Name = hex.EncodeToString(b) } + provisionTokenResource := provisionToken - _, err := r.p.Client.GetToken(ctx, provisionToken.Metadata.Name) + id := provisionTokenResource.Metadata.Name + + _, err = r.p.Client.GetToken(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := provisionToken.Metadata.Name 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)", n, n, n) + " 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 @@ -102,21 +105,20 @@ func (r resourceTeleportProvisionToken) Create(ctx context.Context, req tfsdk.Cr return } - err = provisionToken.CheckAndSetDefaults() + err = provisionTokenResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting ProvisionToken defaults", trace.Wrap(err), "token")) return } - err = r.p.Client.UpsertToken(ctx, provisionToken) + err = r.p.Client.UpsertToken(ctx, provisionTokenResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating ProvisionToken", trace.Wrap(err), "token")) return } - - id := provisionToken.Metadata.Name + + // 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 { @@ -142,11 +144,12 @@ func (r resourceTeleportProvisionToken) Create(ctx context.Context, req tfsdk.Cr return } - provisionToken, ok := provisionTokenI.(*apitypes.ProvisionTokenV2) + 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...) @@ -189,7 +192,7 @@ func (r resourceTeleportProvisionToken) Read(ctx context.Context, req tfsdk.Read 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...) @@ -224,14 +227,14 @@ func (r resourceTeleportProvisionToken) Update(ctx context.Context, req tfsdk.Up if resp.Diagnostics.HasError() { return } + provisionTokenResource := provisionToken - name := provisionToken.Metadata.Name - err := provisionToken.CheckAndSetDefaults() - if err != nil { + 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 { @@ -239,12 +242,13 @@ func (r resourceTeleportProvisionToken) Update(ctx context.Context, req tfsdk.Up return } - err = r.p.Client.UpsertToken(ctx, provisionToken) + 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 @@ -271,7 +275,11 @@ func (r resourceTeleportProvisionToken) Update(ctx context.Context, req tfsdk.Up } } - provisionToken = provisionTokenI.(*apitypes.ProvisionTokenV2) + 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() { @@ -305,13 +313,14 @@ func (r resourceTeleportProvisionToken) Delete(ctx context.Context, req tfsdk.De // ImportState imports ProvisionToken state func (r resourceTeleportProvisionToken) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - provisionTokenI, err := r.p.Client.GetToken(ctx, req.ID) + provisionToken, err := r.p.Client.GetToken(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading ProvisionToken", trace.Wrap(err), "token")) return } - provisionToken := provisionTokenI.(*apitypes.ProvisionTokenV2) + + provisionTokenResource := provisionToken.(*apitypes.ProvisionTokenV2) var state types.Object @@ -321,13 +330,14 @@ func (r resourceTeleportProvisionToken) ImportState(ctx context.Context, req tfs return } - diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionToken, &state) + diags = tfschema.CopyProvisionTokenV2ToTerraform(ctx, provisionTokenResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := provisionTokenResource.GetName() - state.Attrs["id"] = types.String{Value: provisionToken.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_role.go b/terraform/provider/resource_teleport_role.go index 7c1b62617..81b0df7c9 100755 --- a/terraform/provider/resource_teleport_role.go +++ b/terraform/provider/resource_teleport_role.go @@ -55,6 +55,7 @@ func (r resourceTeleportRoleType) NewResource(_ context.Context, p tfsdk.Provide // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportRole) Create(ctx context.Context, req tfsdk.CreateResour } + roleResource := role - _, err := r.p.Client.GetRole(ctx, role.Metadata.Name) + id := roleResource.Metadata.Name + + _, err = r.p.Client.GetRole(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := role.Metadata.Name 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)", n, n, n) + " 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 @@ -90,21 +93,20 @@ func (r resourceTeleportRole) Create(ctx context.Context, req tfsdk.CreateResour return } - err = role.CheckAndSetDefaults() + err = roleResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting Role defaults", trace.Wrap(err), "role")) return } - err = r.p.Client.UpsertRole(ctx, role) + err = r.p.Client.UpsertRole(ctx, roleResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating Role", trace.Wrap(err), "role")) return } - - id := role.Metadata.Name + + // 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 { @@ -130,11 +132,12 @@ func (r resourceTeleportRole) Create(ctx context.Context, req tfsdk.CreateResour return } - role, ok := roleI.(*apitypes.RoleV6) + 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...) @@ -177,7 +180,7 @@ func (r resourceTeleportRole) Read(ctx context.Context, req tfsdk.ReadResourceRe 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...) @@ -212,14 +215,14 @@ func (r resourceTeleportRole) Update(ctx context.Context, req tfsdk.UpdateResour if resp.Diagnostics.HasError() { return } + roleResource := role - name := role.Metadata.Name - err := role.CheckAndSetDefaults() - if err != nil { + 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 { @@ -227,12 +230,13 @@ func (r resourceTeleportRole) Update(ctx context.Context, req tfsdk.UpdateResour return } - err = r.p.Client.UpsertRole(ctx, role) + 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 @@ -259,7 +263,11 @@ func (r resourceTeleportRole) Update(ctx context.Context, req tfsdk.UpdateResour } } - role = roleI.(*apitypes.RoleV6) + 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() { @@ -293,13 +301,14 @@ func (r resourceTeleportRole) Delete(ctx context.Context, req tfsdk.DeleteResour // ImportState imports Role state func (r resourceTeleportRole) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - roleI, err := r.p.Client.GetRole(ctx, req.ID) + role, err := r.p.Client.GetRole(ctx, req.ID) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading Role", trace.Wrap(err), "role")) return } - role := roleI.(*apitypes.RoleV6) + + roleResource := role.(*apitypes.RoleV6) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportRole) ImportState(ctx context.Context, req tfsdk.ImportR return } - diags = tfschema.CopyRoleV6ToTerraform(ctx, role, &state) + diags = tfschema.CopyRoleV6ToTerraform(ctx, roleResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := roleResource.GetName() - state.Attrs["id"] = types.String{Value: role.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_saml_connector.go b/terraform/provider/resource_teleport_saml_connector.go index 64d7e268e..e0d93cbb2 100755 --- a/terraform/provider/resource_teleport_saml_connector.go +++ b/terraform/provider/resource_teleport_saml_connector.go @@ -55,6 +55,7 @@ func (r resourceTeleportSAMLConnectorType) NewResource(_ context.Context, p tfsd // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportSAMLConnector) Create(ctx context.Context, req tfsdk.Cre } + samlConnectorResource := samlConnector - _, err := r.p.Client.GetSAMLConnector(ctx, samlConnector.Metadata.Name, true) + id := samlConnectorResource.Metadata.Name + + _, err = r.p.Client.GetSAMLConnector(ctx, id, true) if !trace.IsNotFound(err) { if err == nil { - n := samlConnector.Metadata.Name 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)", n, n, n) + " 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 @@ -90,21 +93,20 @@ func (r resourceTeleportSAMLConnector) Create(ctx context.Context, req tfsdk.Cre return } - err = samlConnector.CheckAndSetDefaults() + err = samlConnectorResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting SAMLConnector defaults", trace.Wrap(err), "saml")) return } - err = r.p.Client.UpsertSAMLConnector(ctx, samlConnector) + err = r.p.Client.UpsertSAMLConnector(ctx, samlConnectorResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating SAMLConnector", trace.Wrap(err), "saml")) return } - - id := samlConnector.Metadata.Name + + // 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 { @@ -130,11 +132,12 @@ func (r resourceTeleportSAMLConnector) Create(ctx context.Context, req tfsdk.Cre return } - samlConnector, ok := samlConnectorI.(*apitypes.SAMLConnectorV2) + 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...) @@ -177,7 +180,7 @@ func (r resourceTeleportSAMLConnector) Read(ctx context.Context, req tfsdk.ReadR 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...) @@ -212,14 +215,14 @@ func (r resourceTeleportSAMLConnector) Update(ctx context.Context, req tfsdk.Upd if resp.Diagnostics.HasError() { return } + samlConnectorResource := samlConnector - name := samlConnector.Metadata.Name - err := samlConnector.CheckAndSetDefaults() - if err != nil { + 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 { @@ -227,12 +230,13 @@ func (r resourceTeleportSAMLConnector) Update(ctx context.Context, req tfsdk.Upd return } - err = r.p.Client.UpsertSAMLConnector(ctx, samlConnector) + 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 @@ -259,7 +263,11 @@ func (r resourceTeleportSAMLConnector) Update(ctx context.Context, req tfsdk.Upd } } - samlConnector = samlConnectorI.(*apitypes.SAMLConnectorV2) + 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() { @@ -293,13 +301,14 @@ func (r resourceTeleportSAMLConnector) Delete(ctx context.Context, req tfsdk.Del // ImportState imports SAMLConnector state func (r resourceTeleportSAMLConnector) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - samlConnectorI, err := r.p.Client.GetSAMLConnector(ctx, req.ID, true) + 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 } - samlConnector := samlConnectorI.(*apitypes.SAMLConnectorV2) + + samlConnectorResource := samlConnector.(*apitypes.SAMLConnectorV2) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportSAMLConnector) ImportState(ctx context.Context, req tfsd return } - diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnector, &state) + diags = tfschema.CopySAMLConnectorV2ToTerraform(ctx, samlConnectorResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := samlConnectorResource.GetName() - state.Attrs["id"] = types.String{Value: samlConnector.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_trusted_cluster.go b/terraform/provider/resource_teleport_trusted_cluster.go index 7b3a0a5f9..612066eb3 100755 --- a/terraform/provider/resource_teleport_trusted_cluster.go +++ b/terraform/provider/resource_teleport_trusted_cluster.go @@ -55,6 +55,7 @@ func (r resourceTeleportTrustedClusterType) NewResource(_ context.Context, p tfs // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportTrustedCluster) Create(ctx context.Context, req tfsdk.Cr } + trustedClusterResource := trustedCluster - _, err := r.p.Client.GetTrustedCluster(ctx, trustedCluster.Metadata.Name) + id := trustedClusterResource.Metadata.Name + + _, err = r.p.Client.GetTrustedCluster(ctx, id) if !trace.IsNotFound(err) { if err == nil { - n := trustedCluster.Metadata.Name 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)", n, n, n) + " 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 @@ -90,21 +93,20 @@ func (r resourceTeleportTrustedCluster) Create(ctx context.Context, req tfsdk.Cr return } - err = trustedCluster.CheckAndSetDefaults() + err = trustedClusterResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting TrustedCluster defaults", trace.Wrap(err), "trusted_cluster")) return } - _, err = r.p.Client.UpsertTrustedCluster(ctx, trustedCluster) + _, err = r.p.Client.UpsertTrustedCluster(ctx, trustedClusterResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating TrustedCluster", trace.Wrap(err), "trusted_cluster")) return } - - id := trustedCluster.Metadata.Name + + // 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 { @@ -130,11 +132,12 @@ func (r resourceTeleportTrustedCluster) Create(ctx context.Context, req tfsdk.Cr return } - trustedCluster, ok := trustedClusterI.(*apitypes.TrustedClusterV2) + 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...) @@ -177,7 +180,7 @@ func (r resourceTeleportTrustedCluster) Read(ctx context.Context, req tfsdk.Read 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...) @@ -212,14 +215,14 @@ func (r resourceTeleportTrustedCluster) Update(ctx context.Context, req tfsdk.Up if resp.Diagnostics.HasError() { return } + trustedClusterResource := trustedCluster - name := trustedCluster.Metadata.Name - err := trustedCluster.CheckAndSetDefaults() - if err != nil { + 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 { @@ -227,12 +230,13 @@ func (r resourceTeleportTrustedCluster) Update(ctx context.Context, req tfsdk.Up return } - _, err = r.p.Client.UpsertTrustedCluster(ctx, trustedCluster) + _, 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 @@ -259,7 +263,11 @@ func (r resourceTeleportTrustedCluster) Update(ctx context.Context, req tfsdk.Up } } - trustedCluster = trustedClusterI.(*apitypes.TrustedClusterV2) + 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() { @@ -293,13 +301,14 @@ func (r resourceTeleportTrustedCluster) Delete(ctx context.Context, req tfsdk.De // ImportState imports TrustedCluster state func (r resourceTeleportTrustedCluster) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - trustedClusterI, err := r.p.Client.GetTrustedCluster(ctx, req.ID) + 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 } - trustedCluster := trustedClusterI.(*apitypes.TrustedClusterV2) + + trustedClusterResource := trustedCluster.(*apitypes.TrustedClusterV2) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportTrustedCluster) ImportState(ctx context.Context, req tfs return } - diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedCluster, &state) + diags = tfschema.CopyTrustedClusterV2ToTerraform(ctx, trustedClusterResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := trustedClusterResource.GetName() - state.Attrs["id"] = types.String{Value: trustedCluster.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/provider/resource_teleport_user.go b/terraform/provider/resource_teleport_user.go index b8ff360b0..ab68cd350 100755 --- a/terraform/provider/resource_teleport_user.go +++ b/terraform/provider/resource_teleport_user.go @@ -55,6 +55,7 @@ func (r resourceTeleportUserType) NewResource(_ context.Context, p tfsdk.Provide // 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 } @@ -74,13 +75,15 @@ func (r resourceTeleportUser) Create(ctx context.Context, req tfsdk.CreateResour } + userResource := user - _, err := r.p.Client.GetUser(user.Metadata.Name, false) + id := userResource.Metadata.Name + + _, err = r.p.Client.GetUser(id, false) if !trace.IsNotFound(err) { if err == nil { - n := user.Metadata.Name 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)", n, n, n) + " 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 @@ -90,21 +93,20 @@ func (r resourceTeleportUser) Create(ctx context.Context, req tfsdk.CreateResour return } - err = user.CheckAndSetDefaults() + err = userResource.CheckAndSetDefaults() if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error setting User defaults", trace.Wrap(err), "user")) return } - err = r.p.Client.CreateUser(ctx, user) + err = r.p.Client.CreateUser(ctx, userResource) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error creating User", trace.Wrap(err), "user")) return } - - id := user.Metadata.Name + + // 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 { @@ -130,11 +132,12 @@ func (r resourceTeleportUser) Create(ctx context.Context, req tfsdk.CreateResour return } - user, ok := userI.(*apitypes.UserV2) + 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...) @@ -177,7 +180,7 @@ func (r resourceTeleportUser) Read(ctx context.Context, req tfsdk.ReadResourceRe 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...) @@ -212,14 +215,14 @@ func (r resourceTeleportUser) Update(ctx context.Context, req tfsdk.UpdateResour if resp.Diagnostics.HasError() { return } + userResource := user - name := user.Metadata.Name - err := user.CheckAndSetDefaults() - if err != nil { + 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(name, false) if err != nil { @@ -227,12 +230,13 @@ func (r resourceTeleportUser) Update(ctx context.Context, req tfsdk.UpdateResour return } - err = r.p.Client.UpdateUser(ctx, user) + err = r.p.Client.UpdateUser(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 @@ -259,7 +263,11 @@ func (r resourceTeleportUser) Update(ctx context.Context, req tfsdk.UpdateResour } } - user = userI.(*apitypes.UserV2) + 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() { @@ -293,13 +301,14 @@ func (r resourceTeleportUser) Delete(ctx context.Context, req tfsdk.DeleteResour // ImportState imports User state func (r resourceTeleportUser) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { - userI, err := r.p.Client.GetUser(req.ID, false) + user, err := r.p.Client.GetUser(req.ID, false) if err != nil { resp.Diagnostics.Append(diagFromWrappedErr("Error reading User", trace.Wrap(err), "user")) return } - user := userI.(*apitypes.UserV2) + + userResource := user.(*apitypes.UserV2) var state types.Object @@ -309,13 +318,14 @@ func (r resourceTeleportUser) ImportState(ctx context.Context, req tfsdk.ImportR return } - diags = tfschema.CopyUserV2ToTerraform(ctx, user, &state) + diags = tfschema.CopyUserV2ToTerraform(ctx, userResource, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + id := userResource.GetName() - state.Attrs["id"] = types.String{Value: user.Metadata.Name} + state.Attrs["id"] = types.String{Value: id} diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) diff --git a/terraform/reference.mdx b/terraform/reference.mdx index 83d6d9bbb..60134ba70 100755 --- a/terraform/reference.mdx +++ b/terraform/reference.mdx @@ -7,6 +7,7 @@ description: Terraform provider resources reference Supported resources: +- [teleport_access_list](#teleport_access_list) - [teleport_app](#teleport_app) - [teleport_auth_preference](#teleport_auth_preference) - [teleport_bot](#teleport_bot) @@ -83,6 +84,165 @@ provider "teleport" { } ``` +## teleport_access_list + +| Name | Type | Required | Description | +|--------|--------|----------|------------------------------------------------| +| header | object | | header is the header for the resource. | +| spec | object | | spec is the specification for the access list. | + +### header + +header is the header for the resource. + +| Name | Type | Required | Description | +|----------|--------|----------|--------------------------------------------------------------------| +| kind | string | | kind is a resource kind. | +| metadata | object | | metadata is resource metadata. | +| sub_kind | string | | sub_kind is an optional resource sub kind, used in some resources. | +| version | string | | version is version. | + +#### header.metadata + +metadata is resource metadata. + +| Name | Type | Required | Description | +|-------------|----------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| description | string | | description is object description. | +| expires | RFC3339 time | | | +| labels | map of strings | | labels is a set of labels. | +| name | string | * | name is an object name. | +| namespace | string | | namespace is object namespace. The field should be called "namespace" when it returns in Teleport 2.4. | +| revision | string | | revision is an opaque identifier which tracks the versions of a resource over time. Clients should ignore and not alter its value but must return the revision in any updates of a resource. | + +### spec + +spec is the specification for the access list. + +| Name | Type | Required | Description | +|---------------------|--------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| audit | object | | audit describes the frequency that this access list must be audited. | +| description | string | | description is an optional plaintext description of the access list. | +| grants | object | * | grants describes the access granted by membership to this access list. | +| membership_requires | object | * | membership_requires describes the requirements for a user to be a member of the access list. For a membership to an access list to be effective, the user must meet the requirements of Membership_requires and must be in the members list. | +| owners | object | * | owners is a list of owners of the access list. | +| ownership_requires | object | * | ownership_requires describes the requirements for a user to be an owner of the access list. For ownership of an access list to be effective, the user must meet the requirements of ownership_requires and must be in the owners list. | +| title | string | | title is a plaintext short description of the access list. | + +#### spec.audit + +audit describes the frequency that this access list must be audited. + +| Name | Type | Required | Description | +|-----------------|--------------|----------|-------------| +| frequency | duration | | | +| next_audit_date | RFC3339 time | | | + +#### spec.grants + +grants describes the access granted by membership to this access list. + +| Name | Type | Required | Description | +|--------|------------------|----------|-------------------------------------------------------------------------------------| +| roles | array of strings | | roles are the roles that are granted to users who are members of the access list. | +| traits | object | | traits are the traits that are granted to users who are members of the access list. | + +##### spec.grants.traits + +traits are the traits that are granted to users who are members of the access list. + +| Name | Type | Required | Description | +|--------|------------------|----------|-------------------------------------| +| key | string | | key is the name of the trait. | +| values | array of strings | | values is the list of trait values. | + +#### spec.membership_requires + +membership_requires describes the requirements for a user to be a member of the access list. For a membership to an access list to be effective, the user must meet the requirements of Membership_requires and must be in the members list. + +| Name | Type | Required | Description | +|--------|------------------|----------|------------------------------------------------------------------------------| +| roles | array of strings | | roles are the user roles that must be present for the user to obtain access. | +| traits | object | | traits are the traits that must be present for the user to obtain access. | + +##### spec.membership_requires.traits + +traits are the traits that must be present for the user to obtain access. + +| Name | Type | Required | Description | +|--------|------------------|----------|-------------------------------------| +| key | string | | key is the name of the trait. | +| values | array of strings | | values is the list of trait values. | + +#### spec.owners + +owners is a list of owners of the access list. + +| Name | Type | Required | Description | +|-------------|--------|----------|----------------------------------------------------------------------------------| +| description | string | | description is the plaintext description of the owner and why they are an owner. | +| name | string | | name is the username of the owner. | + +#### spec.ownership_requires + +ownership_requires describes the requirements for a user to be an owner of the access list. For ownership of an access list to be effective, the user must meet the requirements of ownership_requires and must be in the owners list. + +| Name | Type | Required | Description | +|--------|------------------|----------|------------------------------------------------------------------------------| +| roles | array of strings | | roles are the user roles that must be present for the user to obtain access. | +| traits | object | | traits are the traits that must be present for the user to obtain access. | + +##### spec.ownership_requires.traits + +traits are the traits that must be present for the user to obtain access. + +| Name | Type | Required | Description | +|--------|------------------|----------|-------------------------------------| +| key | string | | key is the name of the trait. | +| values | array of strings | | values is the list of trait values. | + +Example: + +``` +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 = { + frequency = "3600h" // 150 days + } + } +} + +``` + ## teleport_app | Name | Type | Required | Description | diff --git a/terraform/test/access_list_test.go b/terraform/test/access_list_test.go new file mode 100644 index 000000000..fa8861aa5 --- /dev/null +++ b/terraform/test/access_list_test.go @@ -0,0 +1,81 @@ +/* +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/trace" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func (s *TerraformSuite) TestAccessList() { + 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" + + 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"), + ), + }, + { + 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"), + ), + }, + { + 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"), + ), + }, + { + Config: s.getFixture("access_list_2_expiring.tf"), + PlanOnly: true, + }, + }, + }) +} diff --git a/terraform/test/fixtures/access_list_0_create.tf b/terraform/test/fixtures/access_list_0_create.tf new file mode 100644 index 000000000..53b3fe9fa --- /dev/null +++ b/terraform/test/fixtures/access_list_0_create.tf @@ -0,0 +1,32 @@ +resource "teleport_access_list" "test" { + header = { + 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 = { + frequency = "3600h" + } + } +} diff --git a/terraform/test/fixtures/access_list_1_update.tf b/terraform/test/fixtures/access_list_1_update.tf new file mode 100644 index 000000000..5af8a105a --- /dev/null +++ b/terraform/test/fixtures/access_list_1_update.tf @@ -0,0 +1,36 @@ +resource "teleport_access_list" "test" { + header = { + 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 = { + frequency = "7200h" + } + } +} diff --git a/terraform/test/fixtures/access_list_2_expiring.tf b/terraform/test/fixtures/access_list_2_expiring.tf new file mode 100644 index 000000000..8cbae6682 --- /dev/null +++ b/terraform/test/fixtures/access_list_2_expiring.tf @@ -0,0 +1,37 @@ +resource "teleport_access_list" "test" { + header = { + 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 = { + frequency = "7200h" + } + } +} diff --git a/terraform/test/main_test.go b/terraform/test/main_test.go index 17e1bf91f..f797cdac3 100644 --- a/terraform/test/main_test.go +++ b/terraform/test/main_test.go @@ -130,6 +130,7 @@ func (s *TerraformBaseSuite) SetupSuite() { types.NewRule("saml", unrestricted), types.NewRule("login_rule", unrestricted), types.NewRule("device", unrestricted), + types.NewRule("access_list", unrestricted), }, Logins: []string{me.Username}, }, diff --git a/terraform/tfschema/accesslist/v1/accesslist_terraform.go b/terraform/tfschema/accesslist/v1/accesslist_terraform.go new file mode 100644 index 000000000..e54ab3529 --- /dev/null +++ b/terraform/tfschema/accesslist/v1/accesslist_terraform.go @@ -0,0 +1,2208 @@ +/* +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. +*/ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: teleport/accesslist/v1/accesslist.proto + +package v1 + +import ( + context "context" + fmt "fmt" + math "math" + + proto "github.com/gogo/protobuf/proto" + github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1" + _ "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + _ "github.com/gravitational/teleport/api/gen/proto/go/teleport/trait/v1" + github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/trait/v1" + github_com_hashicorp_terraform_plugin_framework_attr "github.com/hashicorp/terraform-plugin-framework/attr" + github_com_hashicorp_terraform_plugin_framework_diag "github.com/hashicorp/terraform-plugin-framework/diag" + github_com_hashicorp_terraform_plugin_framework_tfsdk "github.com/hashicorp/terraform-plugin-framework/tfsdk" + github_com_hashicorp_terraform_plugin_framework_types "github.com/hashicorp/terraform-plugin-framework/types" + github_com_hashicorp_terraform_plugin_go_tftypes "github.com/hashicorp/terraform-plugin-go/tftypes" + _ "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/timestamppb" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// GenSchemaAccessList returns tfsdk.Schema definition for AccessList +func GenSchemaAccessList(ctx context.Context) (github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema, github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics) { + return github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema{Attributes: map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "header": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "kind": { + Description: "kind is a resource kind.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "metadata": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "description is object description.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "expires": GenSchemaTimestamp(ctx), + "labels": { + Description: "labels is a set of labels.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.MapType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "name": { + Description: "name is an object name.", + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.RequiresReplace()}, + Required: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "namespace": { + Computed: true, + Description: "namespace is object namespace. The field should be called \"namespace\" when it returns in Teleport 2.4.", + Optional: true, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "revision": { + Description: "revision is an opaque identifier which tracks the versions of a resource over time. Clients should ignore and not alter its value but must return the revision in any updates of a resource.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "metadata is resource metadata.", + Optional: true, + }, + "sub_kind": { + Description: "sub_kind is an optional resource sub kind, used in some resources.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "version": { + Description: "version is version.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "header is the header for the resource.", + Optional: true, + }, + "id": { + Computed: true, + Optional: false, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Required: false, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "spec": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "audit": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "frequency": GenSchemaDuration(ctx), + "next_audit_date": GenSchemaTimestamp(ctx), + }), + Description: "audit describes the frequency that this access list must be audited.", + Optional: true, + }, + "description": { + Description: "description is an optional plaintext description of the access list.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "grants": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "roles": { + Description: "roles are the roles that are granted to users who are members of the access list.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "traits": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "key": { + Description: "key is the name of the trait.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "values": { + Description: "values is the list of trait values.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + }), + Description: "traits are the traits that are granted to users who are members of the access list.", + Optional: true, + }, + }), + Description: "grants describes the access granted by membership to this access list.", + Required: true, + }, + "membership_requires": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "roles": { + Description: "roles are the user roles that must be present for the user to obtain access.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "traits": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "key": { + Description: "key is the name of the trait.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "values": { + Description: "values is the list of trait values.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + }), + Description: "traits are the traits that must be present for the user to obtain access.", + Optional: true, + }, + }), + Description: "membership_requires describes the requirements for a user to be a member of the access list. For a membership to an access list to be effective, the user must meet the requirements of Membership_requires and must be in the members list.", + Required: true, + }, + "owners": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "description is the plaintext description of the owner and why they are an owner.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "name": { + Description: "name is the username of the owner.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "owners is a list of owners of the access list.", + Required: true, + }, + "ownership_requires": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "roles": { + Description: "roles are the user roles that must be present for the user to obtain access.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "traits": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "key": { + Description: "key is the name of the trait.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "values": { + Description: "values is the list of trait values.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.ListType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + }), + Description: "traits are the traits that must be present for the user to obtain access.", + Optional: true, + }, + }), + Description: "ownership_requires describes the requirements for a user to be an owner of the access list. For ownership of an access list to be effective, the user must meet the requirements of ownership_requires and must be in the owners list.", + Required: true, + }, + "title": { + Description: "title is a plaintext short description of the access list.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "spec is the specification for the access list.", + Optional: true, + }, + }}, nil +} + +// CopyAccessListFromTerraform copies contents of the source Terraform object into a target struct +func CopyAccessListFromTerraform(_ context.Context, tf github_com_hashicorp_terraform_plugin_framework_types.Object, obj *github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessList) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + { + a, ok := tf.Attrs["header"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Header = nil + if !v.Null && !v.Unknown { + tf := v + obj.Header = &github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1.ResourceHeader{} + obj := obj.Header + { + a, ok := tf.Attrs["kind"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.kind"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Kind = t + } + } + } + { + a, ok := tf.Attrs["sub_kind"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.sub_kind"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.sub_kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SubKind = t + } + } + } + { + a, ok := tf.Attrs["version"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.version"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.version", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Version = t + } + } + } + { + a, ok := tf.Attrs["metadata"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Metadata = nil + if !v.Null && !v.Unknown { + tf := v + obj.Metadata = &github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1.Metadata{} + obj := obj.Metadata + { + a, ok := tf.Attrs["name"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.name"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Name = t + } + } + } + { + a, ok := tf.Attrs["namespace"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.namespace"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.namespace", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Namespace = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["labels"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.labels"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Map) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.Map"}) + } else { + obj.Labels = make(map[string]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.labels", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Labels[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["expires"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.expires"}) + } + CopyFromTimestamp(diags, a, &obj.Expires) + } + { + a, ok := tf.Attrs["revision"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.header.metadata.revision"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.header.metadata.revision", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Revision = t + } + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["spec"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Spec = nil + if !v.Null && !v.Unknown { + tf := v + obj.Spec = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListSpec{} + obj := obj.Spec + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["owners"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.owners"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.owners", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Owners = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListOwner, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.owners", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListOwner + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListOwner{} + obj := t + { + a, ok := tf.Attrs["name"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.owners.name"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.owners.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Name = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.owners.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.owners.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + } + obj.Owners[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["audit"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.audit"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.audit", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Audit = nil + if !v.Null && !v.Unknown { + tf := v + obj.Audit = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListAudit{} + obj := obj.Audit + { + a, ok := tf.Attrs["frequency"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.audit.frequency"}) + } + CopyFromDuration(diags, a, &obj.Frequency) + } + { + a, ok := tf.Attrs["next_audit_date"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.audit.next_audit_date"}) + } + CopyFromTimestamp(diags, a, &obj.NextAuditDate) + } + } + } + } + } + { + a, ok := tf.Attrs["membership_requires"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.MembershipRequires = nil + if !v.Null && !v.Unknown { + tf := v + obj.MembershipRequires = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListRequires{} + obj := obj.MembershipRequires + { + a, ok := tf.Attrs["roles"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires.roles"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Roles = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.roles", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Roles[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["traits"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires.traits"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Traits = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait{} + obj := t + { + a, ok := tf.Attrs["key"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires.traits.key"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Key = t + } + } + } + { + a, ok := tf.Attrs["values"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.membership_requires.traits.values"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Values = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.membership_requires.traits.values", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Values[k] = t + } + } + } + } + } + } + } + obj.Traits[k] = t + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["ownership_requires"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.OwnershipRequires = nil + if !v.Null && !v.Unknown { + tf := v + obj.OwnershipRequires = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListRequires{} + obj := obj.OwnershipRequires + { + a, ok := tf.Attrs["roles"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires.roles"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Roles = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.roles", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Roles[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["traits"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires.traits"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Traits = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait{} + obj := t + { + a, ok := tf.Attrs["key"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires.traits.key"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Key = t + } + } + } + { + a, ok := tf.Attrs["values"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.ownership_requires.traits.values"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Values = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.ownership_requires.traits.values", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Values[k] = t + } + } + } + } + } + } + } + obj.Traits[k] = t + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["grants"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Grants = nil + if !v.Null && !v.Unknown { + tf := v + obj.Grants = &github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessListGrants{} + obj := obj.Grants + { + a, ok := tf.Attrs["roles"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants.roles"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.roles", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Roles = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.roles", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Roles[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["traits"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants.traits"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Traits = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_trait_v1.Trait{} + obj := t + { + a, ok := tf.Attrs["key"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants.traits.key"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Key = t + } + } + } + { + a, ok := tf.Attrs["values"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.grants.traits.values"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Values = make([]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.grants.traits.values", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Values[k] = t + } + } + } + } + } + } + } + obj.Traits[k] = t + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["title"] + if !ok { + diags.Append(attrReadMissingDiag{"AccessList.spec.title"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"AccessList.spec.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Title = t + } + } + } + } + } + } + } + return diags +} + +// CopyAccessListToTerraform copies contents of the source Terraform object into a target struct +func CopyAccessListToTerraform(ctx context.Context, obj *github_com_gravitational_teleport_api_gen_proto_go_teleport_accesslist_v1.AccessList, tf *github_com_hashicorp_terraform_plugin_framework_types.Object) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + tf.Null = false + tf.Unknown = false + if tf.Attrs == nil { + tf.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value) + } + { + a, ok := tf.AttrTypes["header"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["header"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Header == nil { + v.Null = true + } else { + obj := obj.Header + tf := &v + { + t, ok := tf.AttrTypes["kind"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.kind"}) + } else { + v, ok := tf.Attrs["kind"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.kind", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Kind) == "" + } + v.Value = string(obj.Kind) + v.Unknown = false + tf.Attrs["kind"] = v + } + } + { + t, ok := tf.AttrTypes["sub_kind"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.sub_kind"}) + } else { + v, ok := tf.Attrs["sub_kind"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.sub_kind", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.sub_kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SubKind) == "" + } + v.Value = string(obj.SubKind) + v.Unknown = false + tf.Attrs["sub_kind"] = v + } + } + { + t, ok := tf.AttrTypes["version"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.version"}) + } else { + v, ok := tf.Attrs["version"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.version", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.version", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Version) == "" + } + v.Value = string(obj.Version) + v.Unknown = false + tf.Attrs["version"] = v + } + } + { + a, ok := tf.AttrTypes["metadata"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["metadata"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Metadata == nil { + v.Null = true + } else { + obj := obj.Metadata + tf := &v + { + t, ok := tf.AttrTypes["name"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.name"}) + } else { + v, ok := tf.Attrs["name"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.name", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Name) == "" + } + v.Value = string(obj.Name) + v.Unknown = false + tf.Attrs["name"] = v + } + } + { + t, ok := tf.AttrTypes["namespace"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.namespace"}) + } else { + v, ok := tf.Attrs["namespace"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.namespace", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.namespace", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Namespace) == "" + } + v.Value = string(obj.Namespace) + v.Unknown = false + tf.Attrs["namespace"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + a, ok := tf.AttrTypes["labels"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.labels"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.MapType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.MapType"}) + } else { + c, ok := tf.Attrs["labels"].(github_com_hashicorp_terraform_plugin_framework_types.Map) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.Map{ + + ElemType: o.ElemType, + Elems: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Labels)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Labels)) + } + } + if obj.Labels != nil { + t := o.ElemType + for k, a := range obj.Labels { + v, ok := tf.Attrs["labels"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.labels", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = false + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Labels) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["labels"] = c + } + } + } + { + t, ok := tf.AttrTypes["expires"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.expires"}) + } else { + v := CopyToTimestamp(diags, obj.Expires, t, tf.Attrs["expires"]) + tf.Attrs["expires"] = v + } + } + { + t, ok := tf.AttrTypes["revision"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.header.metadata.revision"}) + } else { + v, ok := tf.Attrs["revision"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.header.metadata.revision", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.header.metadata.revision", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Revision) == "" + } + v.Value = string(obj.Revision) + v.Unknown = false + tf.Attrs["revision"] = v + } + } + } + v.Unknown = false + tf.Attrs["metadata"] = v + } + } + } + } + v.Unknown = false + tf.Attrs["header"] = v + } + } + } + { + a, ok := tf.AttrTypes["spec"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["spec"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Spec == nil { + v.Null = true + } else { + obj := obj.Spec + tf := &v + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + a, ok := tf.AttrTypes["owners"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.owners"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.owners", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["owners"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Owners)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Owners)) + } + } + if obj.Owners != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Owners) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Owners)) + } + for k, a := range obj.Owners { + v, ok := tf.Attrs["owners"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["name"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.owners.name"}) + } else { + v, ok := tf.Attrs["name"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.owners.name", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.owners.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Name) == "" + } + v.Value = string(obj.Name) + v.Unknown = false + tf.Attrs["name"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.owners.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.owners.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.owners.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Owners) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["owners"] = c + } + } + } + { + a, ok := tf.AttrTypes["audit"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.audit"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.audit", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["audit"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Audit == nil { + v.Null = true + } else { + obj := obj.Audit + tf := &v + { + t, ok := tf.AttrTypes["frequency"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.audit.frequency"}) + } else { + v := CopyToDuration(diags, obj.Frequency, t, tf.Attrs["frequency"]) + tf.Attrs["frequency"] = v + } + } + { + t, ok := tf.AttrTypes["next_audit_date"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.audit.next_audit_date"}) + } else { + v := CopyToTimestamp(diags, obj.NextAuditDate, t, tf.Attrs["next_audit_date"]) + tf.Attrs["next_audit_date"] = v + } + } + } + v.Unknown = false + tf.Attrs["audit"] = v + } + } + } + { + a, ok := tf.AttrTypes["membership_requires"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["membership_requires"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.MembershipRequires == nil { + v.Null = true + } else { + obj := obj.MembershipRequires + tf := &v + { + a, ok := tf.AttrTypes["roles"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires.roles"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + } + if obj.Roles != nil { + t := o.ElemType + if len(obj.Roles) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + for k, a := range obj.Roles { + v, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.membership_requires.roles", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Roles) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["roles"] = c + } + } + } + { + a, ok := tf.AttrTypes["traits"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires.traits"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.traits", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + } + if obj.Traits != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Traits) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + for k, a := range obj.Traits { + v, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["key"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires.traits.key"}) + } else { + v, ok := tf.Attrs["key"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.membership_requires.traits.key", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Key) == "" + } + v.Value = string(obj.Key) + v.Unknown = false + tf.Attrs["key"] = v + } + } + { + a, ok := tf.AttrTypes["values"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.membership_requires.traits.values"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + } + if obj.Values != nil { + t := o.ElemType + if len(obj.Values) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + for k, a := range obj.Values { + v, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.membership_requires.traits.values", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.membership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Values) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["values"] = c + } + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Traits) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["traits"] = c + } + } + } + } + v.Unknown = false + tf.Attrs["membership_requires"] = v + } + } + } + { + a, ok := tf.AttrTypes["ownership_requires"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["ownership_requires"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.OwnershipRequires == nil { + v.Null = true + } else { + obj := obj.OwnershipRequires + tf := &v + { + a, ok := tf.AttrTypes["roles"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires.roles"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + } + if obj.Roles != nil { + t := o.ElemType + if len(obj.Roles) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + for k, a := range obj.Roles { + v, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.ownership_requires.roles", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.roles", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Roles) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["roles"] = c + } + } + } + { + a, ok := tf.AttrTypes["traits"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires.traits"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.traits", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + } + if obj.Traits != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Traits) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + for k, a := range obj.Traits { + v, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["key"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires.traits.key"}) + } else { + v, ok := tf.Attrs["key"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.ownership_requires.traits.key", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Key) == "" + } + v.Value = string(obj.Key) + v.Unknown = false + tf.Attrs["key"] = v + } + } + { + a, ok := tf.AttrTypes["values"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.ownership_requires.traits.values"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + } + if obj.Values != nil { + t := o.ElemType + if len(obj.Values) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + for k, a := range obj.Values { + v, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.ownership_requires.traits.values", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.ownership_requires.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Values) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["values"] = c + } + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Traits) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["traits"] = c + } + } + } + } + v.Unknown = false + tf.Attrs["ownership_requires"] = v + } + } + } + { + a, ok := tf.AttrTypes["grants"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["grants"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Grants == nil { + v.Null = true + } else { + obj := obj.Grants + tf := &v + { + a, ok := tf.AttrTypes["roles"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants.roles"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.roles", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + } + if obj.Roles != nil { + t := o.ElemType + if len(obj.Roles) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Roles)) + } + for k, a := range obj.Roles { + v, ok := tf.Attrs["roles"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.grants.roles", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.roles", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Roles) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["roles"] = c + } + } + } + { + a, ok := tf.AttrTypes["traits"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants.traits"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.traits", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + } + if obj.Traits != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Traits) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Traits)) + } + for k, a := range obj.Traits { + v, ok := tf.Attrs["traits"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["key"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants.traits.key"}) + } else { + v, ok := tf.Attrs["key"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.grants.traits.key", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.traits.key", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Key) == "" + } + v.Value = string(obj.Key) + v.Unknown = false + tf.Attrs["key"] = v + } + } + { + a, ok := tf.AttrTypes["values"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.grants.traits.values"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + } + if obj.Values != nil { + t := o.ElemType + if len(obj.Values) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Values)) + } + for k, a := range obj.Values { + v, ok := tf.Attrs["values"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.grants.traits.values", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.grants.traits.values", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(a) == "" + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Values) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["values"] = c + } + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Traits) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["traits"] = c + } + } + } + } + v.Unknown = false + tf.Attrs["grants"] = v + } + } + } + { + t, ok := tf.AttrTypes["title"] + if !ok { + diags.Append(attrWriteMissingDiag{"AccessList.spec.title"}) + } else { + v, ok := tf.Attrs["title"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"AccessList.spec.title", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"AccessList.spec.title", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Title) == "" + } + v.Value = string(obj.Title) + v.Unknown = false + tf.Attrs["title"] = v + } + } + } + v.Unknown = false + tf.Attrs["spec"] = v + } + } + } + return diags +} + +// attrReadMissingDiag represents diagnostic message on an attribute missing in the source object +type attrReadMissingDiag struct { + Path string +} + +func (d attrReadMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadMissingDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object Attrs", d.Path) +} + +func (d attrReadMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrReadConversionFailureDiag represents diagnostic message on a failed type conversion on read +type attrReadConversionFailureDiag struct { + Path string + Type string +} + +func (d attrReadConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadConversionFailureDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrReadConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteMissingDiag represents diagnostic message on an attribute missing in the target object +type attrWriteMissingDiag struct { + Path string +} + +func (d attrWriteMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteMissingDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object AttrTypes", d.Path) +} + +func (d attrWriteMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteConversionFailureDiag represents diagnostic message on a failed type conversion on write +type attrWriteConversionFailureDiag struct { + Path string + Type string +} + +func (d attrWriteConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteConversionFailureDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrWriteConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteGeneralError represents diagnostic message on a generic error on write +type attrWriteGeneralError struct { + Path string + Err error +} + +func (d attrWriteGeneralError) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteGeneralError) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteGeneralError) Detail() string { + return fmt.Sprintf("%s: %s", d.Path, d.Err.Error()) +} + +func (d attrWriteGeneralError) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} diff --git a/terraform/tfschema/accesslist/v1/custom_types.go b/terraform/tfschema/accesslist/v1/custom_types.go new file mode 100644 index 000000000..21b214e75 --- /dev/null +++ b/terraform/tfschema/accesslist/v1/custom_types.go @@ -0,0 +1,100 @@ +/* +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 v1 + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/gravitational/teleport-plugins/terraform/tfschema" +) + +func GenSchemaTimestamp(_ context.Context) tfsdk.Attribute { + return tfsdk.Attribute{ + Optional: true, + Type: tfschema.UseRFC3339Time(), + } +} + +func CopyFromTimestamp(diags diag.Diagnostics, v attr.Value, o **timestamppb.Timestamp) { + value, ok := v.(tfschema.TimeValue) + if !ok { + diags.AddError("Error reading from Terraform object", fmt.Sprintf("Can not convert %T to String", v)) + return + } + + if value.IsNull() { + *o = nil + } else { + *o = timestamppb.New(value.Value) + } +} + +func CopyToTimestamp(diags diag.Diagnostics, o *timestamppb.Timestamp, t attr.Type, v attr.Value) attr.Value { + value, ok := v.(tfschema.TimeValue) + if !ok { + value = tfschema.TimeValue{} + } + + if o == nil { + value.Null = true + return value + } + + value.Value = (*o).AsTime() + + return value +} + +func GenSchemaDuration(_ context.Context) tfsdk.Attribute { + return tfsdk.Attribute{ + Optional: true, + Type: tfschema.DurationType{}, + } +} + +func CopyFromDuration(diags diag.Diagnostics, v attr.Value, o **durationpb.Duration) { + value, ok := v.(tfschema.DurationValue) + if !ok { + diags.AddError("Error reading from Terraform object", fmt.Sprintf("Can not convert %T to String", v)) + return + } + + *o = durationpb.New(value.Value) +} + +func CopyToDuration(diags diag.Diagnostics, o *durationpb.Duration, t attr.Type, v attr.Value) attr.Value { + value, ok := v.(tfschema.DurationValue) + if !ok { + value = tfschema.DurationValue{} + } + + if o == nil { + value.Null = true + return value + } + + value.Value = (*o).AsDuration() + + return value +}