Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process snapshot tokens #3135

Merged
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
50f18b4
Added Snapshot TemplateToken to AgentJobRequestMessage
davidomid Feb 7, 2024
f760496
WIP for processing the snapshot token
davidomid Feb 7, 2024
63636f9
Changed snapshot post job step condition to Success, added comments
davidomid Feb 7, 2024
5c7313e
Refactored snapshot post-job step
davidomid Feb 8, 2024
3689161
Added evaluation of snapshot token to retrieve image name
davidomid Feb 8, 2024
b0488ff
Added snapshot to workflow schema
davidomid Feb 8, 2024
da26c9d
Fixed linter error
davidomid Feb 8, 2024
0ef106b
Merge branch 'main' into users/davidomid/process-snapshot-token
davidomid Feb 8, 2024
74cad10
Migrated snapshot logic to new SnapshotOperationProvider
davidomid Feb 8, 2024
2deea1a
Merge branch 'users/davidomid/process-snapshot-token' of https://gith…
davidomid Feb 8, 2024
3d1313c
Fixed linter error
davidomid Feb 8, 2024
88a17ab
Fixed linter errors
davidomid Feb 8, 2024
79c17b4
Fixed linter error
davidomid Feb 8, 2024
dd926d2
Fixed linter errors
davidomid Feb 8, 2024
5416b11
Updated L0 tests
davidomid Feb 9, 2024
aa32d3c
Fixed linter errors
davidomid Feb 9, 2024
0892678
Added new JobExtensionL0 tests for snapshot post-job step
davidomid Feb 9, 2024
f8495a5
Added JobExtensionL0 test case for snapshot mappings
davidomid Feb 9, 2024
4ce5b6a
Merge branch 'main' into users/davidomid/process-snapshot-token
davidomid Feb 12, 2024
ca0c01a
Added SnapshotOperationProviderL0 tests
davidomid Feb 12, 2024
e40bbe4
Merge branch 'users/davidomid/process-snapshot-token' of https://gith…
davidomid Feb 12, 2024
c855632
Enabled nullable types for SnapshotOperationProvider and its tests
davidomid Feb 12, 2024
5bdadfc
Added more assertions to SnapshotOperationProviderL0 tests
davidomid Feb 12, 2024
fbc55d2
Fixed linter errors
davidomid Feb 12, 2024
0400051
Made sure TestHostContexts are disposed of properlyh in SnapshotOpera…
davidomid Feb 12, 2024
856e766
Resolved PR comments
davidomid Feb 14, 2024
c119a25
Fixed formatting
davidomid Feb 14, 2024
694da2f
Removed redundant reference
davidomid Feb 14, 2024
7c7f216
Addressed PR comments
davidomid Feb 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/Runner.Worker/JobExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using GitHub.Services.Common;
using Newtonsoft.Json;
davidomid marked this conversation as resolved.
Show resolved Hide resolved
using Pipelines = GitHub.DistributedTask.Pipelines;

namespace GitHub.Runner.Worker
Expand Down Expand Up @@ -404,6 +405,18 @@ public async Task<List<IStep>> InitializeJob(IExecutionContext jobContext, Pipel
data: (object)jobHookData));
}

// Register custom image creation post-job step if the "snapshot" token is present in the message.
davidomid marked this conversation as resolved.
Show resolved Hide resolved
var snapshotRequest = templateEvaluator.EvaluateJobSnapshotRequest(message.Snapshot, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
davidomid marked this conversation as resolved.
Show resolved Hide resolved
if (snapshotRequest != null)
{
var snapshotOperationProvider = HostContext.GetService<ISnapshotOperationProvider>();
jobContext.RegisterPostJobStep(new JobExtensionRunner(
runAsync: (executionContext, _) => snapshotOperationProvider.CreateSnapshotRequestAsync(executionContext, snapshotRequest),
condition: $"{PipelineTemplateConstants.Success}()",
displayName: $"Create custom image",
data: null));
}

List<IStep> steps = new();
steps.AddRange(preJobSteps);
steps.AddRange(jobSteps);
Expand Down
35 changes: 35 additions & 0 deletions src/Runner.Worker/SnapshotOperationProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#nullable enable
using System.IO;
using System.Threading.Tasks;
using GitHub.DistributedTask.Pipelines;
using GitHub.Runner.Common;
using Newtonsoft.Json;

namespace GitHub.Runner.Worker;

[ServiceLocator(Default = typeof(SnapshotOperationProvider))]
public interface ISnapshotOperationProvider : IRunnerService
{
Task CreateSnapshotRequestAsync(IExecutionContext executionContext, Snapshot snapshotRequest);
}

public class SnapshotOperationProvider : RunnerService, ISnapshotOperationProvider
{
public async Task CreateSnapshotRequestAsync(IExecutionContext executionContext, Snapshot snapshotRequest)
{
var snapshotRequestFilePath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), ".snapshot", "request.json");
davidomid marked this conversation as resolved.
Show resolved Hide resolved
var snapshotRequestDirectoryPath = Path.GetDirectoryName(snapshotRequestFilePath);
if (snapshotRequestDirectoryPath != null)
{
Directory.CreateDirectory(snapshotRequestDirectoryPath);
}

var snapshotRequestJson = JsonConvert.SerializeObject(snapshotRequest);
davidomid marked this conversation as resolved.
Show resolved Hide resolved
await File.WriteAllTextAsync(snapshotRequestFilePath, snapshotRequestJson);
executionContext.Output($"A snapshot request was created with parameters: {snapshotRequestJson}");
executionContext.Output($"Request written to: {snapshotRequestFilePath}");
executionContext.Output("This request will be processed after the job completes. You will not receive any feedback on the snapshot process within the workflow logs of this job.");
executionContext.Output("If the snapshot process is successful, you should see a new image with the requested name in the list of available custom images when creating a new GitHub-hosted Runner.");
await Task.CompletedTask;
}
}
9 changes: 9 additions & 0 deletions src/Sdk/DTPipelines/Pipelines/AgentJobRequestMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public AgentJobRequestMessage(
TemplateToken jobOutputs,
IList<TemplateToken> defaults,
ActionsEnvironmentReference actionsEnvironment,
TemplateToken snapshot,
String messageType = JobRequestMessageTypes.PipelineAgentJobRequest)
{
this.MessageType = messageType;
Expand All @@ -57,6 +58,7 @@ public AgentJobRequestMessage(
this.Workspace = workspaceOptions;
this.JobOutputs = jobOutputs;
this.ActionsEnvironment = actionsEnvironment;
this.Snapshot = snapshot;
m_variables = new Dictionary<String, VariableValue>(variables, StringComparer.OrdinalIgnoreCase);
m_maskHints = new List<MaskHint>(maskHints);
m_steps = new List<JobStep>(steps);
Expand Down Expand Up @@ -237,6 +239,13 @@ public ActionsEnvironmentReference ActionsEnvironment
set;
}

[DataMember(EmitDefaultValue = false)]
public TemplateToken Snapshot
{
get;
set;
}

/// <summary>
/// Gets the collection of variables associated with the current context.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public sealed class PipelineTemplateConstants
public const String Id = "id";
public const String If = "if";
public const String Image = "image";
public const String ImageName = "image-name";
public const String Include = "include";
public const String Inputs = "inputs";
public const String Job = "job";
Expand Down Expand Up @@ -60,6 +61,7 @@ public sealed class PipelineTemplateConstants
public const String Services = "services";
public const String Shell = "shell";
public const String Skipped = "skipped";
public const String Snapshot = "snapshot";
public const String StepEnv = "step-env";
public const String StepIfResult = "step-if-result";
public const String StepWith = "step-with";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,39 @@ internal static List<KeyValuePair<String, JobContainer>> ConvertToJobServiceCont
return result;
}

internal static Snapshot ConvertToJobSnapshotRequest(TemplateContext context, TemplateToken token)
TingluoHuang marked this conversation as resolved.
Show resolved Hide resolved
{
string imageName = null;
if (token is StringToken snapshotStringLiteral)
{
imageName = snapshotStringLiteral.Value;
}
else
{
var snapshotMapping = token.AssertMapping($"{PipelineTemplateConstants.Snapshot}");
foreach (var snapshotPropertyPair in snapshotMapping)
{
var propertyName = snapshotPropertyPair.Key.AssertString($"{PipelineTemplateConstants.Snapshot} key");
switch (propertyName.Value)
{
case PipelineTemplateConstants.ImageName:
imageName = snapshotPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Snapshot} {propertyName}").Value;
break;
default:
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Snapshot} key");
break;
}
}
}

if (String.IsNullOrEmpty(imageName))
{
return null;
}

return new Snapshot(imageName);
}

private static ActionStep ConvertToStep(
TemplateContext context,
TemplateToken stepsItem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,32 @@ public IList<KeyValuePair<String, JobContainer>> EvaluateJobServiceContainers(
return result;
}

public Snapshot EvaluateJobSnapshotRequest(TemplateToken token,
DictionaryContextData contextData,
IList<IFunctionInfo> expressionFunctions)
{
var result = default(Snapshot);

if (token != null && token.Type != TokenType.Null)
{
var context = CreateContext(contextData, expressionFunctions);
try
{
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.Snapshot, token, 0, null, omitHeader: true);
context.Errors.Check();
result = PipelineTemplateConverter.ConvertToJobSnapshotRequest(context, token);
}
catch (Exception ex) when (!(ex is TemplateValidationException))
{
context.Errors.Add(ex);
}

context.Errors.Check();
}

return result;
}

private TemplateContext CreateContext(
DictionaryContextData contextData,
IList<IFunctionInfo> expressionFunctions,
Expand Down
17 changes: 17 additions & 0 deletions src/Sdk/DTPipelines/Pipelines/Snapshot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Runtime.Serialization;

namespace GitHub.DistributedTask.Pipelines
{
[DataContract]
public class Snapshot
{
public Snapshot(string imageName)
{
ImageName = imageName;
}

[DataMember(EmitDefaultValue = false)]
public String ImageName { get; set; }
}
}
21 changes: 20 additions & 1 deletion src/Sdk/DTPipelines/workflow-v1.0.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@
"env": "job-env",
"outputs": "job-outputs",
"defaults": "job-defaults",
"steps": "steps"
"steps": "steps",
"snapshot": "snapshot"
TingluoHuang marked this conversation as resolved.
Show resolved Hide resolved
}
}
},
Expand Down Expand Up @@ -155,6 +156,24 @@
}
},

"snapshot": {
"one-of": [
"non-empty-string",
"snapshot-mapping"
]
},

"snapshot-mapping": {
"mapping": {
"properties": {
"image-name": {
"type": "non-empty-string",
"required": true
}
}
}
},

"runs-on": {
"context": [
"github",
Expand Down
5 changes: 3 additions & 2 deletions src/Test/L0/Listener/JobDispatcherL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ private Pipelines.AgentJobRequestMessage CreateJobRequestMessage()
TaskOrchestrationPlanReference plan = new();
TimelineReference timeline = null;
Guid jobId = Guid.NewGuid();
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
result.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
return result;
}
Expand Down Expand Up @@ -806,7 +806,8 @@ private static AgentJobRequestMessage GetAgentJobRequestMessage()
},
null,
new List<TemplateToken>(),
new ActionsEnvironmentReference("env")
new ActionsEnvironmentReference("env"),
null
);
return message;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Test/L0/Listener/RunnerL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName)
TaskOrchestrationPlanReference plan = new();
TimelineReference timeline = null;
Guid jobId = Guid.NewGuid();
return new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
return new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
}

private JobCancelMessage CreateJobCancelMessage()
Expand Down
2 changes: 1 addition & 1 deletion src/Test/L0/Worker/ActionCommandManagerL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public void EchoProcessCommandDebugOn()
TimelineReference timeline = new();
Guid jobId = Guid.NewGuid();
string jobName = "some job name";
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
{
Alias = Pipelines.PipelineConstants.SelfAlias,
Expand Down
2 changes: 1 addition & 1 deletion src/Test/L0/Worker/CreateStepSummaryCommandL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ private TestHostContext Setup([CallerMemberName] string name = "")
TimelineReference timeline = new();
Guid jobId = Guid.NewGuid();
string jobName = "Summary Job";
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
{
Alias = Pipelines.PipelineConstants.SelfAlias,
Expand Down
Loading
Loading