diff options
| author | xuzhang3 <57888764+xuzhang3@users.noreply.github.com> | 2023-02-21 11:07:59 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-02-21 11:07:59 +0800 |
| commit | 038a57763e38b94214d08deb1a508f99adc73cc9 (patch) | |
| tree | 9f8d11ee1004b1a0f1dc3010d4294faee2318855 | |
| parent | 7d1d91cebb7b6ab89faaacf07eae799496b4822e (diff) | |
| parent | 444277addccc91aa0de5eeb92e9e354933eaf9ab (diff) | |
Merge pull request #716 from xuzhang3/f/agent_pool_optimize
`azuredevops_agent_pool` - Add status handler
7 files changed, 288 insertions, 330 deletions
diff --git a/azuredevops/internal/acceptancetests/data_agentpool_test.go b/azuredevops/internal/acceptancetests/data_agentpool_test.go index 4e190007..fe8d837f 100644 --- a/azuredevops/internal/acceptancetests/data_agentpool_test.go +++ b/azuredevops/internal/acceptancetests/data_agentpool_test.go @@ -12,18 +12,16 @@ import ( "github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/acceptancetests/testutils" ) -func TestAccAgentPool_DataSource(t *testing.T) { +func TestAccDataSourceAgentPool_basic(t *testing.T) { agentPoolName := testutils.GenerateResourceName() - createAgentPool := testutils.HclAgentPoolResource(agentPoolName) - createAndGetAgentPoolData := fmt.Sprintf("%s\n%s", createAgentPool, testutils.HclAgentPoolDataSource()) + tfNode := "data.azuredevops_agent_pool.test" - tfNode := "data.azuredevops_agent_pool.pool" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testutils.PreCheck(t, nil) }, Providers: testutils.GetProviders(), Steps: []resource.TestStep{ { - Config: createAndGetAgentPoolData, + Config: hclDataSourceAgentPoolBasic(agentPoolName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(tfNode, "id"), resource.TestCheckResourceAttr(tfNode, "name", agentPoolName), @@ -35,3 +33,19 @@ func TestAccAgentPool_DataSource(t *testing.T) { }, }) } + +func hclDataSourceAgentPoolBasic(name string) string { + return fmt.Sprintf(` +resource "azuredevops_agent_pool" "test" { + name = "%s" + auto_provision = false + auto_update = false + pool_type = "automation" +} + +data "azuredevops_agent_pool" "test" { + name = azuredevops_agent_pool.test.name +} + +`, name) +} diff --git a/azuredevops/internal/acceptancetests/resource_agentpool_test.go b/azuredevops/internal/acceptancetests/resource_agentpool_test.go index 6b1058d1..5f6c5c81 100644 --- a/azuredevops/internal/acceptancetests/resource_agentpool_test.go +++ b/azuredevops/internal/acceptancetests/resource_agentpool_test.go @@ -6,6 +6,7 @@ package acceptancetests import ( "fmt" + "regexp" "strconv" "testing" @@ -16,19 +17,9 @@ import ( "github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/client" ) -// Verifies that the following sequence of events occurrs without error: -// -// (1) TF apply creates agent pool -// (2) TF state values are set -// (3) Agent pool can be queried by ID and has expected name -// (4) TF apply updates agent pool with new name -// (5) Agent pool can be queried by ID and has expected name -// (6) TF destroy deletes agent pool -// (7) Agent pool can no longer be queried by ID -func TestAccAgentPool_CreateAndUpdate(t *testing.T) { - poolNameFirst := testutils.GenerateResourceName() - poolNameSecond := testutils.GenerateResourceName() - tfNode := "azuredevops_agent_pool.pool" +func TestAccAgentPool_basic(t *testing.T) { + poolName := testutils.GenerateResourceName() + tfNode := "azuredevops_agent_pool.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testutils.PreCheck(t, nil) }, @@ -36,27 +27,56 @@ func TestAccAgentPool_CreateAndUpdate(t *testing.T) { CheckDestroy: checkAgentPoolDestroyed, Steps: []resource.TestStep{ { - Config: testutils.HclAgentPoolResource(poolNameFirst), + Config: hclAgentPoolBasic(poolName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(tfNode, "name", poolNameFirst), + resource.TestCheckResourceAttr(tfNode, "name", poolName), resource.TestCheckResourceAttr(tfNode, "auto_provision", "false"), resource.TestCheckResourceAttr(tfNode, "auto_update", "false"), resource.TestCheckResourceAttr(tfNode, "pool_type", "automation"), - checkAgentPoolExists(poolNameFirst), ), }, { - Config: testutils.HclAgentPoolResource(poolNameSecond), + ResourceName: tfNode, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAgentPool_update(t *testing.T) { + poolName := testutils.GenerateResourceName() + tfNode := "azuredevops_agent_pool.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testutils.PreCheck(t, nil) }, + Providers: testutils.GetProviders(), + CheckDestroy: checkAgentPoolDestroyed, + Steps: []resource.TestStep{ + { + Config: hclAgentPoolBasic(poolName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(tfNode, "name", poolNameSecond), + resource.TestCheckResourceAttr(tfNode, "name", poolName), resource.TestCheckResourceAttr(tfNode, "auto_provision", "false"), resource.TestCheckResourceAttr(tfNode, "auto_update", "false"), resource.TestCheckResourceAttr(tfNode, "pool_type", "automation"), - checkAgentPoolExists(poolNameSecond), ), }, { - // Resource Acceptance Testing https://www.terraform.io/docs/extend/resources/import.html#resource-acceptance-testing-implementation + ResourceName: tfNode, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: hclAgentPoolUpdate(poolName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(tfNode, "name", poolName), + resource.TestCheckResourceAttr(tfNode, "auto_provision", "true"), + resource.TestCheckResourceAttr(tfNode, "auto_update", "true"), + resource.TestCheckResourceAttr(tfNode, "pool_type", "automation"), + ), + }, + { ResourceName: tfNode, ImportState: true, ImportStateVerify: true, @@ -65,37 +85,33 @@ func TestAccAgentPool_CreateAndUpdate(t *testing.T) { }) } -// Given the name of an agent pool, this will return a function that will check whether -// or not the pool (1) exists in the state and (2) exist in AzDO and (3) has the correct name -func checkAgentPoolExists(expectedName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - resource, ok := s.RootModule().Resources["azuredevops_agent_pool.pool"] - if !ok { - return fmt.Errorf("Did not find a agent pool in the TF state") - } - - clients := testutils.GetProvider().Meta().(*client.AggregatedClient) - id, err := strconv.Atoi(resource.Primary.ID) - if err != nil { - return fmt.Errorf("Parse ID error, ID: %v !. Error= %v", resource.Primary.ID, err) - } - - pool, agentPoolErr := clients.TaskAgentClient.GetAgentPool(clients.Ctx, taskagent.GetAgentPoolArgs{PoolId: &id}) - - if agentPoolErr != nil { - return fmt.Errorf("Agent Pool with ID=%d cannot be found!. Error=%v", id, err) - } +func TestAccAgentPool_requiresImportErrorStep(t *testing.T) { + poolName := testutils.GenerateResourceName() + tfNode := "azuredevops_agent_pool.test" - if *pool.Name != expectedName { - return fmt.Errorf("Agent Pool with ID=%d has Name=%s, but expected Name=%s", id, *pool.Name, expectedName) - } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testutils.PreCheck(t, nil) }, + Providers: testutils.GetProviders(), + CheckDestroy: checkAgentPoolDestroyed, + Steps: []resource.TestStep{ + { + Config: hclAgentPoolBasic(poolName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(tfNode, "name", poolName), + resource.TestCheckResourceAttr(tfNode, "auto_provision", "false"), + resource.TestCheckResourceAttr(tfNode, "auto_update", "false"), + resource.TestCheckResourceAttr(tfNode, "pool_type", "automation"), + ), + }, - return nil - } + { + Config: hclAgentPoolResourceRequiresImport(poolName), + ExpectError: requiresImportError(poolName), + }, + }, + }) } -// verifies that agent pool referenced in the state is destroyed. This will be invoked -// *after* terraform destroys the resource but *before* the state is wiped clean. func checkAgentPoolDestroyed(s *terraform.State) error { clients := testutils.GetProvider().Meta().(*client.AggregatedClient) @@ -118,3 +134,40 @@ func checkAgentPoolDestroyed(s *terraform.State) error { return nil } + +func requiresImportError(resourceName string) *regexp.Regexp { + message := "creating agent pool in Azure DevOps: Agent pool %[1]s already exists." + return regexp.MustCompile(fmt.Sprintf(message, resourceName)) +} + +func hclAgentPoolBasic(name string) string { + return fmt.Sprintf(` +resource "azuredevops_agent_pool" "test" { + name = "%s" + auto_provision = false + auto_update = false + pool_type = "automation" +}`, name) +} + +func hclAgentPoolUpdate(name string) string { + return fmt.Sprintf(` +resource "azuredevops_agent_pool" "test" { + name = "%s" + auto_provision = true + auto_update = true + pool_type = "automation" +}`, name) +} + +func hclAgentPoolResourceRequiresImport(name string) string { + return fmt.Sprintf(` +%s + +resource "azuredevops_agent_pool" "import" { + name = azuredevops_agent_pool.test.name + auto_provision = azuredevops_agent_pool.test.auto_provision + auto_update = azuredevops_agent_pool.test.auto_update + pool_type = azuredevops_agent_pool.test.pool_type +}`, hclAgentPoolBasic(name)) +} diff --git a/azuredevops/internal/service/taskagent/data_agentpool.go b/azuredevops/internal/service/taskagent/data_agentpool.go index 914f6bd2..6a9800da 100644 --- a/azuredevops/internal/service/taskagent/data_agentpool.go +++ b/azuredevops/internal/service/taskagent/data_agentpool.go @@ -2,59 +2,72 @@ package taskagent import ( "fmt" + "strconv" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/microsoft/azure-devops-go-api/azuredevops/v6/taskagent" "github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/client" + "github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/utils/converter" ) // DataAgentPool schema and implementation for agent pool data source func DataAgentPool() *schema.Resource { - baseSchema := ResourceAgentPool() - for k, v := range baseSchema.Schema { - if k != "name" { - baseSchema.Schema[k] = &schema.Schema{ - Type: v.Type, - Computed: true, - } - } - } - return &schema.Resource{ - Read: dataSourceAgentPoolRead, - Schema: baseSchema.Schema, + Read: dataSourceAgentPoolRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + ForceNew: false, + Required: true, + ValidateFunc: validation.StringIsNotWhiteSpace, + }, + "pool_type": { + Type: schema.TypeString, + Computed: true, + }, + "auto_provision": { + Type: schema.TypeBool, + Computed: true, + }, + "auto_update": { + Type: schema.TypeBool, + Computed: true, + }, + }, } } func dataSourceAgentPoolRead(d *schema.ResourceData, m interface{}) error { - agentPoolName := d.Get("name").(string) clients := m.(*client.AggregatedClient) - agentPool, err := getAgentPoolByName(clients, &agentPoolName) - if err != nil { - return fmt.Errorf("Error getting agent pool by name: %v", err) - } + poolName := d.Get("name").(string) - flattenAzureAgentPool(d, agentPool) - return nil -} - -func getAgentPoolByName(clients *client.AggregatedClient, name *string) (*taskagent.TaskAgentPool, error) { agentPools, err := clients.TaskAgentClient.GetAgentPools(clients.Ctx, taskagent.GetAgentPoolsArgs{ - PoolName: name, + PoolName: converter.String(poolName), }) if err != nil { - return nil, err + return err } if len(*agentPools) > 1 { - return nil, fmt.Errorf("Found multiple agent pools for name: %s. Agent pools found: %+v", *name, agentPools) + return fmt.Errorf(" Found multiple agent pools for name: %s. Agent pools found: %+v", poolName, agentPools) } if len(*agentPools) == 0 { - return nil, fmt.Errorf("Unable to find agent pool with name: %s", *name) + return fmt.Errorf(" Unable to find agent pool with name: %s", poolName) } - return &(*agentPools)[0], nil + pool := (*agentPools)[0] + + d.SetId(strconv.Itoa(*pool.Id)) + d.Set("name", pool.Name) + d.Set("pool_type", *pool.PoolType) + d.Set("auto_provision", *pool.AutoProvision) + + if pool.AutoUpdate != nil { + d.Set("auto_update", *pool.AutoUpdate) + } + return nil } diff --git a/azuredevops/internal/service/taskagent/resource_agent_queue.go b/azuredevops/internal/service/taskagent/resource_agent_queue.go index 04c18284..5a810163 100644 --- a/azuredevops/internal/service/taskagent/resource_agent_queue.go +++ b/azuredevops/internal/service/taskagent/resource_agent_queue.go @@ -52,7 +52,9 @@ func resourceAgentQueueCreate(d *schema.ResourceData, m interface{}) error { return fmt.Errorf("Error expanding the agent queue resource from state: %+v", err) } - referencedPool, err := azureAgentPoolRead(clients, *queue.Pool.Id) + referencedPool, err := clients.TaskAgentClient.GetAgentPool(clients.Ctx, taskagent.GetAgentPoolArgs{ + PoolId: queue.Pool.Id, + }) if err != nil { return fmt.Errorf("Error looking up referenced agent pool: %+v", err) } diff --git a/azuredevops/internal/service/taskagent/resource_agentpool.go b/azuredevops/internal/service/taskagent/resource_agentpool.go index 0cb02a8d..51ef9c8b 100644 --- a/azuredevops/internal/service/taskagent/resource_agentpool.go +++ b/azuredevops/internal/service/taskagent/resource_agentpool.go @@ -3,7 +3,9 @@ package taskagent import ( "fmt" "strconv" + "time" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/microsoft/azure-devops-go-api/azuredevops/v6/taskagent" @@ -23,6 +25,12 @@ func ResourceAgentPool() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, @@ -57,59 +65,97 @@ func ResourceAgentPool() *schema.Resource { func resourceAzureAgentPoolCreate(d *schema.ResourceData, m interface{}) error { clients := m.(*client.AggregatedClient) - agentPool, err := expandAgentPool(d, true) - if err != nil { - return fmt.Errorf("Error converting terraform data model to AzDO agentPool reference: %+v", err) + + args := taskagent.AddAgentPoolArgs{ + Pool: &taskagent.TaskAgentPool{ + Name: converter.String(d.Get("name").(string)), + PoolType: converter.ToPtr(taskagent.TaskAgentPoolType(d.Get("pool_type").(string))), + AutoProvision: converter.Bool(d.Get("auto_provision").(bool)), + AutoUpdate: converter.Bool(d.Get("auto_update").(bool)), + }, } - createdAgentPool, err := createAzureAgentPool(clients, agentPool) + agentPool, err := clients.TaskAgentClient.AddAgentPool(clients.Ctx, args) if err != nil { - return fmt.Errorf("Error creating agent pool in Azure DevOps: %+v", err) + return fmt.Errorf(" creating agent pool in Azure DevOps: %+v", err) } - if agentPool.AutoUpdate != nil && !*agentPool.AutoUpdate { - agentPool.Id = createdAgentPool.Id - createdAgentPool, err = azureAgentPoolUpdate(clients, agentPool) - if err != nil { - return fmt.Errorf("Error updating agent pool in Azure DevOps: %+v", err) + // auto update can only be set to true on creation + if args.Pool.AutoUpdate != nil && !*args.Pool.AutoUpdate { + updateArgs := taskagent.UpdateAgentPoolArgs{ + PoolId: agentPool.Id, + Pool: &taskagent.TaskAgentPool{ + Name: args.Pool.Name, + PoolType: args.Pool.PoolType, + AutoProvision: args.Pool.AutoProvision, + AutoUpdate: args.Pool.AutoUpdate, + }, } - } + agentPool, err = clients.TaskAgentClient.UpdateAgentPool(clients.Ctx, updateArgs) - flattenAzureAgentPool(d, createdAgentPool) + if err := syncStatus(updateArgs, clients); err != nil { + return err + } + if err != nil { + return fmt.Errorf(" updating agent pool in Azure DevOps: %+v", err) + } + } + d.SetId(strconv.Itoa(*agentPool.Id)) return resourceAzureAgentPoolRead(d, m) } func resourceAzureAgentPoolRead(d *schema.ResourceData, m interface{}) error { + clients := m.(*client.AggregatedClient) + poolID, err := strconv.Atoi(d.Id()) if err != nil { - return fmt.Errorf("Error getting agent pool Id: %+v", err) + return fmt.Errorf(" parse agent pool ID: %+v", err) } - clients := m.(*client.AggregatedClient) - agentPool, err := azureAgentPoolRead(clients, poolID) + agentPool, err := clients.TaskAgentClient.GetAgentPool(clients.Ctx, taskagent.GetAgentPoolArgs{PoolId: &poolID}) if err != nil { if utils.ResponseWasNotFound(err) { d.SetId("") return nil } - return fmt.Errorf("Error looking up agent pool with ID %d. Error: %v", poolID, err) + return fmt.Errorf(" looking up Agent Pool with ID %d. Error: %v", poolID, err) } - flattenAzureAgentPool(d, agentPool) + d.Set("name", agentPool.Name) + d.Set("pool_type", agentPool.PoolType) + d.Set("auto_provision", agentPool.AutoProvision) + + if agentPool.AutoUpdate != nil { + d.Set("auto_update", agentPool.AutoUpdate) + } return nil } func resourceAzureAgentPoolUpdate(d *schema.ResourceData, m interface{}) error { clients := m.(*client.AggregatedClient) - agentPool, err := expandAgentPool(d, false) - if err != nil { - return fmt.Errorf("Error converting terraform data model to AzDO agent pool reference: %+v", err) + + parameter := taskagent.UpdateAgentPoolArgs{ + Pool: &taskagent.TaskAgentPool{ + Name: converter.String(d.Get("name").(string)), + PoolType: converter.ToPtr(taskagent.TaskAgentPoolType(d.Get("pool_type").(string))), + AutoProvision: converter.Bool(d.Get("auto_provision").(bool)), + AutoUpdate: converter.Bool(d.Get("auto_update").(bool)), + }, } - _, err = azureAgentPoolUpdate(clients, agentPool) + poolID, err := strconv.Atoi(d.Id()) if err != nil { - return fmt.Errorf("Error updating agent pool in Azure DevOps: %+v", err) + return fmt.Errorf(" getting agent pool Id: %+v", err) + } + parameter.PoolId = &poolID + + if _, err = clients.TaskAgentClient.UpdateAgentPool(clients.Ctx, parameter); err != nil { + return fmt.Errorf(" updating agent pool in Azure DevOps: %+v", err) + } + + if err := syncStatus(parameter, clients); err != nil { + return err } return resourceAzureAgentPoolRead(d, m) @@ -118,70 +164,68 @@ func resourceAzureAgentPoolUpdate(d *schema.ResourceData, m interface{}) error { func resourceAzureAgentPoolDelete(d *schema.ResourceData, m interface{}) error { poolID, err := strconv.Atoi(d.Id()) if err != nil { - return fmt.Errorf("Error getting agent pool Id: %+v", err) + return fmt.Errorf(" parse agent pool ID: %+v", err) } clients := m.(*client.AggregatedClient) - return clients.TaskAgentClient.DeleteAgentPool(clients.Ctx, taskagent.DeleteAgentPoolArgs{ - PoolId: &poolID, - }) -} - -func createAzureAgentPool(clients *client.AggregatedClient, agentPool *taskagent.TaskAgentPool) (*taskagent.TaskAgentPool, error) { - args := taskagent.AddAgentPoolArgs{ - Pool: agentPool, + if err := clients.TaskAgentClient.DeleteAgentPool(clients.Ctx, taskagent.DeleteAgentPoolArgs{PoolId: &poolID}); err != nil { + return err } - newTaskAgent, err := clients.TaskAgentClient.AddAgentPool(clients.Ctx, args) - return newTaskAgent, err -} - -func azureAgentPoolRead(clients *client.AggregatedClient, poolID int) (*taskagent.TaskAgentPool, error) { - return clients.TaskAgentClient.GetAgentPool(clients.Ctx, taskagent.GetAgentPoolArgs{ - PoolId: &poolID, - }) -} - -func azureAgentPoolUpdate(clients *client.AggregatedClient, agentPool *taskagent.TaskAgentPool) (*taskagent.TaskAgentPool, error) { - return clients.TaskAgentClient.UpdateAgentPool( - clients.Ctx, - taskagent.UpdateAgentPoolArgs{ - PoolId: agentPool.Id, - Pool: &taskagent.TaskAgentPool{ - Name: agentPool.Name, - PoolType: agentPool.PoolType, - AutoProvision: agentPool.AutoProvision, - AutoUpdate: agentPool.AutoUpdate, - }, - }) -} - -func flattenAzureAgentPool(d *schema.ResourceData, agentPool *taskagent.TaskAgentPool) { - d.SetId(strconv.Itoa(*agentPool.Id)) - d.Set("name", converter.ToString(agentPool.Name, "")) - d.Set("pool_type", *agentPool.PoolType) - d.Set("auto_provision", *agentPool.AutoProvision) - - if agentPool.AutoUpdate != nil { - d.Set("auto_update", *agentPool.AutoUpdate) + // waiting resource deleted + stateConf := &resource.StateChangeConf{ + Pending: []string{"Waiting"}, + Target: []string{"Synched"}, + Refresh: func() (interface{}, string, error) { + state := "Waiting" + agentPool, err := clients.TaskAgentClient.GetAgentPool(clients.Ctx, taskagent.GetAgentPoolArgs{PoolId: &poolID}) + if err != nil { + if utils.ResponseWasNotFound(err) { + state = "Synched" + } else { + return nil, "", fmt.Errorf(" looking up Agent Pool with ID: %+v", err) + } + } + if agentPool == nil { + state = "Synched" + } + return state, state, nil + }, + Timeout: 5 * time.Minute, + MinTimeout: 3 * time.Second, + Delay: 1 * time.Second, + ContinuousTargetOccurence: 2, + } + if _, err := stateConf.WaitForStateContext(clients.Ctx); err != nil { + return fmt.Errorf(" waiting for Agent Pool deleted. %v ", err) } + return nil } -func expandAgentPool(d *schema.ResourceData, forCreate bool) (*taskagent.TaskAgentPool, error) { - poolID, err := strconv.Atoi(d.Id()) - if !forCreate && err != nil { - return nil, fmt.Errorf("Error getting agent pool Id: %+v", err) +func syncStatus(params taskagent.UpdateAgentPoolArgs, client *client.AggregatedClient) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{"Waiting"}, + Target: []string{"Synched"}, + Refresh: func() (interface{}, string, error) { + state := "Waiting" + agentPool, err := client.TaskAgentClient.GetAgentPool(client.Ctx, taskagent.GetAgentPoolArgs{PoolId: params.PoolId}) + if err != nil { + return nil, "", fmt.Errorf(" looking up Agent Pool with ID: %+v", err) + } + if *agentPool.AutoUpdate == *params.Pool.AutoUpdate && + *agentPool.AutoProvision == *params.Pool.AutoProvision && + *agentPool.PoolType == *params.Pool.PoolType { + state = "Synched" + } + return state, state, nil + }, + Timeout: 5 * time.Minute, + MinTimeout: 3 * time.Second, + Delay: 1 * time.Second, + ContinuousTargetOccurence: 2, } - - poolType := taskagent.TaskAgentPoolType(d.Get("pool_type").(string)) - - pool := &taskagent.TaskAgentPool{ - Id: &poolID, - Name: converter.String(d.Get("name").(string)), - PoolType: &poolType, - AutoProvision: converter.Bool(d.Get("auto_provision").(bool)), - AutoUpdate: converter.Bool(d.Get("auto_update").(bool)), + if _, err := stateConf.WaitForStateContext(client.Ctx); err != nil { + return fmt.Errorf(" waiting for Agent Pool ready. %v ", err) } - - return pool, nil + return nil } diff --git a/azuredevops/internal/service/taskagent/resource_agentpool_test.go b/azuredevops/internal/service/taskagent/resource_agentpool_test.go deleted file mode 100644 index bafd0c3b..00000000 --- a/azuredevops/internal/service/taskagent/resource_agentpool_test.go +++ /dev/null @@ -1,172 +0,0 @@ -//go:build (all || resource_agentpool) && !exclude_resource_agentpool -// +build all resource_agentpool -// +build !exclude_resource_agentpool - -package taskagent - -// The tests in this file use the mock clients in mock_client.go to mock out -// the Azure DevOps client operations. - -import ( - "context" - "errors" - "fmt" - "math/rand" - "testing" - - "github.com/golang/mock/gomock" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/microsoft/azure-devops-go-api/azuredevops/v6/taskagent" - "github.com/microsoft/terraform-provider-azuredevops/azdosdkmocks" - "github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/client" - "github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/utils/converter" - "github.com/stretchr/testify/require" -) - -var testAgentPoolID = rand.Intn(100) - -var testAgentPool = taskagent.TaskAgentPool{ - Id: &testAgentPoolID, - Name: converter.String("Name"), - PoolType: &taskagent.TaskAgentPoolTypeValues.Automation, - AutoProvision: converter.Bool(false), -} - -// verifies that the flatten/expand round trip yields the same agent pool definition -func TestAgentPool_ExpandFlatten_Roundtrip(t *testing.T) { - resourceData := schema.TestResourceDataRaw(t, ResourceAgentPool().Schema, nil) - flattenAzureAgentPool(resourceData, &testAgentPool) - - agentPoolAfterRoundTrip, err := expandAgentPool(resourceData, true) - require.Nil(t, err) - require.Equal(t, testAgentPool, *agentPoolAfterRoundTrip) -} - -// verifies that the create operation is considered failed if the API call fails. -func TestAgentPool_CreateAgentPool_DoesNotSwallowErrorFromFailedAddAgentCall(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - taskAgentClient := azdosdkmocks.NewMockTaskagentClient(ctrl) - clients := &client.AggregatedClient{ - TaskAgentClient: taskAgentClient, - Ctx: context.Background(), - } - - expectedProjectCreateArgs := taskagent.AddAgentPoolArgs{ - Pool: &testAgentPool, - } - - taskAgentClient. - EXPECT(). - AddAgentPool(clients.Ctx, expectedProjectCreateArgs). - Return(nil, errors.New("AddAgentPool() Failed")). - Times(1) - - newTaskAgentPool, err := createAzureAgentPool(clients, &testAgentPool) - require.Nil(t, newTaskAgentPool) - require.Equal(t, "AddAgentPool() Failed", err.Error()) -} - -func TestAgentPool_DeleteAgentPool_ReturnsErrorIfIdReadFails(t *testing.T) { - client := &client.AggregatedClient{} - - resourceData := schema.TestResourceDataRaw(t, ResourceAgentPool().Schema, nil) - flattenAzureAgentPool(resourceData, &testAgentPool) - resourceData.SetId("") - - err := resourceAzureAgentPoolDelete(resourceData, client) - require.Equal(t, "Error getting agent pool Id: strconv.Atoi: parsing \"\": invalid syntax", err.Error()) -} - -func TestAgentPool_UpdateAgentPool_ReturnsErrorIfIdReadFails(t *testing.T) { - client := &client.AggregatedClient{} - - resourceData := schema.TestResourceDataRaw(t, ResourceAgentPool().Schema, nil) - flattenAzureAgentPool(resourceData, &testAgentPool) - resourceData.SetId("") - - err := resourceAzureAgentPoolUpdate(resourceData, client) - require.Equal(t, "Error converting terraform data model to AzDO agent pool reference: Error getting agent pool Id: strconv.Atoi: parsing \"\": invalid syntax", err.Error()) -} - -func TestAgentPool_UpdateAgentPool_UpdateAndRead(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - taskAgentClient := azdosdkmocks.NewMockTaskagentClient(ctrl) - clients := &client.AggregatedClient{ - TaskAgentClient: taskAgentClient, - Ctx: context.Background(), - } - - agentToUpdate := taskagent.TaskAgentPool{ - Id: &testAgentPoolID, - Name: converter.String("Foo"), - PoolType: &taskagent.TaskAgentPoolTypeValues.Deployment, - AutoProvision: converter.Bool(true), - AutoUpdate: converter.Bool(true), - } - - resourceData := schema.TestResourceDataRaw(t, ResourceAgentPool().Schema, nil) - flattenAzureAgentPool(resourceData, &agentToUpdate) - - taskAgentClient. - EXPECT(). - UpdateAgentPool(clients.Ctx, taskagent.UpdateAgentPoolArgs{ - PoolId: &testAgentPoolID, - Pool: &taskagent.TaskAgentPool{ - Name: agentToUpdate.Name, - PoolType: agentToUpdate.PoolType, - AutoProvision: agentToUpdate.AutoProvision, - AutoUpdate: agentToUpdate.AutoUpdate, - }, - }). - Return(&agentToUpdate, nil). - Times(1) - - taskAgentClient. - EXPECT(). - GetAgentPool(clients.Ctx, taskagent.GetAgentPoolArgs{ - PoolId: &testAgentPoolID, - }). - Return(&agentToUpdate, nil). - Times(1) - - err := resourceAzureAgentPoolUpdate(resourceData, clients) - require.Nil(t, err) - - updatedTaskAgent, _ := expandAgentPool(resourceData, false) - require.Equal(t, agentToUpdate.Id, updatedTaskAgent.Id) - require.Equal(t, agentToUpdate.Name, updatedTaskAgent.Name) - require.Equal(t, agentToUpdate.PoolType, updatedTaskAgent.PoolType) - require.Equal(t, agentToUpdate.AutoProvision, updatedTaskAgent.AutoProvision) - require.Equal(t, agentToUpdate.AutoUpdate, updatedTaskAgent.AutoUpdate) -} - -// validates supported pool types are allowed by the schema -func TestAgentPoolDefinition_PoolTypeIsCorrect(t *testing.T) { - validPoolTypes := []string{ - string(taskagent.TaskAgentPoolTypeValues.Automation), - string(taskagent.TaskAgentPoolTypeValues.Deployment), - } - poolTypeSchema := ResourceAgentPool().Schema["pool_type"] - - for _, repoType := range validPoolTypes { - _, errors := poolTypeSchema.ValidateFunc(repoType, "") - require.Equal(t, 0, len(errors), "Agent pool type unexpectedly did not pass validation") - } -} - -// validates invalid pool types are rejected by the schema -func TestAgentPoolDefinition_WhenPoolTypeIsNotCorrect_ReturnsError(t *testing.T) { - invalidPoolTypes := []string{"", "unknown"} - poolTypeSchema := ResourceAgentPool().Schema["pool_type"] - - for _, poolType := range invalidPoolTypes { - _, errors := poolTypeSchema.ValidateFunc(poolType, "pool_type") - expectedError := fmt.Sprintf("expected pool_type to be one of [automation deployment], got %s", poolType) - require.Equal(t, 1, len(errors), "Agent pool type %v unexpectedly passed validation", poolType) - require.Equal(t, expectedError, errors[0].Error()) - } -} diff --git a/azuredevops/internal/utils/converter/converter.go b/azuredevops/internal/utils/converter/converter.go index dd17e710..6f66a4e0 100644 --- a/azuredevops/internal/utils/converter/converter.go +++ b/azuredevops/internal/utils/converter/converter.go @@ -33,6 +33,10 @@ func Int(value int) *int { return &value } +func ToPtr[E any](e E) *E { + return &e +} + // ASCIIToIntPtr Convert a string to an Int Pointer func ASCIIToIntPtr(value string) (*int, error) { i, err := strconv.Atoi(value) |
