From 0c0b7685d63191451c43e03f9056f130d171f616 Mon Sep 17 00:00:00 2001 From: Carolyn Van Slyck Date: Mon, 25 Jan 2016 17:11:56 -0600 Subject: [PATCH] Compute.KeyPairs Closes #534 --- src/corelib/Compute/v2_1/ComputeApiBuilder.cs | 81 ++++++++++++- src/corelib/Compute/v2_1/ComputeService.cs | 28 ++++- .../Compute/v2_1/ComputeServiceExtensions.cs | 29 ++++- src/corelib/Compute/v2_1/KeyPair.cs | 16 ++- src/corelib/Compute/v2_1/KeyPairDefinition.cs | 7 +- src/corelib/Compute/v2_1/KeyPairDetails.cs | 17 +++ src/corelib/Compute/v2_1/KeyPairExtensions.cs | 16 +++ src/corelib/Compute/v2_1/KeyPairRequest.cs | 20 ++++ src/corelib/Compute/v2_1/KeyPairResponse.cs | 12 ++ .../v2_1/Serialization/KeyPairCollection.cs | 25 ++++ .../v2_1/Serialization/KeyPairConverter.cs | 49 ++++++++ src/corelib/Compute/v2_2/ComputeService.cs | 8 +- .../Compute/v2_2/ComputeServiceExtensions.cs | 6 +- src/corelib/Compute/v2_2/KeyPairDefinition.cs | 6 +- src/corelib/OpenStack.csproj | 6 + .../Compute/v2_1/ComputeTestDataManager.cs | 31 +++++ .../integration/Compute/v2_1/KeyPairTests.cs | 96 +++++++++++++++ .../OpenStack.IntegrationTests.csproj | 1 + src/testing/unit/Compute/v2_1/KeyPairTests.cs | 112 +++++++++++++++++- 19 files changed, 537 insertions(+), 29 deletions(-) create mode 100644 src/corelib/Compute/v2_1/KeyPairDetails.cs create mode 100644 src/corelib/Compute/v2_1/KeyPairExtensions.cs create mode 100644 src/corelib/Compute/v2_1/KeyPairRequest.cs create mode 100644 src/corelib/Compute/v2_1/KeyPairResponse.cs create mode 100644 src/corelib/Compute/v2_1/Serialization/KeyPairCollection.cs create mode 100644 src/corelib/Compute/v2_1/Serialization/KeyPairConverter.cs create mode 100644 src/testing/integration/Compute/v2_1/KeyPairTests.cs diff --git a/src/corelib/Compute/v2_1/ComputeApiBuilder.cs b/src/corelib/Compute/v2_1/ComputeApiBuilder.cs index e1cb582b5..d29fb2d29 100644 --- a/src/corelib/Compute/v2_1/ComputeApiBuilder.cs +++ b/src/corelib/Compute/v2_1/ComputeApiBuilder.cs @@ -978,15 +978,69 @@ public virtual async Task ListImageDetailsAsync(Url url, Cancellat #region Keypairs + /// + public virtual async Task GetKeyPairAsync(string keypairName, CancellationToken cancellationToken = default(CancellationToken)) + where T : IServiceResource + { + if (keypairName == null) + throw new ArgumentNullException("keypairName"); + + PreparedRequest request = await BuildGetKeyPairRequestAsync(keypairName, cancellationToken); + + var result = await request.SendAsync().ReceiveJson(); + result.PropogateOwner(this); + return result; + } + + /// + public virtual async Task BuildGetKeyPairRequestAsync(string keypairName, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await UrlBuilder.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegment($"os-keypairs/{keypairName}") + .Authenticate(AuthenticationProvider) + .SetMicroversion(this) + .PrepareGet(cancellationToken); + } + /// public virtual async Task CreateKeyPairAsync(object keypair, CancellationToken cancellationToken = default(CancellationToken)) + where T : IServiceResource { + if(keypair == null) + throw new ArgumentNullException("keypair"); + PreparedRequest request = await BuildCreateKeyPairRequestAsync(keypair, cancellationToken); - return await request.SendAsync().ReceiveJson(); + var result = await request.SendAsync().ReceiveJson(); + result.PropogateOwner(this); + return result; + } + + /// + public virtual async Task BuildCreateKeyPairRequestAsync(object keypairName, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await UrlBuilder.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegment("os-keypairs") + .Authenticate(AuthenticationProvider) + .SetMicroversion(this) + .PreparePostJson(keypairName, cancellationToken); + } + + /// + public virtual async Task ListKeyPairsAsync(CancellationToken cancellationToken = default(CancellationToken)) + where T : IEnumerable + { + PreparedRequest request = await BuildListKeyPairsRequestAsync(cancellationToken); + var result = await request.SendAsync().ReceiveJson(); + result.PropogateOwner(this); + return result; } /// - public virtual async Task BuildCreateKeyPairRequestAsync(object keypair, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task BuildListKeyPairsRequestAsync(CancellationToken cancellationToken = default(CancellationToken)) { Url endpoint = await UrlBuilder.GetEndpoint(cancellationToken).ConfigureAwait(false); @@ -994,9 +1048,30 @@ public virtual async Task ListImageDetailsAsync(Url url, Cancellat .AppendPathSegment("os-keypairs") .Authenticate(AuthenticationProvider) .SetMicroversion(this) - .PreparePostJson(keypair, cancellationToken); + .PrepareGet(cancellationToken); + } + + /// + public virtual Task DeleteKeyPairAsync(string keypairName, CancellationToken cancellationToken = default(CancellationToken)) + { + if (keypairName == null) + throw new ArgumentNullException("keypairName"); + + return BuildDeleteKeyPairRequestAsync(keypairName, cancellationToken).SendAsync(); } + /// + public virtual async Task BuildDeleteKeyPairRequestAsync(string keypairName, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await UrlBuilder.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return (PreparedRequest)endpoint + .AppendPathSegment($"os-keypairs/{keypairName}") + .Authenticate(AuthenticationProvider) + .SetMicroversion(this) + .PrepareDelete(cancellationToken) + .AllowHttpStatus(HttpStatusCode.NotFound); + } #endregion #region Security Groups diff --git a/src/corelib/Compute/v2_1/ComputeService.cs b/src/corelib/Compute/v2_1/ComputeService.cs index 3030e3a90..29ee28d2c 100644 --- a/src/corelib/Compute/v2_1/ComputeService.cs +++ b/src/corelib/Compute/v2_1/ComputeService.cs @@ -321,13 +321,35 @@ public ComputeService(IAuthenticationProvider authenticationProvider, string reg #region Keypairs + /// + public virtual Task GetKeyPairAsync(string keypairName, CancellationToken cancellationToken = default(CancellationToken)) + { + return _computeApi.GetKeyPairAsync(keypairName, cancellationToken); + } + /// - public virtual Task CreateKeyPairAsync(string name, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task CreateKeyPairAsync(KeyPairRequest request, CancellationToken cancellationToken = default(CancellationToken)) + { + return _computeApi.CreateKeyPairAsync(request, cancellationToken); + } + + /// + public virtual Task ImportKeyPairAsync(KeyPairDefinition keypair, CancellationToken cancellationToken = default(CancellationToken)) { - var keyPair = new KeyPairDefinition(name); - return _computeApi.CreateKeyPairAsync(keyPair, cancellationToken); + return _computeApi.CreateKeyPairAsync(keypair, cancellationToken); } + /// + public virtual async Task> ListKeyPairsAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + return await _computeApi.ListKeyPairsAsync(cancellationToken); + } + + /// + public virtual Task DeleteKeyPairAsync(string keypairName, CancellationToken cancellationToken = default(CancellationToken)) + { + return _computeApi.DeleteKeyPairAsync(keypairName, cancellationToken); + } #endregion #region Security Groups diff --git a/src/corelib/Compute/v2_1/ComputeServiceExtensions.cs b/src/corelib/Compute/v2_1/ComputeServiceExtensions.cs index 61c2abe95..e5207a442 100644 --- a/src/corelib/Compute/v2_1/ComputeServiceExtensions.cs +++ b/src/corelib/Compute/v2_1/ComputeServiceExtensions.cs @@ -300,11 +300,36 @@ public static void DetachVolume(this ComputeService service, Identifier serverId #endregion #region KeyPairs + /// + public static KeyPairDetails GetKeyPair(this ComputeService service, string keypairName) + { + return service.GetKeyPairAsync(keypairName).ForceSynchronous(); + } + /// - public static KeyPair CreateKeyPair(this ComputeService service, string name) + public static KeyPairResponse CreateKeyPair(this ComputeService service, KeyPairRequest request) { - return service.CreateKeyPairAsync(name).ForceSynchronous(); + return service.CreateKeyPairAsync(request).ForceSynchronous(); } + + /// + public static KeyPair ImportKeyPair(this ComputeService service, KeyPairDefinition keyPair) + { + return service.ImportKeyPairAsync(keyPair).ForceSynchronous(); + } + + /// + public static IEnumerable ListKeyPairs(this ComputeService service) + { + return service.ListKeyPairsAsync().ForceSynchronous(); + } + + /// + public static void DeleteKeyPair(this ComputeService service, string keypairName) + { + service.DeleteKeyPairAsync(keypairName).ForceSynchronous(); + } + #endregion #region Security Groups diff --git a/src/corelib/Compute/v2_1/KeyPair.cs b/src/corelib/Compute/v2_1/KeyPair.cs index dba20124c..3569c3715 100644 --- a/src/corelib/Compute/v2_1/KeyPair.cs +++ b/src/corelib/Compute/v2_1/KeyPair.cs @@ -1,13 +1,16 @@ using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OpenStack.Compute.v2_1.Serialization; using OpenStack.Serialization; namespace OpenStack.Compute.v2_1 { /// - [JsonConverterWithConstructor(typeof(RootWrapperConverter), "keypair")] - public class KeyPair : IHaveExtraData + [JsonConverter(typeof(KeyPairConverter))] + public class KeyPair : IHaveExtraData, IServiceResource { /// [JsonProperty("name")] @@ -24,5 +27,14 @@ public class KeyPair : IHaveExtraData /// [JsonExtensionData] IDictionary IHaveExtraData.Data { get; set; } = new Dictionary(); + + object IServiceResource.Owner { get; set; } + + /// + public Task DeleteAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + var compute = this.GetOwnerOrThrow(); + return compute.DeleteKeyPairAsync(Name, cancellationToken); + } } } \ No newline at end of file diff --git a/src/corelib/Compute/v2_1/KeyPairDefinition.cs b/src/corelib/Compute/v2_1/KeyPairDefinition.cs index cc879a9e8..23d9832aa 100644 --- a/src/corelib/Compute/v2_1/KeyPairDefinition.cs +++ b/src/corelib/Compute/v2_1/KeyPairDefinition.cs @@ -1,16 +1,17 @@ using Newtonsoft.Json; -using OpenStack.Serialization; +using OpenStack.Compute.v2_1.Serialization; namespace OpenStack.Compute.v2_1 { /// - [JsonConverterWithConstructor(typeof(RootWrapperConverter), "keypair")] + [JsonConverter(typeof(KeyPairConverter))] public class KeyPairDefinition { /// - public KeyPairDefinition(string name) + public KeyPairDefinition(string name, string publicKey) { Name = name; + PublicKey = publicKey; } /// diff --git a/src/corelib/Compute/v2_1/KeyPairDetails.cs b/src/corelib/Compute/v2_1/KeyPairDetails.cs new file mode 100644 index 000000000..53f084b6c --- /dev/null +++ b/src/corelib/Compute/v2_1/KeyPairDetails.cs @@ -0,0 +1,17 @@ +using System; +using Newtonsoft.Json; + +namespace OpenStack.Compute.v2_1 +{ + /// + public class KeyPairDetails : KeyPair + { + /// + [JsonProperty("id")] + public Identifier Id { get; set; } + + /// + [JsonProperty("created_at")] + public DateTimeOffset Created { get; set; } + } +} \ No newline at end of file diff --git a/src/corelib/Compute/v2_1/KeyPairExtensions.cs b/src/corelib/Compute/v2_1/KeyPairExtensions.cs new file mode 100644 index 000000000..4bdbecb13 --- /dev/null +++ b/src/corelib/Compute/v2_1/KeyPairExtensions.cs @@ -0,0 +1,16 @@ +using OpenStack.Compute.v2_1; +using OpenStack.Synchronous.Extensions; + +// ReSharper disable once CheckNamespace +namespace OpenStack.Synchronous +{ + /// + public static class KeypairExtensions_v2_1 + { + /// + public static void Delete(this KeyPair keypair) + { + keypair.DeleteAsync().ForceSynchronous(); + } + } +} diff --git a/src/corelib/Compute/v2_1/KeyPairRequest.cs b/src/corelib/Compute/v2_1/KeyPairRequest.cs new file mode 100644 index 000000000..cc3713b0e --- /dev/null +++ b/src/corelib/Compute/v2_1/KeyPairRequest.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using OpenStack.Compute.v2_1.Serialization; + +namespace OpenStack.Compute.v2_1 +{ + /// + [JsonConverter(typeof(KeyPairConverter))] + public class KeyPairRequest + { + /// + public KeyPairRequest(string name) + { + Name = name; + } + + /// + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/corelib/Compute/v2_1/KeyPairResponse.cs b/src/corelib/Compute/v2_1/KeyPairResponse.cs new file mode 100644 index 000000000..70e69e002 --- /dev/null +++ b/src/corelib/Compute/v2_1/KeyPairResponse.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace OpenStack.Compute.v2_1 +{ + /// + public class KeyPairResponse : KeyPair + { + /// + [JsonProperty("private_key")] + public string PrivateKey { get; set; } + } +} \ No newline at end of file diff --git a/src/corelib/Compute/v2_1/Serialization/KeyPairCollection.cs b/src/corelib/Compute/v2_1/Serialization/KeyPairCollection.cs new file mode 100644 index 000000000..d1128231a --- /dev/null +++ b/src/corelib/Compute/v2_1/Serialization/KeyPairCollection.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using OpenStack.Serialization; + +namespace OpenStack.Compute.v2_1.Serialization +{ + /// + /// Represents a collection of keypair resources of the . + /// + /// + public class KeyPairCollection : ResourceCollection + where T : IServiceResource + { + /// + /// The requested keypairs. + /// + [JsonProperty("keypairs")] + protected IList KeyPairs => Items; + } + + /// + /// + public class KeyPairCollection : KeyPairCollection + { } +} diff --git a/src/corelib/Compute/v2_1/Serialization/KeyPairConverter.cs b/src/corelib/Compute/v2_1/Serialization/KeyPairConverter.cs new file mode 100644 index 000000000..94a0cb818 --- /dev/null +++ b/src/corelib/Compute/v2_1/Serialization/KeyPairConverter.cs @@ -0,0 +1,49 @@ +using System; +using Newtonsoft.Json; +using OpenStack.Serialization; + +namespace OpenStack.Compute.v2_1.Serialization +{ + /// + /// Handles serialization for Compute KeyPairs, which don't follow the usual convention of not wrapping the object when contained in a list. + /// + public class KeyPairConverter : DefaultJsonConverter + { + private readonly string _name = "keypair"; + + /// + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + // Wrap + writer.WriteStartObject(); + writer.WritePropertyName(_name); + + // Default serialization + base.WriteJson(writer, value, serializer); + + writer.WriteEndObject(); + } + + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + // Skip to the desired property + while (reader.Read()) + { + if (reader.TokenType != JsonToken.PropertyName || reader.Value.ToString() != _name) + continue; + + // Advance to the contained value + reader.Read(); + break; + } + + // Default Deserialization + object result = base.ReadJson(reader, objectType, existingValue, serializer); + + while (reader.Read() && reader.TokenType != JsonToken.EndObject) { } // Advance to end of object + + return result; + } + } +} \ No newline at end of file diff --git a/src/corelib/Compute/v2_2/ComputeService.cs b/src/corelib/Compute/v2_2/ComputeService.cs index 4ac7ab1db..17d8a83af 100644 --- a/src/corelib/Compute/v2_2/ComputeService.cs +++ b/src/corelib/Compute/v2_2/ComputeService.cs @@ -34,13 +34,9 @@ public ComputeService(IAuthenticationProvider authenticationProvider, string reg #region Keypairs /// - public virtual Task CreateKeyPairAsync(string name, KeyPairType? type = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task ImportKeyPairAsync(KeyPairDefinition keypair, CancellationToken cancellationToken = default(CancellationToken)) { - var keyPair = new KeyPairDefinition(name) - { - Type = type - }; - return _computeApiBuilder.CreateKeyPairAsync(keyPair, cancellationToken); + return _computeApiBuilder.CreateKeyPairAsync(keypair, cancellationToken); } #endregion diff --git a/src/corelib/Compute/v2_2/ComputeServiceExtensions.cs b/src/corelib/Compute/v2_2/ComputeServiceExtensions.cs index 1ea2acf34..5e72f61df 100644 --- a/src/corelib/Compute/v2_2/ComputeServiceExtensions.cs +++ b/src/corelib/Compute/v2_2/ComputeServiceExtensions.cs @@ -15,10 +15,10 @@ public static RemoteConsole GetVncConsole(this ComputeService service, Identifie return service.GetVncConsoleAync(serverId, type).ForceSynchronous(); } - /// - public static KeyPair CreateKeyPair(this ComputeService service, string name, KeyPairType? type = null) + /// + public static KeyPair ImportKeyPair(this ComputeService service, KeyPairDefinition keypair) { - return service.CreateKeyPairAsync(name, type).ForceSynchronous(); + return service.ImportKeyPairAsync(keypair).ForceSynchronous(); } } } diff --git a/src/corelib/Compute/v2_2/KeyPairDefinition.cs b/src/corelib/Compute/v2_2/KeyPairDefinition.cs index dbf149157..0f5a9feef 100644 --- a/src/corelib/Compute/v2_2/KeyPairDefinition.cs +++ b/src/corelib/Compute/v2_2/KeyPairDefinition.cs @@ -7,10 +7,10 @@ namespace OpenStack.Compute.v2_2 [JsonConverterWithConstructor(typeof(RootWrapperConverter), "keypair")] public class KeyPairDefinition : v2_1.KeyPairDefinition { - /// - public KeyPairDefinition(string name) : base(name) + /// + public KeyPairDefinition(string name, string publicKey) + : base(name, publicKey) { - Name = name; } /// diff --git a/src/corelib/OpenStack.csproj b/src/corelib/OpenStack.csproj index fd09092b1..d4abb21ee 100644 --- a/src/corelib/OpenStack.csproj +++ b/src/corelib/OpenStack.csproj @@ -103,13 +103,19 @@ + + + + + + diff --git a/src/testing/integration/Compute/v2_1/ComputeTestDataManager.cs b/src/testing/integration/Compute/v2_1/ComputeTestDataManager.cs index 4d903cf96..8f90c8582 100644 --- a/src/testing/integration/Compute/v2_1/ComputeTestDataManager.cs +++ b/src/testing/integration/Compute/v2_1/ComputeTestDataManager.cs @@ -58,6 +58,12 @@ public void Dispose() } catch (AggregateException ex) { errors.AddRange(ex.InnerExceptions); } + try + { + DeleteKeyPairs(_testData.OfType()); + } + catch (AggregateException ex) { errors.AddRange(ex.InnerExceptions); } + try { BlockStorage.Dispose(); @@ -145,5 +151,30 @@ public void DeleteSecurityGroups(IEnumerable securityGroups) Task.WaitAll(deletes); } #endregion + + #region Key Pairs + public KeyPairRequest BuildKeyPairRequest() + { + return new KeyPairRequest(TestData.GenerateName()); + } + + public Task CreateKeyPair() + { + return CreateKeyPair(BuildKeyPairRequest()); + } + + public async Task CreateKeyPair(KeyPairRequest request) + { + var keypair = await _compute.CreateKeyPairAsync(request); + Register(keypair); + return keypair; + } + + public void DeleteKeyPairs(IEnumerable keypairs) + { + var deletes = keypairs.Select(x => x.DeleteAsync()).ToArray(); + Task.WaitAll(deletes); + } + #endregion } } diff --git a/src/testing/integration/Compute/v2_1/KeyPairTests.cs b/src/testing/integration/Compute/v2_1/KeyPairTests.cs new file mode 100644 index 000000000..e068b1109 --- /dev/null +++ b/src/testing/integration/Compute/v2_1/KeyPairTests.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Flurl.Http; +using Xunit; +using Xunit.Abstractions; +using VolumeState = net.openstack.Core.Domain.VolumeState; + +namespace OpenStack.Compute.v2_1 +{ + public class KeyPairTests : IDisposable + { + private readonly ComputeService _compute; + private readonly ComputeTestDataManager _testData; + + public KeyPairTests(ITestOutputHelper testLog) + { + var testOutput = new XunitTraceListener(testLog); + Trace.Listeners.Add(testOutput); + OpenStackNet.Tracing.Http.Listeners.Add(testOutput); + + var authenticationProvider = TestIdentityProvider.GetIdentityProvider(); + _compute = new ComputeService(authenticationProvider, "RegionOne"); + + _testData = new ComputeTestDataManager(_compute); + } + + public void Dispose() + { + Trace.Listeners.Clear(); + OpenStackNet.Tracing.Http.Listeners.Clear(); + + _testData.Dispose(); + } + + [Fact] + public async Task CreateKeyPairTest() + { + var request = _testData.BuildKeyPairRequest(); + + Trace.WriteLine($"Creating keypair named: {request.Name}"); + var keypair = await _testData.CreateKeyPair(request); + + + Trace.WriteLine("Verifying keypair matches requested definition..."); + Assert.NotNull(keypair); + Assert.Equal(request.Name, keypair.Name); + Assert.NotNull(keypair.PrivateKey); + Assert.NotNull(keypair.PrivateKey); + Assert.NotNull(keypair.Fingerprint); + } + + [Fact] + public async Task ImportKeyPairTest() + { + var definition = new KeyPairDefinition(TestData.GenerateName(), "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDrBzodZLiWO6nIGGy9ZOVeFhbF6EaG8HUqrknNVKynH6+Hc5ToY71gmeQGJ7XZTAlyKKdFmPhNPCQCYqFQxjPKD3xTIAoGChlRHfkjYwjefbqxFswi9S0Fi3Lq8mawUVuPmPnuTr8KhL8ibnBbAxZnrcfTKBIoxhU+kN56CCmLnkJc5ouG/UcF+UpqUso45pYRf0YWANyyuafyCmj6NiDxMCGy/vnKUBLzMg8wQ01xGSGOfyGDIwuTFZpoPzjeqEV8oUGvxYt9Xyzh/pPKoOz1cz0wBDaVDpucTz3UEq65F9HhCmdwwjso8MP1K46LkM2JNQWQ0eTotqFvUJEoP2ff Generated-by-Nova"); + + Trace.WriteLine($"Importing keypair named: {definition.Name}"); + var keypair = await _compute.ImportKeyPairAsync(definition); + _testData.Register(keypair); + + Trace.WriteLine("Verifying keypair matches requested definition..."); + Assert.NotNull(keypair); + Assert.Equal(definition.Name, keypair.Name); + Assert.NotNull(keypair.Fingerprint); + } + + [Fact] + public async Task ListKeypairsTest() + { + Trace.WriteLine("Generating test data..."); + var keypair = await _testData.CreateKeyPair(); + await _testData.CreateKeyPair(); + + Trace.WriteLine("Listing keypairs..."); + var results = await _compute.ListKeyPairsAsync(); + + Assert.NotNull(results); + Assert.Contains(results, x => x.Name == keypair.Name); + } + + [Fact] + public async Task DeleteKeyPairTest() + { + var keypair = await _testData.CreateKeyPair(); + Trace.WriteLine($"Created keypair named: {keypair.Name}"); + + Trace.WriteLine($"Deleting keypair..."); + await keypair.DeleteAsync(); + + await Assert.ThrowsAsync(() => _compute.GetKeyPairAsync(keypair.Name)); + } + } +} diff --git a/src/testing/integration/OpenStack.IntegrationTests.csproj b/src/testing/integration/OpenStack.IntegrationTests.csproj index 7fd9ec00f..83bad7408 100644 --- a/src/testing/integration/OpenStack.IntegrationTests.csproj +++ b/src/testing/integration/OpenStack.IntegrationTests.csproj @@ -112,6 +112,7 @@ + diff --git a/src/testing/unit/Compute/v2_1/KeyPairTests.cs b/src/testing/unit/Compute/v2_1/KeyPairTests.cs index 71d6f6830..ab6aff5a4 100644 --- a/src/testing/unit/Compute/v2_1/KeyPairTests.cs +++ b/src/testing/unit/Compute/v2_1/KeyPairTests.cs @@ -1,4 +1,10 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Newtonsoft.Json.Linq; +using OpenStack.Compute.v2_1.Serialization; +using OpenStack.Serialization; using OpenStack.Synchronous; using OpenStack.Testing; using Xunit; @@ -13,20 +19,118 @@ public KeyPairTests() { _computeService = new ComputeService(Stubs.AuthenticationProvider, "region"); } - + + [Fact] + public void DeserializeKeyPairCollection() + { + // this one is odd because even in the list, the items are wrapped with a root that needs to be unwrapped + string json = JObject.Parse(@"{ + 'keypairs': [ + { + 'keypair': { + 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDrBzodZLiWO6nIGGy9ZOVeFhbF6EaG8HUqrknNVKynH6+Hc5ToY71gmeQGJ7XZTAlyKKdFmPhNPCQCYqFQxjPKD3xTIAoGChlRHfkjYwjefbqxFswi9S0Fi3Lq8mawUVuPmPnuTr8KhL8ibnBbAxZnrcfTKBIoxhU+kN56CCmLnkJc5ouG/UcF+UpqUso45pYRf0YWANyyuafyCmj6NiDxMCGy/vnKUBLzMg8wQ01xGSGOfyGDIwuTFZpoPzjeqEV8oUGvxYt9Xyzh/pPKoOz1cz0wBDaVDpucTz3UEq65F9HhCmdwwjso8MP1K46LkM2JNQWQ0eTotqFvUJEoP2ff Generated-by-Nova', + 'name': 'keypair-d20a3d59-9433-4b79-8726-20b431d89c78', + 'fingerprint': 'ce:88:fe:6a:9e:c0:d5:91:08:8b:57:80:be:e6:ec:3d' + } +}, + { + 'keypair': { + 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated-by-Nova', + 'name': 'uploaded-keypair', + 'fingerprint': '1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c' + } + } + ] +}").ToString(); + + var results = OpenStackNet.Configuration.FlurlHttpSettings.JsonSerializer.Deserialize(json); + Assert.NotNull(results); + Assert.Equal(2, results.Count()); + var result = results.First(); + Assert.Empty(((IHaveExtraData)result).Data); + Assert.NotNull(result.PublicKey); + } + + [Fact] + public void GetKeyPair() + { + using (var httpTest = new HttpTest()) + { + const string name = "keypair-name"; + httpTest.RespondWithJson(new KeyPairDetails { Name = name }); + KeyPairDetails result = _computeService.GetKeyPair(name); + + httpTest.ShouldHaveCalled($"*/os-keypairs/{name}"); + Assert.NotNull(result); + Assert.Equal(name, result.Name); + } + } + [Fact] public void CreateKeyPair() { using (var httpTest = new HttpTest()) { - const string name = "{name}"; + const string name = "keypair-name"; + httpTest.RespondWithJson(new KeyPairResponse {Name = name, PrivateKey = Guid.NewGuid().ToString()}); + KeyPairResponse result = _computeService.CreateKeyPair(new KeyPairRequest(name)); + + httpTest.ShouldHaveCalled("*/os-keypairs"); + Assert.NotNull(result); + Assert.Equal(name, result.Name); + Assert.NotNull(result.PrivateKey); + } + } + + [Fact] + public void ImportKeyPair() + { + using (var httpTest = new HttpTest()) + { + const string name = "keypair-name"; httpTest.RespondWithJson(new KeyPair {Name = name}); - KeyPair result = _computeService.CreateKeyPair(name); + KeyPair result = _computeService.ImportKeyPair(new KeyPairDefinition(name, Guid.NewGuid().ToString())); - httpTest.ShouldHaveCalled("*/os-keypair"); + httpTest.ShouldHaveCalled("*/os-keypairs"); Assert.NotNull(result); Assert.Equal(name, result.Name); } } + + [Fact] + public void ListKeypairs() + { + using (var httpTest = new HttpTest()) + { + const string name = "keypair-name"; + httpTest.RespondWithJson(new KeyPairCollection + { + new KeyPair { Name = name } + }); + IEnumerable results = _computeService.ListKeyPairs(); + + httpTest.ShouldHaveCalled("*/os-keypairs"); + Assert.NotNull(results); + Assert.Equal(1, results.Count()); + Assert.Equal(name, results.First().Name); + } + } + + [Theory] + [InlineData(HttpStatusCode.Accepted)] + [InlineData(HttpStatusCode.NotFound)] + public void DeleteKeyPair(HttpStatusCode responseCode) + { + using (var httpTest = new HttpTest()) + { + httpTest.RespondWithJson(new KeyPairDetails {Name = "keypair-name"}); + httpTest.RespondWith((int) responseCode, "All gone!"); + + KeyPair result = _computeService.GetKeyPair("keypair-name"); + result.Delete(); + + httpTest.ShouldHaveCalled("*/os-keypairs"); + } + } } }