mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
Terraform: Convert Auth Preference to generic provider (#69099)
This commit is contained in:
@@ -261,22 +261,6 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
authPreference = payload{
|
||||
Name: "AuthPreference",
|
||||
TypeName: "AuthPreferenceV2",
|
||||
VarName: "authPreference",
|
||||
GetMethod: "GetAuthPreference",
|
||||
CreateMethod: "UpsertAuthPreference",
|
||||
UpdateMethod: "UpsertAuthPreference",
|
||||
UpsertMethodArity: 2,
|
||||
DeleteMethod: "ResetAuthPreference",
|
||||
ID: `"auth_preference"`,
|
||||
Kind: "cluster_auth_preference",
|
||||
HasStaticID: false,
|
||||
TerraformResourceType: "teleport_auth_preference",
|
||||
HasCheckAndSetDefaults: true,
|
||||
}
|
||||
|
||||
clusterMaintenance = payload{
|
||||
Name: "ClusterMaintenanceConfig",
|
||||
TypeName: "ClusterMaintenanceConfigV1",
|
||||
@@ -1121,8 +1105,6 @@ func main() {
|
||||
}
|
||||
|
||||
func genTFSchema() {
|
||||
generateResource(authPreference, singularResource)
|
||||
generateDataSource(authPreference, singularDataSource)
|
||||
generateResource(clusterMaintenance, singularResource)
|
||||
generateDataSource(clusterMaintenance, singularDataSource)
|
||||
generateResource(clusterNetworking, singularResource)
|
||||
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2024 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 legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
"github.com/gravitational/teleport/integrations/terraform/provider/internal/tfdiag"
|
||||
)
|
||||
|
||||
// dataSourceTeleportAuthPreferenceType is the data source metadata type
|
||||
type dataSourceTeleportAuthPreferenceType struct{}
|
||||
|
||||
// dataSourceTeleportAuthPreference is the resource
|
||||
type dataSourceTeleportAuthPreference struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the data source schema
|
||||
func (r dataSourceTeleportAuthPreferenceType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaAuthPreferenceV2(ctx)
|
||||
}
|
||||
|
||||
// NewDataSource creates the empty data source
|
||||
func (r dataSourceTeleportAuthPreferenceType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) {
|
||||
return dataSourceTeleportAuthPreference{
|
||||
p: p.(Provider),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read reads teleport AuthPreference
|
||||
func (r dataSourceTeleportAuthPreference) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) {
|
||||
authPreferenceI, err := r.p.Client().GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
var state types.Object
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
authPreference := authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
diags := tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Remove after updating terraform-plugin to >=v1.5.0.
|
||||
// terraform-plugin-testing version <1.5.0 requires data resources to
|
||||
// implement the 'id' attribute.
|
||||
// https://developer.hashicorp.com/terraform/plugin/framework/acctests#no-id-found-in-attributes
|
||||
v, ok := state.Attrs["id"]
|
||||
if !ok || v.IsNull() {
|
||||
id := authPreference.GetName()
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import "github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
func DataSourceTypes() map[string]tfsdk.DataSourceType {
|
||||
return map[string]tfsdk.DataSourceType{
|
||||
"teleport_app_auth_config": dataSourceTeleportAppAuthConfigType{},
|
||||
"teleport_auth_preference": dataSourceTeleportAuthPreferenceType{},
|
||||
"teleport_cluster_maintenance_config": dataSourceTeleportClusterMaintenanceConfigType{},
|
||||
"teleport_cluster_networking_config": dataSourceTeleportClusterNetworkingConfigType{},
|
||||
"teleport_database": dataSourceTeleportDatabaseType{},
|
||||
@@ -68,7 +67,6 @@ func DataSourceTypes() map[string]tfsdk.DataSourceType {
|
||||
func ResourceTypes() map[string]tfsdk.ResourceType {
|
||||
return map[string]tfsdk.ResourceType{
|
||||
"teleport_app_auth_config": resourceTeleportAppAuthConfigType{},
|
||||
"teleport_auth_preference": resourceTeleportAuthPreferenceType{},
|
||||
"teleport_cluster_maintenance_config": resourceTeleportClusterMaintenanceConfigType{},
|
||||
"teleport_cluster_networking_config": resourceTeleportClusterNetworkingConfigType{},
|
||||
"teleport_database": resourceTeleportDatabaseType{},
|
||||
|
||||
@@ -1,384 +0,0 @@
|
||||
// Code generated by _gen/main.go DO NOT EDIT
|
||||
/*
|
||||
Copyright 2015-2024 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 legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
"github.com/gravitational/teleport/integrations/terraform/provider/internal/tfdiag"
|
||||
)
|
||||
|
||||
// resourceTeleportAuthPreferenceType is the resource metadata type
|
||||
type resourceTeleportAuthPreferenceType struct{}
|
||||
|
||||
// resourceTeleportAuthPreference is the resource
|
||||
type resourceTeleportAuthPreference struct {
|
||||
p Provider
|
||||
}
|
||||
|
||||
// GetSchema returns the resource schema
|
||||
func (r resourceTeleportAuthPreferenceType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
|
||||
return tfschema.GenSchemaAuthPreferenceV2(ctx)
|
||||
}
|
||||
|
||||
// NewResource creates the empty resource
|
||||
func (r resourceTeleportAuthPreferenceType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
|
||||
return resourceTeleportAuthPreference{
|
||||
p: p.(Provider),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create creates the AuthPreference
|
||||
func (r resourceTeleportAuthPreference) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
authPreference := &apitypes.AuthPreferenceV2{}
|
||||
diags = tfschema.CopyAuthPreferenceV2FromTerraform(ctx, plan, authPreference)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := authPreference.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error setting AuthPreference defaults", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
authPreferenceBefore, err := r.p.Client().GetAuthPreference(ctx)
|
||||
if err != nil && !trace.IsNotFound(err) {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client().UpsertAuthPreference(ctx, authPreference)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error creating AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
var authPreferenceI apitypes.AuthPreference
|
||||
|
||||
// Try getting the resource until it exists and is different than the previous ones.
|
||||
// There are two types of singleton resources:
|
||||
// - the ones who can deleted and return a trace.NotFoundErr
|
||||
// - the ones who cannot be deleted, only reset. In this case, the resource revision is used to know if the change got applied.
|
||||
tries := 0
|
||||
retry, err := r.p.Retry()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
tries = tries + 1
|
||||
authPreferenceI, err = r.p.Client().GetAuthPreference(ctx)
|
||||
if trace.IsNotFound(err) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(ctx.Err()), "cluster_auth_preference"))
|
||||
return
|
||||
case <-retry.After():
|
||||
}
|
||||
if tries >= r.p.MaxRetries() {
|
||||
diagMessage := fmt.Sprintf("Error reading AuthPreference (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "cluster_auth_preference")
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
previousMetadata := authPreferenceBefore.GetMetadata()
|
||||
currentMetadata := authPreferenceI.GetMetadata()
|
||||
if previousMetadata.GetRevision() != currentMetadata.GetRevision() || false {
|
||||
break
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(ctx.Err()), "cluster_auth_preference"))
|
||||
return
|
||||
case <-retry.After():
|
||||
}
|
||||
if tries >= r.p.MaxRetries() {
|
||||
diagMessage := fmt.Sprintf("Error reading AuthPreference (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "cluster_auth_preference")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
authPreference, ok := authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
if !ok {
|
||||
resp.Diagnostics.Append(
|
||||
tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Errorf("Can not convert %T to AuthPreferenceV2", authPreferenceI), "cluster_auth_preference"),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: "auth_preference"}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads teleport AuthPreference
|
||||
func (r resourceTeleportAuthPreference) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
|
||||
var state types.Object
|
||||
diags := req.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
authPreferenceI, err := r.p.Client().GetAuthPreference(ctx)
|
||||
if trace.IsNotFound(err) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
authPreference := authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
diags = tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates teleport AuthPreference
|
||||
func (r resourceTeleportAuthPreference) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
|
||||
if !r.p.IsConfigured(resp.Diagnostics) {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
authPreference := &apitypes.AuthPreferenceV2{}
|
||||
diags = tfschema.CopyAuthPreferenceV2FromTerraform(ctx, plan, authPreference)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
err := authPreference.CheckAndSetDefaults()
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error updating AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
authPreferenceBefore, err := r.p.Client().GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = r.p.Client().UpsertAuthPreference(ctx, authPreference)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error updating AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
var authPreferenceI apitypes.AuthPreference
|
||||
|
||||
tries := 0
|
||||
retry, err := r.p.Retry()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
tries = tries + 1
|
||||
authPreferenceI, err = r.p.Client().GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
if authPreferenceBefore.GetMetadata().Revision != authPreferenceI.GetMetadata().Revision || false {
|
||||
break
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(ctx.Err()), "cluster_auth_preference"))
|
||||
return
|
||||
case <-retry.After():
|
||||
}
|
||||
if tries >= r.p.MaxRetries() {
|
||||
diagMessage := fmt.Sprintf("Error reading AuthPreference (tried %d times) - state outdated, please import resource", tries)
|
||||
resp.Diagnostics.AddError(diagMessage, "cluster_auth_preference")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error reading AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
authPreference = authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
diags = tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes Teleport AuthPreference
|
||||
func (r resourceTeleportAuthPreference) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
|
||||
err := r.p.Client().ResetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error deleting AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
|
||||
// ImportState imports AuthPreference state
|
||||
func (r resourceTeleportAuthPreference) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
|
||||
authPreferenceI, err := r.p.Client().GetAuthPreference(ctx)
|
||||
if err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error updating AuthPreference", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
authPreference := authPreferenceI.(*apitypes.AuthPreferenceV2)
|
||||
|
||||
var state types.Object
|
||||
|
||||
diags := resp.State.Get(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
diags = tfschema.CopyAuthPreferenceV2ToTerraform(ctx, authPreference, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := authPreference.GetName()
|
||||
|
||||
state.Attrs["id"] = types.String{Value: id}
|
||||
|
||||
diags = resp.State.Set(ctx, &state)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ModifyPlan modifies the planned value, normalizing null values.
|
||||
func (r resourceTeleportAuthPreference) ModifyPlan(ctx context.Context, req tfsdk.ModifyResourcePlanRequest, resp *tfsdk.ModifyResourcePlanResponse) {
|
||||
// If the entire plan is null, the resource is planned for destruction.
|
||||
if req.Plan.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
|
||||
// If the state is null, the resource is being created. No need to modify plan.
|
||||
if req.State.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
|
||||
var config types.Object
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
authPreference := &apitypes.AuthPreferenceV2{}
|
||||
resp.Diagnostics.Append(tfschema.CopyAuthPreferenceV2FromTerraform(ctx, config, authPreference)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
authPreferenceResource := authPreference
|
||||
|
||||
if err := authPreferenceResource.CheckAndSetDefaults(); err != nil {
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr("Error setting AuthPreference defaults", trace.Wrap(err), "cluster_auth_preference"))
|
||||
return
|
||||
}
|
||||
|
||||
authPreference = authPreferenceResource
|
||||
|
||||
const preserveUnknown = true
|
||||
resp.Diagnostics.Append(tfschema.CopyAuthPreferenceV2ToTerraformPreserveUnknown(ctx, authPreference, &config, preserveUnknown)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var plan types.Object
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
plan.Attrs["spec"] = config.Attrs["spec"]
|
||||
|
||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Teleport
|
||||
// Copyright (C) 2026 Gravitational, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/provider/internal/teleport"
|
||||
"github.com/gravitational/teleport/integrations/terraform/provider/internal/tfdriver"
|
||||
"github.com/gravitational/teleport/integrations/terraform/tfschema"
|
||||
)
|
||||
|
||||
// authPreferenceID is the static identifier used in the terraform state.
|
||||
// Note that types.MetaNameClusterAuthPreference is "cluster-auth-preference"
|
||||
// which can't be used here without causing churn.
|
||||
const authPreferenceID = "auth_preference"
|
||||
|
||||
// NewAuthPreferenceDataSourceType returns the cluster auth preference data source type.
|
||||
func NewAuthPreferenceDataSourceType() tfdriver.DataSourceType[types.AuthPreferenceV2, tfdriver.SingletonIdentifier] {
|
||||
return tfdriver.DataSourceType[types.AuthPreferenceV2, tfdriver.SingletonIdentifier]{
|
||||
NewDataSourceClient: func(p tfsdk.Provider) tfdriver.DataSourceClient[types.AuthPreferenceV2, tfdriver.SingletonIdentifier] {
|
||||
return teleport.NewAuthPreferenceClient(clientFromProvider(p))
|
||||
},
|
||||
Kind: types.KindClusterAuthPreference,
|
||||
Codec: tfdriver.DataSourceCodecFuncs[types.AuthPreferenceV2]{
|
||||
SchemaFunc: tfschema.GenSchemaAuthPreferenceV2,
|
||||
ToStateFunc: tfschema.CopyAuthPreferenceV2ToTerraform,
|
||||
},
|
||||
Identifier: tfdriver.SingletonIdentifierFromName(authPreferenceID),
|
||||
}
|
||||
}
|
||||
|
||||
// NewAuthPreferenceResourceType returns the cluster auth preference resource type.
|
||||
func NewAuthPreferenceResourceType() tfdriver.ResourceType[types.AuthPreferenceV2, tfdriver.SingletonIdentifier] {
|
||||
return tfdriver.ResourceType[types.AuthPreferenceV2, tfdriver.SingletonIdentifier]{
|
||||
NewResourceClient: func(p tfsdk.Provider) tfdriver.ResourceClient[types.AuthPreferenceV2, tfdriver.SingletonIdentifier] {
|
||||
return teleport.NewAuthPreferenceClient(clientFromProvider(p))
|
||||
},
|
||||
Kind: types.KindClusterAuthPreference,
|
||||
Codec: tfdriver.ResourceCodecFuncs[types.AuthPreferenceV2]{
|
||||
SchemaFunc: tfschema.GenSchemaAuthPreferenceV2,
|
||||
ToStateFunc: tfschema.CopyAuthPreferenceV2ToTerraform,
|
||||
FromPlanFunc: tfschema.CopyAuthPreferenceV2FromTerraform,
|
||||
},
|
||||
Normalizer: tfdriver.CheckAndSetDefaults[types.AuthPreferenceV2](),
|
||||
Identifier: tfdriver.SingletonIdentifierPolicy[types.AuthPreferenceV2](authPreferenceID),
|
||||
ResourceRevision: func(st *types.AuthPreferenceV2) string {
|
||||
return st.GetMetadata().Revision
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Teleport
|
||||
// Copyright (C) 2026 Gravitational, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package teleport
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/gravitational/teleport/api/client"
|
||||
clusterconfigv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
|
||||
"github.com/gravitational/teleport/integrations/terraform/provider/internal/tfdriver"
|
||||
)
|
||||
|
||||
// NewAuthPreferenceClient returns a Teleport client to interact with the cluster auth preference.
|
||||
func NewAuthPreferenceClient(c *client.Client) AuthPreferenceClient {
|
||||
return AuthPreferenceClient{client: c}
|
||||
}
|
||||
|
||||
// AuthPreferenceClient manages the Teleport cluster auth preference..
|
||||
type AuthPreferenceClient struct {
|
||||
client *client.Client
|
||||
}
|
||||
|
||||
// Get reads a the Teleport clust auth preference.
|
||||
func (r AuthPreferenceClient) Get(ctx context.Context, _ tfdriver.SingletonIdentifier) (*types.AuthPreferenceV2, error) {
|
||||
pref, err := r.client.ClusterConfigClient().GetAuthPreference(ctx, &clusterconfigv1.GetAuthPreferenceRequest{})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return pref, nil
|
||||
}
|
||||
|
||||
// Create creates the Teleport cluster auth preference.
|
||||
func (r AuthPreferenceClient) Create(ctx context.Context, pref *types.AuthPreferenceV2) error {
|
||||
if _, err := r.client.UpsertAuthPreference(ctx, pref); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Upsert updates the Teleport cluster auth preference.
|
||||
func (r AuthPreferenceClient) Upsert(ctx context.Context, pref *types.AuthPreferenceV2) error {
|
||||
if _, err := r.client.UpsertAuthPreference(ctx, pref); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete resets the Teleport cluster auth preference.
|
||||
func (r AuthPreferenceClient) Delete(ctx context.Context, _ tfdriver.SingletonIdentifier) error {
|
||||
return trace.Wrap(r.client.ResetAuthPreference(ctx))
|
||||
}
|
||||
@@ -130,19 +130,21 @@ func (r Resource[T, I]) Create(ctx context.Context, req tfsdk.CreateResourceRequ
|
||||
}
|
||||
|
||||
id := r.resource.Identifier.FromResource(val)
|
||||
_, err := r.resourceClient.Get(ctx, id)
|
||||
if !trace.IsNotFound(err) {
|
||||
if err == nil {
|
||||
resp.Diagnostics.Append(
|
||||
tfdiag.DiagFromErr(
|
||||
fmt.Sprintf("%q exists in Teleport", r.resource.Kind),
|
||||
trace.Errorf("%[1]s exists in Teleport. Either remove it (tctl rm %[1]s/%[2]v)"+
|
||||
" or import it to the existing state (terraform import teleport_%[1]s.%[2]v %[2]v)", r.resource.Kind, id),
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
_, singletonResource := any(id).(SingletonIdentifier)
|
||||
|
||||
resourceBefore, err := r.resourceClient.Get(ctx, id)
|
||||
switch {
|
||||
case trace.IsNotFound(err):
|
||||
case err == nil && !singletonResource:
|
||||
resp.Diagnostics.Append(
|
||||
tfdiag.DiagFromErr(
|
||||
fmt.Sprintf("%q exists in Teleport", r.resource.Kind),
|
||||
trace.Errorf("%[1]s exists in Teleport. Either remove it (tctl rm %[1]s/%[2]v)"+
|
||||
" or import it to the existing state (terraform import teleport_%[1]s.%[2]v %[2]v)", r.resource.Kind, id),
|
||||
),
|
||||
)
|
||||
return
|
||||
case err != nil:
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr(fmt.Sprintf("Error reading %q", r.resource.Kind), trace.Wrap(err), r.resource.Kind))
|
||||
return
|
||||
}
|
||||
@@ -159,42 +161,41 @@ func (r Resource[T, I]) Create(ctx context.Context, req tfsdk.CreateResourceRequ
|
||||
|
||||
for tries := 1; ; tries++ {
|
||||
retrieved, err := r.resourceClient.Get(ctx, id)
|
||||
if trace.IsNotFound(err) {
|
||||
if tries >= r.runtime.MaxRetries() {
|
||||
diagMessage := fmt.Sprintf("Error reading %q (tried %d times) - state outdated, please import resource", r.resource.Kind, tries)
|
||||
resp.Diagnostics.AddError(diagMessage, r.resource.Kind)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr(fmt.Sprintf("Error reading %q", r.resource.Kind), trace.Wrap(ctx.Err()), r.resource.Kind))
|
||||
return
|
||||
case <-retry.After():
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
switch {
|
||||
case trace.IsNotFound(err):
|
||||
case err != nil:
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr(fmt.Sprintf("Error reading %q", r.resource.Kind), trace.Wrap(err), r.resource.Kind))
|
||||
return
|
||||
}
|
||||
case singletonResource &&
|
||||
resourceBefore != nil &&
|
||||
r.resource.ResourceRevision(resourceBefore) == r.resource.ResourceRevision(retrieved):
|
||||
default:
|
||||
diags = r.resource.Codec.ToState(ctx, retrieved, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: id.String()}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
diags = r.resource.Codec.ToState(ctx, retrieved, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
plan.Attrs["id"] = types.String{Value: id.String()}
|
||||
|
||||
diags = resp.State.Set(ctx, &plan)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
if tries >= r.runtime.MaxRetries() {
|
||||
diagMessage := fmt.Sprintf("Error reading %q (tried %d times) - state outdated, please import resource", r.resource.Kind, tries)
|
||||
resp.Diagnostics.AddError(diagMessage, r.resource.Kind)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
resp.Diagnostics.Append(tfdiag.DiagFromWrappedErr(fmt.Sprintf("Error reading %q", r.resource.Kind), trace.Wrap(ctx.Err()), r.resource.Kind))
|
||||
return
|
||||
case <-retry.After():
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -567,6 +567,7 @@ func (p *Provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceTyp
|
||||
"teleport_access_list": resources.NewAccessListResourceType(),
|
||||
"teleport_access_list_member": resources.NewAccessListMemberResourceType(),
|
||||
"teleport_app": resources.NewAppResourceType(),
|
||||
"teleport_auth_preference": resources.NewAuthPreferenceResourceType(),
|
||||
"teleport_kube_cluster": resources.NewKubernetesClusterResourceType(),
|
||||
"teleport_saml_connector": resources.NewSAMLConnectorResourceType(),
|
||||
"teleport_scoped_role": resources.NewScopedRoleResourceType(),
|
||||
@@ -587,6 +588,7 @@ func (p *Provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourc
|
||||
"teleport_access_list": resources.NewAccessListDataSourceType(),
|
||||
"teleport_access_list_member": resources.NewAccessListMemberDataSourceType(),
|
||||
"teleport_app": resources.NewAppDataSourceType(),
|
||||
"teleport_auth_preference": resources.NewAuthPreferenceDataSourceType(),
|
||||
"teleport_kube_cluster": resources.NewKubernetesClusterDataSourceType(),
|
||||
"teleport_saml_connector": resources.NewSAMLConnectorDataSourceType(),
|
||||
"teleport_scoped_role": resources.NewScopedRoleDataSourceType(),
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package testlib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
|
||||
@@ -80,12 +79,8 @@ func (s *TerraformSuiteOSS) TestAuthPreference() {
|
||||
}
|
||||
|
||||
func (s *TerraformSuiteOSS) TestImportAuthPreference() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.T().Cleanup(cancel)
|
||||
|
||||
r := "teleport_auth_preference"
|
||||
id := "test_import"
|
||||
name := r + "." + id
|
||||
name := r + "." + "auth_preference"
|
||||
|
||||
authPreference := &types.AuthPreferenceV2{
|
||||
Metadata: types.Metadata{},
|
||||
@@ -96,14 +91,14 @@ func (s *TerraformSuiteOSS) TestImportAuthPreference() {
|
||||
err := authPreference.CheckAndSetDefaults()
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
authPreferencesBefore, err := s.client.GetAuthPreference(ctx)
|
||||
authPreferencesBefore, err := s.client.GetAuthPreference(s.T().Context())
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
_, err = s.client.ClusterConfigClient().UpsertAuthPreference(ctx, &clusterconfigv1.UpsertAuthPreferenceRequest{AuthPreference: authPreference})
|
||||
_, err = s.client.ClusterConfigClient().UpsertAuthPreference(s.T().Context(), &clusterconfigv1.UpsertAuthPreferenceRequest{AuthPreference: authPreference})
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
require.Eventually(s.T(), func() bool {
|
||||
authPreferencesCurrent, err := s.client.GetAuthPreference(ctx)
|
||||
authPreferencesCurrent, err := s.client.GetAuthPreference(s.T().Context())
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
return authPreferencesBefore.GetMetadata().Revision != authPreferencesCurrent.GetMetadata().Revision
|
||||
@@ -114,10 +109,10 @@ func (s *TerraformSuiteOSS) TestImportAuthPreference() {
|
||||
IsUnitTest: true,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: s.terraformConfig + "\n" + `resource "` + r + `" "` + id + `" { }`,
|
||||
Config: s.terraformConfig + "\n" + `resource "` + r + `" "auth_preference" { }`,
|
||||
ResourceName: name,
|
||||
ImportState: true,
|
||||
ImportStateId: id,
|
||||
ImportStateId: "auth_preference",
|
||||
ImportStateCheck: func(state []*terraform.InstanceState) error {
|
||||
require.Equal(s.T(), "cluster_auth_preference", state[0].Attributes["kind"])
|
||||
require.Equal(s.T(), "true", state[0].Attributes["spec.disconnect_expired_cert"])
|
||||
|
||||
Reference in New Issue
Block a user