Skip to content

Commit 9d5dfcf

Browse files
Kalarrs TophamNicholas M. Iodice
authored andcommitted
Import build definitions (#228)
Implement import for build definitions
1 parent 0188940 commit 9d5dfcf

12 files changed

+96
-61
lines changed

azuredevops/resource_agentpool.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ func resourceAzureAgentPool() *schema.Resource {
2121
Importer: &schema.ResourceImporter{
2222
State: schema.ImportStatePassthrough,
2323
},
24-
2524
Schema: map[string]*schema.Schema{
2625
"name": {
2726
Type: schema.TypeString,

azuredevops/resource_build_definition.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,18 @@ func resourceBuildDefinition() *schema.Resource {
2121
Read: resourceBuildDefinitionRead,
2222
Update: resourceBuildDefinitionUpdate,
2323
Delete: resourceBuildDefinitionDelete,
24-
24+
Importer: &schema.ResourceImporter{
25+
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
26+
projectID, buildDefinitionID, err := ParseImportedProjectIDAndID(meta.(*config.AggregatedClient), d.Id())
27+
if err != nil {
28+
return nil, fmt.Errorf("Error parsing the build definition ID from the Terraform resource data: %v", err)
29+
}
30+
d.Set("project_id", projectID)
31+
d.SetId(fmt.Sprintf("%d", buildDefinitionID))
32+
33+
return []*schema.ResourceData{d}, nil
34+
},
35+
},
2536
Schema: map[string]*schema.Schema{
2637
"project_id": {
2738
Type: schema.TypeString,
@@ -40,8 +51,8 @@ func resourceBuildDefinition() *schema.Resource {
4051
"path": {
4152
Type: schema.TypeString,
4253
Optional: true,
43-
Default: "\\",
44-
ValidateFunc: validate.FilePathOrEmpty,
54+
Default: `\`,
55+
ValidateFunc: validate.Path,
4556
},
4657
"variable_groups": {
4758
Type: schema.TypeSet,
@@ -115,6 +126,7 @@ func flattenBuildDefinition(d *schema.ResourceData, buildDefinition *build.Build
115126

116127
d.Set("project_id", projectID)
117128
d.Set("name", *buildDefinition.Name)
129+
d.Set("path", *buildDefinition.Path)
118130
d.Set("repository", flattenRepository(buildDefinition))
119131
d.Set("agent_pool_name", *buildDefinition.Queue.Pool.Name)
120132

azuredevops/resource_build_definition_test.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,24 @@ func TestAzureDevOpsBuildDefinition_RepoTypeListIsCorrect(t *testing.T) {
7474
}
7575
}
7676

77-
// validates that and error is thrown if any of the un-supported file path characters are used
77+
// validates that an error is thrown if any of the un-supported path characters are used
7878
func TestAzureDevOpsBuildDefinition_PathInvalidCharacterListIsError(t *testing.T) {
7979
expectedInvalidPathCharacters := []string{"<", ">", "|", ":", "$", "@", "\"", "/", "%", "+", "*", "?"}
8080
pathSchema := resourceBuildDefinition().Schema["path"]
8181

82-
for _, repoType := range expectedInvalidPathCharacters {
83-
_, errors := pathSchema.ValidateFunc(repoType, "")
84-
require.Equal(t, "<>|:$@\"/%+*? are not allowed", errors[0].Error())
82+
for _, invalidCharacter := range expectedInvalidPathCharacters {
83+
_, errors := pathSchema.ValidateFunc(`\`+invalidCharacter, "")
84+
require.Equal(t, "<>|:$@\"/%+*? are not allowed in path", errors[0].Error())
8585
}
8686
}
8787

88+
// validates that an error is thrown if path does not start with slash
89+
func TestAzureDevOpsBuildDefinition_PathInvalidStartingSlashIsError(t *testing.T) {
90+
pathSchema := resourceBuildDefinition().Schema["path"]
91+
_, errors := pathSchema.ValidateFunc("dir\\dir", "")
92+
require.Equal(t, "path must start with backslash", errors[0].Error())
93+
}
94+
8895
// verifies that the flatten/expand round trip yields the same build definition
8996
func TestAzureDevOpsBuildDefinition_ExpandFlatten_Roundtrip(t *testing.T) {
9097
resourceData := schema.TestResourceDataRaw(t, resourceBuildDefinition().Schema, nil)
@@ -205,15 +212,15 @@ func TestAzureDevOpsBuildDefinition_Update_DoesNotSwallowError(t *testing.T) {
205212
// underlying terraform state.
206213
func TestAccAzureDevOpsBuildDefinition_CreateAndUpdate(t *testing.T) {
207214
projectName := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
208-
buildDefinitionPathEmpty := ""
215+
buildDefinitionPathEmpty := `\`
209216
buildDefinitionNameFirst := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
210217
buildDefinitionNameSecond := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
211218

212219
buildDefinitionPathFirst := `\` + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
213220
buildDefinitionPathSecond := `\` + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
214221

215-
buildDefinitionPathThird := buildDefinitionNameFirst + `\` + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
216-
buildDefinitionPathFourth := buildDefinitionNameSecond + `\` + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
222+
buildDefinitionPathThird := `\` + buildDefinitionNameFirst + `\` + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
223+
buildDefinitionPathFourth := `\` + buildDefinitionNameSecond + `\` + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
217224

218225
tfBuildDefNode := "azuredevops_build_definition.build"
219226
resource.Test(t, resource.TestCase{
@@ -276,6 +283,12 @@ func TestAccAzureDevOpsBuildDefinition_CreateAndUpdate(t *testing.T) {
276283
resource.TestCheckResourceAttr(tfBuildDefNode, "path", buildDefinitionPathFourth),
277284
testAccCheckBuildDefinitionResourceExists(buildDefinitionNameFirst),
278285
),
286+
}, {
287+
// Resource Acceptance Testing https://www.terraform.io/docs/extend/resources/import.html#resource-acceptance-testing-implementation
288+
ResourceName: tfBuildDefNode,
289+
ImportStateIdFunc: testAccImportStateIDFunc(tfBuildDefNode),
290+
ImportState: true,
291+
ImportStateVerify: true,
279292
},
280293
},
281294
})

azuredevops/resource_group.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ func resourceGroup() *schema.Resource {
2929
Importer: &schema.ResourceImporter{
3030
State: schema.ImportStatePassthrough,
3131
},
32-
3332
Schema: map[string]*schema.Schema{
3433
"scope": {
3534
Type: schema.TypeString,

azuredevops/resource_project.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package azuredevops
22

33
import (
44
"fmt"
5+
"github.com/microsoft/terraform-provider-azuredevops/azuredevops/utils/tfhelper"
56
"log"
67
"os"
78
"strconv"
@@ -32,8 +33,6 @@ func resourceProject() *schema.Resource {
3233
Importer: &schema.ResourceImporter{
3334
State: schema.ImportStatePassthrough,
3435
},
35-
36-
//https://godoc.org/github.com/hashicorp/terraform/helper/schema#Schema
3736
Schema: map[string]*schema.Schema{
3837
"project_name": {
3938
Type: schema.TypeString,
@@ -330,3 +329,35 @@ func lookupProcessTemplateName(clients *config.AggregatedClient, templateID stri
330329

331330
return *process.Name, nil
332331
}
332+
333+
// ParseImportedProjectIDAndID : Parse the Id (projectId/int) or (projectName/int)
334+
func ParseImportedProjectIDAndID(clients *config.AggregatedClient, id string) (string, int, error) {
335+
project, resourceID, err := tfhelper.ParseImportedID(id)
336+
if err != nil {
337+
return "", 0, err
338+
}
339+
340+
// Get the project ID
341+
currentProject, err := projectRead(clients, project, project)
342+
if err != nil {
343+
return "", 0, err
344+
}
345+
346+
return currentProject.Id.String(), resourceID, nil
347+
}
348+
349+
// ParseImportedProjectIDAndUUID : Parse the Id (projectId/uuid) or (projectName/uuid)
350+
func ParseImportedProjectIDAndUUID(clients *config.AggregatedClient, id string) (string, string, error) {
351+
project, resourceID, err := tfhelper.ParseImportedUUID(id)
352+
if err != nil {
353+
return "", "", err
354+
}
355+
356+
// Get the project ID
357+
currentProject, err := projectRead(clients, project, project)
358+
if err != nil {
359+
return "", "", err
360+
}
361+
362+
return currentProject.Id.String(), resourceID, nil
363+
}

azuredevops/resource_variable_group.go

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ func resourceVariableGroup() *schema.Resource {
1818
Read: resourceVariableGroupRead,
1919
Update: resourceVariableGroupUpdate,
2020
Delete: resourceVariableGroupDelete,
21-
2221
Importer: &schema.ResourceImporter{
2322
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
24-
projectID, variableGroupID, err := ParseImportedProjectIDAndVariableGroupID(meta.(*config.AggregatedClient), d.Id())
23+
projectID, variableGroupID, err := ParseImportedProjectIDAndID(meta.(*config.AggregatedClient), d.Id())
2524
if err != nil {
2625
return nil, fmt.Errorf("Error parsing the variable group ID from the Terraform resource data: %v", err)
2726
}
@@ -349,19 +348,3 @@ func flattenAllowAccess(d *schema.ResourceData, definitionResource *[]build.Defi
349348
}
350349
d.Set("allow_access", allowAccess)
351350
}
352-
353-
// ParseImportedProjectIDAndVariableGroupID : Parse the Id (projectId/variableGroupId) or (projectName/variableGroupId)
354-
func ParseImportedProjectIDAndVariableGroupID(clients *config.AggregatedClient, id string) (string, int, error) {
355-
project, resourceID, err := tfhelper.ParseImportedID(id)
356-
if err != nil {
357-
return "", 0, err
358-
}
359-
360-
// Get the project ID
361-
currentProject, err := projectRead(clients, project, project)
362-
if err != nil {
363-
return "", 0, err
364-
}
365-
366-
return currentProject.Id.String(), resourceID, nil
367-
}

azuredevops/utils/tfhelper/tfhelper.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package tfhelper
33
import (
44
"encoding/json"
55
"fmt"
6-
"github.com/google/uuid"
6+
"github.com/hashicorp/go-uuid"
77
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
88
"github.com/microsoft/terraform-provider-azuredevops/azuredevops/utils/secretmemo"
99
"log"
@@ -114,16 +114,16 @@ func ParseImportedID(id string) (string, int, error) {
114114
return project, resourceID, nil
115115
}
116116

117-
// ParseImportedUUID parse the imported uuid Id from the terraform import
117+
// ParseImportedUUID parse the imported uuid from the terraform import
118118
func ParseImportedUUID(id string) (string, string, error) {
119119
parts := strings.SplitN(id, "/", 2)
120120
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
121121
return "", "", fmt.Errorf("unexpected format of ID (%s), expected projectid/resourceId", id)
122122
}
123123
project := parts[0]
124-
resourceUUID, err := uuid.Parse(parts[1])
124+
_, err := uuid.ParseUUID(parts[1])
125125
if err != nil {
126-
return "", "", fmt.Errorf("error a uuid but got: %+v", err)
126+
return "", "", fmt.Errorf("%s isn't a valid UUID", parts[1])
127127
}
128-
return project, resourceUUID.String(), nil
128+
return project, parts[1], nil
129129
}

azuredevops/utils/validate/file_path.go

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,30 @@ import (
55
"regexp"
66
)
77

8-
// InvalidWindowsFilePathRegExp is a regex helper.
9-
var InvalidWindowsFilePathRegExp = regexp.MustCompile("[<>|:$@\"/%+*?]")
8+
// InvalidWindowsPathRegExp is a regex helper.
9+
var InvalidWindowsPathRegExp = regexp.MustCompile("[<>|:$@\"/%+*?]")
1010

11-
// FilePath validates that the string does not contain characters (equal to [<>|:$@\"/%+*?])
12-
func FilePath(i interface{}, k string) (warnings []string, errors []error) {
11+
// Path validates that the string does not contain characters (equal to [<>|:$@\"/%+*?])
12+
func Path(i interface{}, k string) (warnings []string, errors []error) {
1313
v, ok := i.(string)
1414
if !ok {
1515
errors = append(errors, fmt.Errorf("expected type of %q to be string", k))
1616
return
1717
}
1818

19-
p := InvalidWindowsFilePathRegExp.MatchString(v)
20-
if p {
21-
errors = append(errors, fmt.Errorf("<>|:$@\"/%%+*? are not allowed"))
22-
return
19+
if len(v) < 1 {
20+
errors = append(errors, fmt.Errorf("path can not be empty"))
2321
}
2422

25-
return warnings, errors
26-
}
27-
28-
// FilePathOrEmpty allows empty string otherwise validates that the string does not contain characters (equal to [<>|:$@\"/%+*?])
29-
func FilePathOrEmpty(i interface{}, k string) (warnings []string, errors []error) {
30-
v, ok := i.(string)
31-
if !ok {
32-
errors = append(errors, fmt.Errorf("expected type of %q to be string", k))
33-
return
23+
if v[:1] != `\` {
24+
errors = append(errors, fmt.Errorf("path must start with backslash"))
3425
}
3526

36-
if v == "" {
27+
p := InvalidWindowsPathRegExp.MatchString(v)
28+
if p {
29+
errors = append(errors, fmt.Errorf("<>|:$@\"/%%+*? are not allowed in path"))
3730
return
3831
}
3932

40-
return FilePath(i, k)
33+
return warnings, errors
4134
}

website/docs/r/agent_pool.html.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ In addition to all arguments above, the following attributes are exported:
2828
* [Azure DevOps Service REST API 5.1 - Agent Pools](https://docs.microsoft.com/en-us/rest/api/azure/devops/distributedtask/pools?view=azure-devops-rest-5.1)
2929

3030
## Import
31-
Azure DevOps Agent Pools can be imported using the agent pool id, e.g.
31+
Azure DevOps Agent Pools can be imported using the agent pool Id, e.g.
3232

3333
```
3434
terraform import azuredevops_agent_pool.pool 42

website/docs/r/build_definition.html.markdown

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,10 @@ In addition to all arguments above, the following attributes are exported:
6767
* [Azure DevOps Service REST API 5.1 - Build Definitions](https://docs.microsoft.com/en-us/rest/api/azure/devops/build/definitions?view=azure-devops-rest-5.1)
6868

6969
## Import
70-
71-
Not supported
70+
Azure DevOps Build Definitions can be imported using the project name/definitions Id or by the project Guid/definitions Id, e.g.
71+
72+
```
73+
terraform import azuredevops_build_definition.build "Test Project"/10
74+
or
75+
terraform import azuredevops_build_definition.build 782a8123-1019-xxxx-xxxx-xxxxxxxx/10
76+
```

website/docs/r/project.html.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ In addition to all arguments above, the following attributes are exported:
3333
* [Azure DevOps Service REST API 5.1 - Projects](https://docs.microsoft.com/en-us/rest/api/azure/devops/core/projects?view=azure-devops-rest-5.1)
3434

3535
## Import
36-
Azure DevOps Projects can be imported using the project name or by the project Guid id, e.g.
36+
Azure DevOps Projects can be imported using the project name or by the project Guid, e.g.
3737

3838
```
3939
terraform import azuredevops_project.project "Test Project"

website/docs/r/variable_group.html.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ In addition to all arguments above, the following attributes are exported:
5454
* [Azure DevOps Service REST API 5.1 - Authorized Resources](https://docs.microsoft.com/en-us/rest/api/azure/devops/build/authorizedresources?view=azure-devops-rest-5.1)
5555

5656
## Import
57-
Azure DevOps Variable groups can be imported using the project name/variable group Id or by the project Guid id/variable group Id, e.g.
57+
Azure DevOps Variable groups can be imported using the project name/variable group Id or by the project Guid/variable group Id, e.g.
5858

5959
```
6060
terraform import azuredevops_project.project "Test Project"/10

0 commit comments

Comments
 (0)