org-converge 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/TODO CHANGED
@@ -2,8 +2,9 @@
2
2
  #+TODO: TODO | DONE CANCELED
3
3
  #+startup: showeverything
4
4
 
5
- * [/] 0.0.4 version
5
+ * [0/6] 0.0.4 version
6
6
 
7
+ - [ ] Support runlists notifications via =#+NAME:=
7
8
  - [ ] Macros can be loaded and applied to the configuration
8
9
  - [ ] Actually support converging and idempotency
9
10
  + Do not do an operation unless it is required
@@ -12,10 +13,13 @@
12
13
  - [ ] Heuristics for determining which binary to use for running the script
13
14
  - [ ] Display how the run would look like without making changes
14
15
  : org-converge setupfile.org --dry-run
16
+ - [ ] Use :eval for evaling blocks (off by default)
17
+ - [ ] Can use :dir for running a process relative to that directory
15
18
 
16
- * [0/3] 0.0.3 version
19
+ * [2/2] 0.0.3 version
17
20
 
18
- - [ ] Support run lists for sequential execution
21
+ - [X] Support sequential and parallel execution
22
+ - [X] Can use :mkdirp for 0644 permissions
19
23
 
20
24
  * [2/2] 0.0.2 version of org-converge
21
25
 
@@ -35,7 +39,7 @@ Need some basic functionality of what Org babel offers first.
35
39
  - [X] ~--tangle~ flag
36
40
  - [X] Support a root dir for when not running relative to the directory
37
41
 
38
- * [1/18] Ideas
42
+ * [1/19] Ideas
39
43
  ** CANCELED How to set the permissions from the directory from the file that is being tangled when it does not exists?
40
44
 
41
45
  By default, this would be 0644, but we also need to specify the
@@ -51,6 +55,7 @@ until I can think of something better.
51
55
 
52
56
  http://orgmode.org/manual/Specific-header-arguments.html#Specific-header-arguments
53
57
 
58
+ ** TODO Use sshkit for running remote processes
54
59
  ** TODO We don't need to create the directories in most cases (:mkdirp yes)
55
60
 
56
61
  Something like this is not required because the ~:tangle~ blocks
data/bin/org-converge CHANGED
@@ -8,7 +8,7 @@ doc = <<OPTIONS
8
8
  org-converge: A light configuration management tool for Org mode
9
9
 
10
10
  Usage:
11
- org-converge <org_file> [--tangle] [--showfiles] [--log=<logfile>] [--root-dir=<root_dir>]
11
+ org-converge <org_file> [--tangle] [--showfiles] [--log=<logfile>] [--root-dir=<root_dir>] [--runmode=<runmode>]
12
12
 
13
13
  Options:
14
14
 
@@ -6,21 +6,26 @@
6
6
 
7
7
  The following will setup and configure ~fluentd~ on a node.
8
8
 
9
- * Settings
10
-
11
- These can be defined with Org mode macro directives, but thinking on
12
- other ways to do it as well.
13
-
14
- #+macro: fluentd_port 4224
15
- #+macro: fluentd_filepath here.log
16
-
17
9
  * Setup :setup:
18
10
 
19
11
  Fluentd can be installed either as a package from treasure data or
20
12
  just as a gem. In this example, we only install as a gem:
21
13
 
22
- #+begin_src sh
23
- gem install fluentd
14
+ # Fails because of permissions
15
+ #+begin_src sh :shebang #!/bin/bash
16
+ gem list | grep fluentd
17
+ fluentd_installed=$?
18
+ if [ $fluentd_installed -ne 0 ]; then
19
+ echo "Installing fluentd...";
20
+ gem install fluentd
21
+ fi
22
+
23
+ gem list | grep fluent-logger
24
+ fluentd_logger_installed=$?
25
+ if [ $fluentd_logger_installed -ne 0 ]; then
26
+ echo "Installing fluent-logger for Ruby...";
27
+ gem install fluent-logger
28
+ fi
24
29
  #+end_src
25
30
 
26
31
  * Configuration :config:
@@ -28,23 +33,49 @@ gem install fluentd
28
33
  Set Fluentd to listen on the ~fluentd_port~ port and to write logs
29
34
  to ~fluentd_filepath~.
30
35
 
31
- #+begin_src conf :tangle etc/fluentd.conf
36
+ #+begin_src conf :tangle fluentd.conf
32
37
  <source>
33
38
  type forward
34
- port {{{ fluentd_port }}}
39
+ port 4224
35
40
  </source>
36
41
 
37
42
  <match **>
38
43
  type file
39
- path {{{ fluentd_filepath }}}
44
+ path here.log
45
+ flush_interval 0s
40
46
  </match>
41
47
  #+end_src
42
48
 
43
49
  # A script to daemonize the process could be written here, as well as
44
50
  # any /etc/default/* required settings...
45
51
 
52
+ * Flusher
53
+
54
+ Send some logs:
55
+
56
+ #+begin_src ruby :shebang #!/usr/local/bin/ruby
57
+ require 'fluent-logger'
58
+
59
+ sleep 2 # Wait for fluentd to start at the beginning
60
+
61
+ Fluent::Logger::FluentLogger.open(nil, :host => 'localhost', :port => 4224)
62
+
63
+ 100.times do |n|
64
+ Fluent::Logger.post("myapp.access", {"agent"=>"foo - #{n}"})
65
+ sleep 0.1
66
+ end
67
+ #+end_src
68
+
69
+ * Tailer
70
+
71
+ And yet another process which will be tailing the file that we are sending logs too:
72
+
73
+ #+begin_src sh :shebang #!/bin/bash
74
+ tail -f here.*
75
+ #+end_src
76
+
46
77
  * Start which was configured here :start:
47
78
 
48
- #+begin_src sh
49
- fluentd -c etc/fluentd.conf -vvv
79
+ #+begin_src bash :shebang #!/bin/bash
80
+ fluentd -c fluentd.conf -vvv
50
81
  #+end_src
@@ -16,25 +16,33 @@ module Orgmode
16
16
  @root_dir = options[:root_dir]
17
17
  end
18
18
 
19
- # TODO: should be able to tangle relatively to a dir
20
19
  def tangle!
21
20
  logger.info "Tangling #{ob.tangle.keys.count} files..."
22
21
 
23
- ob.tangle.each do |tangle_file, lines|
22
+ ob.tangle.each do |tangle_file, script|
24
23
  file = if @root_dir
25
24
  File.join(@root_dir, tangle_file)
26
25
  else
27
26
  tangle_file
28
27
  end
29
28
 
30
- logger.info "BEGIN(#{tangle_file}): Tangling #{lines.count} lines at '#{file}'"
31
- # TODO: should abort when the directory does not exists
32
- # Org mode blocks have :mkdirp true
29
+ logger.info "BEGIN(#{tangle_file}): Tangling #{script[:lines].split('\n').count} lines at '#{file}'"
33
30
  # TODO: should abort when the directory failed because of permissions
34
31
  # TODO: should apply :tangle-mode for permissions
35
- if not Dir.exists?(File.dirname(file))
36
- logger.error "Could not tangle #{file} because directory does not exists!"
37
- raise TangleError
32
+ directory = File.expand_path(File.dirname(file))
33
+ if not Dir.exists?(directory)
34
+ begin
35
+ if script[:header][:mkdirp] == 'true'
36
+ p script
37
+ logger.info "Create dir for #{file} since it does not exists..."
38
+ FileUtils.mkdir_p(File.dirname(file), :mode => 0755)
39
+ else
40
+ logger.warn "Cannot tangle #{file} because directory does not exists!"
41
+ end
42
+ rescue => e
43
+ p e
44
+ raise TangleError
45
+ end
38
46
  end
39
47
 
40
48
  if File.exists?(file)
@@ -43,7 +51,7 @@ module Orgmode
43
51
 
44
52
  begin
45
53
  File.open(file, 'w') do |f|
46
- lines.each do |line|
54
+ script[:lines].each_line do |line|
47
55
  f.puts line
48
56
  end
49
57
  end
@@ -64,11 +72,10 @@ module Orgmode
64
72
 
65
73
  ob.scripts.each_pair do |script_key, script|
66
74
  file = script_key.to_s
67
- if File.exists?(file)
68
- logger.warn "File already exists at #{file}, it will be overwritten"
69
- end
75
+ logger.warn "File already exists at #{file}, it will be overwritten" if File.exists?(file)
70
76
 
71
- File.open(File.join(run_dir, file), 'w') do |f|
77
+ # Files with :shebang are executable by default
78
+ File.open(File.join(run_dir, file), 'w', 0755) do |f|
72
79
  script[:lines].each_line do |line|
73
80
  f.puts line
74
81
  end
@@ -8,7 +8,7 @@ module Orgmode
8
8
 
9
9
  # ~@tangle~ files are put in the right path
10
10
  # : @tangle['/path'] = [Lines]
11
- @tangle = Hash.new {|h,k| h[k] = []}
11
+ @tangle = Hash.new {|h,k| h[k] = {:lines => '', :header => {}, :lang => ''}}
12
12
 
13
13
  # ~@scripts~ are tangled in order and ran
14
14
  # : @scripts = [text, text, ...]
@@ -18,7 +18,7 @@ module Orgmode
18
18
  @buffer = ''
19
19
  end
20
20
 
21
- def push_mode(mode, indent);
21
+ def push_mode(mode, indent)
22
22
  super(mode, indent)
23
23
  end
24
24
 
@@ -34,13 +34,19 @@ module Orgmode
34
34
  case
35
35
  when line.block_header_arguments[':tangle']
36
36
  @current_tangle = line.block_header_arguments[':tangle']
37
+ @tangle[@current_tangle][:header] = {
38
+ :shebang => line.block_header_arguments[':shebang'],
39
+ :mkdirp => line.block_header_arguments[':mkdirp']
40
+ }
41
+ @tangle[@current_tangle][:lang] = line.block_lang
37
42
  when line.block_header_arguments[':shebang']
38
43
  @current_tangle = nil
39
44
  @buffer = ''
40
45
 
41
46
  # Need to keep track of the options from a block before running it
42
47
  @scripts[@scripts_counter][:header] = {
43
- :shebang => line.block_header_arguments[':shebang']
48
+ :shebang => line.block_header_arguments[':shebang'],
49
+ :mkdirp => line.block_header_arguments[':mkdirp']
44
50
  }
45
51
  @scripts[@scripts_counter][:lang] = line.block_lang
46
52
 
@@ -52,7 +58,7 @@ module Orgmode
52
58
  case
53
59
  when (line.assigned_paragraph_type == :code and @current_tangle)
54
60
  # Need to keep track of the current tangle to buffer its lines
55
- @tangle[@current_tangle] << line
61
+ @tangle[@current_tangle][:lines] << line.output_text << "\n"
56
62
  when (line.assigned_paragraph_type == :code)
57
63
  # When a tangle is not going on, it means that the lines would go
58
64
  # into a runnable script
@@ -10,7 +10,11 @@ module OrgConverge
10
10
  @dotorg = options['<org_file>']
11
11
  @logger = Logger.new(options['--log'] || STDOUT)
12
12
  @root_dir = options['--root-dir']
13
- @run_dir = File.expand_path('run')
13
+ @run_dir = if @root_dir
14
+ File.expand_path(File.join(@root_dir, 'run'))
15
+ else
16
+ File.expand_path('run')
17
+ end
14
18
  @ob = Orgmode::Parser.new(File.read(dotorg)).babelize
15
19
  @babel = nil
16
20
  end
@@ -33,7 +37,16 @@ module OrgConverge
33
37
 
34
38
  def converge!
35
39
  tangle!
36
- run_blocks!
40
+ case @options['--runmode']
41
+ when 'parallel'
42
+ run_blocks_in_parallel!
43
+ when 'sequential'
44
+ run_blocks_sequentially!
45
+ when 'runlist'
46
+ # TODO
47
+ else
48
+ run_blocks_in_parallel!
49
+ end
37
50
  end
38
51
 
39
52
  def tangle!
@@ -42,9 +55,29 @@ module OrgConverge
42
55
  logger.error "Cannot converge because there were errors during tangle step".red
43
56
  end
44
57
 
58
+ def run_blocks_sequentially!
59
+ @engine = OrgConverge::Engine.new(:logger => @logger, :babel => @babel)
60
+ babel.tangle_runnable_blocks!(@run_dir)
61
+
62
+ runlist_stack = []
63
+ babel.ob.scripts.each do |key, script|
64
+ runlist_stack << [key, script]
65
+ end
66
+
67
+ while not runlist_stack.empty?
68
+ key, script = runlist_stack.shift
69
+ with_running_engine do |engine|
70
+ file = File.expand_path("#{@run_dir}/#{key}")
71
+ cmd = "#{script[:lang]} #{file}"
72
+ engine.register script[:lang], cmd, { :cwd => @root_dir, :logger => logger }
73
+ end
74
+ end
75
+ logger.info "Run has completed successfully.".green
76
+ end
77
+
45
78
  # TODO: Too much foreman has made this running blocks in parallel the default behavior.
46
79
  # We should actually be supporting run lists instead, but liking this experiment so far.
47
- def run_blocks!
80
+ def run_blocks_in_parallel!
48
81
  @engine = OrgConverge::Engine.new(:logger => @logger, :babel => @babel)
49
82
  babel.tangle_runnable_blocks!(@run_dir)
50
83
  babel.ob.scripts.each do |key, script|
@@ -54,6 +87,13 @@ module OrgConverge
54
87
  end
55
88
  logger.info "Running code blocks now! (#{babel.ob.scripts.count} runnable blocks found in total)"
56
89
  @engine.start
90
+ logger.info "Run has completed successfully.".green
91
+ end
92
+
93
+ def with_running_engine
94
+ engine = OrgConverge::Engine.new(:logger => @logger, :babel => @babel)
95
+ yield engine
96
+ engine.start
57
97
  end
58
98
 
59
99
  def babel
@@ -61,11 +101,9 @@ module OrgConverge
61
101
  end
62
102
 
63
103
  def showfiles
64
- ob.tangle.each do |file, lines|
104
+ ob.tangle.each do |file, block|
65
105
  puts "---------- #{file} --------------".green
66
- lines.each do |line|
67
- puts line
68
- end
106
+ puts block[:lines]
69
107
  end
70
108
 
71
109
  ob.scripts.each do |index, block|
@@ -25,9 +25,8 @@ module OrgConverge
25
25
  watch_for_output
26
26
  sleep 0.1
27
27
  begin
28
- status = watch_for_termination
28
+ status = watch_for_termination
29
29
  end while @running.count > 0
30
- logger.info "Run has completed successfully.".green
31
30
  end
32
31
 
33
32
  # Overriden: we do not consider process formations
@@ -1,3 +1,3 @@
1
1
  module OrgConverge
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -6,6 +6,7 @@ with all output being flushed to the screen.
6
6
  - Count some numbers with bash
7
7
 
8
8
  #+begin_src sh :shebang #!/bin/bash
9
+ echo '' > out.log
9
10
  for i in `seq 1 5`; do
10
11
  echo "Writing! $i"
11
12
  echo "hello $i" >> out.log
@@ -16,6 +17,7 @@ with all output being flushed to the screen.
16
17
  - Count some numbers with ruby
17
18
 
18
19
  #+begin_src ruby :shebang #!/usr/bin/ruby
20
+ sleep 0.2
19
21
  10.times do |n|
20
22
  puts "And now writing! #{n}"
21
23
  File.open("out.log", "a") {|f| f.puts "Hello again #{n}" }
@@ -16,3 +16,10 @@ mysql:
16
16
  host: somewhere-example.local
17
17
  password: 111111111
18
18
  #+end_src
19
+
20
+ When directory does not exists, it should create
21
+ when =:mkdirp= is specified.
22
+
23
+ #+begin_src yaml :tangle config/hello.yml :mkdirp true
24
+ config: 'hello'
25
+ #+end_src
@@ -0,0 +1,15 @@
1
+ # -*- mode: org; mode: auto-fill; -*-
2
+ #+TITLE: Runlist example
3
+
4
+ In this example, we will have scripts being chained one after another.
5
+
6
+ #+begin_src sh :shebang #!/bin/bash
7
+ sleep 5
8
+ echo "first" > out.log
9
+ #+end_src
10
+
11
+ The script below will only be called once the one above is run.
12
+
13
+ #+begin_src ruby :shebang #!/usr/local/bin/ruby
14
+ File.open("out.log", 'a') {|f| f.puts "second" }
15
+ #+end_src
@@ -1,28 +1,58 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe OrgConverge::Command do
4
- context "when converging 'basic_tangle'" do
5
- example_dir = File.join(EXAMPLES_DIR, 'basic_tangle')
6
4
 
7
- it "should tangle a 'conf.yml' file" do
8
- setup_file = File.join(example_dir, 'setup.org')
9
- o = OrgConverge::Command.new({
10
- '<org_file>' => setup_file,
11
- '--root-dir' => example_dir
12
- })
13
- success = o.execute!
14
- success.should == true
5
+ it "should converge 'basic_tangle'" do
6
+ example_dir = File.join(EXAMPLES_DIR, 'basic_tangle')
15
7
 
16
- expected_contents = File.read(File.join(example_dir, 'conf.yml.expected'))
8
+ setup_file = File.join(example_dir, 'setup.org')
9
+ o = OrgConverge::Command.new({
10
+ '<org_file>' => setup_file,
11
+ '--root-dir' => example_dir
12
+ })
13
+ success = o.execute!
14
+ success.should == true
17
15
 
18
- resulting_file = File.join(example_dir, 'conf.yml')
19
- File.exists?(resulting_file).should == true
16
+ expected_contents = File.read(File.join(example_dir, 'conf.yml.expected'))
17
+ resulting_file = File.join(example_dir, 'conf.yml')
18
+ File.exists?(resulting_file).should == true
20
19
 
21
- result = File.read(resulting_file)
20
+ result = File.read(resulting_file)
21
+ result.should == expected_contents
22
22
 
23
- result.should == expected_contents
24
- end
23
+ resulting_file = File.join(example_dir, 'config/hello.yml')
24
+ File.exists?(resulting_file).should == true
25
25
  end
26
- end
27
26
 
27
+ it "should converge 'basic_run_example'" do
28
+ example_dir = File.join(EXAMPLES_DIR, 'basic_run_example')
29
+ setup_file = File.join(example_dir, 'setup.org')
30
+ o = OrgConverge::Command.new({
31
+ '<org_file>' => setup_file,
32
+ '--root-dir' => example_dir
33
+ })
34
+ success = o.execute!
35
+ success.should == true
36
+
37
+ expected_contents = File.read(File.join(example_dir, 'out.log'))
38
+ expected_contents.lines.count.should == 16
39
+ end
28
40
 
41
+ it "should converge 'runlist_example' sequentially" do
42
+ example_dir = File.join(EXAMPLES_DIR, 'runlist_example')
43
+ setup_file = File.join(example_dir, 'setup.org')
44
+
45
+ o = OrgConverge::Command.new({
46
+ '<org_file>' => setup_file,
47
+ '--root-dir' => example_dir,
48
+ '--runmode' => 'sequential'
49
+ })
50
+ success = o.execute!
51
+ success.should == true
52
+
53
+ File.executable?(File.join(example_dir, 'run/0')).should == true
54
+ File.executable?(File.join(example_dir, 'run/1')).should == true
55
+ expected_contents = "first\nsecond\n"
56
+ File.read(File.join(example_dir, 'out.log')).should == expected_contents
57
+ end
58
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: org-converge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-30 00:00:00.000000000 Z
12
+ date: 2014-04-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: docopt
16
- requirement: &70243331730100 !ruby/object:Gem::Requirement
16
+ requirement: &70113563924580 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - =
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.5.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70243331730100
24
+ version_requirements: *70113563924580
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: org-ruby
27
- requirement: &70243331729260 !ruby/object:Gem::Requirement
27
+ requirement: &70113563923840 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.9.2
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70243331729260
35
+ version_requirements: *70113563923840
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: foreman
38
- requirement: &70243331728140 !ruby/object:Gem::Requirement
38
+ requirement: &70113563921200 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 0.63.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70243331728140
46
+ version_requirements: *70113563921200
47
47
  description: A light configuration management tool for Org mode
48
48
  email:
49
49
  - waldemar.quevedo@gmail.com
@@ -75,6 +75,7 @@ files:
75
75
  - spec/converge_examples/basic_run_example/setup.org
76
76
  - spec/converge_examples/basic_tangle/conf.yml.expected
77
77
  - spec/converge_examples/basic_tangle/setup.org
78
+ - spec/converge_examples/runlist_example/setup.org
78
79
  - spec/converge_spec.rb
79
80
  - spec/spec_helper.rb
80
81
  homepage: https://github.com/wallyqs/org-converge
@@ -106,6 +107,7 @@ test_files:
106
107
  - spec/converge_examples/basic_run_example/setup.org
107
108
  - spec/converge_examples/basic_tangle/conf.yml.expected
108
109
  - spec/converge_examples/basic_tangle/setup.org
110
+ - spec/converge_examples/runlist_example/setup.org
109
111
  - spec/converge_spec.rb
110
112
  - spec/spec_helper.rb
111
113
  has_rdoc: