seijaku 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63fdce33cce3f7b4496f7ba0968a069554f351f5b1ea37d84c0dc20b7cfb3d49
4
- data.tar.gz: 01fc22c0d064e13bbf514633545ec4f947176a531bf57fe479294d816f60154b
3
+ metadata.gz: 638106c6524b04b68139b67af16ff6b1f66d74fa82b05d15be1b2c26f42e5684
4
+ data.tar.gz: 40b3f7a10c0575df85225a04ebc040c5d6d09521ebc789148a6f888319e9ec31
5
5
  SHA512:
6
- metadata.gz: ffc15482a72317f1617f65374e2de9dd86008d7d4faa64dc3a34fe9dc9e5f940f77982f2a009a82c54613b1226135e3b8a58a2a37d126e6b0a6b105d4de7b0f6
7
- data.tar.gz: 3bdfd31aac02d9bdd5be33b5f77f05327ac4eaa05af82654b00d9fb4c30382be07c0879ae1ac91deaaf36468e934a6f0696b7e6223659332de25a2ed22f31fca
6
+ metadata.gz: 4387d2f340c6d3552f6ad91529ae5600e94ce1fd02e49556fef4e8bd1a125b4a078d5fcaca099a5ca766116db8481e08e1889214b92d8422bac1f14ed01ba646
7
+ data.tar.gz: 2bd4543e36d3ae4abbeaf8317995c2a689be7fd1ae0409f1b3ed34842ac58779802577399116608242979153d92f271df7db663aa999baa694ae1fbc3dd56c42
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2023-12-12
4
+
5
+ - SSH executor added with `ssh` YAML dictionnary and `host` Task support.
6
+ - Documentation added in `./docs` directory
7
+
3
8
  ## [0.1.0] - 2023-12-11
4
9
 
5
10
  - Initial release
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # Seijaku
2
2
 
3
+ [![Seijaku version](https://badge.fury.io/rb/seijaku.svg)](https://badge.fury.io/rb/seijaku)
4
+
3
5
  Seijaku is a program that allows you to execute shell commands listed in YAML payload files, ensuring that they are executed correctly.
6
+ It includes a lightweight scheduler that will take care of executing payloads with specific delay between two executions.
4
7
 
5
8
  ## Concepts
6
9
 
@@ -12,6 +15,8 @@ Each task can have "pre" and "post" tasks, for example to create and delete fold
12
15
 
13
16
  A step sometimes needs variables in order to be performed correctly: Seijaku supports the direct definition of variables or from an environment variable of the shell running Seijaku.
14
17
 
18
+ A scheduler is a file listing payloads that must be executed with unique specifications: a name, a delay between two runs and the path to the concerned payload.
19
+
15
20
  ## Example
16
21
 
17
22
  ```yaml
@@ -49,6 +54,21 @@ tasks:
49
54
  - ssh: echo "executed on host"
50
55
  ```
51
56
 
57
+ ### Scheduler
58
+
59
+ ```yaml
60
+ name: my-scheduler
61
+
62
+ payloads:
63
+ - payload: ./test/my-payload.yaml
64
+ name: My test Payload
65
+ every: 3600 # executed every hour
66
+
67
+ - payload: ./test/another-payload.yaml
68
+ name: Another payload
69
+ every: 60 # executed every minute
70
+ ```
71
+
52
72
  ## Installation
53
73
 
54
74
  ### Dependencies
@@ -66,5 +86,6 @@ gem install seijaku
66
86
 
67
87
  ```bash
68
88
  seijaku -h
69
- seijaku -f ./my-payload.yaml
89
+ seijaku -f ./my-payload.yaml # one-time payload execution
90
+ seijaku -s ./my-scheduler.yaml # recurrent payloads execution with delay
70
91
  ```
data/bin/seijaku CHANGED
@@ -19,6 +19,7 @@ module App
19
19
  opts = OptionParser.new
20
20
  opts.banner = "Seijaku: simply runs YAML tasks with shell"
21
21
  opts.on("-f", "--file FILE", "Payload file path") { |o| options[:payload] = o }
22
+ opts.on("-s", "--scheduler FILE", "Scheduler file") { |o| options[:scheduler] = o }
22
23
  opts.on("-h", "--help", "Shows help and exit") do
23
24
  puts opts
24
25
  exit(0)
@@ -26,17 +27,41 @@ module App
26
27
 
27
28
  opts.parse!
28
29
 
29
- if options[:payload].nil?
30
+ if options[:payload].nil? and options[:scheduler].nil?
30
31
  puts opts
31
- exit 0
32
+ puts "Either -f or -s must be set"
33
+ exit 1
32
34
  end
33
35
 
34
- payload_file = YAML.safe_load(
35
- File.read(options[:payload])
36
- )
36
+ if options[:payload] and options[:scheduler]
37
+ puts opts
38
+ puts "Either -f or -s must be set"
39
+ exit 1
40
+ end
37
41
 
38
- logger.info "Starting Seijaku. Payload: #{payload_file["name"]}"
42
+ if options[:payload]
43
+ payload_file = YAML.safe_load(
44
+ File.read(options[:payload])
45
+ )
39
46
 
40
- payload = Payload.new(payload_file, logger)
41
- payload.execute!
47
+ logger.info "Starting Seijaku. Payload: #{payload_file["name"]}"
48
+
49
+ payload = Payload.new(payload_file, logger)
50
+ payload.execute!
51
+ end
52
+
53
+ if options[:scheduler]
54
+ scheduler_file = YAML.safe_load(
55
+ File.read(options[:scheduler])
56
+ )
57
+
58
+ logger.info "Starting Seijaku. Scheduler: #{scheduler_file["name"]}"
59
+ begin
60
+ scheduler = Scheduler.new(scheduler_file, logger)
61
+ scheduler.monitor!
62
+ rescue Interrupt
63
+ scheduler.soft_exit!
64
+ puts "All payloads did stop their execution gracefully."
65
+ end
66
+ end
42
67
  end
@@ -0,0 +1,54 @@
1
+ # Schedulers
2
+
3
+ Since version `0.3.0`, Seijaku implements a task scheduler that permits us to execute payloads with interval between two runs.
4
+
5
+ Instead of using the `-f` parameter to run a payload, you can use the `-s (--scheduler)` parameter to pass a `schedulerfile` to Seijaku.
6
+
7
+ ## How it works ?
8
+
9
+ Ruby isn't great at parallelism, but it does integrate `Threads`. The Scheduler will assign a Thread to all the payloads to execute.
10
+
11
+ ## Example
12
+
13
+ Here is a really basic payload for example purpose:
14
+
15
+ ```yaml
16
+ name: my-payload
17
+ tasks:
18
+ - name: say hello world
19
+ steps:
20
+ - sh: echo "hello world"
21
+ ```
22
+
23
+ If you want to run this payload every 10 seconds, you can create a scheduler file (call it whatever you want), describing the awaited behavior.
24
+
25
+ ```yaml
26
+ name: "my-scheduler"
27
+
28
+ payloads:
29
+ - payload: ./my-payload.yaml
30
+ every: 10
31
+ name: say-hello-world-10-s
32
+ ```
33
+
34
+ ## Graceful stop
35
+
36
+ CTRL+C (Interrupt signal) sends an information to all the SchedulerExecutors running, asking them to do not re-schedule the payload execution they are in charge of once the current execution is finished.
37
+
38
+ ```
39
+ ...
40
+ > CTRL+C
41
+ [2023-12-14T18:48:12.509483 #8841] INFO -- : Soft exit asked by user, waiting for payloads to stop naturally...
42
+ I, [2023-12-14T18:48:12.509789 #8841] INFO -- : test payload: still waiting...
43
+ I, [2023-12-14T18:48:12.509856 #8841] INFO -- : another-payload: still waiting...
44
+ I, [2023-12-14T18:48:13.515211 #8841] INFO -- : test payload: still waiting...
45
+ I, [2023-12-14T18:48:13.515496 #8841] INFO -- : another-payload: still waiting...
46
+ I, [2023-12-14T18:48:14.520005 #8841] INFO -- : test payload: still waiting...
47
+ I, [2023-12-14T18:48:14.520262 #8841] INFO -- : another-payload: stopped gracefully.
48
+ I, [2023-12-14T18:48:15.524617 #8841] INFO -- : test payload: stopped gracefully.
49
+ All payloads did stop their execution gracefully.
50
+ ```
51
+
52
+ ## Work in progress
53
+
54
+ * at the moment, only `every` is accepted. Cron formulas could one day be an option but its implementation is more complex.
@@ -4,6 +4,8 @@ module Seijaku
4
4
  # Payload refers to the payload YAML file submitted by the user.
5
5
  # it includes tasks, steps, variables and name.
6
6
  class Payload
7
+ attr_reader :name
8
+
7
9
  def initialize(payload, logger)
8
10
  @name = payload.fetch("name")
9
11
  @variables = get_variables(payload.fetch("variables"))
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seijaku
4
+ class Scheduler
5
+ def initialize(scheduler, logger)
6
+ @name = scheduler.fetch("name", nil)
7
+ @logger = logger
8
+ @scheduler_executors = scheduler.fetch("payloads", []).map do |payload_spec|
9
+ payload = File.read(payload_spec["payload"]).then do |data|
10
+ YAML.safe_load(data).then do |data|
11
+ Payload.new(data, logger)
12
+ end
13
+ end
14
+
15
+ SchedulerExecutor.new(payload, payload_spec, logger)
16
+ end
17
+ end
18
+
19
+ def monitor!
20
+ thd = []
21
+ @scheduler_executors.each do |scheduler_executor|
22
+ @logger.info "scheduling... #{scheduler_executor.name}"
23
+ thd << Thread.new do
24
+ scheduler_executor.execute!
25
+ end
26
+ end
27
+
28
+ thd.each &:join
29
+ end
30
+
31
+ def soft_exit!
32
+ @logger.info "Soft exit asked by user, waiting for payloads to stop naturally..."
33
+ thd = []
34
+ @scheduler_executors.each do |scheduler_executor|
35
+ thd << Thread.new do
36
+ scheduler_executor.status = :exiting
37
+ while scheduler_executor.status != :exited
38
+ @logger.info "#{scheduler_executor.name}: still waiting..."
39
+ sleep 1
40
+ end
41
+
42
+ @logger.info "#{scheduler_executor.name}: stopped gracefully"
43
+ end
44
+ end
45
+ thd.each &:join
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ module Seijaku
2
+ class SchedulerExecutor
3
+ attr_reader :name
4
+ attr_accessor :status
5
+
6
+ def initialize(payload_obj, scheduler_spec, logger)
7
+ @name = scheduler_spec.fetch("name", nil)
8
+ @every = scheduler_spec.fetch("every", nil)
9
+ @payload_file = scheduler_spec.fetch("payload")
10
+
11
+ @logger = logger
12
+ @payload = payload_obj
13
+ @status = :awaiting
14
+ end
15
+
16
+ def execute!
17
+ while @status != :exiting do
18
+ @logger.info "[SCHEDULER] <<- starting execution: #{@name}"
19
+
20
+ @status = :started
21
+ @payload.execute!
22
+ @status = :awaiting
23
+
24
+ @logger.info "[SCHEDULER] <<- finished execution: #{@name} (#{@every}s)"
25
+ sleep @every
26
+ end
27
+
28
+ @status = :exited
29
+ end
30
+ end
31
+ end
data/lib/seijaku/step.rb CHANGED
@@ -24,7 +24,7 @@ module Seijaku
24
24
  def execute!
25
25
  result = SHExecutor.new(@sh, @variables).run! if @sh
26
26
  result = BashExecutor.new(@bash, @variables).run! if @bash
27
- result = SSHExecutor.new(@ssh, @variables, @task, @ssh_hosts).run!
27
+ result = SSHExecutor.new(@ssh, @variables, @task, @ssh_hosts).run! if @ssh
28
28
 
29
29
  if result[:exit_status] != 0
30
30
  logger_output(result)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Seijaku
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/seijaku.rb CHANGED
@@ -12,6 +12,9 @@ require_relative "seijaku/executors/sh"
12
12
  require_relative "seijaku/executors/bash"
13
13
  require_relative "seijaku/executors/ssh"
14
14
 
15
+ require_relative "seijaku/scheduler"
16
+ require_relative "seijaku/scheduler_executor"
17
+
15
18
  module Seijaku
16
19
  class Error < StandardError; end
17
20
  class VariableError < Error; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seijaku
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohlrabbit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-12 00:00:00.000000000 Z
11
+ date: 2023-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt_pbkdf
@@ -71,11 +71,14 @@ files:
71
71
  - docs/1 - basic.md
72
72
  - docs/2 - variables.md
73
73
  - docs/3 - ssh.md
74
+ - docs/4 - scheduler.md
74
75
  - lib/seijaku.rb
75
76
  - lib/seijaku/executors/bash.rb
76
77
  - lib/seijaku/executors/sh.rb
77
78
  - lib/seijaku/executors/ssh.rb
78
79
  - lib/seijaku/payload.rb
80
+ - lib/seijaku/scheduler.rb
81
+ - lib/seijaku/scheduler_executor.rb
79
82
  - lib/seijaku/ssh/group.rb
80
83
  - lib/seijaku/ssh/host.rb
81
84
  - lib/seijaku/step.rb