//--------------------------------------------------------------------- // 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"); }