Client Encryption : Adds new Encryption.Custom package (#2682)
In the Encryption package, we currently have code for both single tenant (public model) and multi-tenant (Teams model) use case. We want to separate the two by creating a new package for the Teams model (Microsoft.Azure.Cosmos.Encryption.Custom) which would include all code under [Custom](https://github.com/Azure/azure-cosmos-dotnet-v3/tree/master/Microsoft.Azure.Cosmos.Encryption/src/Custom). This would allow us to GA the public model alone for now as desired.
This commit is contained in:
Родитель
96474e3ae4
Коммит
849dad4bc7
|
@ -6,6 +6,7 @@
|
|||
<ClientPreviewSuffixVersion>preview</ClientPreviewSuffixVersion>
|
||||
<DirectVersion>3.21.1</DirectVersion>
|
||||
<EncryptionVersion>1.0.0-previewV16</EncryptionVersion>
|
||||
<CustomEncryptionVersion>1.0.0-preview</CustomEncryptionVersion>
|
||||
<HybridRowVersion>1.1.0-preview3</HybridRowVersion>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<AboveDirBuildProps>$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))</AboveDirBuildProps>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
|
||||
</Project>
|
|
@ -0,0 +1,8 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.Encryption.Custom.Tests" + Microsoft.Azure.Cosmos.Encryption.Custom.AssemblyKeys.TestPublicKey)]
|
||||
[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests" + Microsoft.Azure.Cosmos.Encryption.Custom.AssemblyKeys.TestPublicKey)]
|
|
@ -0,0 +1,35 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// This is an empty implementation of CosmosDiagnosticsContext which has been plumbed through the DataEncryptionKeyProvider & EncryptionContainer.
|
||||
/// This may help adding diagnostics more easily in future.
|
||||
/// </summary>
|
||||
internal class CosmosDiagnosticsContext
|
||||
{
|
||||
private static readonly CosmosDiagnosticsContext UnusedSingleton = new CosmosDiagnosticsContext();
|
||||
private static readonly IDisposable UnusedScopeSingleton = new Scope();
|
||||
|
||||
public static CosmosDiagnosticsContext Create(RequestOptions options)
|
||||
{
|
||||
return CosmosDiagnosticsContext.UnusedSingleton;
|
||||
}
|
||||
|
||||
public IDisposable CreateScope(string scope)
|
||||
{
|
||||
return CosmosDiagnosticsContext.UnusedScopeSingleton;
|
||||
}
|
||||
|
||||
private class Scope : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
/// <summary>
|
||||
/// The default Cosmos JSON.NET serializer.
|
||||
/// </summary>
|
||||
internal sealed class CosmosJsonDotNetSerializer
|
||||
{
|
||||
private static readonly Encoding DefaultEncoding = new UTF8Encoding(
|
||||
encoderShouldEmitUTF8Identifier: false,
|
||||
throwOnInvalidBytes: true);
|
||||
|
||||
private readonly JsonSerializerSettings serializerSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Create a serializer that uses the JSON.net serializer
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is internal to reduce exposure of JSON.net types so
|
||||
/// it is easier to convert to System.Text.Json
|
||||
/// </remarks>
|
||||
internal CosmosJsonDotNetSerializer(JsonSerializerSettings jsonSerializerSettings = null)
|
||||
{
|
||||
this.serializerSettings = jsonSerializerSettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a Stream to the passed in type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object that should be deserialized</typeparam>
|
||||
/// <param name="stream">An open stream that is readable that contains JSON</param>
|
||||
/// <returns>The object representing the deserialized stream</returns>
|
||||
public T FromStream<T>(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
if (typeof(Stream).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
return (T)(object)stream;
|
||||
}
|
||||
|
||||
using (StreamReader sr = new StreamReader(stream))
|
||||
using (JsonTextReader jsonTextReader = new JsonTextReader(sr))
|
||||
{
|
||||
JsonSerializer jsonSerializer = this.GetSerializer();
|
||||
return jsonSerializer.Deserialize<T>(jsonTextReader);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an object to a open readable stream.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object being serialized</typeparam>
|
||||
/// <param name="input">The object to be serialized</param>
|
||||
/// <returns>An open readable stream containing the JSON of the serialized object</returns>
|
||||
public MemoryStream ToStream<T>(T input)
|
||||
{
|
||||
MemoryStream streamPayload = new MemoryStream();
|
||||
using (StreamWriter streamWriter = new StreamWriter(streamPayload, encoding: CosmosJsonDotNetSerializer.DefaultEncoding, bufferSize: 1024, leaveOpen: true))
|
||||
using (JsonWriter writer = new JsonTextWriter(streamWriter))
|
||||
{
|
||||
writer.Formatting = Newtonsoft.Json.Formatting.None;
|
||||
JsonSerializer jsonSerializer = this.GetSerializer();
|
||||
jsonSerializer.Serialize(writer, input);
|
||||
writer.Flush();
|
||||
streamWriter.Flush();
|
||||
}
|
||||
|
||||
streamPayload.Position = 0;
|
||||
return streamPayload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JsonSerializer has hit a race conditions with custom settings that cause null reference exception.
|
||||
/// To avoid the race condition a new JsonSerializer is created for each call
|
||||
/// </summary>
|
||||
private JsonSerializer GetSerializer()
|
||||
{
|
||||
return JsonSerializer.Create(this.serializerSettings);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using Microsoft.Azure.Cosmos;
|
||||
|
||||
internal sealed class DecryptedResponseMessage : ResponseMessage
|
||||
{
|
||||
private readonly ResponseMessage responseMessage;
|
||||
private Stream decryptedContent;
|
||||
private bool isDisposed = false;
|
||||
|
||||
public DecryptedResponseMessage(
|
||||
ResponseMessage responseMessage,
|
||||
Stream decryptedContent)
|
||||
{
|
||||
this.responseMessage = responseMessage;
|
||||
this.decryptedContent = decryptedContent;
|
||||
}
|
||||
|
||||
public override Stream Content
|
||||
{
|
||||
get => this.decryptedContent;
|
||||
set => this.decryptedContent = value;
|
||||
}
|
||||
|
||||
public override string ContinuationToken => this.responseMessage.ContinuationToken;
|
||||
|
||||
public override CosmosDiagnostics Diagnostics => this.responseMessage.Diagnostics;
|
||||
|
||||
public override string ErrorMessage => this.responseMessage.ErrorMessage;
|
||||
|
||||
public override Headers Headers => this.responseMessage.Headers;
|
||||
|
||||
public override RequestMessage RequestMessage => this.responseMessage.RequestMessage;
|
||||
|
||||
public override HttpStatusCode StatusCode => this.responseMessage.StatusCode;
|
||||
|
||||
public override bool IsSuccessStatusCode => this.responseMessage.IsSuccessStatusCode;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && !this.isDisposed)
|
||||
{
|
||||
this.isDisposed = true;
|
||||
if (this.decryptedContent != null)
|
||||
{
|
||||
this.decryptedContent.Dispose();
|
||||
this.decryptedContent = null;
|
||||
}
|
||||
|
||||
if (this.responseMessage != null)
|
||||
{
|
||||
this.responseMessage.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
internal sealed class EncryptionTransactionalBatchOperationResult : TransactionalBatchOperationResult
|
||||
{
|
||||
private readonly Stream encryptionResourceStream;
|
||||
private readonly TransactionalBatchOperationResult response;
|
||||
|
||||
public EncryptionTransactionalBatchOperationResult(TransactionalBatchOperationResult response, Stream encryptionResourceStream)
|
||||
{
|
||||
this.response = response;
|
||||
this.encryptionResourceStream = encryptionResourceStream;
|
||||
}
|
||||
|
||||
public override Stream ResourceStream => this.encryptionResourceStream;
|
||||
|
||||
public override HttpStatusCode StatusCode => this.response.StatusCode;
|
||||
|
||||
public override bool IsSuccessStatusCode => this.response.IsSuccessStatusCode;
|
||||
|
||||
public override string ETag => this.response.ETag;
|
||||
|
||||
public override TimeSpan RetryAfter => this.response.RetryAfter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
internal sealed class EncryptionTransactionalBatchOperationResult<T> : TransactionalBatchOperationResult<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TransactionalBatchOperationResult{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="result">BatchOperationResult with stream resource.</param>
|
||||
/// <param name="resource">Deserialized resource.</param>
|
||||
internal EncryptionTransactionalBatchOperationResult(T resource)
|
||||
{
|
||||
this.Resource = resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content of the resource.
|
||||
/// </summary>
|
||||
public override T Resource { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
internal sealed class EncryptionTransactionalBatchResponse : TransactionalBatchResponse
|
||||
{
|
||||
private readonly IReadOnlyList<TransactionalBatchOperationResult> results;
|
||||
private readonly TransactionalBatchResponse response;
|
||||
private readonly CosmosSerializer cosmosSerializer;
|
||||
private bool isDisposed = false;
|
||||
|
||||
public EncryptionTransactionalBatchResponse(
|
||||
IReadOnlyList<TransactionalBatchOperationResult> results,
|
||||
TransactionalBatchResponse response,
|
||||
CosmosSerializer cosmosSerializer)
|
||||
{
|
||||
this.results = results;
|
||||
this.response = response;
|
||||
this.cosmosSerializer = cosmosSerializer;
|
||||
}
|
||||
|
||||
public override TransactionalBatchOperationResult this[int index] => this.results[index];
|
||||
|
||||
public override TransactionalBatchOperationResult<T> GetOperationResultAtIndex<T>(int index)
|
||||
{
|
||||
TransactionalBatchOperationResult result = this.results[index];
|
||||
|
||||
T resource = default;
|
||||
if (result.ResourceStream != null)
|
||||
{
|
||||
resource = this.cosmosSerializer.FromStream<T>(result.ResourceStream);
|
||||
}
|
||||
|
||||
return new EncryptionTransactionalBatchOperationResult<T>(resource);
|
||||
}
|
||||
|
||||
public override IEnumerator<TransactionalBatchOperationResult> GetEnumerator()
|
||||
{
|
||||
return this.results.GetEnumerator();
|
||||
}
|
||||
|
||||
public override Headers Headers => this.response.Headers;
|
||||
|
||||
public override string ActivityId => this.response.ActivityId;
|
||||
|
||||
public override double RequestCharge => this.response.RequestCharge;
|
||||
|
||||
public override TimeSpan? RetryAfter => this.response.RetryAfter;
|
||||
|
||||
public override HttpStatusCode StatusCode => this.response.StatusCode;
|
||||
|
||||
public override string ErrorMessage => this.response.ErrorMessage;
|
||||
|
||||
public override bool IsSuccessStatusCode => this.response.IsSuccessStatusCode;
|
||||
|
||||
public override int Count => this.results?.Count ?? 0;
|
||||
|
||||
public override CosmosDiagnostics Diagnostics => this.response.Diagnostics;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && !this.isDisposed)
|
||||
{
|
||||
this.isDisposed = true;
|
||||
|
||||
if (this.response != null)
|
||||
{
|
||||
this.response.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyName>Microsoft.Azure.Cosmos.Encryption.Custom</AssemblyName>
|
||||
<RootNamespace>Microsoft.Azure.Cosmos.Encryption.Custom</RootNamespace>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
<IsPreview>true</IsPreview>
|
||||
|
||||
<CurrentDate>$([System.DateTime]::Now.ToString(yyyyMMdd))</CurrentDate>
|
||||
<Version>$(CustomEncryptionVersion)</Version>
|
||||
<Company>Microsoft Corporation</Company>
|
||||
<Authors>Microsoft</Authors>
|
||||
<Description>This is an internal library that provides an implementation for client-side encryption for Azure Cosmos DB's SQL API for multi-tenant use case. For more information, refer to https://aka.ms/CosmosCustomClientEncryption</Description>
|
||||
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
|
||||
<Title>Microsoft Azure Cosmos DB client-side encryption library for multi-tenant</Title>
|
||||
<PackageId>Microsoft.Azure.Cosmos.Encryption.Custom</PackageId>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
<PackageLicenseUrl>https://aka.ms/netcoregaeula</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/Azure/azure-cosmos-dotnet-v3</PackageProjectUrl>
|
||||
<PackageIconUrl>http://go.microsoft.com/fwlink/?LinkID=288890</PackageIconUrl>
|
||||
<PackageTags>microsoft;azure;cosmos;cosmosdb;documentdb;docdb;nosql;azureofficial;dotnetcore;netcore;netstandard;client;encryption;byok</PackageTags>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="..\..\Microsoft.Azure.Cosmos\src\stylecop.json" Link="stylecop.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(SdkProjectRef)' != 'True' ">
|
||||
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.20.0-preview" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(SdkProjectRef)' == 'True' ">
|
||||
<ProjectReference Include="..\..\Microsoft.Azure.Cosmos\src\Microsoft.Azure.Cosmos.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Core" Version="1.3.0" />
|
||||
<PackageReference Include="Azure.Identity" Version="1.1.1" />
|
||||
<PackageReference Include="Microsoft.Data.Encryption.Cryptography" Version="0.2.0-pre" />
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="16.0.102" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net46'">
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SigningType>Product</SigningType>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>..\..\35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(SdkProjectRef)' == 'True' ">
|
||||
<DefineConstants>$(DefineConstants);SDKPROJECTREF</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -0,0 +1,12 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
internal static class AssemblyKeys
|
||||
{
|
||||
/// <summary>TestPublicKey is an unsupported strong key for testing and internal use only</summary>
|
||||
internal const string TestPublicKey = ", PublicKey=0024000004800000940000000602000000240000525341310004000001000100197c25d0a04f73cb271e8181dba1c0c713df8deebb25864541a66670500f34896d280484b45fe1ff6c29f2ee7aa175d8bcbd0c83cc23901a894a86996030f6292ce6eda6e6f3e6c74b3c5a3ded4903c951e6747e6102969503360f7781bf8bf015058eb89b7621798ccc85aaca036ff1bc1556bb7f62de15908484886aa8bbae";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Cache which supports asynchronous value initialization.
|
||||
/// It ensures that for given key only single inintialization funtion is running at any point in time.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">Type of keys.</typeparam>
|
||||
/// <typeparam name="TValue">Type of values.</typeparam>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD105:Avoid method overloads that assume TaskScheduler.Current", Justification = "Mirrored file.")]
|
||||
internal sealed class AsyncCache<TKey, TValue>
|
||||
{
|
||||
private readonly IEqualityComparer<TValue> valueEqualityComparer;
|
||||
private readonly IEqualityComparer<TKey> keyEqualityComparer;
|
||||
|
||||
private ConcurrentDictionary<TKey, AsyncLazy<TValue>> values;
|
||||
|
||||
public AsyncCache(IEqualityComparer<TValue> valueEqualityComparer, IEqualityComparer<TKey> keyEqualityComparer = null)
|
||||
{
|
||||
this.keyEqualityComparer = keyEqualityComparer ?? EqualityComparer<TKey>.Default;
|
||||
this.values = new ConcurrentDictionary<TKey, AsyncLazy<TValue>>(this.keyEqualityComparer);
|
||||
this.valueEqualityComparer = valueEqualityComparer;
|
||||
}
|
||||
|
||||
public AsyncCache()
|
||||
: this(EqualityComparer<TValue>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys => this.values.Keys;
|
||||
|
||||
public void Set(TKey key, TValue value)
|
||||
{
|
||||
AsyncLazy<TValue> lazyValue = new AsyncLazy<TValue>(value);
|
||||
|
||||
// Access it to mark as created+completed, so that further calls to getasync do not overwrite.
|
||||
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
|
||||
TValue x = lazyValue.Value.Result;
|
||||
#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits
|
||||
|
||||
this.values.AddOrUpdate(key, lazyValue, (k, existingValue) =>
|
||||
{
|
||||
// Observe all exceptions thrown for existingValue.
|
||||
if (existingValue.IsValueCreated)
|
||||
{
|
||||
Task unused = existingValue.Value.ContinueWith(c => c.Exception, TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
|
||||
return lazyValue;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Gets value corresponding to <paramref name="key"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If another initialization function is already running, new initialization function will not be started.
|
||||
/// The result will be result of currently running initialization function.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If previous initialization function is successfully completed - value returned by it will be returned unless
|
||||
/// it is equal to <paramref name="obsoleteValue"/>, in which case new initialization function will be started.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If previous initialization function failed - new one will be launched.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="key">Key for which to get a value.</param>
|
||||
/// <param name="obsoleteValue">Value which is obsolete and needs to be refreshed.</param>
|
||||
/// <param name="singleValueInitFunc">Initialization function.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <param name="forceRefresh">Skip cached value and generate new value.</param>
|
||||
/// <returns>Cached value or value returned by initialization function.</returns>
|
||||
public async Task<TValue> GetAsync(
|
||||
TKey key,
|
||||
TValue obsoleteValue,
|
||||
Func<Task<TValue>> singleValueInitFunc,
|
||||
CancellationToken cancellationToken,
|
||||
bool forceRefresh = false)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (this.values.TryGetValue(key, out AsyncLazy<TValue> initialLazyValue))
|
||||
{
|
||||
// If we haven't computed the value or we're currently computing it, then return it...
|
||||
if (!initialLazyValue.IsValueCreated || !initialLazyValue.Value.IsCompleted)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await initialLazyValue.Value;
|
||||
}
|
||||
|
||||
// It does not matter to us if this instance of the task throws - the lambda that failed was provided by a different caller.
|
||||
// The exception that we see here will be handled/logged by whatever caller provided the failing lambda, if any. Our part is catching and observing it.
|
||||
// As such, we discard this exception and will retry with our own lambda below, for which we will let exception bubble up.
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// Don't check Task if there's an exception or it's been canceled. Accessing Task.Exception marks it as observed, which we want.
|
||||
else if (initialLazyValue.Value.Exception == null && !initialLazyValue.Value.IsCanceled)
|
||||
{
|
||||
TValue cachedValue = await initialLazyValue.Value;
|
||||
|
||||
// If not forcing refresh or obsolete value, use cached value.
|
||||
if (!forceRefresh && !this.valueEqualityComparer.Equals(cachedValue, obsoleteValue))
|
||||
{
|
||||
return cachedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsyncLazy<TValue> newLazyValue = new AsyncLazy<TValue>(singleValueInitFunc, cancellationToken);
|
||||
|
||||
// Update the new task in the cache - compare-and-swap style.
|
||||
AsyncLazy<TValue> actualValue = this.values.AddOrUpdate(
|
||||
key,
|
||||
newLazyValue,
|
||||
(existingKey, existingValue) => object.ReferenceEquals(existingValue, initialLazyValue) ? newLazyValue : existingValue);
|
||||
|
||||
// Task starts running here.
|
||||
Task<TValue> generator = actualValue.Value;
|
||||
|
||||
// Even if the current thread goes away, all exceptions will be observed.
|
||||
Task unused = generator.ContinueWith(c => c.Exception, TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
return await generator;
|
||||
}
|
||||
|
||||
public void Remove(TKey key)
|
||||
{
|
||||
if (this.values.TryRemove(key, out AsyncLazy<TValue> initialLazyValue) && initialLazyValue.IsValueCreated)
|
||||
{
|
||||
// Observe all exceptions thrown.
|
||||
Task unused = initialLazyValue.Value.ContinueWith(c => c.Exception, TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryRemoveIfCompleted(TKey key)
|
||||
{
|
||||
if (this.values.TryGetValue(key, out AsyncLazy<TValue> initialLazyValue) && initialLazyValue.IsValueCreated && initialLazyValue.Value.IsCompleted)
|
||||
{
|
||||
// Accessing Exception marks as observed.
|
||||
_ = initialLazyValue.Value.Exception;
|
||||
|
||||
// This is a nice trick to do "atomic remove if value not changed".
|
||||
// ConcurrentDictionary inherits from ICollection<KVP<..>>, which allows removal of specific key value pair, instead of removal just by key.
|
||||
ICollection<KeyValuePair<TKey, AsyncLazy<TValue>>> valuesAsCollection = this.values as ICollection<KeyValuePair<TKey, AsyncLazy<TValue>>>;
|
||||
Debug.Assert(valuesAsCollection != null, "Values collection expected to implement ICollection<KVP<TKey, AsyncLazy<TValue>>.");
|
||||
return valuesAsCollection?.Remove(new KeyValuePair<TKey, AsyncLazy<TValue>>(key, initialLazyValue)) ?? false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove value from cache and return it if present.
|
||||
/// </summary>
|
||||
/// <param name="key"> Key </param>
|
||||
/// <returns>Value if present, default value if not present.</returns>
|
||||
public async Task<TValue> RemoveAsync(TKey key)
|
||||
{
|
||||
if (this.values.TryRemove(key, out AsyncLazy<TValue> initialLazyValue))
|
||||
{
|
||||
try
|
||||
{
|
||||
return await initialLazyValue.Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ConcurrentDictionary<TKey, AsyncLazy<TValue>> newValues = new ConcurrentDictionary<TKey, AsyncLazy<TValue>>(this.keyEqualityComparer);
|
||||
ConcurrentDictionary<TKey, AsyncLazy<TValue>> oldValues = Interlocked.Exchange(ref this.values, newValues);
|
||||
|
||||
// Ensure all tasks are observed.
|
||||
foreach (AsyncLazy<TValue> value in oldValues.Values)
|
||||
{
|
||||
if (value.IsValueCreated)
|
||||
{
|
||||
Task unused = value.Value.ContinueWith(c => c.Exception, TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
}
|
||||
|
||||
oldValues.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a background task that will started refreshing the cached value for a given key.
|
||||
/// This observes the same logic as GetAsync - a running value will still take precedence over a call to this.
|
||||
/// </summary>
|
||||
/// <param name="key">Key.</param>
|
||||
/// <param name="singleValueInitFunc">Generator function.</param>
|
||||
public void BackgroundRefreshNonBlocking(TKey key, Func<Task<TValue>> singleValueInitFunc)
|
||||
{
|
||||
// Trigger background refresh of cached value.
|
||||
// Fire and forget.
|
||||
Task unused = Task.Factory.StartNewOnCurrentTaskSchedulerAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we don't have a value, or we have one that has completed running (i.e. if a value is currently being generated, we do nothing).
|
||||
if (!this.values.TryGetValue(key, out AsyncLazy<TValue> initialLazyValue) || (initialLazyValue.IsValueCreated && initialLazyValue.Value.IsCompleted))
|
||||
{
|
||||
// Use GetAsync to trigger the generation of a value.
|
||||
await this.GetAsync(
|
||||
key,
|
||||
default, // obsolete value unused since forceRefresh: true
|
||||
singleValueInitFunc,
|
||||
CancellationToken.None,
|
||||
forceRefresh: true);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Observe all exceptions.
|
||||
}
|
||||
}).Unwrap();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
internal sealed class AsyncLazy<T> : Lazy<Task<T>>
|
||||
{
|
||||
public AsyncLazy(T value)
|
||||
: base(() => Task.FromResult(value))
|
||||
{
|
||||
}
|
||||
|
||||
public AsyncLazy(Func<T> valueFactory, CancellationToken cancellationToken)
|
||||
: base(() => Task.Factory.StartNewOnCurrentTaskSchedulerAsync(valueFactory, cancellationToken)) // Task.Factory.StartNew() allows specifying task scheduler to use which is critical for compute gateway to track physical consumption.
|
||||
{
|
||||
}
|
||||
|
||||
public AsyncLazy(Func<Task<T>> taskFactory, CancellationToken cancellationToken)
|
||||
: base(() => Task.Factory.StartNewOnCurrentTaskSchedulerAsync(taskFactory, cancellationToken).Unwrap()) // Task.Factory.StartNew() allows specifying task scheduler to use which is critical for compute gateway to track physical consumption.
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.ExceptionServices;
|
|
@ -0,0 +1,42 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions to task factory methods that are meant to be used as patterns of invocation of asynchronous operations
|
||||
/// inside compute gateway ensuring continuity of execution on the current task scheduler.
|
||||
/// Task scheduler is used to track resource consumption per tenant so it is critical that all async activity
|
||||
/// pertaining to the tenant runs on the same task scheduler.
|
||||
/// </summary>
|
||||
internal static class TaskFactoryExtensions
|
||||
{
|
||||
/// <summary>Creates and starts a <see cref="System.Threading.Tasks.Task{TResult}" /> on the current task scheduler.</summary>
|
||||
/// <returns>The started <see cref="System.Threading.Tasks.Task{TResult}" />.</returns>
|
||||
/// <param name="taskFactory">Instance of the <see cref="System.Threading.Tasks.TaskFactory" /> to use for starting the task.</param>
|
||||
/// <param name="function">A function delegate that returns the future result to be available through the <see cref="System.Threading.Tasks.Task{TResult}" />.</param>
|
||||
/// <typeparam name="TResult">The type of the result available through the <see cref="System.Threading.Tasks.Task{TResult}" />.</typeparam>
|
||||
/// <exception cref="System.ArgumentNullException">The exception that is thrown when the <paramref name="function" /> argument is null.</exception>
|
||||
public static Task<TResult> StartNewOnCurrentTaskSchedulerAsync<TResult>(this TaskFactory taskFactory, Func<TResult> function)
|
||||
{
|
||||
return taskFactory.StartNew(function, default, TaskCreationOptions.None, TaskScheduler.Current);
|
||||
}
|
||||
|
||||
/// <summary>Creates and starts a <see cref="System.Threading.Tasks.Task{TResult}" /> on the current task scheduler.</summary>
|
||||
/// <returns>The started <see cref="System.Threading.Tasks.Task{TResult}" />.</returns>
|
||||
/// <param name="taskFactory">Instance of the <see cref="System.Threading.Tasks.TaskFactory" /> to use for starting the task.</param>
|
||||
/// <param name="function">A function delegate that returns the future result to be available through the <see cref="System.Threading.Tasks.Task{TResult}" />.</param>
|
||||
/// <param name="cancellationToken">The <see cref="System.Threading.Tasks.TaskFactory.CancellationToken" /> that will be assigned to the new task.</param>
|
||||
/// <typeparam name="TResult">The type of the result available through the <see cref="System.Threading.Tasks.Task{TResult}" />.</typeparam>
|
||||
/// <exception cref="System.ArgumentNullException">The exception that is thrown when the <paramref name="function" /> argument is null.</exception>
|
||||
public static Task<TResult> StartNewOnCurrentTaskSchedulerAsync<TResult>(this TaskFactory taskFactory, Func<TResult> function, CancellationToken cancellationToken)
|
||||
{
|
||||
return taskFactory.StartNew(function, cancellationToken, TaskCreationOptions.None, TaskScheduler.Current);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
namespace Microsoft.Azure.Cosmos.Encryption
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
|
@ -1,6 +1,7 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
{
|
||||
using System;
|
|
@ -12,8 +12,6 @@ namespace Microsoft.Azure.Cosmos.Encryption.EmulatorTests
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using global::Azure.Core;
|
||||
using global::Azure.Identity;
|
||||
using Microsoft.Azure.Cosmos;
|
||||
using Microsoft.Azure.Cosmos.Encryption.Custom;
|
||||
using Microsoft.Azure.Cosmos.Scripts;
|
|
@ -0,0 +1,74 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Platform>AnyCPU</Platform>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RootNamespace>Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests</RootNamespace>
|
||||
<AssemblyName>Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests</AssemblyName>
|
||||
<IsEmulatorTest>true</IsEmulatorTest>
|
||||
<EmulatorFlavor>master</EmulatorFlavor>
|
||||
<DisableCopyEmulator>True</DisableCopyEmulator>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.7" />
|
||||
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
|
||||
<PackageReference Include="System.Security.SecureString" Version="4.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="Moq" Version="4.8.2" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.8.11" />
|
||||
<PackageReference Include="System.Reflection" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\..\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.EmulatorTests\Utils\ConfigurationManager.cs" Link="Utils\ConfigurationManager.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\..\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.EmulatorTests\settings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net46'">
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.Azure.Cosmos.Encryption.Custom.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>..\..\..\testkey.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(SdkProjectRef)' == 'True' ">
|
||||
<DefineConstants>$(DefineConstants);SDKPROJECTREF</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -0,0 +1,88 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.EmulatorTests
|
||||
{
|
||||
using Microsoft.Azure.Cosmos.Fluent;
|
||||
using Microsoft.Azure.Cosmos.Utils;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
internal static class TestCommon
|
||||
{
|
||||
internal static Stream ToStream<T>(T input)
|
||||
{
|
||||
string s = JsonConvert.SerializeObject(input);
|
||||
return new MemoryStream(Encoding.UTF8.GetBytes(s));
|
||||
}
|
||||
|
||||
internal static T FromStream<T>(Stream stream)
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(stream))
|
||||
using (JsonReader reader = new JsonTextReader(sr))
|
||||
{
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
return serializer.Deserialize<T>(reader);
|
||||
}
|
||||
}
|
||||
|
||||
internal static MemoryStream GenerateStreamFromString(string s)
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
StreamWriter writer = new StreamWriter(stream);
|
||||
writer.Write(s);
|
||||
writer.Flush();
|
||||
stream.Position = 0;
|
||||
return stream;
|
||||
}
|
||||
|
||||
private static (string endpoint, string authKey) GetAccountInfo()
|
||||
{
|
||||
string authKey = ConfigurationManager.AppSettings["MasterKey"];
|
||||
string endpoint = ConfigurationManager.AppSettings["GatewayEndpoint"];
|
||||
|
||||
return (endpoint, authKey);
|
||||
}
|
||||
|
||||
internal static CosmosClientBuilder GetClientBuilder(string resourceToken)
|
||||
{
|
||||
(string endpoint, string authKey) accountInfo = TestCommon.GetAccountInfo();
|
||||
CosmosClientBuilder clientBuilder = new CosmosClientBuilder(
|
||||
accountEndpoint: accountInfo.endpoint,
|
||||
authKeyOrResourceToken: resourceToken ?? accountInfo.authKey);
|
||||
|
||||
return clientBuilder;
|
||||
}
|
||||
|
||||
internal static CosmosClient CreateCosmosClient(Action<CosmosClientBuilder> customizeClientBuilder = null)
|
||||
{
|
||||
CosmosClientBuilder cosmosClientBuilder = GetClientBuilder(resourceToken: null);
|
||||
customizeClientBuilder?.Invoke(cosmosClientBuilder);
|
||||
|
||||
return cosmosClientBuilder.Build();
|
||||
}
|
||||
|
||||
internal static CosmosClient CreateCosmosClient(string resourceToken, Action<CosmosClientBuilder> customizeClientBuilder = null)
|
||||
{
|
||||
CosmosClientBuilder cosmosClientBuilder = GetClientBuilder(resourceToken);
|
||||
customizeClientBuilder?.Invoke(cosmosClientBuilder);
|
||||
|
||||
return cosmosClientBuilder.Build();
|
||||
}
|
||||
|
||||
internal static CosmosClient CreateCosmosClient(
|
||||
bool useGateway)
|
||||
{
|
||||
CosmosClientBuilder cosmosClientBuilder = GetClientBuilder(resourceToken: null);
|
||||
if (useGateway)
|
||||
{
|
||||
cosmosClientBuilder.WithConnectionModeGateway();
|
||||
}
|
||||
|
||||
return cosmosClientBuilder.Build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
namespace Microsoft.Azure.Cosmos.Encryption.Tests
|
||||
{
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
[TestCategory("Windows")]
|
||||
[TestCategory("UpdateContract")]
|
||||
[TestClass]
|
||||
public class ContractEnforcementTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void ContractChanges()
|
||||
{
|
||||
Cosmos.Tests.Contracts.ContractEnforcement.ValidateContractContainBreakingChanges(
|
||||
dllName: "Microsoft.Azure.Cosmos.Encryption.Custom",
|
||||
baselinePath: "DotNetSDKEncryptionCustomAPI.json",
|
||||
breakingChangesPath: "DotNetSDKEncryptionCustomAPIChanges.json");
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,59 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Platform>AnyCPU</Platform>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RootNamespace>Microsoft.Azure.Cosmos.Encryption.Tests</RootNamespace>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\..\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Contracts\ContractEnforcement.cs" Link="Contracts\ContractEnforcement.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="Moq" Version="4.8.3" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Contracts\DotNetSDKEncryptionCustomAPI.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net46'">
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.Azure.Cosmos.Encryption.Custom.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>..\..\..\testkey.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,123 @@
|
|||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Encryption.Tests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
internal static class TestCommon
|
||||
{
|
||||
internal static byte[] GenerateRandomByteArray()
|
||||
{
|
||||
Random random = new Random();
|
||||
byte[] b = new byte[10];
|
||||
random.NextBytes(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
internal static byte[] EncryptData(byte[] plainText)
|
||||
{
|
||||
return plainText.Select(b => (byte)(b + 1)).ToArray();
|
||||
}
|
||||
|
||||
internal static byte[] DecryptData(byte[] cipherText)
|
||||
{
|
||||
return cipherText.Select(b => (byte)(b - 1)).ToArray();
|
||||
}
|
||||
|
||||
internal static Stream ToStream<T>(T input)
|
||||
{
|
||||
string s = JsonConvert.SerializeObject(input);
|
||||
return new MemoryStream(Encoding.UTF8.GetBytes(s));
|
||||
}
|
||||
|
||||
internal static T FromStream<T>(Stream stream)
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(stream))
|
||||
using (JsonReader reader = new JsonTextReader(sr))
|
||||
{
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
return serializer.Deserialize<T>(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private static JObject ParseStream(Stream stream)
|
||||
{
|
||||
return JObject.Load(new JsonTextReader(new StreamReader(stream)));
|
||||
}
|
||||
|
||||
internal class TestDoc
|
||||
{
|
||||
public static List<string> PathsToEncrypt { get; } = new List<string>() { "/SensitiveStr", "/SensitiveInt" };
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string PK { get; set; }
|
||||
|
||||
public string NonSensitive { get; set; }
|
||||
|
||||
public string SensitiveStr { get; set; }
|
||||
|
||||
public int SensitiveInt { get; set; }
|
||||
|
||||
public TestDoc()
|
||||
{
|
||||
}
|
||||
|
||||
public TestDoc(TestDoc other)
|
||||
{
|
||||
this.Id = other.Id;
|
||||
this.PK = other.PK;
|
||||
this.NonSensitive = other.NonSensitive;
|
||||
this.SensitiveStr = other.SensitiveStr;
|
||||
this.SensitiveInt = other.SensitiveInt;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is TestDoc doc
|
||||
&& this.Id == doc.Id
|
||||
&& this.PK == doc.PK
|
||||
&& this.NonSensitive == doc.NonSensitive
|
||||
&& this.SensitiveInt == doc.SensitiveInt
|
||||
&& this.SensitiveStr == this.SensitiveStr;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hashCode = 1652434776;
|
||||
hashCode = (hashCode * -1521134295) + EqualityComparer<string>.Default.GetHashCode(this.Id);
|
||||
hashCode = (hashCode * -1521134295) + EqualityComparer<string>.Default.GetHashCode(this.PK);
|
||||
hashCode = (hashCode * -1521134295) + EqualityComparer<string>.Default.GetHashCode(this.NonSensitive);
|
||||
hashCode = (hashCode * -1521134295) + EqualityComparer<string>.Default.GetHashCode(this.SensitiveStr);
|
||||
hashCode = (hashCode * -1521134295) + EqualityComparer<int>.Default.GetHashCode(this.SensitiveInt);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public static TestDoc Create(string partitionKey = null)
|
||||
{
|
||||
return new TestDoc()
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
PK = partitionKey ?? Guid.NewGuid().ToString(),
|
||||
NonSensitive = Guid.NewGuid().ToString(),
|
||||
SensitiveStr = Guid.NewGuid().ToString(),
|
||||
SensitiveInt = new Random().Next()
|
||||
};
|
||||
}
|
||||
|
||||
public Stream ToStream()
|
||||
{
|
||||
return TestCommon.ToStream(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
//------------------------------------------------------------
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyName>Microsoft.Azure.Cosmos.Encryption</AssemblyName>
|
||||
<RootNamespace>Microsoft.Azure.Cosmos.Encryption</RootNamespace>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
<IsPreview>true</IsPreview>
|
||||
|
||||
<CurrentDate>$([System.DateTime]::Now.ToString(yyyyMMdd))</CurrentDate>
|
||||
|
@ -44,10 +44,6 @@
|
|||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="16.0.102" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Custom\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RootNamespace>Microsoft.Azure.Cosmos</RootNamespace>
|
||||
<RootNamespace>Microsoft.Azure.Cosmos.Encryption.EmulatorTests</RootNamespace>
|
||||
<AssemblyName>Microsoft.Azure.Cosmos.Encryption.EmulatorTests</AssemblyName>
|
||||
<IsEmulatorTest>true</IsEmulatorTest>
|
||||
<EmulatorFlavor>master</EmulatorFlavor>
|
||||
<DisableCopyEmulator>True</DisableCopyEmulator>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,7 +8,7 @@
|
|||
<IsPackable>false</IsPackable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RootNamespace>Microsoft.Azure.Cosmos.Encryption.Tests</RootNamespace>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -24,6 +24,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Encryption.Custom", "Encryption.Custom", "{51F858D8-707E-4F21-BCC6-4D6123832E4F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Encryption.Custom", "Microsoft.Azure.Cosmos.Encryption.Custom\src\Microsoft.Azure.Cosmos.Encryption.Custom.csproj", "{D7C78D76-A740-4129-BAAE-894640F95D74}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests", "Microsoft.Azure.Cosmos.Encryption.Custom\tests\EmulatorTests\Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests.csproj", "{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Encryption.Custom.Tests", "Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj", "{B5B3631D-AC2F-4257-855D-D6FE12F20B60}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Cover|Any CPU = Cover|Any CPU
|
||||
|
@ -118,6 +126,42 @@ Global
|
|||
{0535FD1C-D1A0-435A-8F8C-8CF588DF262F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0535FD1C-D1A0-435A-8F8C-8CF588DF262F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0535FD1C-D1A0-435A-8F8C-8CF588DF262F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Cover|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Cover|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Cover|x64.ActiveCfg = Debug|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Cover|x64.Build.0 = Debug|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Cover|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Cover|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Cover|x64.ActiveCfg = Debug|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Cover|x64.Build.0 = Debug|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Cover|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Cover|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Cover|x64.ActiveCfg = Debug|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Cover|x64.Build.0 = Debug|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -126,6 +170,9 @@ Global
|
|||
{4D0FBC91-268E-44DD-AC10-6851A69C52FB} = {B382B965-7D1B-4DE8-8007-197741C49F88}
|
||||
{D6A405BF-15E1-46D3-8C13-210BA043ADC5} = {B382B965-7D1B-4DE8-8007-197741C49F88}
|
||||
{0535FD1C-D1A0-435A-8F8C-8CF588DF262F} = {B382B965-7D1B-4DE8-8007-197741C49F88}
|
||||
{D7C78D76-A740-4129-BAAE-894640F95D74} = {51F858D8-707E-4F21-BCC6-4D6123832E4F}
|
||||
{F87719DB-BB52-4B12-9D9C-F6AE30BAB3D7} = {51F858D8-707E-4F21-BCC6-4D6123832E4F}
|
||||
{B5B3631D-AC2F-4257-855D-D6FE12F20B60} = {51F858D8-707E-4F21-BCC6-4D6123832E4F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C6A1D820-CB03-4DE6-87D1-46EF476F0040}
|
||||
|
|
|
@ -53,3 +53,14 @@ if(!(Test-Path -Path $updatedContractFile)){
|
|||
Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\Contracts\DotNetSDKEncryptionAPI.json"
|
||||
Write-Output ("Updated contract " + $updatedContractFile)
|
||||
}
|
||||
|
||||
#Run the Encryption.Custom SDK contract tests
|
||||
$projResult = dotnet test '.\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release
|
||||
|
||||
$updatedContractFile = ".\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\bin\Release\netcoreapp3.1\Contracts\DotNetSDKEncryptionCustomAPIChanges.json"
|
||||
if(!(Test-Path -Path $updatedContractFile)){
|
||||
Write-Error ("The contract file did not get updated with the build. Please fix the test to output the contract file: " + $updatedContractFile)
|
||||
}else{
|
||||
Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\Contracts\DotNetSDKEncryptionCustomAPI.json"
|
||||
Write-Output ("Updated contract " + $updatedContractFile)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
trigger: none
|
||||
|
||||
pr: none
|
||||
|
||||
variables:
|
||||
ReleaseArguments: ' --filter "TestCategory!=Quarantine" --verbosity normal '
|
||||
VmImage: windows-latest # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops
|
||||
BuildConfiguration: Release
|
||||
|
||||
stages:
|
||||
- stage:
|
||||
displayName: Publish
|
||||
jobs:
|
||||
- job:
|
||||
pool:
|
||||
vmImage: $(VmImage)
|
||||
|
||||
steps:
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Build Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
inputs:
|
||||
command: build
|
||||
configuration: $(BuildConfiguration)
|
||||
nugetConfigPath: NuGet.config
|
||||
projects: Microsoft.Azure.Cosmos.Encryption.Custom/src/Microsoft.Azure.Cosmos.Encryption.Custom.csproj
|
||||
arguments: --configuration $(BuildConfiguration) -p:Optimize=true
|
||||
versioningScheme: OFF
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Create Encryption.Custom SDK NuGet Package'
|
||||
inputs:
|
||||
command: custom
|
||||
projects: 'Microsoft.Azure.Cosmos.Encryption.Custom\src\Microsoft.Azure.Cosmos.Encryption.Custom.csproj'
|
||||
custom: pack
|
||||
arguments: '-v detailed -c $(BuildConfiguration) --no-build --no-restore -o "$(Build.ArtifactStagingDirectory)\bin\AnyCPU\$(BuildConfiguration)\Microsoft.Azure.Cosmos.Encryption.Custom"'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Create Encryption.Custom SDK NuGet Symbols Package'
|
||||
inputs:
|
||||
command: custom
|
||||
projects: 'Microsoft.Azure.Cosmos.Encryption.Custom\src\Microsoft.Azure.Cosmos.Encryption.Custom.csproj'
|
||||
custom: pack
|
||||
arguments: '-v detailed -c $(BuildConfiguration) --no-build --include-symbols /p:SymbolPackageFormat=snupkg --no-restore -o "$(Build.ArtifactStagingDirectory)\bin\AnyCPU\$(BuildConfiguration)\Microsoft.Azure.Cosmos.Encryption.Custom"'
|
||||
|
||||
- task: AzureFileCopy@2
|
||||
displayName: ' Copy Artifacts to Azure SDK Release blob storage'
|
||||
condition: and(succeeded(),ne(variables['BlobVersion'], ''))
|
||||
inputs:
|
||||
SourcePath: '$(Build.ArtifactStagingDirectory)/bin/AnyCPU/$(BuildConfiguration)/Microsoft.Azure.Cosmos.Encryption.Custom'
|
||||
azureSubscription: azuresdkpartnerdrops
|
||||
Destination: AzureBlob
|
||||
storage: azuresdkpartnerdrops
|
||||
ContainerName: 'drops'
|
||||
BlobPrefix: 'cosmosdb/csharp/encryption.custom/$(BlobVersion)'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifacts: Microsoft.Azure.Cosmos.Encryption.Custom'
|
||||
inputs:
|
||||
artifactName: Microsoft.Azure.Cosmos.Encryption.Custom
|
||||
#
|
|
@ -46,6 +46,25 @@ jobs:
|
|||
arguments: -p:Optimize=true -p:IsPreview=true;SdkProjectRef=true
|
||||
versioningScheme: OFF
|
||||
|
||||
- job:
|
||||
displayName: Encryption.Custom Project Ref SDK Preview ${{ parameters.BuildConfiguration }}
|
||||
pool:
|
||||
vmImage: ${{ parameters.VmImage }}
|
||||
|
||||
steps:
|
||||
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
|
||||
clean: true # if true, execute `execute git clean -ffdx && git reset --hard HEAD` before fetching
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Build Microsoft.Azure.Cosmos.Encryption.Custom Project Ref
|
||||
inputs:
|
||||
command: build
|
||||
configuration: $(parameters.BuildConfiguration)
|
||||
nugetConfigPath: NuGet.config
|
||||
projects: Microsoft.Azure.Cosmos.sln
|
||||
arguments: -p:Optimize=true -p:IsPreview=true;SdkProjectRef=true
|
||||
versioningScheme: OFF
|
||||
|
||||
- job:
|
||||
displayName: Preview Tests ${{ parameters.BuildConfiguration }}
|
||||
pool:
|
||||
|
|
|
@ -111,3 +111,27 @@ jobs:
|
|||
nugetConfigPath: NuGet.config
|
||||
publishTestResults: true
|
||||
testRunTitle: Microsoft.Azure.Cosmos.Encryption.EmulatorTests
|
||||
|
||||
- job:
|
||||
displayName: Encryption.Custom EmulatorTests ${{ parameters.BuildConfiguration }}
|
||||
timeoutInMinutes: 90
|
||||
condition: and(succeeded(), eq('${{ parameters.OS }}', 'Windows'))
|
||||
pool:
|
||||
vmImage: ${{ parameters.VmImage }}
|
||||
|
||||
steps:
|
||||
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
|
||||
clean: true # if true, execute `execute git clean -ffdx && git reset --hard HEAD` before fetching
|
||||
|
||||
- template: emulator-setup.yml
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests
|
||||
condition: succeeded()
|
||||
inputs:
|
||||
command: test
|
||||
projects: 'Microsoft.Azure.Cosmos.Encryption.Custom/tests/EmulatorTests/*.csproj'
|
||||
arguments: ${{ parameters.Arguments }} --configuration ${{ parameters.BuildConfiguration }} /p:OS=${{ parameters.OS }}
|
||||
nugetConfigPath: NuGet.config
|
||||
publishTestResults: true
|
||||
testRunTitle: Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests
|
||||
|
|
Загрузка…
Ссылка в новой задаче