package provider import ( "context" "fmt" "net/http" "os" "time" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" edgeclient "edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect/v2" ) var _ provider.Provider = &EdgeConnectProvider{} type EdgeConnectProvider struct { version string } type EdgeConnectProviderModel struct { Endpoint types.String `tfsdk:"endpoint"` Token types.String `tfsdk:"token"` Username types.String `tfsdk:"username"` Password types.String `tfsdk:"password"` } func (p *EdgeConnectProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { resp.TypeName = "edge-connect" resp.Version = p.version } func (p *EdgeConnectProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ MarkdownDescription: "Provider for Edge Connect API. Supports authentication via token or username/password. " + "Configuration can be provided via attributes or environment variables.", Attributes: map[string]schema.Attribute{ "endpoint": schema.StringAttribute{ MarkdownDescription: "Edge Connect API endpoint. Can also be set via `EDGE_CONNECT_ENDPOINT` environment variable.", Optional: true, }, "token": schema.StringAttribute{ MarkdownDescription: "Edge Connect API token. Required if username/password are not provided. " + "Can also be set via `EDGE_CONNECT_TOKEN` environment variable.", Optional: true, Sensitive: true, }, "username": schema.StringAttribute{ MarkdownDescription: "Edge Connect API username. Required if token is not provided. " + "Can also be set via `EDGE_CONNECT_USERNAME` environment variable.", Optional: true, }, "password": schema.StringAttribute{ MarkdownDescription: "Edge Connect API password. Required if token is not provided. " + "Can also be set via `EDGE_CONNECT_PASSWORD` environment variable.", Optional: true, Sensitive: true, }, }, } } func (p *EdgeConnectProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { var data EdgeConnectProviderModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } // Read configuration values, falling back to environment variables endpoint := data.Endpoint.ValueString() if endpoint == "" { endpoint = os.Getenv("EDGE_CONNECT_ENDPOINT") } token := data.Token.ValueString() if token == "" { token = os.Getenv("EDGE_CONNECT_TOKEN") } username := data.Username.ValueString() if username == "" { username = os.Getenv("EDGE_CONNECT_USERNAME") } password := data.Password.ValueString() if password == "" { password = os.Getenv("EDGE_CONNECT_PASSWORD") } // Validate endpoint if endpoint == "" { resp.Diagnostics.AddAttributeError( path.Root("endpoint"), "Missing Edge Connect API Endpoint", "The provider cannot create the Edge Connect API client as there is a missing or empty value for the Edge Connect API endpoint. "+ "Set the endpoint value in the configuration or use the EDGE_CONNECT_ENDPOINT environment variable.", ) } // Validate authentication: either token OR username/password must be provided hasToken := token != "" hasUsernamePassword := username != "" && password != "" if !hasToken && !hasUsernamePassword { resp.Diagnostics.AddError( "Missing Authentication Credentials", "The provider requires authentication credentials. Please provide either:\n"+ " - token: API token for authentication (via 'token' attribute or EDGE_CONNECT_TOKEN environment variable), or\n"+ " - username and password: Username and password for authentication (via 'username'/'password' attributes or EDGE_CONNECT_USERNAME/EDGE_CONNECT_PASSWORD environment variables)", ) } if hasToken && hasUsernamePassword { resp.Diagnostics.AddError( "Conflicting Authentication Methods", "Both token and username/password authentication methods are provided. Please use only one authentication method.", ) } // Validate username/password pair if (username != "" && password == "") || (username == "" && password != "") { resp.Diagnostics.AddError( "Incomplete Username/Password Credentials", "Both username and password must be provided together for username/password authentication.", ) } if resp.Diagnostics.HasError() { return } ctx = tflog.SetField(ctx, "edge_connect_endpoint", endpoint) if hasToken { ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "edge_connect_token") tflog.Debug(ctx, "Creating Edge Connect client with token authentication") } else { ctx = tflog.SetField(ctx, "edge_connect_username", username) ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "edge_connect_password") tflog.Debug(ctx, "Creating Edge Connect client with username/password authentication") } var client *edgeclient.Client if token != "" { client = edgeclient.NewClient(endpoint, edgeclient.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}), edgeclient.WithAuthProvider(edgeclient.NewStaticTokenProvider(token)), edgeclient.WithLogger(tfLogger{}), ) } else { client = edgeclient.NewClientWithCredentials(endpoint, username, password, edgeclient.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}), edgeclient.WithLogger(tfLogger{}), ) } // TODO: Configure client with authentication credentials // If using token: client.SetToken(token) // If using username/password: client.SetCredentials(username, password) resp.DataSourceData = client resp.ResourceData = client tflog.Info(ctx, "Configured Edge Connect client", map[string]any{"success": true}) } func (p *EdgeConnectProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ NewAppResource, NewAppInstanceResource, } } func (p *EdgeConnectProvider) DataSources(ctx context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ NewAppDataSource, NewAppInstanceDataSource, } } func New(version string) func() provider.Provider { return func() provider.Provider { return &EdgeConnectProvider{ version: version, } } } type tfLogger struct{} func (tfLogger) Printf(format string, v ...interface{}) { tflog.Debug(context.TODO(), fmt.Sprintf(format, v...)) }