Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial pass of default_organization support #37

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/provider/provider.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ provider "aap" {
host = "https://localhost:8043"
username = "ansible"
password = "test123!"
default_orgaization = 4
insecure_skip_verify = true
}

Expand Down
23 changes: 14 additions & 9 deletions internal/provider/inventory_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"log"
"path"

"github.com/ansible/terraform-provider-aap/internal/provider/customtypes"
Expand All @@ -24,13 +25,16 @@ var (
)

// NewInventoryResource is a helper function to simplify the provider implementation.
func NewInventoryResource() resource.Resource {
return &InventoryResource{}
func NewInventoryResource(providerModel *aapProviderModel) resource.Resource {
return &InventoryResource{
providerModel: providerModel,
}
}

// InventoryResource is the resource implementation.
type InventoryResource struct {
client ProviderHTTPClient
providerModel *aapProviderModel
client ProviderHTTPClient
}

// Metadata returns the resource type name.
Expand Down Expand Up @@ -75,7 +79,7 @@ func (r *InventoryResource) Schema(_ context.Context, _ resource.SchemaRequest,
int64planmodifier.UseStateForUnknown(),
},
Description: "Identifier for the organization the inventory should be created in. " +
"If not provided, the inventory will be created in the default organization.",
"If not specified on the resource or in the provider block, the inventory will be created in the default organization.",
},
"url": schema.StringAttribute{
Computed: true,
Expand Down Expand Up @@ -115,7 +119,7 @@ func (r *InventoryResource) Create(ctx context.Context, req resource.CreateReque
}

// Generate request body from inventory data
createRequestBody, diags := data.generateRequestBody()
createRequestBody, diags := data.generateRequestBody(*r.providerModel)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
Expand Down Expand Up @@ -192,7 +196,7 @@ func (r *InventoryResource) Update(ctx context.Context, req resource.UpdateReque
}

// Generate request body from inventory data
updateRequestBody, diags := data.generateRequestBody()
updateRequestBody, diags := data.generateRequestBody(*r.providerModel)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
Expand Down Expand Up @@ -252,15 +256,17 @@ type inventoryResourceModel struct {
}

// generateRequestBody creates a JSON encoded request body from the inventory resource data.
func (r *inventoryResourceModel) generateRequestBody() ([]byte, diag.Diagnostics) {
func (r *inventoryResourceModel) generateRequestBody(providerModel aapProviderModel) ([]byte, diag.Diagnostics) {
// Convert inventory resource data to API data model
var organizationId int64

// Use default organization if not provided
if r.Organization.ValueInt64() == 0 {
organizationId = 1
organizationId = providerModel.DefaultOrganization.ValueInt64()
log.Printf("Organization ID set to the Default: %d", organizationId)
} else {
organizationId = r.Organization.ValueInt64()
log.Printf("Organization ID set by the Resource: %d", organizationId)
}
inventory := InventoryAPIModel{
Organization: organizationId,
Expand All @@ -279,7 +285,6 @@ func (r *inventoryResourceModel) generateRequestBody() ([]byte, diag.Diagnostics
)
return nil, diags
}

return jsonBody, nil
}

Expand Down
14 changes: 12 additions & 2 deletions internal/provider/inventory_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/diag"
fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
Expand All @@ -26,7 +27,7 @@ func TestInventoryResourceSchema(t *testing.T) {
schemaResponse := &fwresource.SchemaResponse{}

// Instantiate the InventoryResource and call its Schema method
NewInventoryResource().Schema(ctx, schemaRequest, schemaResponse)
NewInventoryResource(&aapProviderModel{}).Schema(ctx, schemaRequest, schemaResponse)

if schemaResponse.Diagnostics.HasError() {
t.Fatalf("Schema method diagnostics: %+v", schemaResponse.Diagnostics)
Expand Down Expand Up @@ -89,7 +90,16 @@ func TestInventoryResourceGenerateRequestBody(t *testing.T) {

for _, test := range testTable {
t.Run(test.name, func(t *testing.T) {
actual, diags := test.input.generateRequestBody()
providerModel := aapProviderModel{
Host: basetypes.StringValue{},
Username: basetypes.StringValue{},
Password: basetypes.StringValue{},
DefaultOrganization: types.Int64Value(1),
InsecureSkipVerify: basetypes.BoolValue{},
Timeout: basetypes.Int64Value{},
}

actual, diags := test.input.generateRequestBody(providerModel)
if diags.HasError() {
t.Fatal(diags.Errors())
}
Expand Down
61 changes: 49 additions & 12 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type aapProvider struct {
// provider is built and ran locally, and "test" when running acceptance
// testing.
version string
config aapProviderModel
}

// Metadata returns the provider type name.
Expand All @@ -56,12 +57,17 @@ func (p *aapProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *
Optional: true,
Sensitive: true,
},
"default_organization": schema.Int64Attribute{
Optional: true,
Description: "Default organization ID to use for resources that require an organization ID. " +
"Defaults to 1 (the Default Organization) if not provided.",
},
"insecure_skip_verify": schema.BoolAttribute{
Optional: true,
},
"timeout": schema.Int64Attribute{
Optional: true,
Description: "Timeout specifies a time limit for requests made to the AAP server." +
Description: "Timeout specifies a time limit for requests made to the AAP server. " +
"Defaults to 5 if not provided. A Timeout of zero means no timeout.",
},
},
Expand Down Expand Up @@ -107,18 +113,18 @@ func (p *aapProvider) Configure(ctx context.Context, req provider.ConfigureReque
var host, username, password string
var insecureSkipVerify bool
var timeout int64
config.ReadValues(&host, &username, &password, &insecureSkipVerify, &timeout, resp)
var default_organization int64
config.ReadValues(&host, &username, &password, &default_organization, &insecureSkipVerify, &timeout, resp)
if resp.Diagnostics.HasError() {
return
}

// Explicitly set the DefaultOrganization to the value read from the configuration
config.DefaultOrganization = types.Int64Value(default_organization)
// If any of the expected configurations are missing, return
// errors with provider-specific guidance.

if len(host) == 0 {
AddConfigurationAttributeError(resp, "host", "AAP_HOST", false)
}

if len(username) == 0 {
AddConfigurationAttributeError(resp, "username", "AAP_USERNAME", false)
}
Expand All @@ -139,6 +145,8 @@ func (p *aapProvider) Configure(ctx context.Context, req provider.ConfigureReque
// type Configure methods.
resp.DataSourceData = client
resp.ResourceData = client
// make the config available through the provider
p.config = config
}

// DataSources defines the data sources implemented in the provider.
Expand All @@ -148,10 +156,17 @@ func (p *aapProvider) DataSources(_ context.Context) []func() datasource.DataSou
}
}

// Wrapper function to adapt NewInventoryResource to the expected signature and be able to pass the default org around
func NewInventoryResourceWrapper(providerModel *aapProviderModel) func() resource.Resource {
return func() resource.Resource {
return NewInventoryResource(providerModel)
}
}

// Resources defines the resources implemented in the provider.
func (p *aapProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewInventoryResource,
NewInventoryResourceWrapper(&p.config),
NewJobResource,
NewGroupResource,
NewHostResource,
Expand All @@ -160,11 +175,12 @@ func (p *aapProvider) Resources(_ context.Context) []func() resource.Resource {

// aapProviderModel maps provider schema data to a Go type.
type aapProviderModel struct {
Host types.String `tfsdk:"host"`
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
InsecureSkipVerify types.Bool `tfsdk:"insecure_skip_verify"`
Timeout types.Int64 `tfsdk:"timeout"`
Host types.String `tfsdk:"host"`
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
DefaultOrganization types.Int64 `tfsdk:"default_organization"`
InsecureSkipVerify types.Bool `tfsdk:"insecure_skip_verify"`
Timeout types.Int64 `tfsdk:"timeout"`
}

func (p *aapProviderModel) checkUnknownValue(resp *provider.ConfigureResponse) {
Expand All @@ -180,6 +196,10 @@ func (p *aapProviderModel) checkUnknownValue(resp *provider.ConfigureResponse) {
AddConfigurationAttributeError(resp, "password", "AAP_PASSWORD", true)
}

if p.DefaultOrganization.IsUnknown() {
AddConfigurationAttributeError(resp, "default_organization", "AAP_DEFAULT_ORGANIZATION", true)
}

if p.InsecureSkipVerify.IsUnknown() {
AddConfigurationAttributeError(resp, "insecure_skip_verify", "AAP_INSECURE_SKIP_VERIFY", true)
}
Expand All @@ -192,9 +212,10 @@ func (p *aapProviderModel) checkUnknownValue(resp *provider.ConfigureResponse) {
const (
DefaultTimeOut = 5 // Default http session timeout
DefaultInsecureSkipVerify = false // Default value for insecure skip verify
DefaultOrganization = 1 // Default organization ID
)

func (p *aapProviderModel) ReadValues(host, username, password *string, insecureSkipVerify *bool,
func (p *aapProviderModel) ReadValues(host, username, password *string, defaultOrganization *int64, insecureSkipVerify *bool,
timeout *int64, resp *provider.ConfigureResponse) {
// Set default values from env variables
*host = os.Getenv("AAP_HOST")
Expand All @@ -217,6 +238,22 @@ func (p *aapProviderModel) ReadValues(host, username, password *string, insecure
*password = p.Password.ValueString()
}

// setting default organization value
*defaultOrganization = DefaultOrganization
if !p.DefaultOrganization.IsNull() {
*defaultOrganization = p.DefaultOrganization.ValueInt64()
} else if intValue := os.Getenv("AAP_DEFAULT_ORGANIZATION"); intValue != "" {
// convert string into int64 value
*defaultOrganization, err = strconv.ParseInt(intValue, 10, 64)
if err != nil {
resp.Diagnostics.AddAttributeError(
path.Root("default_organization"),
"Invalid value for default_organization",
"The provider cannot create the AAP API client as the value provided for default_organization is not a valid int64 value.",
)
}
}

if !p.InsecureSkipVerify.IsNull() {
*insecureSkipVerify = p.InsecureSkipVerify.ValueBool()
} else if boolValue := os.Getenv("AAP_INSECURE_SKIP_VERIFY"); boolValue != "" {
Expand Down
Loading