Skip to content

Commit

Permalink
Merge pull request #1301 from hashicorp/gs/refactor-run-task-resource…
Browse files Browse the repository at this point in the history
…s-part3

Migrate run task datasources to plugin model
  • Loading branch information
glennsarti authored Apr 4, 2024
2 parents c8bee09 + f9fe472 commit fb12855
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 78 deletions.
151 changes: 110 additions & 41 deletions internal/provider/data_source_organization_run_task.go
Original file line number Diff line number Diff line change
@@ -1,73 +1,142 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

// NOTE: This is a legacy resource and should be migrated to the Plugin
// Framework if substantial modifications are planned. See
// docs/new-resources.md if planning to use this code as boilerplate for
// a new resource.

package provider

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"context"
"fmt"

tfe "github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &dataSourceOrganizationRunTask{}
_ datasource.DataSourceWithConfigure = &dataSourceOrganizationRunTask{}
)

func dataSourceTFEOrganizationRunTask() *schema.Resource {
return &schema.Resource{
Read: dataSourceTFEOrganizationRunTaskRead,
// TODO: This model, and the following conversion function, need to be kept the same as the Org. Run Task Resource
// (internal/provider/resource_tfe_organization_run_task.go) but it only differs by the HMAC Key. In the future we
// should put in a change to add HMAC Key into this model and then we can share the struct. And more importantly we
// we can use the later schema changes.
type modelDataTFEOrganizationRunTaskV0 struct {
Category types.String `tfsdk:"category"`
Description types.String `tfsdk:"description"`
Enabled types.Bool `tfsdk:"enabled"`
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Organization types.String `tfsdk:"organization"`
URL types.String `tfsdk:"url"`
}

func dataModelFromTFEOrganizationRunTask(v *tfe.RunTask) modelDataTFEOrganizationRunTaskV0 {
result := modelDataTFEOrganizationRunTaskV0{
Category: types.StringValue(v.Category),
Description: types.StringValue(v.Description),
Enabled: types.BoolValue(v.Enabled),
ID: types.StringValue(v.ID),
Name: types.StringValue(v.Name),
Organization: types.StringValue(v.Organization.Name),
URL: types.StringValue(v.URL),
}

return result
}

// NewOrganizationRunTaskDataSource is a helper function to simplify the provider implementation.
func NewOrganizationRunTaskDataSource() datasource.DataSource {
return &dataSourceOrganizationRunTask{}
}

// dataSourceOrganizationRunTask is the data source implementation.
type dataSourceOrganizationRunTask struct {
config ConfiguredClient
}

// Metadata returns the data source type name.
func (d *dataSourceOrganizationRunTask) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_organization_run_task"
}

func (d *dataSourceOrganizationRunTask) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "Service-generated identifier for the task",
},
"name": schema.StringAttribute{
Required: true,
},

"organization": {
Type: schema.TypeString,
"organization": schema.StringAttribute{
Optional: true,
},

"url": {
Type: schema.TypeString,
"url": schema.StringAttribute{
Optional: true,
},

"category": {
Type: schema.TypeString,
"category": schema.StringAttribute{
Optional: true,
},

"enabled": {
Type: schema.TypeBool,
"enabled": schema.BoolAttribute{
Optional: true,
},

"description": {
Type: schema.TypeString,
"description": schema.StringAttribute{
Optional: true,
},
},
}
}

func dataSourceTFEOrganizationRunTaskRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(ConfiguredClient)
name := d.Get("name").(string)
organization, err := config.schemaOrDefaultOrganization(d)
if err != nil {
return err
// Configure adds the provider configured client to the data source.
func (d *dataSourceOrganizationRunTask) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(ConfiguredClient)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected tfe.ConfiguredClient, got %T. This is a bug in the tfe provider, so please report it on GitHub.", req.ProviderData),
)

return
}
d.config = client
}

// Read refreshes the Terraform state with the latest data.
func (d *dataSourceOrganizationRunTask) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data modelDataTFEOrganizationRunTaskV0

// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

var organization string
resp.Diagnostics.Append(d.config.dataOrDefaultOrganization(ctx, req.Config, &organization)...)
if resp.Diagnostics.HasError() {
return
}

task, err := fetchOrganizationRunTask(name, organization, config.Client)
task, err := fetchOrganizationRunTask(data.Name.ValueString(), organization, d.config.Client)
if err != nil {
return err
resp.Diagnostics.AddError("Error reading Organization Run Task",
fmt.Sprintf("Could not read Run Task %q in organization %q, unexpected error: %s", data.Name.String(), organization, err.Error()),
)
return
}

d.Set("url", task.URL)
d.Set("category", task.Category)
d.Set("enabled", task.Enabled)
d.Set("description", task.Description)
d.SetId(task.ID)
// We can never read the HMACkey (Write-only) so assume it's the default (empty)
result := dataModelFromTFEOrganizationRunTask(task)

return nil
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &result)...)
}
123 changes: 88 additions & 35 deletions internal/provider/data_source_workspace_run_task.go
Original file line number Diff line number Diff line change
@@ -1,74 +1,117 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

// NOTE: This is a legacy resource and should be migrated to the Plugin
// Framework if substantial modifications are planned. See
// docs/new-resources.md if planning to use this code as boilerplate for
// a new resource.

package provider

import (
"context"
"fmt"

"github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
tfe "github.com/hashicorp/go-tfe"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &dataSourceWorkspaceRunTask{}
_ datasource.DataSourceWithConfigure = &dataSourceWorkspaceRunTask{}
)

func dataSourceTFEWorkspaceRunTask() *schema.Resource {
return &schema.Resource{
Read: dataSourceTFEWorkspaceRunTaskRead,
// NewWorkspaceRunTaskDataSource is a helper function to simplify the provider implementation.
func NewWorkspaceRunTaskDataSource() datasource.DataSource {
return &dataSourceWorkspaceRunTask{}
}

Schema: map[string]*schema.Schema{
"workspace_id": {
// dataSourceWorkspaceRunTask is the data source implementation.
type dataSourceWorkspaceRunTask struct {
config ConfiguredClient
}

// Metadata returns the data source type name.
func (d *dataSourceWorkspaceRunTask) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_workspace_run_task"
}

func (d *dataSourceWorkspaceRunTask) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "Service-generated identifier for the task",
Computed: true,
},
"workspace_id": schema.StringAttribute{
Description: "The id of the workspace.",
Type: schema.TypeString,
Required: true,
},

"task_id": {
"task_id": schema.StringAttribute{
Description: "The id of the run task.",
Type: schema.TypeString,
Required: true,
},

"enforcement_level": {
"enforcement_level": schema.StringAttribute{
Description: "The enforcement level of the task.",
Type: schema.TypeString,
Computed: true,
},

"stage": {
"stage": schema.StringAttribute{
Description: "Which stage the task will run in.",
Type: schema.TypeString,
Computed: true,
},
},
}
}

func dataSourceTFEWorkspaceRunTaskRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(ConfiguredClient)
// Configure adds the provider configured client to the data source.
func (d *dataSourceWorkspaceRunTask) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(ConfiguredClient)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected tfe.ConfiguredClient, got %T. This is a bug in the tfe provider, so please report it on GitHub.", req.ProviderData),
)

workspaceID := d.Get("workspace_id").(string)
taskID := d.Get("task_id").(string)
return
}
d.config = client
}

// Read refreshes the Terraform state with the latest data.
func (d *dataSourceWorkspaceRunTask) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data modelTFEWorkspaceRunTaskV0

// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

workspaceID := data.WorkspaceID.ValueString()
taskID := data.TaskID.ValueString()
var wstask *tfe.WorkspaceRunTask = nil

// Create an options struct.
options := &tfe.WorkspaceRunTaskListOptions{}
for {
list, err := config.Client.WorkspaceRunTasks.List(ctx, workspaceID, options)
list, err := d.config.Client.WorkspaceRunTasks.List(ctx, workspaceID, options)
if err != nil {
return fmt.Errorf("Error retrieving tasks for workspace %s: %w", workspaceID, err)
resp.Diagnostics.AddError("Error retrieving tasks for workspace",
fmt.Sprintf("Error retrieving tasks for workspace %s: %s", workspaceID, err.Error()),
)
return
}

for _, wstask := range list.Items {
if wstask.RunTask.ID == taskID {
d.Set("enforcement_level", string(wstask.EnforcementLevel))
d.Set("stage", string(wstask.Stage))
d.SetId(wstask.ID)
return nil
for _, item := range list.Items {
if item.RunTask.ID == taskID {
wstask = item
break
}
}
if wstask != nil {
break
}

// Exit the loop when we've seen all pages.
if list.CurrentPage >= list.TotalPages {
Expand All @@ -79,5 +122,15 @@ func dataSourceTFEWorkspaceRunTaskRead(d *schema.ResourceData, meta interface{})
options.PageNumber = list.NextPage
}

return fmt.Errorf("could not find workspace run task %s in workspace %s", taskID, workspaceID)
if wstask == nil {
resp.Diagnostics.AddError("Error reading Workspace Run Task",
fmt.Sprintf("Could not find task %q in workspace %q", taskID, workspaceID),
)
return
}

result := modelFromTFEWorkspaceRunTask(wstask)

// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &result)...)
}
2 changes: 0 additions & 2 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ func Provider() *schema.Provider {
"tfe_ip_ranges": dataSourceTFEIPRanges(),
"tfe_oauth_client": dataSourceTFEOAuthClient(),
"tfe_organization_membership": dataSourceTFEOrganizationMembership(),
"tfe_organization_run_task": dataSourceTFEOrganizationRunTask(),
"tfe_organization_tags": dataSourceTFEOrganizationTags(),
"tfe_project": dataSourceTFEProject(),
"tfe_slug": dataSourceTFESlug(),
Expand All @@ -97,7 +96,6 @@ func Provider() *schema.Provider {
"tfe_team_project_access": dataSourceTFETeamProjectAccess(),
"tfe_workspace": dataSourceTFEWorkspace(),
"tfe_workspace_ids": dataSourceTFEWorkspaceIDs(),
"tfe_workspace_run_task": dataSourceTFEWorkspaceRunTask(),
"tfe_variables": dataSourceTFEWorkspaceVariables(),
"tfe_variable_set": dataSourceTFEVariableSet(),
"tfe_policy_set": dataSourceTFEPolicySet(),
Expand Down
2 changes: 2 additions & 0 deletions internal/provider/provider_next.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,14 @@ func (p *frameworkProvider) Configure(ctx context.Context, req provider.Configur

func (p *frameworkProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewOrganizationRunTaskDataSource,
NewRegistryGPGKeyDataSource,
NewRegistryGPGKeysDataSource,
NewRegistryProviderDataSource,
NewRegistryProvidersDataSource,
NewNoCodeModuleDataSource,
NewSAMLSettingsDataSource,
NewWorkspaceRunTaskDataSource,
}
}

Expand Down

0 comments on commit fb12855

Please sign in to comment.