mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-17 12:28:07 +00:00
Implement with_tasks option to expand the task information (#3343)
* Implement with_tasks option to expand the task information * Added tests * format
This commit is contained in:
@ -135,10 +135,14 @@ public class Jobs {
|
|||||||
|
|
||||||
static JobTaskInfo TaskToJobTaskInfo(Task t) => new(t.TaskId, t.Config.Task.Type, t.State);
|
static JobTaskInfo TaskToJobTaskInfo(Task t) => new(t.TaskId, t.Config.Task.Type, t.State);
|
||||||
|
|
||||||
// TODO: search.WithTasks is not checked in Python code?
|
var tasks = _context.TaskOperations.SearchStates(jobId);
|
||||||
|
if (search.WithTasks ?? false) {
|
||||||
var taskInfo = await _context.TaskOperations.SearchStates(jobId).Select(TaskToJobTaskInfo).ToListAsync();
|
var ts = await tasks.ToListAsync();
|
||||||
return await RequestHandling.Ok(req, JobResponse.ForJob(job, taskInfo));
|
return await RequestHandling.Ok(req, JobResponse.ForJob(job, ts));
|
||||||
|
} else {
|
||||||
|
var taskInfo = await tasks.Select(TaskToJobTaskInfo).ToListAsync();
|
||||||
|
return await RequestHandling.Ok(req, JobResponse.ForJob(job, taskInfo));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var jobs = await _context.JobOperations.SearchState(states: search.State ?? Enumerable.Empty<JobState>()).ToListAsync();
|
var jobs = await _context.JobOperations.SearchState(states: search.State ?? Enumerable.Empty<JobState>()).ToListAsync();
|
||||||
|
@ -282,7 +282,8 @@ public record Task(
|
|||||||
ISecret<Authentication>? Auth = null,
|
ISecret<Authentication>? Auth = null,
|
||||||
DateTimeOffset? Heartbeat = null,
|
DateTimeOffset? Heartbeat = null,
|
||||||
DateTimeOffset? EndTime = null,
|
DateTimeOffset? EndTime = null,
|
||||||
StoredUserInfo? UserInfo = null) : StatefulEntityBase<TaskState>(State) {
|
StoredUserInfo? UserInfo = null) : StatefulEntityBase<TaskState>(State), IJobTaskInfo {
|
||||||
|
public TaskType Type => Config.Task.Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record TaskEvent(
|
public record TaskEvent(
|
||||||
@ -898,11 +899,19 @@ public record JobConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonDerivedType(typeof(Task), typeDiscriminator: "Task")]
|
||||||
|
[JsonDerivedType(typeof(JobTaskInfo), typeDiscriminator: "JobTaskInfo")]
|
||||||
|
public interface IJobTaskInfo {
|
||||||
|
Guid TaskId { get; }
|
||||||
|
TaskType Type { get; }
|
||||||
|
TaskState State { get; }
|
||||||
|
}
|
||||||
|
|
||||||
public record JobTaskInfo(
|
public record JobTaskInfo(
|
||||||
Guid TaskId,
|
Guid TaskId,
|
||||||
TaskType Type,
|
TaskType Type,
|
||||||
TaskState State
|
TaskState State
|
||||||
);
|
) : IJobTaskInfo;
|
||||||
|
|
||||||
public record Job(
|
public record Job(
|
||||||
[PartitionKey][RowKey] Guid JobId,
|
[PartitionKey][RowKey] Guid JobId,
|
||||||
|
@ -96,13 +96,13 @@ public record JobResponse(
|
|||||||
JobConfig Config,
|
JobConfig Config,
|
||||||
string? Error,
|
string? Error,
|
||||||
DateTimeOffset? EndTime,
|
DateTimeOffset? EndTime,
|
||||||
List<JobTaskInfo>? TaskInfo,
|
IEnumerable<IJobTaskInfo>? TaskInfo,
|
||||||
StoredUserInfo? UserInfo,
|
StoredUserInfo? UserInfo,
|
||||||
[property: JsonPropertyName("Timestamp")] // must retain capital T for backcompat
|
[property: JsonPropertyName("Timestamp")] // must retain capital T for backcompat
|
||||||
DateTimeOffset? Timestamp
|
DateTimeOffset? Timestamp
|
||||||
// not including UserInfo from Job model
|
// not including UserInfo from Job model
|
||||||
) : BaseResponse() {
|
) : BaseResponse() {
|
||||||
public static JobResponse ForJob(Job j, List<JobTaskInfo>? taskInfo)
|
public static JobResponse ForJob(Job j, IEnumerable<IJobTaskInfo>? taskInfo)
|
||||||
=> new(
|
=> new(
|
||||||
JobId: j.JobId,
|
JobId: j.JobId,
|
||||||
State: j.State,
|
State: j.State,
|
||||||
|
@ -175,4 +175,55 @@ public abstract class JobsTestBase : FunctionTestBase {
|
|||||||
var metadata = Assert.Single(container.Value);
|
var metadata = Assert.Single(container.Value);
|
||||||
Assert.Equal(new KeyValuePair<string, string>("container_type", "logs"), metadata);
|
Assert.Equal(new KeyValuePair<string, string>("container_type", "logs"), metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Async.Task Get_CanFindSpecificJobWithTaskInfo() {
|
||||||
|
|
||||||
|
var taskConfig = new TaskConfig(_jobId, new List<Guid>(), new TaskDetails(TaskType.Coverage, 60));
|
||||||
|
var task = new Task(_jobId, Guid.NewGuid(), TaskState.Running, Os.Windows, taskConfig);
|
||||||
|
|
||||||
|
await Context.InsertAll(
|
||||||
|
new Job(_jobId, JobState.Stopped, _config, null), task);
|
||||||
|
|
||||||
|
var func = new Jobs(Context, LoggerProvider.CreateLogger<Jobs>());
|
||||||
|
|
||||||
|
var ctx = new TestFunctionContext();
|
||||||
|
var result = await func.Run(TestHttpRequestData.FromJson("GET", new JobSearch(JobId: _jobId, WithTasks: false)), ctx);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
|
||||||
|
var response = BodyAs<JobResponse>(result);
|
||||||
|
Assert.Equal(_jobId, response.JobId);
|
||||||
|
Assert.NotNull(response.TaskInfo);
|
||||||
|
var returnedTasks = response.TaskInfo.OfType<JobTaskInfo>().ToList();
|
||||||
|
Assert.NotEmpty(returnedTasks);
|
||||||
|
Assert.Equal(task.TaskId, returnedTasks[0].TaskId);
|
||||||
|
Assert.Equal(task.State, returnedTasks[0].State);
|
||||||
|
Assert.Equal(task.Config.Task.Type, returnedTasks[0].Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Async.Task Get_CanFindSpecificJobWithFullTask() {
|
||||||
|
var taskConfig = new TaskConfig(_jobId, new List<Guid>(), new TaskDetails(TaskType.Coverage, 60));
|
||||||
|
var task = new Task(_jobId, Guid.NewGuid(), TaskState.Running, Os.Windows, taskConfig);
|
||||||
|
|
||||||
|
await Context.InsertAll(
|
||||||
|
new Job(_jobId, JobState.Stopped, _config, null), task);
|
||||||
|
|
||||||
|
var func = new Jobs(Context, LoggerProvider.CreateLogger<Jobs>());
|
||||||
|
|
||||||
|
var ctx = new TestFunctionContext();
|
||||||
|
var result = await func.Run(TestHttpRequestData.FromJson("GET", new JobSearch(JobId: _jobId, WithTasks: true)), ctx);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
|
||||||
|
var response = BodyAs<JobResponse>(result);
|
||||||
|
Assert.Equal(_jobId, response.JobId);
|
||||||
|
Assert.NotNull(response.TaskInfo);
|
||||||
|
var returnedTasks = response.TaskInfo.OfType<Task>().ToList();
|
||||||
|
Assert.NotEmpty(returnedTasks);
|
||||||
|
Assert.Equal(task.TaskId, returnedTasks[0].TaskId);
|
||||||
|
Assert.Equal(task.State, returnedTasks[0].State);
|
||||||
|
Assert.Equal(task.Config.Task.Type, returnedTasks[0].Type);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1308,14 +1308,16 @@ class Jobs(Endpoint):
|
|||||||
"DELETE", models.Job, data=requests.JobGet(job_id=job_id_expanded)
|
"DELETE", models.Job, data=requests.JobGet(job_id=job_id_expanded)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get(self, job_id: UUID_EXPANSION) -> models.Job:
|
def get(self, job_id: UUID_EXPANSION, with_tasks: bool = False) -> models.Job:
|
||||||
"""Get information about a specific job"""
|
"""Get information about a specific job"""
|
||||||
job_id_expanded = self._disambiguate_uuid(
|
job_id_expanded = self._disambiguate_uuid(
|
||||||
"job_id", job_id, lambda: [str(x.job_id) for x in self.list()]
|
"job_id", job_id, lambda: [str(x.job_id) for x in self.list()]
|
||||||
)
|
)
|
||||||
self.logger.debug("get job: %s", job_id_expanded)
|
self.logger.debug("get job: %s", job_id_expanded)
|
||||||
job = self._req_model(
|
job = self._req_model(
|
||||||
"GET", models.Job, data=requests.JobGet(job_id=job_id_expanded)
|
"GET",
|
||||||
|
models.Job,
|
||||||
|
data=requests.JobGet(job_id=job_id_expanded, with_tasks=with_tasks),
|
||||||
)
|
)
|
||||||
return job
|
return job
|
||||||
|
|
||||||
|
@ -463,17 +463,6 @@ class JobTaskInfo(BaseModel):
|
|||||||
state: TaskState
|
state: TaskState
|
||||||
|
|
||||||
|
|
||||||
class Job(BaseModel):
|
|
||||||
timestamp: Optional[datetime] = Field(alias="Timestamp")
|
|
||||||
job_id: UUID = Field(default_factory=uuid4)
|
|
||||||
state: JobState = Field(default=JobState.init)
|
|
||||||
config: JobConfig
|
|
||||||
error: Optional[str]
|
|
||||||
end_time: Optional[datetime] = None
|
|
||||||
task_info: Optional[List[JobTaskInfo]]
|
|
||||||
user_info: Optional[UserInfo]
|
|
||||||
|
|
||||||
|
|
||||||
class TaskHeartbeatEntry(BaseModel):
|
class TaskHeartbeatEntry(BaseModel):
|
||||||
task_id: UUID
|
task_id: UUID
|
||||||
job_id: Optional[UUID]
|
job_id: Optional[UUID]
|
||||||
@ -757,6 +746,17 @@ class Task(BaseModel):
|
|||||||
user_info: Optional[UserInfo]
|
user_info: Optional[UserInfo]
|
||||||
|
|
||||||
|
|
||||||
|
class Job(BaseModel):
|
||||||
|
timestamp: Optional[datetime] = Field(alias="Timestamp")
|
||||||
|
job_id: UUID = Field(default_factory=uuid4)
|
||||||
|
state: JobState = Field(default=JobState.init)
|
||||||
|
config: JobConfig
|
||||||
|
error: Optional[str]
|
||||||
|
end_time: Optional[datetime] = None
|
||||||
|
task_info: Optional[List[Union[Task, JobTaskInfo]]]
|
||||||
|
user_info: Optional[UserInfo]
|
||||||
|
|
||||||
|
|
||||||
class NetworkConfig(BaseModel):
|
class NetworkConfig(BaseModel):
|
||||||
address_space: str = Field(default="10.0.0.0/8")
|
address_space: str = Field(default="10.0.0.0/8")
|
||||||
subnet: str = Field(default="10.0.0.0/16")
|
subnet: str = Field(default="10.0.0.0/16")
|
||||||
|
@ -39,13 +39,13 @@ class BaseRequest(BaseModel):
|
|||||||
|
|
||||||
class JobGet(BaseRequest):
|
class JobGet(BaseRequest):
|
||||||
job_id: UUID
|
job_id: UUID
|
||||||
|
with_tasks: Optional[bool]
|
||||||
|
|
||||||
|
|
||||||
class JobSearch(BaseRequest):
|
class JobSearch(BaseRequest):
|
||||||
job_id: Optional[UUID]
|
job_id: Optional[UUID]
|
||||||
state: Optional[List[JobState]]
|
state: Optional[List[JobState]]
|
||||||
task_state: Optional[List[TaskState]]
|
task_state: Optional[List[TaskState]]
|
||||||
with_tasks: Optional[bool]
|
|
||||||
|
|
||||||
|
|
||||||
class NotificationCreate(BaseRequest, NotificationConfig):
|
class NotificationCreate(BaseRequest, NotificationConfig):
|
||||||
|
Reference in New Issue
Block a user