Skip to content

Commit

Permalink
WIP octopusdeploy_tenant_projects datasource
Browse files Browse the repository at this point in the history
  • Loading branch information
domenicsim1 committed Aug 15, 2024
1 parent 2261c2a commit c04d33d
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 0 deletions.
201 changes: 201 additions & 0 deletions octopusdeploy_framework/datasource_tenant_projects.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package octopusdeploy_framework

import (
"context"
"fmt"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"golang.org/x/exp/slices"
)

type TenantProjectsDataModel struct {
SpaceID types.String `tfsdk:"space_id"`
TenantIDs types.List `tfsdk:"tenant_ids"`
ProjectIDs types.List `tfsdk:"project_ids"`
EnvironmentIDs types.List `tfsdk:"environment_ids"`
TenantProjects types.List `tfsdk:"tenant_projects"`
}

type tenantProjectsDataSource struct {
*Config
}

func NewTenantProjectDataSource() datasource.DataSource {
return &tenantProjectsDataSource{}
}

func (*tenantProjectsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = util.GetTypeName("tenant_projects")
}

func (t *tenantProjectsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
t.Config = DataSourceConfiguration(req, resp)
}

func (*tenantProjectsDataSource) Schema(_ context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = datasourceSchema.Schema{
Description: "Provides information about existing tenants.",
Attributes: map[string]datasourceSchema.Attribute{
"tenant_ids": schemas.GetQueryIDsDatasourceSchema(),
"project_ids": schemas.GetQueryIDsDatasourceSchema(),
"environment_ids": schemas.GetQueryIDsDatasourceSchema(),
"space_id": schemas.GetSpaceIdDatasourceSchema("tenant projects", false),
},
Blocks: map[string]datasourceSchema.Block{
"tenant_projects": datasourceSchema.ListNestedBlock{
Description: "A list of related tenants, projects and environments that match the filter(s).",
NestedObject: datasourceSchema.NestedBlockObject{
Attributes: map[string]datasourceSchema.Attribute{
"id": schemas.GetIdDatasourceSchema(true),
"tenant_id": datasourceSchema.StringAttribute{
Description: "The tenant ID associated with this tenant.",
Computed: true,
},
"project_id": datasourceSchema.StringAttribute{
Description: "The project ID associated with this tenant.",
Computed: true,
},
"environment_ids": datasourceSchema.ListAttribute{
Description: "The environment IDs associated with this tenant.",
ElementType: types.StringType,
Computed: true,
},
},
},
},
},
}
}

func (t *tenantProjectsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data TenantProjectsDataModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

tenantIDs := util.ExpandStringList(data.TenantIDs)
projectIDs := util.ExpandStringList(data.ProjectIDs)
environmentIDs := util.ExpandStringList(data.EnvironmentIDs)
spaceID := data.SpaceID.ValueString()

if tenantIDs == nil && projectIDs == nil {
resp.Diagnostics.AddError("must provide at least one tenant or one project", "tenant IDs and project IDs are nil")
}

var tenantData []*tenants.Tenant
if tenantIDs == nil {
tenantData = make([]*tenants.Tenant, 0)
for _, projectID := range projectIDs {
// todo: should consider using concurrency
tenantsByProjectID, err := getTenantByProjectID(t.Client, projectID, spaceID)
if err != nil {
resp.Diagnostics.AddError("unable to load tenant data for project", err.Error())
return
}
tenantData = append(tenantData, tenantsByProjectID...)
}
} else {
tenantQuery := tenants.TenantsQuery{
IDs: tenantIDs,
}
tenantResource, err := tenants.Get(t.Client, spaceID, tenantQuery)
if err != nil {
resp.Diagnostics.AddError("unable to load tenant data", err.Error())
return
}
tenantData, err = tenantResource.GetAllPages(t.Client.Sling())
if err != nil {
resp.Diagnostics.AddError("unable to load tenant data", err.Error())
return
}
}

// todo: refactor
flattenedTenantProjects := make([]any, 0)
for _, tenant := range tenantData {
if projectIDs == nil {
for projectID, envIDs := range tenant.ProjectEnvironments {
if environmentIDs != nil {
for _, envID := range envIDs {
if slices.Contains(environmentIDs, envID) {
flattenedTenantProjects = append(flattenedTenantProjects, flattenTenantProject(tenant, projectID))
break
}
}
} else {
flattenedTenantProjects = append(flattenedTenantProjects, flattenTenantProject(tenant, projectID))
}
}
continue
}
for _, projectID := range projectIDs {
if envIDs, ok := tenant.ProjectEnvironments[projectID]; ok {
if environmentIDs != nil {
for _, envID := range envIDs {
if slices.Contains(environmentIDs, envID) {
flattenedTenantProjects = append(flattenedTenantProjects, flattenTenantProject(tenant, projectID))
break
}
}
} else {
flattenedTenantProjects = append(flattenedTenantProjects, flattenTenantProject(tenant, projectID))
}
}

}
}

data.TenantProjects, _ = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: tenantProjectType()}, flattenedTenantProjects)
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func getTenantByProjectID(c *client.Client, projectID string, spaceID string) ([]*tenants.Tenant, error) {
tenantQuery := tenants.TenantsQuery{
ProjectID: projectID,
}
tenantResource, err := tenants.Get(c, spaceID, tenantQuery)
if err != nil {
return nil, err
}
tenantData, err := tenantResource.GetAllPages(c.Sling())
if err != nil {
return nil, err
}
return tenantData, nil
}

func buildTenantProjectID(spaceID string, projectID string, tenantID string) string {
return fmt.Sprintf("%s:%s:%s", spaceID, projectID, tenantID)
}

func tenantProjectType() map[string]attr.Type {
return map[string]attr.Type{
"id": types.StringType,
"tenant_id": types.StringType,
"project_id": types.StringType,
"environment_ids": types.ListType{ElemType: types.StringType},
}
}

func flattenTenantProject(tenant *tenants.Tenant, projectID string) attr.Value {
environmentIDs := make([]attr.Value, len(tenant.ProjectEnvironments[projectID]))
for i, envID := range tenant.ProjectEnvironments[projectID] {
environmentIDs[i] = types.StringValue(envID)
}

environmentIdList, _ := types.ListValue(types.StringType, environmentIDs)

return types.ObjectValueMust(tenantProjectType(), map[string]attr.Value{
"id": types.StringValue(buildTenantProjectID(tenant.SpaceID, projectID, tenant.ID)),
"tenant_id": types.StringValue(tenant.ID),
"project_id": types.StringValue(projectID),
"environment_ids": environmentIdList,
})
}
1 change: 1 addition & 0 deletions octopusdeploy_framework/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func (p *octopusDeployFrameworkProvider) DataSources(ctx context.Context) []func
NewTenantsDataSource,
NewTagSetsDataSource,
NewScriptModuleDataSource,
NewTenantProjectDataSource,
}
}

Expand Down

0 comments on commit c04d33d

Please sign in to comment.