Fix error reporting for creating a Scaleset under a missing Pool (#2844)

This commit is contained in:
George Pollard 2023-02-16 11:32:21 +13:00 committed by GitHub
parent 5ea0128727
commit f3524c37aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 20 deletions

View File

@ -44,7 +44,7 @@ public class AgentCommands {
private async Async.Task<HttpResponseData> Delete(HttpRequestData req) {
var request = await RequestHandling.ParseRequest<NodeCommandDelete>(req);
if (!request.IsOk || request.OkV == null) {
if (!request.IsOk) {
return await _context.RequestHandling.NotOk(req, request.ErrorV, typeof(NodeCommandDelete).ToString());
}
var nodeCommand = request.OkV;

View File

@ -63,7 +63,7 @@ public class Scaleset {
// verify the pool exists
var poolResult = await _context.PoolOperations.GetByName(create.PoolName);
if (!poolResult.IsOk) {
return await _context.RequestHandling.NotOk(req, answer.ErrorV, "ScalesetCreate");
return await _context.RequestHandling.NotOk(req, poolResult.ErrorV, "ScalesetCreate");
}
var pool = poolResult.OkV;

View File

@ -2,7 +2,7 @@
namespace Microsoft.OneFuzz.Service {
public struct ResultVoid<T_Error> {
public readonly struct ResultVoid<T_Error> {
public static ResultVoid<T_Error> Ok() => new();
public static ResultVoid<T_Error> Error(T_Error err) => new(err);
@ -16,7 +16,7 @@ namespace Microsoft.OneFuzz.Service {
}
public struct Result<T_Ok, T_Error> {
public readonly struct Result<T_Ok, T_Error> {
public static Result<T_Ok, T_Error> Ok(T_Ok ok) => new(ok);
public static Result<T_Ok, T_Error> Error(T_Error err) => new(err);
@ -37,17 +37,17 @@ namespace Microsoft.OneFuzz.Service {
public static OneFuzzResult<T> Ok<T>(T val) => OneFuzzResult<T>.Ok(val);
}
public struct OneFuzzResult<T_Ok> {
static Error NoError = new(0);
public readonly struct OneFuzzResult<T_Ok> {
[MemberNotNullWhen(returnValue: true, member: nameof(OkV))]
[MemberNotNullWhen(returnValue: false, member: nameof(ErrorV))]
public bool IsOk { get; }
public T_Ok? OkV { get; }
public Error ErrorV { get; }
public Error? ErrorV { get; }
private OneFuzzResult(T_Ok ok) => (OkV, ErrorV, IsOk) = (ok, NoError, true);
private OneFuzzResult(T_Ok ok) => (OkV, ErrorV, IsOk) = (ok, null, true);
private OneFuzzResult(ErrorCode errorCode, string[] errors) => (OkV, ErrorV, IsOk) = (default, new Error(errorCode, errors), false);
@ -63,14 +63,14 @@ namespace Microsoft.OneFuzz.Service {
public static implicit operator OneFuzzResult<T_Ok>(Error err) => new(err);
}
public struct OneFuzzResultVoid {
static Error NoError = new(0);
public readonly struct OneFuzzResultVoid {
[MemberNotNullWhen(returnValue: false, member: nameof(ErrorV))]
public bool IsOk { get; }
public Error ErrorV { get; }
public Error? ErrorV { get; }
public OneFuzzResultVoid() => (ErrorV, IsOk) = (NoError, true);
public OneFuzzResultVoid() => (ErrorV, IsOk) = (null, true);
private OneFuzzResultVoid(ErrorCode errorCode, string[] errors) => (ErrorV, IsOk) = (new Error(errorCode, errors), false);

View File

@ -181,11 +181,9 @@ public class ReproOperations : StatefulOrm<Repro, VmState, ReproOperations>, IRe
if (vmData == null) {
return await _context.ReproOperations.SetError(
repro,
OneFuzzResultVoid.Error(
new Error(
ErrorCode.VM_CREATE_FAILED,
"failed before launching extensions"
).ErrorV
);
new string[] { "failed before launching extensions" }));
}
if (vmData.ProvisioningState == "Failed") {
@ -232,10 +230,11 @@ public class ReproOperations : StatefulOrm<Repro, VmState, ReproOperations>, IRe
.Select(status => $"{status.Code} {status.DisplayStatus} {status.Message}")
.ToArray();
return await SetError(repro, OneFuzzResultVoid.Error(
ErrorCode.VM_CREATE_FAILED,
errors
).ErrorV);
return await SetError(
repro,
new Error(
ErrorCode.VM_CREATE_FAILED,
errors));
}
public async Task<OneFuzzResultVoid> BuildReproScript(Repro repro) {

View File

@ -102,4 +102,38 @@ public abstract class ScalesetTestBase : FunctionTestBase {
// auth should not be included in the response
Assert.Null(resp.Auth);
}
[Fact]
public async Async.Task Create_Scaleset_Under_NonExistent_Pool_Provides_Error() {
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
// override the found user credentials
var userObjectId = Guid.NewGuid();
var userInfo = new UserInfo(ApplicationId: Guid.NewGuid(), ObjectId: userObjectId, "upn");
Context.UserCredentials = new TestUserCredentials(Logger, Context.ConfigOperations, OneFuzzResult<UserInfo>.Ok(userInfo));
await Context.InsertAll(
// user must be admin
new InstanceConfig(Context.ServiceConfiguration.OneFuzzInstanceName!) { Admins = new[] { userObjectId } });
var poolName = PoolName.Parse("nosuchpool");
// pool not created
var req = new ScalesetCreate(
poolName,
TestVmssOperations.TestSku,
"Image",
Region: null,
Size: 1,
SpotInstances: false,
Tags: new Dictionary<string, string>());
var func = new ScalesetFunction(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.FromJson("POST", req));
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode);
var body = BodyAs<ProblemDetails>(result);
Assert.Equal(ErrorCode.INVALID_REQUEST.ToString(), body.Title);
Assert.Contains("unable to find pool with name nosuchpool", body.Detail);
}
}