Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
stdevel committed Sep 26, 2024
1 parent f8c917f commit 1530c5e
Show file tree
Hide file tree
Showing 9 changed files with 872 additions and 50 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ website/node_modules
*.iml
*.test
*.iml
trace.txt

website/vendor

Expand Down
15 changes: 15 additions & 0 deletions examples/provider-install-verification/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
terraform {
required_providers {
uyuni = {
source = "registry.terraform.io/svalabs/uyuni"
}
}
}

provider "uyuni" {
host = "192.168.1.100"
username = "admin"
password = "admin"
}

# data "uyuni_users" "example" {}
27 changes: 27 additions & 0 deletions examples/users/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
terraform {
required_providers {
uyuni = {
source = "registry.terraform.io/svalabs/uyuni"
}
}
}

provider "uyuni" {
host = "192.168.1.100"
username = "admin"
password = "admin"
}

data "uyuni_users" "my_users" {}

resource "uyuni_user" "sgiertz" {
login = "sgiertz"
firstname = "Simone"
lastname = "Giertz"
email = "[email protected]"
password = "test123"
}

output "users" {
value = data.uyuni_users.my_users
}
22 changes: 21 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/hashicorp/terraform-provider-scaffolding-framework
module terraform-provider-uyuni

go 1.22.7

Expand All @@ -7,14 +7,18 @@ require (
github.com/hashicorp/terraform-plugin-go v0.24.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.10.0
github.com/uyuni-project/uyuni-tools v0.0.0-20240925104919-172b63dcc7ae
)

require (
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
github.com/agext/levenshtein v1.2.2 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/briandowns/spinner v1.23.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fsnotify/fsnotify v1.4.7 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
Expand All @@ -28,6 +32,7 @@ require (
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/hc-install v0.8.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/hcl/v2 v2.21.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.21.0 // indirect
Expand All @@ -36,7 +41,9 @@ require (
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
Expand All @@ -45,8 +52,17 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rs/zerolog v1.30.0 // indirect
github.com/spf13/afero v1.1.2 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/jwalterweatherman v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.0 // indirect
github.com/stretchr/testify v1.8.2 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
Expand All @@ -56,10 +72,14 @@ require (
golang.org/x/net v0.26.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.23.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
google.golang.org/grpc v1.66.2 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/ini.v1 v1.51.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
297 changes: 297 additions & 0 deletions go.sum

Large diffs are not rendered by default.

214 changes: 170 additions & 44 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
@@ -1,93 +1,219 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"net/http"
"os"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/function"
"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"
"github.com/uyuni-project/uyuni-tools/shared/api"
)

// Ensure ScaffoldingProvider satisfies various provider interfaces.
var _ provider.Provider = &ScaffoldingProvider{}
var _ provider.ProviderWithFunctions = &ScaffoldingProvider{}
// Ensure the implementation satisfies the expected interfaces.
var (
_ provider.Provider = &uyuniProvider{}
)

// ScaffoldingProvider defines the provider implementation.
type ScaffoldingProvider struct {
// uyuniProvider is the provider implementation.
type uyuniProvider struct {
// version is set to the provider version on release, "dev" when the
// provider is built and ran locally, and "test" when running acceptance
// testing.
version string
}

// ScaffoldingProviderModel describes the provider data model.
type ScaffoldingProviderModel struct {
Endpoint types.String `tfsdk:"endpoint"`
// uyuniProviderModel maps provider schema data to a Go type.
type uyuniProviderModel struct {
Host types.String `tfsdk:"host"`
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
}

// New is a helper function to simplify provider server and testing implementation.
func New(version string) func() provider.Provider {
return func() provider.Provider {
return &uyuniProvider{
version: version,
}
}
}

func (p *ScaffoldingProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "scaffolding"
// Metadata returns the provider type name.
func (p *uyuniProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "uyuni"
resp.Version = p.version
}

func (p *ScaffoldingProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
// Schema defines the provider-level schema for configuration data.
func (p *uyuniProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"endpoint": schema.StringAttribute{
MarkdownDescription: "Example provider attribute",
Optional: true,
"host": schema.StringAttribute{
Optional: true,
},
"username": schema.StringAttribute{
Optional: true,
},
"password": schema.StringAttribute{
Optional: true,
Sensitive: true,
},
},
}
}

func (p *ScaffoldingProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var data ScaffoldingProviderModel
// Configure prepares a uyuni API client for data sources and resources.
func (p *uyuniProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
tflog.Info(ctx, "Configuring Uyuni client")

// Retrieve provider data from configuration
var config uyuniProviderModel
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

// If practitioner provided a configuration value for any of the
// attributes, it must be a known value.

if config.Host.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("host"),
"Unknown Uyuni API Host",
"The provider cannot create the Uyuni API client as there is an unknown configuration value for the Uyuni API host. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the Uyuni_HOST environment variable.",
)
}

resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if config.Username.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("username"),
"Unknown Uyuni API Username",
"The provider cannot create the Uyuni API client as there is an unknown configuration value for the Uyuni API username. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the Uyuni_USERNAME environment variable.",
)
}

if config.Password.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("password"),
"Unknown Uyuni API Password",
"The provider cannot create the Uyuni API client as there is an unknown configuration value for the Uyuni API password. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the Uyuni_PASSWORD environment variable.",
)
}

if resp.Diagnostics.HasError() {
return
}

// Configuration values are now available.
// if data.Endpoint.IsNull() { /* ... */ }
// Default values to environment variables, but override
// with Terraform configuration value if set.

// Example client configuration for data sources and resources
client := http.DefaultClient
resp.DataSourceData = client
resp.ResourceData = client
}
host := os.Getenv("UYUNI_HOST")
username := os.Getenv("UYUNI_USERNAME")
password := os.Getenv("UYUNI_PASSWORD")

func (p *ScaffoldingProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewExampleResource,
if !config.Host.IsNull() {
host = config.Host.ValueString()
}
}

func (p *ScaffoldingProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewExampleDataSource,
if !config.Username.IsNull() {
username = config.Username.ValueString()
}

if !config.Password.IsNull() {
password = config.Password.ValueString()
}

// If any of the expected configurations are missing, return
// errors with provider-specific guidance.

if host == "" {
resp.Diagnostics.AddAttributeError(
path.Root("host"),
"Missing Uyuni API Host",
"The provider cannot create the Uyuni API client as there is a missing or empty value for the Uyuni API host. "+
"Set the host value in the configuration or use the Uyuni_HOST environment variable. "+
"If either is already set, ensure the value is not empty.",
)
}

if username == "" {
resp.Diagnostics.AddAttributeError(
path.Root("username"),
"Missing Uyuni API Username",
"The provider cannot create the Uyuni API client as there is a missing or empty value for the Uyuni API username. "+
"Set the username value in the configuration or use the Uyuni_USERNAME environment variable. "+
"If either is already set, ensure the value is not empty.",
)
}

if password == "" {
resp.Diagnostics.AddAttributeError(
path.Root("password"),
"Missing Uyuni API Password",
"The provider cannot create the Uyuni API client as there is a missing or empty value for the Uyuni API password. "+
"Set the password value in the configuration or use the Uyuni_PASSWORD environment variable. "+
"If either is already set, ensure the value is not empty.",
)
}

if resp.Diagnostics.HasError() {
return
}

ctx = tflog.SetField(ctx, "uyuni_host", host)
ctx = tflog.SetField(ctx, "uyuni_username", username)
ctx = tflog.SetField(ctx, "uyuni_password", password)
ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "uyuni_password")

tflog.Debug(ctx, "Creating HashiCups client")

// Create a new Uyuni client using the configuration values
var _conn = api.ConnectionDetails{
Server: host,
User: username,
Password: password,
CAcert: "",
Insecure: true,
}
client, err := api.Init(&_conn)

if err != nil {
resp.Diagnostics.AddError(
"Unable to Create Uyuni API Client",
"An unexpected error occurred when creating the Uyuni API client. "+
"If the error is not clear, please contact the provider developers.\n\n"+
"Uyuni Client Error: "+err.Error(),
)
return
}

// Make the Uyuni client available during DataSource and Resource
// type Configure methods.
resp.DataSourceData = client
resp.ResourceData = client

tflog.Info(ctx, "Configured Uyuni client", map[string]any{"success": true})
}

func (p *ScaffoldingProvider) Functions(ctx context.Context) []func() function.Function {
return []func() function.Function{
NewExampleFunction,
// DataSources defines the data sources implemented in the provider.
func (p *uyuniProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewUsersDataSource,
}
}

func New(version string) func() provider.Provider {
return func() provider.Provider {
return &ScaffoldingProvider{
version: version,
}
// Resources defines the resources implemented in the provider.
func (p *uyuniProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewUserResource,
}
}
Loading

0 comments on commit 1530c5e

Please sign in to comment.