@@ -18,9 +18,11 @@ import (
1818 "context"
1919 "fmt"
2020 "log"
21+ "net/http"
2122 "net/url"
2223 "path"
2324 "regexp"
25+ "time"
2426
2527 apiv1 "github.com/google/cloud-android-orchestration/api/v1"
2628 "github.com/google/cloud-android-orchestration/pkg/app/accounts"
@@ -79,27 +81,19 @@ func (m *GCEInstanceManager) ListZones() (*apiv1.ListZonesResponse, error) {
7981}
8082
8183func (m * GCEInstanceManager ) GetHostAddr (zone string , host string ) (string , error ) {
82- instance , err := m .getHostInstance (zone , host )
84+ ins , err := m .getHostInstance (zone , host )
8385 if err != nil {
8486 return "" , err
8587 }
86- ilen := len (instance .NetworkInterfaces )
87- if ilen == 0 {
88- log .Printf ("host instance %s in zone %s is missing a network interface" , host , zone )
89- return "" , errors .NewInternalError ("host instance missing a network interface" , nil )
90- }
91- if ilen > 1 {
92- log .Printf ("host instance %s in zone %s has %d network interfaces" , host , zone , ilen )
93- }
94- return instance .NetworkInterfaces [0 ].NetworkIP , nil
88+ return getHostAddrWithIns (ins )
9589}
9690
9791func (m * GCEInstanceManager ) GetHostURL (zone string , host string ) (* url.URL , error ) {
98- addr , err := m .GetHostAddr (zone , host )
92+ ins , err := m .getHostInstance (zone , host )
9993 if err != nil {
10094 return nil , err
10195 }
102- return url . Parse ( fmt . Sprintf ( "%s://%s:%d" , m .Config . HostOrchestratorProtocol , addr , m . Config . GCP . HostOrchestratorPort ) )
96+ return getHostURLWithIns ( ins , & m .Config )
10397}
10498
10599const operationStatusDone = "DONE"
@@ -253,16 +247,89 @@ func (m *GCEInstanceManager) WaitOperation(zone string, user accounts.User, name
253247 if op .Status != operationStatusDone {
254248 return nil , errors .NewServiceUnavailableError ("Wait for operation timed out" , nil )
255249 }
256- getter := opResultGetter {Service : m .Service , Op : op }
257- return getter .Get ()
250+ getter := opResultGetter {
251+ Service : m .Service ,
252+ Op : op ,
253+ Config : & m .Config ,
254+ }
255+ res , err := getter .Get ()
256+ if err != nil {
257+ return nil , err
258+ }
259+ if hostInst , ok := res .(* apiv1.HostInstance ); ok && op .OperationType == "insert" {
260+ return m .WaitHostAvailability (zone , user , hostInst .Name )
261+ }
262+ return res , nil
263+ }
264+
265+ func (m * GCEInstanceManager ) WaitHostAvailability (zone string , user accounts.User , host string ) (* apiv1.HostInstance , error ) {
266+ ins , err := m .getHostInstance (zone , host )
267+ if err != nil {
268+ return nil , err
269+ }
270+ hostInstance , err := BuildHostInstance (ins )
271+ if err != nil {
272+ return nil , err
273+ }
274+ client , err := getHostClientWithIns (ins , & m .Config )
275+ if err != nil {
276+ return nil , err
277+ }
278+ if err := m .waitForOrchestrator (zone , hostInstance , client ); err != nil {
279+ return nil , err
280+ }
281+ return hostInstance , nil
282+ }
283+
284+ func (m * GCEInstanceManager ) waitForOrchestrator (zone string , host * apiv1.HostInstance , client HostClient ) error {
285+ check := func () error {
286+ _ , err := m .Service .Instances .Get (m .Config .GCP .ProjectID , zone , host .Name ).Context (context .TODO ()).Do ()
287+ if err != nil {
288+ if apiErr , ok := err .(* googleapi.Error ); ok && apiErr .Code == http .StatusNotFound {
289+ return errors .NewNotFoundError ("Host was deleted concurrently" , err )
290+ }
291+ return fmt .Errorf ("failed to check host existence: %w" , err )
292+ }
293+ return nil
294+ }
295+
296+ return WaitForHostReady (client , 5 * time .Minute , 5 * time .Second , check )
297+ }
298+
299+ func getHostAddrWithIns (ins * compute.Instance ) (string , error ) {
300+ ilen := len (ins .NetworkInterfaces )
301+ if ilen == 0 {
302+ log .Printf ("host instance %s in zone %s is missing a network interface" , ins .Name , ins .Zone )
303+ return "" , errors .NewInternalError ("host instance missing a network interface" , nil )
304+ }
305+ if ilen > 1 {
306+ log .Printf ("host instance %s in zone %s has %d network interfaces" , ins .Name , ins .Zone , ilen )
307+ }
308+ return ins .NetworkInterfaces [0 ].NetworkIP , nil
309+ }
310+
311+ func getHostURLWithIns (ins * compute.Instance , config * Config ) (* url.URL , error ) {
312+ addr , err := getHostAddrWithIns (ins )
313+ if err != nil {
314+ return nil , err
315+ }
316+ return url .Parse (fmt .Sprintf ("%s://%s:%d" , config .HostOrchestratorProtocol , addr , config .GCP .HostOrchestratorPort ))
317+ }
318+
319+ func getHostClientWithIns (ins * compute.Instance , config * Config ) (HostClient , error ) {
320+ url , err := getHostURLWithIns (ins , config )
321+ if err != nil {
322+ return nil , err
323+ }
324+ return NewNetHostClient (url , config .AllowSelfSignedHostSSLCertificate ), nil
258325}
259326
260327func (m * GCEInstanceManager ) GetHostClient (zone string , host string ) (HostClient , error ) {
261- url , err := m .GetHostURL (zone , host )
328+ ins , err := m .getHostInstance (zone , host )
262329 if err != nil {
263330 return nil , err
264331 }
265- return NewNetHostClient ( url , m .Config . AllowSelfSignedHostSSLCertificate ), nil
332+ return getHostClientWithIns ( ins , & m .Config )
266333}
267334
268335func (m * GCEInstanceManager ) getHostInstance (zone string , host string ) (* compute.Instance , error ) {
@@ -326,6 +393,7 @@ var (
326393type opResultGetter struct {
327394 Service * compute.Service
328395 Op * compute.Operation
396+ Config * Config
329397}
330398
331399func (g * opResultGetter ) Get () (any , error ) {
0 commit comments