예제 #1
0
        public static async Task <NetworkCredential> ResetWindowsUserAsync(
            this InstancesResource resource,
            InstanceLocator instanceRef,
            string username,
            TimeSpan timeout,
            CancellationToken token)
        {
            using (var timeoutCts = new CancellationTokenSource())
            {
                timeoutCts.CancelAfter(timeout);

                using (var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, token))
                {
                    try
                    {
                        return(await ResetWindowsUserAsync(
                                   resource,
                                   instanceRef,
                                   username,
                                   combinedCts.Token).ConfigureAwait(false));
                    }
                    catch (Exception e) when(e.IsCancellation() && timeoutCts.IsCancellationRequested)
                    {
                        TraceSources.Common.TraceError(e);
                        // This task was cancelled because of a timeout, not because
                        // the enclosing job was cancelled.
                        throw new PasswordResetException(
                                  $"Timeout waiting for Compute Engine agent to reset password for user {username}. " +
                                  "Verify that the agent is running.");
                    }
                }
            }
        }
예제 #2
0
 /// <summary>
 /// Read serial port output as a continuous stream.
 /// </summary>
 public static IAsyncReader <string> GetSerialPortOutputStream(
     this InstancesResource resource,
     InstanceLocator instanceRef,
     ushort port)
 {
     return(new SerialPortStream(resource, instanceRef, port));
 }
        public void SetUp()
        {
            var service = TestProject.CreateComputeService();

            this.instancesResource = service.Instances;
            this.projectsResource  = service.Projects;
        }
예제 #4
0
 /// <summary>
 /// Read serial port output as a continuous stream.
 /// </summary>
 public static SerialPortStream GetSerialPortOutputStream(
     this InstancesResource resource,
     VmInstanceReference instanceRef,
     ushort port)
 {
     return(new SerialPortStream(resource, instanceRef, port));
 }
예제 #5
0
 public SerialPortStream(
     InstancesResource instancesResource,
     InstanceLocator instanceRef,
     ushort port)
 {
     this.instancesResource = instancesResource;
     this.port     = port;
     this.instance = instanceRef;
 }
예제 #6
0
 public SerialPortStream(
     InstancesResource instancesResource,
     VmInstanceReference instanceRef,
     ushort port)
 {
     this.instancesResource = instancesResource;
     this.port     = port;
     this.instance = instanceRef;
 }
예제 #7
0
 /// <summary>
 /// Read serial port output as a continuous stream.
 /// </summary>
 public static SerialPortStream GetSerialPortOutputStream(
     this InstancesResource resource,
     string project,
     string zone,
     string instance,
     ushort port)
 {
     return(GetSerialPortOutputStream(
                resource,
                new VmInstanceReference(project, zone, instance),
                port));
 }
예제 #8
0
 /// <summary>
 /// Adds or overwrites a metadata key/value pair to a GCE
 /// instance. Any existing metadata is kept as is.
 /// </summary>
 public static Task AddMetadataAsync(
     this InstancesResource resource,
     string project,
     string zone,
     string instance,
     string key,
     string value)
 {
     return(AddMetadataAsync(
                resource,
                new VmInstanceReference(project, zone, instance),
                key,
                value));
 }
예제 #9
0
 /// <summary>
 /// Reset a SAM account password. If the SAM account does not exist,
 /// it is created and made a local Administrator.
 /// </summary>
 /// <see href="https://cloud.google.com/compute/docs/instances/windows/automate-pw-generation"/>
 public static Task <NetworkCredential> ResetWindowsUserAsync(
     this InstancesResource resource,
     string project,
     string zone,
     string instance,
     string username,
     CancellationToken token)
 {
     return(ResetWindowsUserAsync(
                resource,
                new InstanceLocator(project, zone, instance),
                username,
                token));
 }
예제 #10
0
 public static Task AddMetadataAsync(
     this InstancesResource resource,
     InstanceLocator instanceRef,
     Metadata metadata,
     CancellationToken token)
 {
     return(UpdateMetadataAsync(
                resource,
                instanceRef,
                existingMetadata =>
     {
         existingMetadata.Add(metadata);
     },
                token));
 }
예제 #11
0
        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");
        }
예제 #12
0
 /// <summary>
 /// Adds or overwrites a metadata key/value pair to a GCE
 /// instance. Any existing metadata is kept as is.
 /// </summary>
 public static Task AddMetadataAsync(
     this InstancesResource resource,
     string project,
     string zone,
     string instance,
     string key,
     string value,
     CancellationToken token)
 {
     return(AddMetadataAsync(
                resource,
                new InstanceLocator(project, zone, instance),
                key,
                value,
                token));
 }
예제 #13
0
        /// <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());
        }
예제 #14
0
        //---------------------------------------------------------------------
        // 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);
            }
        }
예제 #15
0
 /// <summary>
 /// Adds or overwrites a metadata key/value pair to a GCE
 /// instance. Any existing metadata is kept as is.
 /// </summary>
 public static Task AddMetadataAsync(
     this InstancesResource resource,
     InstanceLocator instanceRef,
     string key,
     string value,
     CancellationToken token)
 {
     return(AddMetadataAsync(
                resource,
                instanceRef,
                new Metadata()
     {
         Items = new List <Metadata.ItemsData>()
         {
             new Metadata.ItemsData()
             {
                 Key = key,
                 Value = value
             }
         }
     },
                token));
 }
예제 #16
0
 public void SetUp()
 {
     this.instancesResource = TestProject.CreateComputeService().Instances;
 }
예제 #17
0
        /// <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;
                        }
                    }
                }
            }
        }
예제 #18
0
        /// <summary>
        /// Reset a SAM account password. If the SAM account does not exist,
        /// it is created and made a local Administrator.
        /// </summary>
        /// <see href="https://cloud.google.com/compute/docs/instances/windows/automate-pw-generation"/>
        public static async Task <NetworkCredential> ResetWindowsUserAsync(
            this InstancesResource resource,
            InstanceLocator instanceRef,
            string username,
            CancellationToken token)
        {
            using (TraceSources.Common.TraceMethod().WithParameters(instanceRef, username))
                using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(RsaKeySize))
                {
                    var keyParameters = rsa.ExportParameters(false);

                    var requestPayload = new RequestPayload()
                    {
                        ExpireOn = DateTime.UtcNow.AddMinutes(5),
                        Username = username,
                        Email    = username,
                        Modulus  = Convert.ToBase64String(keyParameters.Modulus),
                        Exponent = Convert.ToBase64String(keyParameters.Exponent),
                    };

                    // Send the request to the instance via a special metadata entry.
                    try
                    {
                        var requestJson = JsonConvert.SerializeObject(requestPayload);
                        await resource.AddMetadataAsync(
                            instanceRef,
                            MetadataKey,
                            requestJson,
                            token).ConfigureAwait(false);
                    }
                    catch (GoogleApiException e) when(e.Error != null && e.Error.Code == 404)
                    {
                        TraceSources.Common.TraceVerbose("Instance does not exist: {0}", e.Message);

                        throw new PasswordResetException(
                                  $"Instance {instanceRef.Name} was not found.");
                    }
                    catch (GoogleApiException e) when(e.Error == null || e.Error.Code == 403)
                    {
                        TraceSources.Common.TraceVerbose(
                            "Setting request payload metadata failed: {0} ({1})",
                            e.Message,
                            e.Error?.Errors.EnsureNotNull().Select(er => er.Reason).FirstOrDefault());

                        // Setting metadata failed due to lack of permissions. Note that
                        // the Error object is not always populated, hence the OR filter.

                        throw new PasswordResetException(
                                  "You do not have sufficient permissions to reset a Windows password. " +
                                  "You need the 'Service Account User' and " +
                                  "'Compute Instance Admin' roles (or equivalent custom roles) " +
                                  "to perform this action.");
                    }

                    // Read response from serial port.
                    using (var serialPortStream = resource.GetSerialPortOutputStream(
                               instanceRef,
                               SerialPort))
                    {
                        // It is rare, but sometimes a single JSON can be split over multiple
                        // API reads. Therefore, maintain a buffer.
                        var logBuffer = new StringBuilder(64 * 1024);
                        while (true)
                        {
                            TraceSources.Common.TraceVerbose("Waiting for agent to supply response...");

                            token.ThrowIfCancellationRequested();

                            string logDelta = await serialPortStream.ReadAsync(token).ConfigureAwait(false);

                            if (string.IsNullOrEmpty(logDelta))
                            {
                                // Reached end of stream, wait and try again.
                                await Task.Delay(500, token).ConfigureAwait(false);

                                continue;
                            }

                            logBuffer.Append(logDelta);

                            var response = logBuffer.ToString().Split('\n')
                                           .Where(line => line.Contains(requestPayload.Modulus))
                                           .FirstOrDefault();
                            if (response == null)
                            {
                                // That was not the output we are looking for, keep reading.
                                continue;
                            }

                            var responsePayload = JsonConvert.DeserializeObject <ResponsePayload>(response);
                            if (!string.IsNullOrEmpty(responsePayload.ErrorMessage))
                            {
                                throw new PasswordResetException(responsePayload.ErrorMessage);
                            }

                            var password = rsa.Decrypt(
                                Convert.FromBase64String(responsePayload.EncryptedPassword),
                                true);

                            return(new NetworkCredential(
                                       username,
                                       new UTF8Encoding().GetString(password),
                                       null));
                        }
                    }
                }
        }
 /// <summary>Constructs a new resource.</summary>
 public LocationsResource(Google.Apis.Services.IClientService service)
 {
     this.service = service;
     Instances    = new InstancesResource(service);
 }
 public void SetUp()
 {
     this.instancesResource = ComputeEngine.Connect().Service.Instances;
 }
예제 #21
0
        /// <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");
        }
예제 #22
0
        /// <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;
                        }
                    }
                }
            }
        }
        public async Task AddExistingInstances(
            InstancesResource instancesResource,
            DisksResource disksResource,
            string projectId)
        {
            // NB. Instances.list returns the disks associated with each
            // instance, but lacks the information about the source image.
            // Therefore, load disks separately.

            using (TraceSources.LogAnalysis.TraceMethod().WithParameters(projectId))
            {
                // TODO: Paging
                var disks = await disksResource.AggregatedList(projectId).ExecuteAsync();

                var sourceImagaesByDisk = disks.Items == null
                    ? new Dictionary <string, string>()
                    : disks.Items.Values
                                          .Where(v => v.Disks != null)
                                          .EnsureNotNull()
                                          .SelectMany(v => v.Disks)
                                          .EnsureNotNull()
                                          .ToDictionary(d => d.SelfLink, d => d.SourceImage);

                // TODO: Paging
                var instances = await instancesResource.AggregatedList(projectId).ExecuteAsync();

                if (instances.Items != null)
                {
                    foreach (var list in instances.Items.Values)
                    {
                        if (list.Instances == null)
                        {
                            continue;
                        }

                        foreach (var instance in list.Instances)
                        {
                            TraceSources.LogAnalysis.TraceVerbose("Adding {0}", instance.Id);

                            var bootDiskUrl = instance.Disks
                                              .EnsureNotNull()
                                              .Where(d => d.Boot != null && d.Boot.Value)
                                              .EnsureNotNull()
                                              .Select(d => d.Source)
                                              .EnsureNotNull()
                                              .FirstOrDefault();
                            GlobalResourceReference image = null;
                            if (bootDiskUrl != null &&
                                sourceImagaesByDisk.TryGetValue(bootDiskUrl, out string imageUrl) &&
                                imageUrl != null)
                            {
                                image = GlobalResourceReference.FromString(imageUrl);
                            }

                            AddExistingInstance(
                                (ulong)instance.Id.Value,
                                new VmInstanceReference(
                                    projectId,
                                    ShortZoneIdFromUrl(instance.Zone),
                                    instance.Name),
                                image,
                                instance.Status == "RUNNING"
                                    ? InstanceState.Running
                                    : InstanceState.Terminated,
                                DateTime.Now,
                                instance.Scheduling.NodeAffinities != null && instance.Scheduling.NodeAffinities.Any()
                                    ? Tenancy.SoleTenant
                                    : Tenancy.Fleet);
                        }
                    }
                }
            }
        }