seijaku 0.1.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: 473323709a5634fbd29899d6745d1145f5bfbd69c073b94739aa988e6851f13e
4
- data.tar.gz: 5ed3dd59fa73d5bcd30664b480d1c1dcef7b826447c9e2229ab90e0115c1de05
3
+ metadata.gz: 638106c6524b04b68139b67af16ff6b1f66d74fa82b05d15be1b2c26f42e5684
4
+ data.tar.gz: 40b3f7a10c0575df85225a04ebc040c5d6d09521ebc789148a6f888319e9ec31
5
5
  SHA512:
6
- metadata.gz: 72e7fcf802e5e1ad2c9813eca4a7cf1f5e88db0d083ce29afb64e94c14453cbe0494f9f7bb71ebd5844b8d2ce947418b2d02e5099b476446505be2999c85cdc1
7
- data.tar.gz: a5e9f5c1459a24fcb327d2d272589f84a3ec1eb323da7afe93e6f2840605e3f70d0c833cb706c21df60a0f384020991c34ed738d1009aff64e0e2cb8553be460
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,22 +1,32 @@
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
 
7
10
  Payloads are YAML files that describe the various tasks Seijaku will have to perform (in order). Each task contains one or more steps.
8
11
 
9
- A step is a shell command to be executed. Seijaku currently supports the following shells: bash and sh.
12
+ A step is a shell command to be executed. Seijaku currently supports the following shells: bash, sh and ssh.
10
13
 
11
14
  Each task can have "pre" and "post" tasks, for example to create and delete folders, or install and uninstall software needed to run a task.
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
18
23
  name: my payload
19
24
 
25
+ ssh:
26
+ - host: my-host
27
+ user: user
28
+ port: 22
29
+
20
30
  variables:
21
31
  MY_VARIABLE: a static variable
22
32
  MY_ENV_VARIABLE: $MY_ENV_VARIABLE
@@ -37,6 +47,26 @@ tasks:
37
47
  soft_fail: true
38
48
  post:
39
49
  - sh: "do something after"
50
+
51
+ - name: task with SSH executor
52
+ host: my-host
53
+ steps:
54
+ - ssh: echo "executed on host"
55
+ ```
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
40
70
  ```
41
71
 
42
72
  ## Installation
@@ -46,11 +76,16 @@ tasks:
46
76
  * Ruby 2.5+
47
77
  * Rubygem
48
78
 
49
-
50
79
  Install Seijaku using Gem:
51
80
 
52
81
  ```bash
53
82
  gem install seijaku
54
83
  ```
55
84
 
85
+ ## Usage
56
86
 
87
+ ```bash
88
+ seijaku -h
89
+ seijaku -f ./my-payload.yaml # one-time payload execution
90
+ seijaku -s ./my-scheduler.yaml # recurrent payloads execution with delay
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,10 @@
1
+ # Basic
2
+
3
+ ```yaml
4
+ name: basic payload
5
+ tasks:
6
+ - name: do something useful
7
+ steps:
8
+ - sh: echo "sh executor"
9
+ - bash: echo "bash executor"
10
+ ```
@@ -0,0 +1,24 @@
1
+ # Variables
2
+
3
+ Steps can call environment variables, listed in the `variables` dictionary.
4
+
5
+ Variable values can either be given in the payload (static) or be defined from the executive shell.
6
+
7
+ ```yaml
8
+ name: payload with variables
9
+
10
+ variables:
11
+ ENV_VARIABLE: value
12
+ PROVIDED_ENV_VARIABLE: $PROVIDED_ENV_VARIABLE
13
+
14
+ tasks:
15
+ - name: do something useful
16
+ steps:
17
+ - sh: echo "sh executor: $ENV_VARIABLE"
18
+ - bash: echo "bash executor: $PROVIDED_ENV_VARIABLE"
19
+ ```
20
+
21
+ ```bash
22
+ export PROVIDED_ENV_VARIABLE=value
23
+ seijaku -f ./docs/variables.yaml
24
+ ```
data/docs/3 - ssh.md ADDED
@@ -0,0 +1,78 @@
1
+ # SSH
2
+
3
+ Since version `0.1.0`, Seijaku implements a SSH step executor. Only one SSH host can be set per task.
4
+
5
+
6
+ ```yaml
7
+ name: payload with SSH executor
8
+
9
+ ssh:
10
+ - host: my-host
11
+ port: 22
12
+ user: ubuntu
13
+
14
+ tasks:
15
+ - name: do something useful
16
+ host: my-host
17
+ steps:
18
+ - ssh: echo "connection test"
19
+ ```
20
+
21
+ It is recommended to run `ssh-add` before running Seijaku, as each step would require you to enter your password.
22
+
23
+ ## Variables
24
+
25
+ Variables can be set from the Seijaku executive host and ran in the SSH host context:
26
+
27
+ ```yaml
28
+ name: payload with SSH executor (variables)
29
+
30
+ ssh:
31
+ - host: my-host
32
+ port: 22
33
+ user: ubuntu
34
+
35
+ variables:
36
+ ENV_VARIABLE: value
37
+ PROVIDED_ENV_VARIABLE: $PROVIDED_ENV_VARIABLE
38
+
39
+ tasks:
40
+ - name: do something useful
41
+ host: my-host
42
+ steps:
43
+ - ssh: echo "connection test"
44
+ - ssh: echo "$ENV_VARIABLE"
45
+ - ssh: echo "$PROVIDED_ENV_VARIABLE"
46
+ ```
47
+
48
+ ## Bastion / Jump host / Proxy
49
+
50
+ You sometimes need to jump over a publicly exposed server to reach another one. Seijaku supports SSH Proxy, with a maximum of 1 hop.
51
+
52
+ ```yaml
53
+ name: payload with SSH executor (bastion)
54
+
55
+ ssh:
56
+ - host: bastion-host
57
+ port: 22
58
+ user: bastion_user
59
+
60
+ - host: my-host
61
+ port: 22
62
+ user: ubuntu
63
+ bastion: bastion-host
64
+
65
+ variables:
66
+ ENV_VARIABLE: value
67
+ PROVIDED_ENV_VARIABLE: $PROVIDED_ENV_VARIABLE
68
+
69
+ tasks:
70
+ - name: do something useful
71
+ host: my-host
72
+ steps:
73
+ - ssh: echo "connection test"
74
+ - ssh: echo "$ENV_VARIABLE"
75
+ - ssh: echo "$PROVIDED_ENV_VARIABLE"
76
+ ```
77
+
78
+ The `ssh.[].host` must be a reachable address (domain name or IP).
@@ -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.
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module Seijaku
6
+ # executes `bash` commands
7
+ class BashExecutor
8
+ def initialize(raw, variables)
9
+ @command = ["bash", "-c", "'#{raw}'"]
10
+ @variables = variables
11
+ end
12
+
13
+ def run!
14
+ stdout, stderr, exit_status = Open3.capture3(
15
+ @variables,
16
+ @command.join(" ")
17
+ )
18
+
19
+ {
20
+ stdout: stdout.chomp,
21
+ stderr: stderr.chomp,
22
+ exit_status: exit_status.exitstatus,
23
+ command: @command.join(" ")
24
+ }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module Seijaku
6
+ # Execute `sh` commands
7
+ class SHExecutor
8
+ def initialize(raw, variables)
9
+ @command = ["sh", "-c", "'#{raw}'"]
10
+ @variables = variables
11
+ end
12
+
13
+ def run!
14
+ stdout, stderr, exit_status = Open3.capture3(
15
+ @variables,
16
+ @command.join(" ")
17
+ )
18
+
19
+ {
20
+ stdout: stdout.chomp,
21
+ stderr: stderr.chomp,
22
+ exit_status: exit_status.exitstatus,
23
+ command: @command.join(" ")
24
+ }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/ssh"
4
+ require "net/ssh/proxy/jump"
5
+
6
+ module Seijaku
7
+ # SSHExecutor connects to SSH host and runs command
8
+ class SSHExecutor
9
+ def initialize(raw, variables, task, ssh_hosts)
10
+ @command = ["sh", "-c", "'#{raw}'"]
11
+ @hosts = ssh_hosts
12
+ @variables = variables
13
+ @task = task
14
+ @ssh_hosts = ssh_hosts
15
+
16
+ raise SSHExecutorError, "no ssh host defined in payload", [] if ssh_hosts.nil?
17
+ end
18
+
19
+ def run!
20
+ machine = @ssh_hosts.hosts[@task.host]
21
+ result = { command: @command.join(" "), stdout: nil, stderr: nil }
22
+ status = {}
23
+ options = machine.bastion ? { proxy: bastion_setup } : {}
24
+ ssh = Net::SSH.start(machine.host, machine.user, port: machine.port, **options)
25
+ ssh.exec!(@command.join(" "), status: status) do |_ch, stream, data|
26
+ result[:stdout] = data.chomp if stream == :stdout
27
+ result[:stderr] = data.chomp unless stream == :stdout
28
+ end
29
+ ssh.close
30
+
31
+ result[:exit_status] = status[:exit_code]
32
+ result
33
+ end
34
+
35
+ def bastion_setup
36
+ bastion_name = @ssh_hosts.hosts[@task.host].bastion
37
+ bastion_host = @ssh_hosts.hosts[bastion_name]
38
+
39
+ connect_str = "#{bastion_host.user}@#{bastion_host.host}:#{bastion_host.port}"
40
+ Net::SSH::Proxy::Jump.new(connect_str)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seijaku
4
+ # Payload refers to the payload YAML file submitted by the user.
5
+ # it includes tasks, steps, variables and name.
6
+ class Payload
7
+ attr_reader :name
8
+
9
+ def initialize(payload, logger)
10
+ @name = payload.fetch("name")
11
+ @variables = get_variables(payload.fetch("variables"))
12
+ @ssh_hosts = SSHGroup.new(payload.fetch("ssh", []))
13
+ @tasks = payload.fetch("tasks").map do |task|
14
+ Task.new(task, @variables, logger, @ssh_hosts)
15
+ end
16
+ end
17
+
18
+ def execute!
19
+ @tasks.each(&:execute!)
20
+ end
21
+
22
+ def get_variables(payload_variables)
23
+ sorted_variables = {}
24
+ variables = payload_variables.map do |var|
25
+ Variable.new(var)
26
+ end
27
+
28
+ variables.each do |var|
29
+ sorted_variables.merge!({ var.key => var.value })
30
+ end
31
+
32
+ sorted_variables
33
+ end
34
+ end
35
+ end
@@ -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
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seijaku
4
+ # Group of SSH hosts
5
+ class SSHGroup
6
+ attr_reader :hosts
7
+
8
+ def initialize(ssh_group)
9
+ @hosts = {}
10
+ ssh_group.each do |host|
11
+ @hosts[host["host"]] = Host.new(host)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seijaku
4
+ # Host of SSHGroup
5
+ class Host
6
+ attr_reader :host, :port, :user, :bastion
7
+
8
+ def initialize(host)
9
+ @host = host.fetch("host", "localhost")
10
+ @port = host.fetch("port", 22)
11
+ @bastion = host.fetch("bastion", nil)
12
+ @user = host.fetch("user", "root")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seijaku
4
+ # A step is a bash or sh command that must be executed.
5
+ # steps execution is always fifo.
6
+ class Step
7
+ attr_reader :command, :pipeline
8
+
9
+ def initialize(step, variables, pipeline, logger, task, ssh_hosts = nil)
10
+ @sh = step.fetch("sh", nil)
11
+ @bash = step.fetch("bash", nil)
12
+ @ssh = step.fetch("ssh", nil)
13
+ @soft_fail = step.fetch("soft_fail", false)
14
+ @output = step.fetch("output", false)
15
+ @variables = variables
16
+ @pipeline = pipeline
17
+ @logger = logger
18
+ @task = task
19
+ @ssh_hosts = ssh_hosts
20
+
21
+ @command = (@sh || @bash) || @ssh
22
+ end
23
+
24
+ def execute!
25
+ result = SHExecutor.new(@sh, @variables).run! if @sh
26
+ result = BashExecutor.new(@bash, @variables).run! if @bash
27
+ result = SSHExecutor.new(@ssh, @variables, @task, @ssh_hosts).run! if @ssh
28
+
29
+ if result[:exit_status] != 0
30
+ logger_output(result)
31
+ exit(1) unless @soft_fail
32
+ end
33
+
34
+ return unless @output
35
+
36
+ %i[stdout stderr].each do |stream|
37
+ puts format("%<stream>s:\t %<result>s", stream: stream, result: result[stream])
38
+ end
39
+ end
40
+
41
+ def logger_output(result)
42
+ @logger.info <<~OUTPUT
43
+ command: `#{result[:command]}`
44
+ exit_code: #{result[:exit_status]}
45
+ stdout: #{result[:stdout]}
46
+ stderr: #{result[:stderr]}"
47
+ OUTPUT
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seijaku
4
+ # Task is composed of a name, an array of Step (Pre, Post)
5
+ class Task
6
+ attr_reader :host
7
+
8
+ def initialize(task, variables, logger, ssh_hosts = nil)
9
+ @name = task.fetch("name", nil)
10
+ @host = task.fetch("host", nil)
11
+ @steps = task.fetch("steps", []).map { |step| Step.new(step, variables, :steps, logger, self, ssh_hosts) }
12
+ @pre_steps = task.fetch("pre", []).map { |step| Step.new(step, variables, :pre, logger, self, ssh_hosts) }
13
+ @post_steps = task.fetch("post", []).map { |step| Step.new(step, variables, :post, logger, self, ssh_hosts) }
14
+ @logger = logger
15
+
16
+ raise TaskError, "no name set in task", [] if @name.nil?
17
+ end
18
+
19
+ def execute!
20
+ [@pre_steps, @steps, @post_steps].each do |pipeline_steps|
21
+ pipeline_steps.each do |step|
22
+ @logger.info(format("[%<name>s:%<pipeline>s] running %<command>s",
23
+ name: @name,
24
+ pipeline: step.pipeline,
25
+ command: step.command))
26
+
27
+ step.execute!
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seijaku
4
+ # variable
5
+ class Variable
6
+ attr_reader :key, :value
7
+
8
+ def initialize(variable)
9
+ @key = variable.first
10
+
11
+ value = variable.last
12
+ @value = value
13
+
14
+ return unless value.start_with?("$")
15
+
16
+ env_key = value.split("$").last
17
+ env_value = ENV.fetch(env_key, nil)
18
+
19
+ raise VariableError, "no value set for #{env_key}", [] if env_value.nil?
20
+
21
+ @value = env_value
22
+ end
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Seijaku
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/seijaku.rb CHANGED
@@ -5,12 +5,19 @@ require_relative "seijaku/payload"
5
5
  require_relative "seijaku/variable"
6
6
  require_relative "seijaku/task"
7
7
  require_relative "seijaku/step"
8
+ require_relative "seijaku/ssh/group"
9
+ require_relative "seijaku/ssh/host"
8
10
 
9
11
  require_relative "seijaku/executors/sh"
10
12
  require_relative "seijaku/executors/bash"
13
+ require_relative "seijaku/executors/ssh"
14
+
15
+ require_relative "seijaku/scheduler"
16
+ require_relative "seijaku/scheduler_executor"
11
17
 
12
18
  module Seijaku
13
19
  class Error < StandardError; end
14
20
  class VariableError < Error; end
15
21
  class TaskError < Error; end
22
+ class SSHExecutorError < Error; end
16
23
  end
data/seijaku.gemspec CHANGED
@@ -32,9 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.executables = %w[seijaku]
33
33
  spec.require_paths = ["lib"]
34
34
 
35
- # Uncomment to register a new dependency of your gem
36
- # spec.add_dependency "example-gem", "~> 1.0"
37
-
38
- # For more information and examples about making a new gem, check out our
39
- # guide at: https://bundler.io/guides/creating_gem.html
35
+ spec.add_dependency "bcrypt_pbkdf", "~> 1.1"
36
+ spec.add_dependency "ed25519", "~> 1.3"
37
+ spec.add_dependency "net-ssh", "~> 7.2"
40
38
  end
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seijaku
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.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-11 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2023-12-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bcrypt_pbkdf
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ed25519
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: net-ssh
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '7.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '7.2'
13
55
  description: CLI tool ingesting YAML files to execute tasks on different shells with
14
56
  pre and post routines.
15
57
  email:
@@ -26,7 +68,22 @@ files:
26
68
  - README.md
27
69
  - Rakefile
28
70
  - bin/seijaku
71
+ - docs/1 - basic.md
72
+ - docs/2 - variables.md
73
+ - docs/3 - ssh.md
74
+ - docs/4 - scheduler.md
29
75
  - lib/seijaku.rb
76
+ - lib/seijaku/executors/bash.rb
77
+ - lib/seijaku/executors/sh.rb
78
+ - lib/seijaku/executors/ssh.rb
79
+ - lib/seijaku/payload.rb
80
+ - lib/seijaku/scheduler.rb
81
+ - lib/seijaku/scheduler_executor.rb
82
+ - lib/seijaku/ssh/group.rb
83
+ - lib/seijaku/ssh/host.rb
84
+ - lib/seijaku/step.rb
85
+ - lib/seijaku/task.rb
86
+ - lib/seijaku/variable.rb
30
87
  - lib/seijaku/version.rb
31
88
  - seijaku.gemspec
32
89
  - sig/seijaku.rbs