зеркало из https://github.com/Azure/benchpress.git
expose gRPC endpoint for deploying at the subscription scope (#24)
* expose gRPC endpoint for deploying at the subscription scope * not the template name I wanted
This commit is contained in:
Родитель
027e4f456b
Коммит
59a2644e76
|
@ -12,6 +12,7 @@ public class ArmDeploymentServiceTests {
|
|||
private readonly Mock<ArmDeploymentCollection> subscriptionDeploymentsMock;
|
||||
private const string validSubId = "a3a01f37-665c-4ee8-9bc3-3adf7ebcec0d";
|
||||
private const string validRgName = "test-rg";
|
||||
private const string validLocation = "eastus";
|
||||
private const string smapleFiles = "../../../SampleFiles";
|
||||
private const string standaloneTemplate = $"{smapleFiles}/storage-account.json";
|
||||
private const string templateWithParams = $"{smapleFiles}/storage-account-needs-params.json";
|
||||
|
@ -92,19 +93,20 @@ public class ArmDeploymentServiceTests {
|
|||
{
|
||||
var subMock = SetUpSubscriptionMock(validSubId);
|
||||
SetUpDeploymentsMock(subscriptionDeploymentsMock);
|
||||
await armDeploymentService.DeployArmToSubscriptionAsync(validSubId, templateWithParams, parameters);
|
||||
await armDeploymentService.DeployArmToSubscriptionAsync(validSubId, validLocation, templateWithParams, parameters);
|
||||
VerifyDeploymentsMock(subscriptionDeploymentsMock);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "a3a01f37-665c-4ee8-9bc3-3adf7ebcec0d")]
|
||||
[InlineData("main.bicep", "")]
|
||||
public async Task DeployArmToSubscriptionAsync_MissingParameter_ThrowsException(string templatePath, string subId)
|
||||
[InlineData("main.bicep", "", "a3a01f37-665c-4ee8-9bc3-3adf7ebcec0d")]
|
||||
[InlineData("", "eastus", "a3a01f37-665c-4ee8-9bc3-3adf7ebcec0d")]
|
||||
[InlineData("main.bicep", "eastus", "")]
|
||||
public async Task DeployArmToSubscriptionAsync_MissingParameter_ThrowsException(string templatePath, string location, string subId)
|
||||
{
|
||||
var subMock = SetUpSubscriptionMock(subId);
|
||||
SetUpDeploymentsMock(subscriptionDeploymentsMock);
|
||||
var ex = await Assert.ThrowsAsync<ArgumentException>(
|
||||
async () => await armDeploymentService.DeployArmToSubscriptionAsync(subId, templatePath)
|
||||
async () => await armDeploymentService.DeployArmToSubscriptionAsync(subId, location, templatePath)
|
||||
);
|
||||
Assert.Equal("One or more parameters were missing or empty", ex.Message);
|
||||
}
|
||||
|
@ -116,7 +118,7 @@ public class ArmDeploymentServiceTests {
|
|||
var excepectedMessage = "Deployment template validation failed";
|
||||
SetUpDeploymentExceptionMock(subscriptionDeploymentsMock, new RequestFailedException(excepectedMessage));
|
||||
var ex = await Assert.ThrowsAsync<RequestFailedException>(
|
||||
async () => await armDeploymentService.DeployArmToSubscriptionAsync(validSubId, templateWithParams)
|
||||
async () => await armDeploymentService.DeployArmToSubscriptionAsync(validSubId, validLocation, templateWithParams)
|
||||
);
|
||||
Assert.Equal(excepectedMessage, ex.Message);
|
||||
}
|
||||
|
@ -127,13 +129,22 @@ public class ArmDeploymentServiceTests {
|
|||
var subMock = SetUpSubscriptionMock(validSubId);
|
||||
SetUpDeploymentsMock(subscriptionDeploymentsMock);
|
||||
var ex = await Assert.ThrowsAsync<RequestFailedException>(
|
||||
async () => await armDeploymentService.DeployArmToSubscriptionAsync("The Wrong Subscription", standaloneTemplate)
|
||||
async () => await armDeploymentService.DeployArmToSubscriptionAsync("The Wrong Subscription", validLocation, standaloneTemplate)
|
||||
);
|
||||
Assert.Equal("Subscription Not Found", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateDeploymentContent_WithoutParameters()
|
||||
{
|
||||
var subMock = SetUpSubscriptionMock(validSubId);
|
||||
SetUpDeploymentsMock(subscriptionDeploymentsMock);
|
||||
await armDeploymentService.DeployArmToSubscriptionAsync(validSubId, validLocation, standaloneTemplate);
|
||||
VerifyDeploymentsMock(subscriptionDeploymentsMock);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateDeploymentContent_WithoutLocation()
|
||||
{
|
||||
var subMock = SetUpSubscriptionMock(validSubId);
|
||||
var rgMock = SetUpResourceGroupMock(subMock, validRgName);
|
||||
|
|
|
@ -78,6 +78,64 @@ public class DeploymentServiceTests
|
|||
Assert.Equal(expectedMessage, result.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeploymentSubCreate_DeploysToSub_WithTranspiledFiles()
|
||||
{
|
||||
SetUpSuccessfulTranspilation(validSubRequest.BicepFilePath, templatePath);
|
||||
SetUpSuccessfulSubDeployment(validSubRequest, templatePath);
|
||||
var result = await deploymentService.DeploymentSubCreate(validSubRequest, context);
|
||||
Assert.True(result.Success);
|
||||
VerifySubDeployment(validSubRequest, templatePath);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("main.bicep", "", "a3a01f37-665c-4ee8-9bc3-3adf7ebcec0d")]
|
||||
[InlineData("", "eastus", "a3a01f37-665c-4ee8-9bc3-3adf7ebcec0d")]
|
||||
[InlineData("main.bicep", "eastus", "")]
|
||||
public async Task DeploymentSubCreate_FailsOnMissingParameters(string bicepFilePath, string location, string subscriptionNameOrId)
|
||||
{
|
||||
var request = SetUpSubRequest(bicepFilePath, location, subscriptionNameOrId);
|
||||
SetUpSuccessfulTranspilation(validSubRequest.BicepFilePath, templatePath);
|
||||
SetUpSuccessfulSubDeployment(request, templatePath);
|
||||
var result = await deploymentService.DeploymentSubCreate(request, context);
|
||||
Assert.False(result.Success);
|
||||
VerifyNoTranspilation();
|
||||
VerifyNoDeployments();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeploymentSubCreate_ReturnsFailureOnTranspileException()
|
||||
{
|
||||
var expectedMessage = "the bicep file was malformed";
|
||||
SetUpExceptionThrowingTranspilation(new Exception(expectedMessage));
|
||||
SetUpSuccessfulSubDeployment(validSubRequest, "template.json");
|
||||
var result = await deploymentService.DeploymentSubCreate(validSubRequest, context);
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal(expectedMessage, result.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeploymentSubCreate_ReturnsFailureOnFailedDeployment()
|
||||
{
|
||||
SetUpSuccessfulTranspilation(validSubRequest.BicepFilePath, templatePath);
|
||||
var expectedReason = "Failure occured during deployment";
|
||||
SetUpFailedSubDeployment(expectedReason);
|
||||
var result = await deploymentService.DeploymentSubCreate(validSubRequest, context);
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal(expectedReason, result.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeploymentSubpCreate_ReturnsFailureOnDeploymentException()
|
||||
{
|
||||
SetUpSuccessfulTranspilation(validSubRequest.BicepFilePath, templatePath);
|
||||
var expectedMessage = "the template was malformed";
|
||||
SetUpExceptionThrowingSubDeployment(new Exception(expectedMessage));
|
||||
var result = await deploymentService.DeploymentSubCreate(validSubRequest, context);
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal(expectedMessage, result.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact(Skip = "Not Implemented")]
|
||||
public async Task DeleteGroup_DeletesAllResources()
|
||||
{
|
||||
|
@ -97,6 +155,13 @@ public class DeploymentServiceTests
|
|||
SubscriptionNameOrId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
private readonly DeploymentSubRequest validSubRequest = new DeploymentSubRequest
|
||||
{
|
||||
BicepFilePath = "main.bicep",
|
||||
Location = "eastus",
|
||||
SubscriptionNameOrId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
private DeploymentGroupRequest SetUpGroupRequest(string bicepFilePath, string resourceGroupName, string subscriptionNameOrId)
|
||||
{
|
||||
return new DeploymentGroupRequest
|
||||
|
@ -107,6 +172,16 @@ public class DeploymentServiceTests
|
|||
};
|
||||
}
|
||||
|
||||
private DeploymentSubRequest SetUpSubRequest(string bicepFilePath, string location, string subscriptionNameOrId)
|
||||
{
|
||||
return new DeploymentSubRequest
|
||||
{
|
||||
BicepFilePath = bicepFilePath,
|
||||
Location = location,
|
||||
SubscriptionNameOrId = subscriptionNameOrId
|
||||
};
|
||||
}
|
||||
|
||||
private void SetUpSuccessfulTranspilation(string bicepFilePath, string armTemplatePath)
|
||||
{
|
||||
bicepTranspileServiceMock.Setup(x => x.BuildAsync(bicepFilePath)).ReturnsAsync(armTemplatePath);
|
||||
|
@ -183,6 +258,52 @@ public class DeploymentServiceTests
|
|||
Times.Once);
|
||||
}
|
||||
|
||||
private void SetUpSuccessfulSubDeployment(DeploymentSubRequest request, string templatePath)
|
||||
{
|
||||
var operation = SetupDeploymentOperation(true, "OK");
|
||||
armDeploymentMock.Setup(x => x.DeployArmToSubscriptionAsync(
|
||||
request.SubscriptionNameOrId,
|
||||
request.Location,
|
||||
templatePath,
|
||||
request.ParameterFilePath,
|
||||
It.IsAny<Azure.WaitUntil>()))
|
||||
.ReturnsAsync(operation);
|
||||
}
|
||||
|
||||
private void SetUpFailedSubDeployment(string reason)
|
||||
{
|
||||
var operation = SetupDeploymentOperation(false, reason);
|
||||
armDeploymentMock.Setup(x => x.DeployArmToSubscriptionAsync(
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<Azure.WaitUntil>()))
|
||||
.ReturnsAsync(operation);
|
||||
}
|
||||
|
||||
private void SetUpExceptionThrowingSubDeployment(Exception ex)
|
||||
{
|
||||
armDeploymentMock.Setup(x => x.DeployArmToSubscriptionAsync(
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<Azure.WaitUntil>()))
|
||||
.ThrowsAsync(ex);
|
||||
}
|
||||
|
||||
private void VerifySubDeployment(DeploymentSubRequest request, string templatePath)
|
||||
{
|
||||
armDeploymentMock.Verify(x => x.DeployArmToSubscriptionAsync(
|
||||
request.SubscriptionNameOrId,
|
||||
request.Location,
|
||||
templatePath,
|
||||
request.ParameterFilePath,
|
||||
It.IsAny<Azure.WaitUntil>()),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
private void VerifyNoDeployments()
|
||||
{
|
||||
armDeploymentMock.Verify(x => x.DeployArmToResourceGroupAsync(
|
||||
|
@ -192,5 +313,13 @@ public class DeploymentServiceTests
|
|||
It.IsAny<string>(),
|
||||
It.IsAny<Azure.WaitUntil>()),
|
||||
Times.Never);
|
||||
|
||||
armDeploymentMock.Verify(x => x.DeployArmToSubscriptionAsync(
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<Azure.WaitUntil>()),
|
||||
Times.Never);
|
||||
}
|
||||
}
|
|
@ -20,11 +20,11 @@ public class ArmDeploymentService : IArmDeploymentService {
|
|||
return await CreateGroupDeployment(rg, waitUntil, NewDeploymentName, deploymentContent);
|
||||
}
|
||||
|
||||
public async Task<ArmOperation<ArmDeploymentResource>> DeployArmToSubscriptionAsync(string subscriptionNameOrId, string armTemplatePath, string? parametersPath = null, WaitUntil waitUtil = WaitUntil.Completed)
|
||||
public async Task<ArmOperation<ArmDeploymentResource>> DeployArmToSubscriptionAsync(string subscriptionNameOrId, string location, string armTemplatePath, string? parametersPath = null, WaitUntil waitUtil = WaitUntil.Completed)
|
||||
{
|
||||
ValidateParameters(subscriptionNameOrId, armTemplatePath);
|
||||
ValidateParameters(subscriptionNameOrId, location, armTemplatePath);
|
||||
SubscriptionResource sub = await client.GetSubscriptions().GetAsync(subscriptionNameOrId);
|
||||
var deploymentContent = await CreateDeploymentContent(armTemplatePath, parametersPath);
|
||||
var deploymentContent = await CreateDeploymentContent(armTemplatePath, parametersPath, location);
|
||||
return await CreateSubscriptionDeployment(sub, waitUtil, NewDeploymentName, deploymentContent);
|
||||
}
|
||||
|
||||
|
@ -34,18 +34,26 @@ public class ArmDeploymentService : IArmDeploymentService {
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<ArmDeploymentContent> CreateDeploymentContent(string armTemplatePath, string? parametersPath) {
|
||||
private async Task<ArmDeploymentContent> CreateDeploymentContent(string armTemplatePath, string? parametersPath, string? location = null) {
|
||||
var templateContent = (await File.ReadAllTextAsync(armTemplatePath)).TrimEnd();
|
||||
var properties = new ArmDeploymentProperties(ArmDeploymentMode.Incremental) {
|
||||
var properties = new ArmDeploymentProperties(ArmDeploymentMode.Incremental)
|
||||
{
|
||||
Template = BinaryData.FromString(templateContent)
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(parametersPath)) {
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(parametersPath))
|
||||
{
|
||||
var paramteresContent = (await File.ReadAllTextAsync(parametersPath)).TrimEnd();
|
||||
properties.Parameters = BinaryData.FromString(parametersPath);
|
||||
}
|
||||
|
||||
return new ArmDeploymentContent(properties);
|
||||
var content = new ArmDeploymentContent(properties);
|
||||
if (!string.IsNullOrWhiteSpace(location))
|
||||
{
|
||||
content.Location = location;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
// These extension methods are wrapped to allow mocking in our tests
|
||||
|
|
|
@ -49,6 +49,41 @@ public class DeploymentService : Deployment.DeploymentBase
|
|||
}
|
||||
}
|
||||
|
||||
public override async Task<DeploymentResult> DeploymentSubCreate(DeploymentSubRequest request, ServerCallContext context)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.BicepFilePath)
|
||||
|| string.IsNullOrWhiteSpace(request.Location)
|
||||
|| string.IsNullOrWhiteSpace(request.SubscriptionNameOrId))
|
||||
{
|
||||
return new DeploymentResult
|
||||
{
|
||||
Success = false,
|
||||
ErrorMessage = $"One or more of the following required parameters was missing: {nameof(request.BicepFilePath)}, {nameof(request.Location)}, and {nameof(request.SubscriptionNameOrId)}"
|
||||
};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var armTemplatePath = await bicepTranspileService.BuildAsync(request.BicepFilePath);
|
||||
var deployment = await armDeploymentService.DeployArmToSubscriptionAsync(request.SubscriptionNameOrId, request.Location, armTemplatePath, request.ParameterFilePath);
|
||||
var response = deployment.WaitForCompletionResponse();
|
||||
|
||||
return new DeploymentResult
|
||||
{
|
||||
Success = !response.IsError,
|
||||
ErrorMessage = response.ReasonPhrase
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new DeploymentResult
|
||||
{
|
||||
Success = false,
|
||||
ErrorMessage = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<DeploymentResult> DeleteGroup(DeleteGroupRequest request, ServerCallContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
|
|
@ -2,5 +2,5 @@ namespace BenchPress.TestEngine.Services;
|
|||
|
||||
public interface IArmDeploymentService {
|
||||
Task<ArmOperation<ArmDeploymentResource>> DeployArmToResourceGroupAsync(string subscriptionNameOrId, string resourceGroupName, string armTemplatePath, string? parametersPath = null, Azure.WaitUntil waitUtil = Azure.WaitUntil.Completed);
|
||||
Task<ArmOperation<ArmDeploymentResource>> DeployArmToSubscriptionAsync(string subscriptionNameOrId, string armTemplatePath, string? parametersPath = null, Azure.WaitUntil waitUtil = Azure.WaitUntil.Completed);
|
||||
Task<ArmOperation<ArmDeploymentResource>> DeployArmToSubscriptionAsync(string subscriptionNameOrId, string location, string armTemplatePath, string? parametersPath = null, Azure.WaitUntil waitUtil = Azure.WaitUntil.Completed);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ option csharp_namespace = "BenchPress.TestEngine";
|
|||
// Other scopes: subscription, management group, and tenant.
|
||||
service Deployment {
|
||||
rpc DeploymentGroupCreate (DeploymentGroupRequest) returns (DeploymentResult);
|
||||
rpc DeploymentSubCreate (DeploymentSubRequest) returns (DeploymentResult);
|
||||
rpc DeleteGroup (DeleteGroupRequest) returns (DeploymentResult);
|
||||
}
|
||||
|
||||
|
@ -18,6 +19,13 @@ message DeploymentGroupRequest {
|
|||
string subscription_name_or_id = 4;
|
||||
}
|
||||
|
||||
message DeploymentSubRequest {
|
||||
string bicep_file_path = 1;
|
||||
string parameter_file_path = 2;
|
||||
string location = 3;
|
||||
string subscription_name_or_id = 4;
|
||||
}
|
||||
|
||||
message DeleteGroupRequest {
|
||||
string resource_group_name = 1;
|
||||
string subscription_name_or_id = 2;
|
||||
|
|
Загрузка…
Ссылка в новой задаче