up
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/template",
|
||||
"author": "StellaOps",
|
||||
"classifications": ["StellaOps", "Plugin", "Scheduler", "Job"],
|
||||
"identity": "StellaOps.Plugin.Scheduler",
|
||||
"name": "StellaOps Scheduled Job Plugin",
|
||||
"shortName": "stellaops-plugin-scheduler",
|
||||
"description": "A template for creating StellaOps scheduled job plugins",
|
||||
"tags": {
|
||||
"language": "C#",
|
||||
"type": "project"
|
||||
},
|
||||
"sourceName": "MyJob",
|
||||
"preferNameDirectory": true,
|
||||
"symbols": {
|
||||
"jobName": {
|
||||
"type": "parameter",
|
||||
"datatype": "text",
|
||||
"description": "The name of the scheduled job (e.g., 'Cleanup' for CleanupJob)",
|
||||
"defaultValue": "MyJob",
|
||||
"replaces": "MyJob",
|
||||
"fileRename": "MyJob"
|
||||
},
|
||||
"cronSchedule": {
|
||||
"type": "parameter",
|
||||
"datatype": "text",
|
||||
"description": "Default cron schedule for the job",
|
||||
"defaultValue": "0 0 * * *",
|
||||
"replaces": "0 0 * * *"
|
||||
},
|
||||
"namespace": {
|
||||
"type": "parameter",
|
||||
"datatype": "text",
|
||||
"description": "The root namespace for the plugin",
|
||||
"defaultValue": "StellaOps.Plugin.MyJob",
|
||||
"replaces": "StellaOps.Plugin.MyJob"
|
||||
},
|
||||
"pluginVersion": {
|
||||
"type": "parameter",
|
||||
"datatype": "text",
|
||||
"description": "The initial plugin version",
|
||||
"defaultValue": "1.0.0",
|
||||
"replaces": "1.0.0-template"
|
||||
}
|
||||
},
|
||||
"postActions": [
|
||||
{
|
||||
"description": "Restore NuGet packages required by this project",
|
||||
"manualInstructions": [{ "text": "Run 'dotnet restore'" }],
|
||||
"actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
|
||||
"continueOnError": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StellaOps.DependencyInjection;
|
||||
using StellaOps.DependencyInjection.Validation;
|
||||
|
||||
namespace StellaOps.Plugin.MyJob;
|
||||
|
||||
/// <summary>
|
||||
/// Registers MyJob services with the dependency injection container.
|
||||
/// </summary>
|
||||
public sealed class MyJobDependencyInjectionRoutine : IDependencyInjectionRoutine
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IServiceCollection Register(IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// Register options with fail-fast validation
|
||||
services.AddOptionsWithValidation<MyJobOptions, MyJobOptionsValidator>(
|
||||
MyJobOptions.SectionName);
|
||||
|
||||
// Register the scheduled job
|
||||
services.AddSingleton<IScheduledJob, MyJob>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
105
templates/stellaops-plugin-scheduler/MyJob.cs
Normal file
105
templates/stellaops-plugin-scheduler/MyJob.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Plugin.Versioning;
|
||||
|
||||
// Declare plugin version for compatibility checking
|
||||
[assembly: StellaPluginVersion("1.0.0-template", MinimumHostVersion = "1.0.0")]
|
||||
|
||||
namespace StellaOps.Plugin.MyJob;
|
||||
|
||||
/// <summary>
|
||||
/// A scheduled job that runs on a configurable cron schedule.
|
||||
/// </summary>
|
||||
public sealed class MyJob : IScheduledJob
|
||||
{
|
||||
private readonly ILogger<MyJob> _logger;
|
||||
private readonly MyJobOptions _options;
|
||||
|
||||
public MyJob(ILogger<MyJob> logger, IOptions<MyJobOptions> options)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string JobId => "my-job";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DisplayName => "My Scheduled Job";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string CronSchedule => _options.CronSchedule;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<JobResult> ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogInformation("Starting scheduled job {JobId} at {StartTime}", JobId, context.ScheduledTime);
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: Implement your job logic here
|
||||
await Task.Delay(100, cancellationToken);
|
||||
|
||||
_logger.LogInformation("Completed scheduled job {JobId}", JobId);
|
||||
|
||||
return new JobResult(
|
||||
Success: true,
|
||||
Message: "Job completed successfully",
|
||||
ItemsProcessed: 0,
|
||||
Errors: Array.Empty<string>());
|
||||
}
|
||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to execute scheduled job {JobId}", JobId);
|
||||
|
||||
return new JobResult(
|
||||
Success: false,
|
||||
Message: ex.Message,
|
||||
ItemsProcessed: 0,
|
||||
Errors: new[] { ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scheduled job that can be executed by the scheduler.
|
||||
/// </summary>
|
||||
public interface IScheduledJob
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier for this job.
|
||||
/// </summary>
|
||||
string JobId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name for this job.
|
||||
/// </summary>
|
||||
string DisplayName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cron schedule expression for this job.
|
||||
/// </summary>
|
||||
string CronSchedule { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes the scheduled job.
|
||||
/// </summary>
|
||||
Task<JobResult> ExecuteAsync(JobContext context, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context for job execution.
|
||||
/// </summary>
|
||||
public sealed record JobContext(
|
||||
DateTimeOffset ScheduledTime,
|
||||
DateTimeOffset ActualStartTime,
|
||||
string? CorrelationId = null);
|
||||
|
||||
/// <summary>
|
||||
/// Result of a job execution.
|
||||
/// </summary>
|
||||
public sealed record JobResult(
|
||||
bool Success,
|
||||
string? Message,
|
||||
int ItemsProcessed,
|
||||
IReadOnlyList<string> Errors);
|
||||
43
templates/stellaops-plugin-scheduler/MyJobOptions.cs
Normal file
43
templates/stellaops-plugin-scheduler/MyJobOptions.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace StellaOps.Plugin.MyJob;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration options for the MyJob scheduled plugin.
|
||||
/// </summary>
|
||||
public sealed class MyJobOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The configuration section name.
|
||||
/// </summary>
|
||||
public const string SectionName = "Plugins:MyJob";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cron schedule expression.
|
||||
/// Default: "0 0 * * *" (daily at midnight).
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CronSchedule { get; set; } = "0 0 * * *";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the job is enabled.
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum execution time in minutes.
|
||||
/// </summary>
|
||||
[Range(1, 1440)]
|
||||
public int MaxExecutionMinutes { get; set; } = 60;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the batch size for processing items.
|
||||
/// </summary>
|
||||
[Range(1, 10000)]
|
||||
public int BatchSize { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to continue processing on error.
|
||||
/// </summary>
|
||||
public bool ContinueOnError { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using StellaOps.DependencyInjection.Validation;
|
||||
|
||||
namespace StellaOps.Plugin.MyJob;
|
||||
|
||||
/// <summary>
|
||||
/// Validates <see cref="MyJobOptions"/> configuration.
|
||||
/// </summary>
|
||||
public sealed class MyJobOptionsValidator : OptionsValidatorBase<MyJobOptions>
|
||||
{
|
||||
protected override string SectionPrefix => MyJobOptions.SectionName;
|
||||
|
||||
protected override void ValidateOptions(MyJobOptions options, ValidationContext context)
|
||||
{
|
||||
context
|
||||
.RequireNotEmpty(options.CronSchedule, nameof(options.CronSchedule))
|
||||
.RequirePositive(options.MaxExecutionMinutes, nameof(options.MaxExecutionMinutes))
|
||||
.RequirePositive(options.BatchSize, nameof(options.BatchSize))
|
||||
.RequireInRange(options.MaxExecutionMinutes, nameof(options.MaxExecutionMinutes), 1, 1440)
|
||||
.RequireInRange(options.BatchSize, nameof(options.BatchSize), 1, 10000);
|
||||
|
||||
// Validate cron expression format (basic check)
|
||||
if (!string.IsNullOrWhiteSpace(options.CronSchedule))
|
||||
{
|
||||
var parts = options.CronSchedule.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length < 5 || parts.Length > 6)
|
||||
{
|
||||
context.AddError(nameof(options.CronSchedule), "must be a valid cron expression with 5 or 6 fields.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
templates/stellaops-plugin-scheduler/README.md
Normal file
54
templates/stellaops-plugin-scheduler/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# MyJob Scheduled Plugin
|
||||
|
||||
A StellaOps scheduled job plugin template.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Update the `MyJobOptions.cs` with your job-specific configuration
|
||||
2. Implement the job logic in `MyJob.cs`
|
||||
3. Build and sign your plugin
|
||||
|
||||
## Configuration
|
||||
|
||||
Add the following to your `appsettings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"Plugins": {
|
||||
"MyJob": {
|
||||
"Enabled": true,
|
||||
"CronSchedule": "0 0 * * *",
|
||||
"MaxExecutionMinutes": 60,
|
||||
"BatchSize": 100,
|
||||
"ContinueOnError": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Cron Schedule Examples
|
||||
|
||||
- `0 0 * * *` - Daily at midnight
|
||||
- `0 */6 * * *` - Every 6 hours
|
||||
- `0 0 * * 0` - Weekly on Sunday at midnight
|
||||
- `0 0 1 * *` - Monthly on the 1st at midnight
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
dotnet build -c Release
|
||||
```
|
||||
|
||||
## Signing
|
||||
|
||||
For production use, sign your plugin with Cosign:
|
||||
|
||||
```bash
|
||||
cosign sign --key $COSIGN_KEY bin/Release/net10.0/StellaOps.Plugin.MyJob.dll
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
dotnet test
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>1.0.0-template</Version>
|
||||
<Description>StellaOps MyJob Scheduled Plugin</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Reference StellaOps plugin infrastructure -->
|
||||
<!-- Adjust paths based on your repository structure -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\__Libraries\StellaOps.Plugin\StellaOps.Plugin.csproj" />
|
||||
<ProjectReference Include="..\..\src\__Libraries\StellaOps.DependencyInjection\StellaOps.DependencyInjection.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user