diff --git a/Makefile b/Makefile index 7c9382d..db23012 100644 --- a/Makefile +++ b/Makefile @@ -7,4 +7,4 @@ build: run: cd entrypoint && dotnet run -- --user ${USER} --password ${PASSWORD} -H ${HOST} test: - dotnet test + cd tests && dotnet test diff --git a/src/Cron.fs b/src/Cron.fs index 0d27168..f7089c9 100644 --- a/src/Cron.fs +++ b/src/Cron.fs @@ -8,7 +8,6 @@ open Pentole open Datatypes open Pentole.String -open Pentole.Path open Pentole.Map type User = string @@ -23,13 +22,13 @@ let private parse_expr (now: Instant) text = | true -> Ok c | false -> Error $"Can't parse cron expression: {text}" - let to_pattern text = - match Pentole.String.split " " text |> List.head with + let to_pattern (text: string) = + match text with | Prefix "@after" job -> After job |> Ok | _ -> Error $"Can't parse as pattern: {text}" match to_cron text, to_pattern text with - | Error e, Error _ -> Error e + | Error e, Error p -> Error $"Can't parse {text} neither as pattern or cron expression" | _, Ok p -> Ok (Pattern p) | Ok cron_expr, _ -> (now.ToDateTimeOffset(), local_tz_net) @@ -40,7 +39,7 @@ let private parse_expr (now: Instant) text = else Instant.FromDateTimeOffset i.Value |> Cron |> Ok) -let private parse now (db: Database.Requirements) = +let private parse now (db: Requirements) = let when_ = parse_expr now db.``when`` let env = db.environment @@ -52,7 +51,7 @@ let private parse now (db: Database.Requirements) = else Ok (splitted.[0], splitted.[1])) - let executable = Path.of_string db.executable |> Result.bind FileSystem.resolve + let executable = Which.which db.executable let workdir = Path.of_string db.workdir |> Result.bind FileSystem.resolve let last_completed = @@ -76,8 +75,7 @@ let private parse now (db: Database.Requirements) = type JobKey = { j: string; h: string } -let sort_cron_jobs (now: Instant) (db_crons: Database.Requirements seq) = - +let sort_cron_jobs (now: Instant) (db_crons: Requirements seq) = let build_deps (lst: CronJob list) = let standalone, deps = @@ -90,7 +88,7 @@ let sort_cron_jobs (now: Instant) (db_crons: Database.Requirements seq) = in index, deps in let rec build_job_table acc (index: Map) = function - | [] -> Ok acc + | [] -> Map.values acc |> Ok | {when_=Cron _}::_ -> invalidOp "The jobs should have been partitioned" | {when_=Pattern (After jb)} as x::xs -> let father = {j=jb; h=x.hostname} @@ -100,7 +98,7 @@ let sort_cron_jobs (now: Instant) (db_crons: Database.Requirements seq) = Map.find father index |> Result.map List.singleton | Some p -> Ok p if previous |> Result.isError then - Error "Invalid job definition. No such job_name {jb} in host {x.hostname}" + Error $"Invalid job definition. No such job_name {jb} in host {x.hostname}" else let p = Result.get previous let acc' = Map.add father (x::p) acc @@ -109,4 +107,4 @@ let sort_cron_jobs (now: Instant) (db_crons: Database.Requirements seq) = db_crons |> ResultList.collect (parse now) |> Result.map build_deps - |> Result.map (fun (standalone, deps) -> build_job_table Map.empty standalone deps) + |> Result.bind (fun (standalone, deps) -> build_job_table Map.empty standalone deps) diff --git a/src/Database.fs b/src/Database.fs index 44aa5c3..d4c6c44 100644 --- a/src/Database.fs +++ b/src/Database.fs @@ -9,6 +9,8 @@ open Serilog open Npgsql open Dapper +open Datatypes + let private connstring = let c = Environment.Environment() $"Server={c.pg_host};Database={c.pg_dbname};" + @@ -58,19 +60,6 @@ let make (logger: ILogger) = let wait_notification (ct: CancellationToken) (db: t) = db.connection.WaitAsync ct -[] -type Requirements = { - job_name: string - ``when``: string - executable: string - user: string - workdir: string - hostname: string - args: string array - environment: string - done_at: System.DateTime option -} - let gather_requirements (hostname: string) (ct: CancellationToken) (db: t) = let query = """select c.job_name, c."when", c.executable, c.user, c.workdir, c.args, h.hostname, diff --git a/src/Datatypes.fs b/src/Datatypes.fs index 97ec5eb..a870d54 100644 --- a/src/Datatypes.fs +++ b/src/Datatypes.fs @@ -27,3 +27,16 @@ type CronJob = { hostname: string last_completed_at: Instant } + +[] +type Requirements = { + job_name: string + ``when``: string + executable: string + user: string + workdir: string + hostname: string + args: string array + environment: string + done_at: System.DateTime option +} diff --git a/src/Map.fs b/src/Map.fs index 2ab33c2..8066daa 100644 --- a/src/Map.fs +++ b/src/Map.fs @@ -5,3 +5,6 @@ module Map = match Map.tryFind key map with | Some v -> Ok v | None -> Error $"Can't find key {key}" + + let values (map: Map<'k, 'v>): 'v seq = + Map.values map diff --git a/src/src.fsproj b/src/src.fsproj index 72bc45a..56710bc 100644 --- a/src/src.fsproj +++ b/src/src.fsproj @@ -11,6 +11,7 @@ + @@ -38,6 +39,7 @@ + diff --git a/tests/UnitTest1.fs b/tests/UnitTest1.fs index d5f2e35..cfba32e 100644 --- a/tests/UnitTest1.fs +++ b/tests/UnitTest1.fs @@ -2,7 +2,42 @@ module tests open NUnit.Framework open Pentole.TestsExtensions +open Bidello.Datatypes +open Bidello + +open Pentole.String [] -let Test1 () = - Assert.Pass() +let string_prefix_active_pattern () = + match "@after job" with + | Prefix "@after" j -> Assert.Pass () + | _ -> Assert.Pass () + + match "@after job " with + | Prefix "@after" j -> Assert.Pass () + | _ -> Assert.Pass () + +let bj = + { job_name = "j1" + hostname = "h1" + ``when`` = "* * * * *" + executable = "echo" + workdir = "/"; user = "nobody" + args = [||]; environment = ""; + done_at = None } + +[] +let job_deps () = + + let requirements = [ + bj + {bj with job_name = "j2"} + {bj with hostname = "h2"} + {bj with job_name = "j1_after"; ``when``="@after j1"} + // TODO: another test with this at h2 + ] + + let now = NodaTime.SystemClock.Instance.GetCurrentInstant () + let cjs = Cron.sort_cron_jobs now requirements + + Assert.ok_is_equal Seq.empty cjs