test hooks and a bug fix (#1879)

Co-authored-by: stas <statis@microsoft.com>
This commit is contained in:
Stas
2022-05-02 18:07:09 -07:00
committed by GitHub
parent 5ccd519065
commit 1f276e13e5
8 changed files with 246 additions and 24 deletions

View File

@ -41,27 +41,7 @@ public class TestHooks {
return response;
}
[Function("InstanceConfig")]
public async Task<HttpResponseData> InstanceConfig([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/instance-config")] HttpRequestData req) {
_log.Info("Fetching instance config");
var config = await _configOps.Fetch();
if (config is null) {
_log.Error("Instance config is null");
Error err = new(ErrorCode.INVALID_REQUEST, new[] { "Instance config is null" });
var resp = req.CreateResponse(HttpStatusCode.InternalServerError);
await resp.WriteAsJsonAsync(err);
return resp;
} else {
await _events.SendEvent(new EventInstanceConfigUpdated(config));
var str = (new EntityConverter()).ToJsonString(config);
var resp = req.CreateResponse(HttpStatusCode.OK);
await resp.WriteStringAsync(str);
return resp;
}
}
[Function("GetKeyvaultAddress")]
public async Task<HttpResponseData> GetKeyVaultAddress([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/secrets/keyvaultaddress")] HttpRequestData req) {

View File

@ -0,0 +1,35 @@
using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.OneFuzz.Service;
namespace ApiService.TestHooks {
public class ContainerTestHooks {
private readonly ILogTracer _log;
private readonly IConfigOperations _configOps;
private readonly IContainers _containers;
public ContainerTestHooks(ILogTracer log, IConfigOperations configOps, IContainers containers) {
_log = log.WithTag("TestHooks", "ContainerTestHooks");
_configOps = configOps;
_containers = containers;
}
[Function("GetInstanceId")]
public async Task<HttpResponseData> GetInstanceId([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/containers/instanceId")] HttpRequestData req) {
_log.Info("Get instance ID");
var instanceId = await _containers.GetInstanceId();
var resp = req.CreateResponse(HttpStatusCode.OK);
await resp.WriteStringAsync(instanceId.ToString());
return resp;
}
}
}

View File

@ -0,0 +1,65 @@
using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.OneFuzz.Service;
namespace ApiService.TestHooks {
public class CredsTestHookks {
private readonly ILogTracer _log;
private readonly IConfigOperations _configOps;
private readonly ICreds _creds;
public CredsTestHookks(ILogTracer log, IConfigOperations configOps, ICreds creds) {
_log = log.WithTag("TestHooks", "ContainerTestHooks");
_configOps = configOps;
_creds = creds;
}
[Function("GetSubscription")]
public async Task<HttpResponseData> GetSubscription([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/creds/subscription")] HttpRequestData req) {
_log.Info("Get subscription");
var resp = req.CreateResponse(HttpStatusCode.OK);
await resp.WriteStringAsync(_creds.GetSubscription().ToString());
return resp;
}
[Function("GetBaseResourceGroup")]
public async Task<HttpResponseData> GetBaseResourceGroup([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/creds/baseResourceGroup")] HttpRequestData req) {
_log.Info("Get base resource group");
var resp = req.CreateResponse(HttpStatusCode.OK);
await resp.WriteStringAsync(_creds.GetBaseResourceGroup().ToString());
return resp;
}
[Function("GetInstanceName")]
public async Task<HttpResponseData> GetInstanceName([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/creds/instanceName")] HttpRequestData req) {
_log.Info("Get instance name");
var resp = req.CreateResponse(HttpStatusCode.OK);
await resp.WriteStringAsync(_creds.GetInstanceName().ToString());
return resp;
}
[Function("GetBaseRegion")]
public async Task<HttpResponseData> GetBaseRegion([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/creds/baseRegion")] HttpRequestData req) {
_log.Info("Get base region");
var resp = req.CreateResponse(HttpStatusCode.OK);
var region = await _creds.GetBaseRegion();
await resp.WriteStringAsync(region);
return resp;
}
[Function("GetInstanceUrl")]
public async Task<HttpResponseData> GetInstanceUrl([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/creds/instanceUrl")] HttpRequestData req) {
_log.Info("Get instance url");
var resp = req.CreateResponse(HttpStatusCode.OK);
await resp.WriteStringAsync(_creds.GetInstanceUrl().ToString());
return resp;
}
}
}

View File

@ -0,0 +1,36 @@
using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.OneFuzz.Service;
namespace ApiService.TestHooks {
public class DiskOperationsTestHooks {
private readonly ILogTracer _log;
private readonly IConfigOperations _configOps;
private readonly IDiskOperations _diskOps;
private readonly ICreds _creds;
public DiskOperationsTestHooks(ILogTracer log, IConfigOperations configOps, IDiskOperations diskOps, ICreds creds) {
_log = log.WithTag("TestHooks", "ContainerTestHooks");
_configOps = configOps;
_diskOps = diskOps;
_creds = creds;
}
[Function("GetDiskNames")]
public async Task<HttpResponseData> GetSubscription([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/disks")] HttpRequestData req) {
_log.Info("Get disk names");
var resp = req.CreateResponse(HttpStatusCode.OK);
var diskNames = _diskOps.ListDisks(_creds.GetBaseResourceGroup()).ToList().Select(x => x.Data.Name);
await resp.WriteAsJsonAsync(diskNames);
return resp;
}
}
}

View File

@ -0,0 +1,72 @@
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.OneFuzz.Service;
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
namespace ApiService.TestHooks {
public class InstanceConfigTestHooks {
private readonly ILogTracer _log;
private readonly IConfigOperations _configOps;
public InstanceConfigTestHooks(ILogTracer log, IConfigOperations configOps) {
_log = log.WithTag("TestHooks", "InstanceConfigTestHooks");
_configOps = configOps;
}
[Function("GetInstanceConfig")]
public async Task<HttpResponseData> Get([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "testhooks/instance-config")] HttpRequestData req) {
_log.Info("Fetching instance config");
var config = await _configOps.Fetch();
if (config is null) {
_log.Error("Instance config is null");
Error err = new(ErrorCode.INVALID_REQUEST, new[] { "Instance config is null" });
var resp = req.CreateResponse(HttpStatusCode.InternalServerError);
await resp.WriteAsJsonAsync(err);
return resp;
} else {
var str = (new EntityConverter()).ToJsonString(config);
var resp = req.CreateResponse(HttpStatusCode.OK);
await resp.WriteStringAsync(str);
return resp;
}
}
[Function("PatchInstanceConfig")]
public async Task<HttpResponseData> Patch([HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = "testhooks/instance-config")] HttpRequestData req) {
_log.Info("Patch instance config");
var s = await req.ReadAsStringAsync();
var newInstanceConfig = JsonSerializer.Deserialize<InstanceConfig>(s!, EntityConverter.GetJsonSerializerOptions());
if (newInstanceConfig is null) {
var resp = req.CreateResponse();
resp.StatusCode = HttpStatusCode.BadRequest;
await resp.WriteAsJsonAsync(new { Error = "Instance config is not set" });
return resp;
} else {
var query = UriExtension.GetQueryComponents(req.Url);
bool isNew = UriExtension.GetBoolValue("isNew", query, false);
//requireEtag wont' work since our current schema does not return etag to the client when getting data form the table, so
// there is no way to know which etag to use
bool requireEtag = UriExtension.GetBoolValue("requireEtag", query, false);
await _configOps.Save(newInstanceConfig, isNew, requireEtag);
var resp = req.CreateResponse();
resp.StatusCode = HttpStatusCode.OK;
return resp;
}
}
}
}

View File

@ -0,0 +1,30 @@
namespace ApiService.TestHooks {
public class UriExtension {
public static IDictionary<string, string> GetQueryComponents(System.Uri uri) {
var queryComponents = uri.GetComponents(UriComponents.Query, UriFormat.UriEscaped).Split("&");
var q =
from cs in queryComponents
where !string.IsNullOrEmpty(cs)
let i = cs.IndexOf('=')
select new KeyValuePair<string, string>(Uri.UnescapeDataString(cs.Substring(0, i)), Uri.UnescapeDataString(cs.Substring(i + 1)));
return new Dictionary<string, string>(q);
}
public static bool GetBoolValue(string key, IDictionary<string, string> query, bool defaultValue = false) {
bool v;
if (query.ContainsKey(key)) {
if (!bool.TryParse(query[key], out v)) {
v = defaultValue;
}
} else {
v = defaultValue;
}
return v;
}
}
}

View File

@ -20,7 +20,7 @@ public interface ICreds {
public ResourceGroupResource GetResourceGroupResource();
public string GetBaseRegion();
public Async.Task<string> GetBaseRegion();
public Uri GetInstanceUrl();
}
@ -74,8 +74,12 @@ public class Creds : ICreds {
return ArmClient.GetResourceGroupResource(resourceId);
}
public string GetBaseRegion() {
return ArmClient.GetResourceGroupResource(GetResourceGroupResourceIdentifier()).Data.Location.Name;
public async Async.Task<string> GetBaseRegion() {
var rg = await ArmClient.GetResourceGroupResource(GetResourceGroupResourceIdentifier()).GetAsync();
if (rg.GetRawResponse().IsError) {
throw new Exception($"Failed to get base region due to [{rg.GetRawResponse().Status}] {rg.GetRawResponse().ReasonPhrase}");
}
return rg.Value.Data.Location.Name;
}
public Uri GetInstanceUrl() {

View File

@ -48,7 +48,7 @@ public class ReproOperations : StatefulOrm<Repro, VmState>, IReproOperations {
}
vmConfig = new TaskVm(
_creds.GetBaseRegion(),
await _creds.GetBaseRegion(),
DEFAULT_SKU,
DEFAULT_OS[task.Os],
null