private static async Task AwaitInstanceCreatedAndReady( InstancesResource resource, InstanceLocator locator) { for (int i = 0; i < 60; i++) { try { var instance = await resource.Get( locator.ProjectId, locator.Zone, locator.Name) .ExecuteAsync(); // Determine the name of the guest attribute we need to await. var guestAttributeToAwait = instance.Metadata.Items .EnsureNotNull() .FirstOrDefault(item => item.Key == GuestAttributeToAwaitKey) .Value; var request = resource.GetGuestAttributes( locator.ProjectId, locator.Zone, locator.Name); request.QueryPath = GuestAttributeNamespace + "/"; var guestAttributes = await request.ExecuteAsync(); if (guestAttributes .QueryValue .Items .Where(item => item.Namespace__ == GuestAttributeNamespace && item.Key == guestAttributeToAwait) .Any()) { return; } } catch (Exception) { } TraceSources.Common.TraceVerbose( "Waiting for instance {0} to become ready...", locator.Name); await Task.Delay(5 * 1000); } throw new TimeoutException($"Timeout waiting for {locator} to become ready"); }
/// <summary> /// Query a metadata entry for an instance. /// </summary> /// <returns>null if not set/found</returns> public static async Task <Metadata.ItemsData> QueryMetadataKeyAsync( this InstancesResource resource, InstanceLocator instanceRef, string key) { var instance = await resource.Get( instanceRef.ProjectId, instanceRef.Zone, instanceRef.Name).ExecuteAsync().ConfigureAwait(false); if (instance.Metadata == null || instance.Metadata.Items == null) { return(null); } return(instance.Metadata.Items .Where(i => i.Key == key) .FirstOrDefault()); }
//--------------------------------------------------------------------- // Instance metadata. //--------------------------------------------------------------------- /// <summary> /// Modify instance metadata. /// </summary> public static async Task UpdateMetadataAsync( this InstancesResource resource, InstanceLocator instanceRef, Action <Metadata> updateMetadata, CancellationToken token, uint maxAttempts = DefaultAttempts) { using (CommonTraceSources.Default.TraceMethod().WithParameters(instanceRef)) { await UpdateMetadataAsync( async() => { var instance = await resource.Get( instanceRef.ProjectId, instanceRef.Zone, instanceRef.Name) .ExecuteAsync(token) .ConfigureAwait(false); return(instance.Metadata); }, async metadata => { await resource.SetMetadata( metadata, instanceRef.ProjectId, instanceRef.Zone, instanceRef.Name) .ExecuteAndAwaitOperationAsync( instanceRef.ProjectId, token) .ConfigureAwait(false); }, updateMetadata, maxAttempts) .ConfigureAwait(false); } }
/// <summary> /// Adds or overwrites a metadata key/value pair to a GCE /// instance. Any existing metadata is kept as is. /// </summary> public static async Task AddMetadataAsync( this InstancesResource resource, InstanceLocator instanceRef, string key, string value, CancellationToken token) { using (TraceSources.Common.TraceMethod().WithParameters(instanceRef, key)) { for (int attempt = 0; attempt < 6; attempt++) { var instance = await resource.Get( instanceRef.ProjectId, instanceRef.Zone, instanceRef.Name).ExecuteAsync(token).ConfigureAwait(false); var metadata = instance.Metadata; if (metadata.Items == null) { metadata.Items = new List <Metadata.ItemsData>(); } var existingEntry = metadata.Items .Where(i => i.Key == key) .FirstOrDefault(); if (existingEntry != null) { existingEntry.Value = value; } else { metadata.Items.Add(new Metadata.ItemsData() { Key = key, Value = value }); } TraceSources.Common.TraceVerbose("Setting metdata {0} on {1}...", key, instanceRef.Name); try { await resource.SetMetadata( metadata, instanceRef.ProjectId, instanceRef.Zone, instanceRef.Name).ExecuteAndAwaitOperationAsync(instanceRef.ProjectId, token).ConfigureAwait(false); break; } catch (GoogleApiException e) { if (e.Error != null && e.Error.Code == 412) { // Fingerprint mismatch - that happens when somebody else updated metadata // in patallel. int backoff = 100; TraceSources.Common.TraceWarning( "SetMetadata failed with {0} - retrying after {1}ms", e.Message, e.Error?.Code, backoff); await Task.Delay(backoff).ConfigureAwait(false); } else { TraceSources.Common.TraceWarning( "Setting metdata failed {0} (code error {1})", e.Message, e.Error?.Code); throw; } } } } }
/// <summary> /// Adds or overwrites a metadata key/value pair to a GCE /// instance. Any existing metadata is kept as is. /// </summary> public static async Task AddMetadataAsync( this InstancesResource resource, InstanceLocator instanceRef, Metadata metadata, CancellationToken token) { using (TraceSources.Common.TraceMethod().WithParameters(instanceRef)) { for (int attempt = 0; attempt < 6; attempt++) { TraceSources.Common.TraceVerbose("Adding metadata {0} on {1}...", metadata, instanceRef.Name); // // NB. Metadata must be updated all-at-once. Therefore, // fetch the existing entries first before merging them // with the new entries. // var instance = await resource.Get( instanceRef.ProjectId, instanceRef.Zone, instanceRef.Name).ExecuteAsync(token).ConfigureAwait(false); var mergedMetadata = instance.Metadata; mergedMetadata.Add(metadata); try { await resource.SetMetadata( mergedMetadata, instanceRef.ProjectId, instanceRef.Zone, instanceRef.Name).ExecuteAndAwaitOperationAsync(instanceRef.ProjectId, token).ConfigureAwait(false); break; } catch (GoogleApiException e) { if (e.Error != null && e.Error.Code == 412) { // Fingerprint mismatch - that happens when somebody else updated metadata // in patallel. int backoff = 100; TraceSources.Common.TraceWarning( "SetMetadata failed with {0} - retrying after {1}ms", e.Message, e.Error?.Code, backoff); await Task.Delay(backoff).ConfigureAwait(false); } else { TraceSources.Common.TraceWarning( "Setting metdata failed {0} (code error {1})", e.Message, e.Error?.Code); throw; } } } } }
/// <summary> /// Adds or overwrites a metadata key/value pair to a GCE /// instance. Any existing metadata is kept as is. /// </summary> public static async Task AddMetadataAsync( this InstancesResource resource, VmInstanceReference instanceRef, string key, string value) { var instance = await resource.Get( instanceRef.ProjectId, instanceRef.Zone, instanceRef.InstanceName).ExecuteAsync().ConfigureAwait(false); var metadata = instance.Metadata; if (metadata.Items == null) { metadata.Items = new List <Metadata.ItemsData>(); } var existingEntry = metadata.Items .Where(i => i.Key == key) .FirstOrDefault(); if (existingEntry != null) { existingEntry.Value = value; } else { metadata.Items.Add(new Metadata.ItemsData() { Key = key, Value = value }); } TraceSources.Compute.TraceVerbose("Setting metdata {0} on {1}...", key, instanceRef.InstanceName); await resource.SetMetadata( metadata, instanceRef.ProjectId, instanceRef.Zone, instanceRef.InstanceName).ExecuteAsync().ConfigureAwait(false); // Setting the metadata might fail silently, cf b/140226028 - so check if // it worked. Sometimes, the change also takes a few seconds to apply (sic). for (int attempt = 0; attempt < 10; attempt++) { var appliedValue = await resource.QueryMetadataKeyAsync( instanceRef, key).ConfigureAwait(false); if (appliedValue != null && appliedValue.Value == value) { // Success. return; } else { // Wait and retry. TraceSources.Compute.TraceVerbose("Metdata change not applied yet, trying again..."); await Task.Delay(500).ConfigureAwait(false); } } throw new GoogleApiException("Compute", "Setting metdata failed"); }