@@ -14,6 +14,8 @@ import (
14
14
"github.com/microsoft/azure-devops-go-api/azuredevops/operations"
15
15
)
16
16
17
+ var projectCreateTimeoutSeconds time.Duration = 30
18
+
17
19
func resourceProject () * schema.Resource {
18
20
return & schema.Resource {
19
21
Create : resourceProjectCreate ,
@@ -66,7 +68,7 @@ func resourceProjectCreate(d *schema.ResourceData, m interface{}) error {
66
68
return fmt .Errorf ("Error converting terraform data model to AzDO project reference: %+v" , err )
67
69
}
68
70
69
- err = createProject (clients , project )
71
+ err = createProject (clients , project , projectCreateTimeoutSeconds )
70
72
if err != nil {
71
73
return fmt .Errorf ("Error creating project in Azure DevOps: %+v" , err )
72
74
}
@@ -76,63 +78,55 @@ func resourceProjectCreate(d *schema.ResourceData, m interface{}) error {
76
78
}
77
79
78
80
// Make API call to create the project and wait for an async success/fail response from the service
79
- func createProject (clients * aggregatedClient , project * core.TeamProject ) error {
81
+ func createProject (clients * aggregatedClient , project * core.TeamProject , timeoutSeconds time. Duration ) error {
80
82
operationRef , err := clients .CoreClient .QueueCreateProject (clients .ctx , core.QueueCreateProjectArgs {ProjectToCreate : project })
81
83
if err != nil {
82
84
return err
83
85
}
84
86
85
- err = waitForAsyncOperationSuccess (clients , operationRef )
87
+ err = waitForAsyncOperationSuccess (clients , operationRef , timeoutSeconds )
86
88
if err != nil {
87
89
return err
88
90
}
89
91
90
92
return nil
91
93
}
92
94
93
- func waitForAsyncOperationSuccess (clients * aggregatedClient , operationRef * operations.OperationReference ) error {
94
- maxAttempts := 30
95
- currentAttempt := 1
96
-
97
- for currentAttempt <= maxAttempts {
98
- result , err := clients .OperationsClient .GetOperation (clients .ctx , operations.GetOperationArgs {
99
- OperationId : operationRef .Id ,
100
- PluginId : operationRef .PluginId ,
101
- })
102
-
103
- if err != nil {
104
- return err
105
- }
106
-
107
- if * result .Status == operations .OperationStatusValues .Succeeded {
108
- // Sometimes without the sleep, the subsequent operations won't find the project...
109
- time .Sleep (2 * time .Second )
110
- return nil
95
+ func waitForAsyncOperationSuccess (clients * aggregatedClient , operationRef * operations.OperationReference , timeoutSeconds time.Duration ) error {
96
+ timeout := time .After (timeoutSeconds * time .Second )
97
+ ticker := time .NewTicker (1 * time .Second )
98
+ defer ticker .Stop ()
99
+
100
+ for {
101
+ select {
102
+ case <- ticker .C :
103
+ result , err := clients .OperationsClient .GetOperation (clients .ctx , operations.GetOperationArgs {
104
+ OperationId : operationRef .Id ,
105
+ PluginId : operationRef .PluginId ,
106
+ })
107
+
108
+ if err != nil {
109
+ return err
110
+ }
111
+
112
+ if * result .Status == operations .OperationStatusValues .Succeeded {
113
+ return nil
114
+ }
115
+ case <- timeout :
116
+ return fmt .Errorf ("Operation was not successful after %d seconds" , timeoutSeconds )
111
117
}
112
-
113
- currentAttempt ++
114
- time .Sleep (1 * time .Second )
115
118
}
116
-
117
- return fmt .Errorf ("Operation was not successful after %d attempts" , maxAttempts )
118
119
}
119
120
121
+
120
122
func resourceProjectRead (d * schema.ResourceData , m interface {}) error {
121
123
clients := m .(* aggregatedClient )
122
124
123
- projectID := d .Id ()
124
- if projectID == "" {
125
- // project name can be used as an identifier for the core.Projects API:
126
- // https://docs.microsoft.com/en-us/rest/api/azure/devops/core/projects/get?view=azure-devops-rest-5.0
127
- projectID = d .Get ("project_name" ).(string )
128
- }
129
- project , err := clients .CoreClient .GetProject (clients .ctx , core.GetProjectArgs {
130
- ProjectId : & projectID ,
131
- IncludeCapabilities : converter .Bool (true ),
132
- IncludeHistory : converter .Bool (false ),
133
- })
125
+ id := d .Id ()
126
+ name := d .Get ("project_name" ).(string )
127
+ project , err := projectRead (clients , id , name )
134
128
if err != nil {
135
- return fmt .Errorf ("Error looking up project given ID: %v %v " , projectID , err )
129
+ return fmt .Errorf ("Error looking up project with ID %s and Name %s " , id , name )
136
130
}
137
131
138
132
err = flattenProject (clients , d , project )
@@ -142,6 +136,22 @@ func resourceProjectRead(d *schema.ResourceData, m interface{}) error {
142
136
return nil
143
137
}
144
138
139
+ // Lookup a project using the ID, or name if the ID is not set. Note, usage of the name in place
140
+ // of the ID is an explicitly stated supported behavior:
141
+ // https://docs.microsoft.com/en-us/rest/api/azure/devops/core/projects/get?view=azure-devops-rest-5.0
142
+ func projectRead (clients * aggregatedClient , projectID string , projectName string ) (* core.TeamProject , error ) {
143
+ identifier := projectID
144
+ if identifier == "" {
145
+ identifier = projectName
146
+ }
147
+
148
+ return clients .CoreClient .GetProject (clients .ctx , core.GetProjectArgs {
149
+ ProjectId : & identifier ,
150
+ IncludeCapabilities : converter .Bool (true ),
151
+ IncludeHistory : converter .Bool (false ),
152
+ })
153
+ }
154
+
145
155
func resourceProjectUpdate (d * schema.ResourceData , m interface {}) error {
146
156
return resourceProjectRead (d , m )
147
157
}
0 commit comments