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:
anujtoshniwal 2021-09-16 17:48:15 +05:30 коммит произвёл GitHub
Родитель 96474e3ae4
Коммит 849dad4bc7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
94 изменённых файлов: 2390 добавлений и 1096 удалений

Просмотреть файл

@ -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