Use TokenCredential for Cosmos tests (#33956)

This commit is contained in:
Andriy Svyryd 2024-06-11 13:44:45 -07:00 коммит произвёл GitHub
Родитель 22aec2c45d
Коммит dd9bc368bf
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
14 изменённых файлов: 178 добавлений и 30 удалений

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

@ -6,6 +6,7 @@ MinimumVisualStudioVersion = 17.0.31521.260
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B9E4CC99-199C-4E3B-9EC5-D1FDFCD6C27B}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
azure-pipelines-public.yml = azure-pipelines-public.yml
azure-pipelines.yml = azure-pipelines.yml
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
@ -154,10 +155,6 @@ Global
{4F7C93F3-A30F-4061-804C-32293DC256A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F7C93F3-A30F-4061-804C-32293DC256A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F7C93F3-A30F-4061-804C-32293DC256A1}.Release|Any CPU.Build.0 = Release|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Debug|Any CPU.Build.0 = Debug|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Release|Any CPU.ActiveCfg = Release|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Release|Any CPU.Build.0 = Release|Any CPU
{715C38E9-B2F5-4DB2-8025-0C6492DEBDD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{715C38E9-B2F5-4DB2-8025-0C6492DEBDD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{715C38E9-B2F5-4DB2-8025-0C6492DEBDD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -370,6 +367,10 @@ Global
{2AC6A8AC-5C0A-422A-B21A-CDC8D75F20A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2AC6A8AC-5C0A-422A-B21A-CDC8D75F20A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2AC6A8AC-5C0A-422A-B21A-CDC8D75F20A3}.Release|Any CPU.Build.0 = Release|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Debug|Any CPU.Build.0 = Debug|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Release|Any CPU.ActiveCfg = Release|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -377,7 +378,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{2D66A1DA-D102-4DD9-960B-7D863BBB53DE} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{4F7C93F3-A30F-4061-804C-32293DC256A1} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{711EE8F3-F92D-4470-8B0B-25D8B13EF282} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{715C38E9-B2F5-4DB2-8025-0C6492DEBDD4} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{11B51A41-47CB-4EDB-9D8A-17095A65034A} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{D3D0A8E8-EC2F-4E01-8650-8554E186A66F} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
@ -432,6 +432,7 @@ Global
{01F86E65-6448-424C-AAB5-9C6427EF6FD4} = {258D5057-81B9-40EC-A872-D21E27452749}
{3D935B7D-80BD-49AD-BDC9-E1B0C9D9494F} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{2AC6A8AC-5C0A-422A-B21A-CDC8D75F20A3} = {258D5057-81B9-40EC-A872-D21E27452749}
{711EE8F3-F92D-4470-8B0B-25D8B13EF282} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {285A5EB4-BCF4-40EB-B9E1-DF6DBCB5E705}

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

@ -172,20 +172,30 @@ extends:
steps:
- bash: |
echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-nightly-test.documents.azure.com:443/"
echo "##vso[task.setvariable variable=_CosmosToken]$(ef-nightly-cosmos-key)"
displayName: Prepare to run Cosmos tests on ef-nightly-test
condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '0'), endsWith(variables['_runCounter'], '2'), endsWith(variables['_runCounter'], '4'), endsWith(variables['_runCounter'], '6'), endsWith(variables['_runCounter'], '8')))
- bash: |
echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-pr-test.documents.azure.com:443/"
echo "##vso[task.setvariable variable=_CosmosToken]$(ef-pr-cosmos-test)"
displayName: Prepare to run Cosmos tests on ef-pr-test
condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '1'), endsWith(variables['_runCounter'], '3'), endsWith(variables['_runCounter'], '5'), endsWith(variables['_runCounter'], '7'), endsWith(variables['_runCounter'], '9')))
- template: /eng/common/templates-official/steps/enable-internal-sources.yml
- template: /eng/common/templates-official/steps/enable-internal-runtimes.yml
- script: eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
- task: AzureCLI@2
displayName: Build
inputs:
azureSubscription: EFCosmosTesting
addSpnToEnvironment: true
scriptType: bash
scriptLocation: 'inlineScript'
inlineScript: |
az account get-access-token --scope https://ef-pr-test.documents.azure.com/.default --output none
az account get-access-token --scope https://ef-nightly-test.documents.azure.com/.default --output none
eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
env:
Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
Test__Cosmos__AuthToken: $(_CosmosToken)
Test__Cosmos__UseTokenCredential: true
Test__Cosmos__SubscriptionId: d709b837-4a74-4aec-addc-b6e4b9b23e7e
Test__Cosmos__ResourceGroup: efcosmosci
name: Build
templateContext:
outputs:

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

@ -40,6 +40,7 @@
<MicrosoftCodeAnalysisVersion>4.8.0</MicrosoftCodeAnalysisVersion>
<MicrosoftCodeAnalysisTestingVersion>1.1.2-beta1.24121.1</MicrosoftCodeAnalysisTestingVersion>
<AzureIdentityVersion>1.11.3</AzureIdentityVersion>
<AzureResourceManagerCosmosDBVersion>1.3.2</AzureResourceManagerCosmosDBVersion>
<OpenTelemetryExporterInMemoryVersion>1.8.1</OpenTelemetryExporterInMemoryVersion>
</PropertyGroup>
</Project>

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

@ -8,6 +8,7 @@ namespace Microsoft.EntityFrameworkCore;
#nullable disable
[CosmosCondition(CosmosCondition.DoesNotUseTokenCredential)]
public class ConfigPatternsCosmosTest(ConfigPatternsCosmosTest.CosmosFixture fixture)
: IClassFixture<ConfigPatternsCosmosTest.CosmosFixture>
{

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

@ -7,6 +7,7 @@ namespace Microsoft.EntityFrameworkCore;
#nullable disable
[CosmosCondition(CosmosCondition.DoesNotUseTokenCredential)]
public class ConnectionSpecificationTest
{
[ConditionalFact]

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

@ -78,6 +78,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftExtensionsConfigurationEnvironmentVariablesVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonVersion)" />
<PackageReference Include="Azure.Identity" Version="$(AzureIdentityVersion)" />
<PackageReference Include="Azure.ResourceManager.CosmosDB" Version="$(AzureResourceManagerCosmosDBVersion)" />
</ItemGroup>
</Project>

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

@ -498,7 +498,6 @@ public class EmbeddedDocumentsTest : IClassFixture<EmbeddedDocumentsTest.CosmosF
Guid addressGuid;
await using (var context = new EmbeddedTransportationContext(options))
{
await context.Database.EnsureCreatedAsync();
var person = new Person { Id = 1 };
address = new Address { Street = "Second", City = "Village" };
person.Addresses.Add(address);

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

@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Azure.Core;
using Newtonsoft.Json.Linq;
namespace Microsoft.EntityFrameworkCore;
@ -37,14 +38,27 @@ public class ReloadTest
private readonly string _connectionUri = testStore.ConnectionUri;
private readonly string _authToken = testStore.AuthToken;
private readonly string _name = testStore.Name;
private readonly TokenCredential _tokenCredential = testStore.TokenCredential;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseCosmos(
_connectionUri,
_authToken,
_name,
b => b.ApplyConfiguration());
{
if (TestEnvironment.UseTokenCredential)
{
optionsBuilder.UseCosmos(
_connectionUri,
_tokenCredential,
_name,
b => b.ApplyConfiguration());
}
else
{
optionsBuilder.UseCosmos(
_connectionUri,
_authToken,
_name,
b => b.ApplyConfiguration());
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{

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

@ -5,6 +5,7 @@ namespace Microsoft.EntityFrameworkCore.Storage;
#nullable disable
[CosmosCondition(CosmosCondition.DoesNotUseTokenCredential)]
public class CosmosDatabaseCreatorTest
{
public static IEnumerable<object[]> IsAsyncData = new object[][] { [false], [true] };

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

@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Microsoft.EntityFrameworkCore.TestUtilities;
[Flags]
public enum CosmosCondition
{
UsesTokenCredential = 1 << 0,
DoesNotUseTokenCredential = 1 << 1,
}

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

@ -0,0 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
namespace Microsoft.EntityFrameworkCore.TestUtilities;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class CosmosConditionAttribute(CosmosCondition conditions) : Attribute, ITestCondition
{
public CosmosCondition Conditions { get; set; } = conditions;
public ValueTask<bool> IsMetAsync()
{
var isMet = true;
if (Conditions.HasFlag(CosmosCondition.UsesTokenCredential))
{
isMet &= TestEnvironment.UseTokenCredential;
}
if (Conditions.HasFlag(CosmosCondition.DoesNotUseTokenCredential))
{
isMet &= !TestEnvironment.UseTokenCredential;
}
return ValueTask.FromResult(isMet);
}
public string SkipReason
=> string.Format(
"The test Cosmos account does not meet these conditions: '{0}'",
string.Join(
", ", Enum.GetValues(typeof(CosmosCondition))
.Cast<Enum>()
.Where(Conditions.HasFlag)
.Select(f => Enum.GetName(typeof(CosmosCondition), f))));
}

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

@ -22,7 +22,12 @@ public class CosmosTestHelpers : TestHelpers
=> services.AddEntityFrameworkCosmos();
public override DbContextOptionsBuilder UseProviderOptions(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseCosmos(
=> TestEnvironment.UseTokenCredential
? optionsBuilder.UseCosmos(
TestEnvironment.DefaultConnection,
TestEnvironment.TokenCredential,
"UnitTests")
: optionsBuilder.UseCosmos(
TestEnvironment.DefaultConnection,
TestEnvironment.AuthToken,
"UnitTests");

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

@ -1,8 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Net;
using System.Net.Sockets;
using Azure;
using Azure.Core;
using Azure.ResourceManager;
using Azure.ResourceManager.CosmosDB;
using Azure.ResourceManager.CosmosDB.Models;
using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
using Newtonsoft.Json;
@ -82,11 +87,9 @@ public class CosmosTestStore : TestStore
=> new TestStoreContext(this);
public override DbContextOptionsBuilder AddProviderOptions(DbContextOptionsBuilder builder)
=> builder.UseCosmos(
ConnectionUri,
AuthToken,
Name,
_configureCosmos);
=> TestEnvironment.UseTokenCredential
? builder.UseCosmos(ConnectionUri, TokenCredential, Name, _configureCosmos)
: builder.UseCosmos(ConnectionUri, AuthToken, Name, _configureCosmos);
public static async ValueTask<bool> IsConnectionAvailableAsync()
{
@ -166,8 +169,9 @@ public class CosmosTestStore : TestStore
private async Task CreateFromFile(DbContext context)
{
if (await context.Database.EnsureCreatedAsync())
if (await EnsureCreatedAsync(context))
{
await context.Database.EnsureCreatedAsync();
var cosmosClient = context.GetService<ICosmosClientWrapper>();
var serializer = CosmosClientWrapper.Serializer;
using var fs = new FileStream(_dataFilePath!, FileMode.Open, FileAccess.Read);
@ -223,10 +227,49 @@ public class CosmosTestStore : TestStore
}
}
private static readonly ArmClient _armClient = new(TestEnvironment.TokenCredential);
public async Task<bool> EnsureCreatedAsync(DbContext context, CancellationToken cancellationToken = default)
{
if (!TestEnvironment.UseTokenCredential)
{
var cosmosClientWrapper = context.GetService<ICosmosClientWrapper>();
return await cosmosClientWrapper.CreateDatabaseIfNotExistsAsync(null, cancellationToken);
}
var databaseAccount = await GetDBAccount(cancellationToken);
var collection = databaseAccount.Value.GetCosmosDBSqlDatabases();
var sqlDatabaseCreateUpdateOptions = new CosmosDBSqlDatabaseCreateOrUpdateContent(TestEnvironment.AzureLocation,
new CosmosDBSqlDatabaseResourceInfo(Name));
var databaseResponse = (await collection.CreateOrUpdateAsync(
WaitUntil.Completed, Name, sqlDatabaseCreateUpdateOptions, cancellationToken)).GetRawResponse();
return databaseResponse.Status == (int)HttpStatusCode.Created;
}
private async Task<bool> EnsureDeletedAsync(DbContext context, CancellationToken cancellationToken = default)
{
if (!TestEnvironment.UseTokenCredential)
{
return await context.Database.EnsureDeletedAsync(cancellationToken);
}
var databaseAccount = await GetDBAccount(cancellationToken);
var databaseResponse = (await databaseAccount.Value.GetCosmosDBSqlDatabase(Name, cancellationToken).Value.DeleteAsync(
WaitUntil.Completed, cancellationToken)).GetRawResponse();
return databaseResponse.Status == (int)HttpStatusCode.Created;
}
private async Task<global::Azure.Response<CosmosDBAccountResource>> GetDBAccount(CancellationToken cancellationToken)
{
var accountName = new Uri(ConnectionUri).Host.Split('.').First();
var databaseAccountIdentifier = CosmosDBAccountResource.CreateResourceIdentifier(
TestEnvironment.SubscriptionId, TestEnvironment.ResourceGroup, accountName);
return await _armClient.GetCosmosDBAccountResource(databaseAccountIdentifier).GetAsync(cancellationToken);
}
public override async Task CleanAsync(DbContext context)
{
var cosmosClientWrapper = context.GetService<ICosmosClientWrapper>();
var created = await cosmosClientWrapper.CreateDatabaseIfNotExistsAsync(null);
var created = await EnsureCreatedAsync(context);
try
{
if (!created)
@ -288,7 +331,7 @@ public class CosmosTestStore : TestStore
{
try
{
await context.Database.EnsureDeletedAsync();
await EnsureDeletedAsync(context);
}
catch (Exception)
{
@ -316,7 +359,7 @@ public class CosmosTestStore : TestStore
GetTestStoreIndex(ServiceProvider).RemoveShared(GetType().Name + Name);
}
await _storeContext.Database.EnsureDeletedAsync();
await EnsureDeletedAsync(_storeContext);
}
_storeContext.Dispose();
@ -327,7 +370,16 @@ public class CosmosTestStore : TestStore
private readonly CosmosTestStore _testStore = testStore;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseCosmos(_testStore.ConnectionUri, _testStore.AuthToken, _testStore.Name, _testStore._configureCosmos);
{
if (TestEnvironment.UseTokenCredential)
{
optionsBuilder.UseCosmos(_testStore.ConnectionUri, _testStore.TokenCredential, _testStore.Name, _testStore._configureCosmos);
}
else
{
optionsBuilder.UseCosmos(_testStore.ConnectionUri, _testStore.AuthToken, _testStore.Name, _testStore._configureCosmos);
}
}
}
private class FakeUpdateEntry : IUpdateEntry

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

@ -11,6 +11,9 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities;
public static class TestEnvironment
{
private static readonly string _emulatorAuthToken =
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
public static IConfiguration Config { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("cosmosConfig.json", optional: true)
@ -24,12 +27,22 @@ public static class TestEnvironment
: Config["DefaultConnection"];
public static string AuthToken { get; } = string.IsNullOrEmpty(Config["AuthToken"])
? "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
? _emulatorAuthToken
: Config["AuthToken"];
public static string ConnectionString { get; } = $"AccountEndpoint={DefaultConnection};AccountKey={AuthToken}";
public static bool UseTokenCredential { get; } = Config["UseTokenCredential"] == "true";
public static TokenCredential TokenCredential { get; } = new DefaultAzureCredential();
public static bool IsEmulator { get; } = DefaultConnection.StartsWith("https://localhost:8081", StringComparison.Ordinal);
public static string SubscriptionId { get; } = Config["SubscriptionId"];
public static string ResourceGroup { get; } = Config["ResourceGroup"];
public static AzureLocation AzureLocation { get; } = string.IsNullOrEmpty(Config["AzureLocation"])
? AzureLocation.WestUS
: Enum.Parse<AzureLocation>(Config["AzureLocation"]);
public static bool IsEmulator { get; } = !UseTokenCredential && (AuthToken == _emulatorAuthToken);
}