├── .gitignore
├── .gitmodules
├── .rspec
├── .rubocop.yml
├── .travis.yml
├── Appraisals
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── bin
└── shadow_puppet
├── examples
└── foo.rb
├── gemfiles
├── 2.1.gemfile.lock
├── 2.2.gemfile
├── 2.2.gemfile.lock
├── 2.3.11.gemfile.lock
├── 2.3.gemfile
├── 2.3.gemfile.lock
├── 3.0.gemfile
├── 3.0.gemfile.lock
├── 3.1.gemfile
├── 3.1.gemfile.lock
├── 3.2.gemfile
└── 3.2.gemfile.lock
├── lib
├── shadow_puppet.rb
└── shadow_puppet
│ ├── core_ext.rb
│ ├── manifest.rb
│ ├── test.rb
│ └── version.rb
├── rubocop-todo.yml
├── shadow_puppet.gemspec
└── spec
├── cli_spec.rb
├── core_ext_spec.rb
├── fixtures
├── cli_spec_manifest.rb
└── manifests.rb
├── manifest_spec.rb
├── spec_helper.rb
├── test_spec.rb
└── type_spec.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | doc
3 | tmp
4 | Gemfile.lock
5 | .rvmrc
6 | .ruby-version
7 | .ruby-gemset
8 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "doc"]
2 | path = doc
3 | url = git://github.com/railsmachine/shadow_puppet.git
4 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --require ./spec/spec_helper.rb --backtrace --color --format d
2 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: rubocop-todo.yml
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 1.8.7
4 | - 1.9.2
5 | - 1.9.3
6 | gemfile:
7 | - gemfiles/2.2.gemfile
8 | - gemfiles/2.3.gemfile
9 | - gemfiles/3.0.gemfile
10 | - gemfiles/3.1.gemfile
11 | - gemfiles/3.2.gemfile
12 | bundler_args: --without debug
13 | # quiet notifications for now
14 | notifications:
15 | email: false
16 | irc:
17 | channels:
18 | - "irc.freenode.org#moonshine"
19 |
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | # 2.1 lacks deep_merge, so no more love
2 | #appraise "2.1" do
3 | # gem "activesupport", "~> 2.1.0"
4 | #end
5 |
6 | appraise "2.2" do
7 | gem "activesupport", "~> 2.2.0"
8 | end
9 |
10 |
11 | appraise "2.3" do
12 | gem "activesupport", "~> 2.3.0"
13 | end
14 |
15 | appraise "3.0" do
16 | gem "activesupport", "~> 3.0.0"
17 | end
18 |
19 | appraise "3.1" do
20 | gem "activesupport", "~> 3.1.0"
21 | end
22 |
23 | appraise "3.2" do
24 | gem "activesupport", "~> 3.2.0"
25 | end
26 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gemspec
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ShadowPuppet
2 |
3 | ShadowPuppet is a Ruby DSL for Puppet, extracted out of the work we at Rails
4 | Machine are doing on [Moonshine](http://blog.railsmachine.com/articles/2009/01/16/moonshine-configuration-management-and-deployment/).
5 |
6 | ShadowPuppet provides a DSL for creating collections ("manifests") of Puppet
7 | Resources in Ruby. For documentation on writing these manifests, please see
8 | ShadowPuppet::Manifest.
9 |
10 | A [binary](http://railsmachine.github.com/shadow_puppet/files/bin/shadow_puppet.html) is provided to parse and execute a
11 | ShadowPuppet::Manifest.
12 |
13 | ## Running the Test Suite
14 |
15 | First time:
16 |
17 | $ gem install bundler
18 | $ bundle install
19 | $ bundle exec rake appraisal:install
20 |
21 | Run against all versions of activesupport:
22 |
23 | $ bundle exec rake appraisal spec
24 |
25 |
26 | Run against a specific one, ie 3.2:
27 |
28 | $ bundle exec rake appraisal:3.2 spec
29 |
30 |
31 | ***
32 |
33 | All content copyright © 2014, [Rails Machine, LLC](http://railsmachine.com)
34 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rspec/core/rake_task"
3 | require "rubocop/rake_task"
4 |
5 | RSpec::Core::RakeTask.new
6 | #Rubocop::RakeTask.new
7 |
8 | task :default => [:spec, :rubocop]
9 |
--------------------------------------------------------------------------------
/bin/shadow_puppet:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #== Sample manifest:
3 | #
4 | # $ cat examples/foo.rb
5 | # class Foo < ShadowPuppet::Manifest
6 | # recipe :demo, :text => 'foo'
7 | # recipe :some_gems
8 | #
9 | # def some_gems
10 | # package 'rails', :ensure => :updated, :provider => :gem
11 | # package 'railsmachine', :ensure => '1.0.5', :provider => :gem, :require => package('capistrano')
12 | # package 'capistrano', :ensure => :updated, :provider => :gem
13 | # end
14 | #
15 | # def demo(options = {})
16 | # exec 'sample', :command => "echo '#{options[:text]}' > /tmp/sample.txt"
17 | # file '/tmp/sample2.txt', :ensure => :present, :content => Facter.to_hash.inspect
18 | # end
19 | # end
20 | #
21 | #== Executing this manifest:
22 | #
23 | # $ shadow_puppet examples/foo.rb
24 | # notice: /shadow_puppet:19861340/Exec[foo]/returns: executed successfully
25 | # $
26 | #
27 | #The shadow_puppet binary parses the given ruby code, which is
28 | #expected to contain a class named Foo that inherits from
29 | #ShadowPuppet::Manifest. An instance of this class is created, and the
30 | #execute method is called. All output is printed to the console.
31 | require 'bundler'
32 |
33 | def unindent(string)
34 | indentation = string[/\A\s*/]
35 | string.strip.gsub(/^#{indentation}/, "")
36 | end
37 |
38 | begin
39 | require 'optparse'
40 | require 'shadow_puppet/version'
41 |
42 | ShadowPuppetOptions = Struct.new(:graph, :noop, :ignore)
43 | options = ShadowPuppetOptions.new
44 |
45 | opts = OptionParser.new do |opts|
46 | opts.banner = "Usage: shadow_puppet [options] "
47 |
48 | opts.separator ""
49 | opts.separator "Specific options:"
50 | opts.version = ShadowPuppet::VERSION
51 |
52 | opts.on("--tutorial", "A simple tutorial for usage") do
53 | puts unindent(<<-EOF)
54 | NAME
55 | Shadow Puppet
56 |
57 | AUTHOR
58 | Jesse Newland
59 | jesse@railsmachine.com
60 |
61 | DESCRIPTION
62 | A Ruby DSL for puppet
63 |
64 | EXAMPLES
65 | Sample manifest:
66 |
67 | # foo.rb
68 | class Foo < ShadowPuppet::Manifest
69 | recipe :foo
70 |
71 | def foo
72 | exec :foo, :command => 'echo "foo" > /tmp/foo.txt'
73 | file '/tmp/example.txt', :ensure => :present, :content => Facter.to_hash.inspect
74 | end
75 | end
76 |
77 | Executing this manifest:
78 |
79 | $ shadow_puppet foo.rb
80 | notice: /shadow_puppet:19861340/Exec[foo]/returns: executed successfully
81 | $
82 |
83 | The shadow_puppet binary parses the given ruby code, which is
84 | expected to contain a class named Foo that inherits from
85 | ShadowPuppet::Manifest. An instance of this class is created, and the
86 | execute method is called. All output is printed to the console.
87 | EOF
88 |
89 | exit(1)
90 | end
91 |
92 | opts.on("-g", "--graph=[FILE]", "File to write graph .dot to") do |graph|
93 | options.graph = graph
94 | end
95 |
96 | opts.on("-n", "--noop", "Run in a no-op or dry-run mode") do
97 | options.noop = true
98 | end
99 |
100 | opts.on("-i", "--ignore", "Always exit with 0 exit code even if a Puppet task fails") do
101 | options.ignore = true
102 | end
103 |
104 | opts.on_tail("-h", "--help", "Show this message") do
105 | puts opts
106 | exit
107 | end
108 | end
109 |
110 | opts.parse!
111 |
112 | # Take any variables set on the command line and update ENV
113 | ARGV.delete_if do |arg|
114 | next unless arg =~ /^(\w+)=(.*)$/
115 | ENV[$1] = $2
116 | end
117 |
118 | unless filename = ARGV[0]
119 | puts "Error: Manifest filename must be provided\n\n"
120 | puts opts
121 | exit(1)
122 | end
123 |
124 | $LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib'))
125 | require 'shadow_puppet/core_ext'
126 | require 'shadow_puppet'
127 | require 'fileutils'
128 |
129 | if options.noop
130 | Puppet[:noop] = true
131 | Puppet[:show_diff] = true
132 | end
133 |
134 | Puppet::Util::Log.newdestination(:console)
135 |
136 | klass = File.basename(filename, ".rb")
137 | require filename
138 | manifest = klass.camelize.constantize.new
139 |
140 | if options.graph
141 | manifest.graph_to(klass.classify, options.graph)
142 | else
143 | if manifest.execute! or options.ignore
144 | exit(0)
145 | else
146 | exit(1)
147 | end
148 | end
149 |
150 | rescue Errno::EACCES
151 | puts "Please run shadow_puppet as root"
152 | rescue Exception => e
153 | if e.instance_of?(SystemExit)
154 | raise
155 | else
156 | puts "Uncaught exception: #{e.class}: #{e.message}"
157 | puts "\t#{e.backtrace.join("\n\t")}"
158 | exit(1)
159 | end
160 | end
161 |
--------------------------------------------------------------------------------
/examples/foo.rb:
--------------------------------------------------------------------------------
1 | class Foo < ShadowPuppet::Manifest
2 | recipe :demo, :text => 'foo'
3 | recipe :some_gems
4 |
5 | def some_gems
6 | package 'rails', :ensure => :updated, :provider => :gem
7 | package 'railsmachine', :ensure => '1.0.5', :provider => :gem, :require => package('capistrano')
8 | package 'capistrano', :ensure => :updated, :provider => :gem
9 | end
10 |
11 | def demo(options = {})
12 | exec 'sample', :command => "echo '#{options[:text]}' > /tmp/sample.txt"
13 | file '/tmp/sample2.txt', :ensure => :present, :content => Facter.to_hash.inspect
14 | end
15 | end
--------------------------------------------------------------------------------
/gemfiles/2.1.gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/technicalpickles/appraisal.git
3 | revision: a755e5a086fa545f13c7465009e3fb8d4b4ff571
4 | specs:
5 | appraisal (0.3.8)
6 | bundler
7 | rake
8 |
9 | GEM
10 | remote: http://rubygems.org/
11 | specs:
12 | activesupport (2.1.2)
13 | archive-tar-minitar (0.5.2)
14 | builder (3.0.0)
15 | columnize (0.3.4)
16 | diff-lcs (1.1.3)
17 | facter (1.6.1)
18 | git (1.2.5)
19 | highline (1.6.2)
20 | i18n (0.6.0)
21 | jeweler (1.6.4)
22 | bundler (~> 1.0)
23 | git (>= 1.2.5)
24 | rake
25 | json (1.6.1)
26 | linecache (0.46)
27 | rbx-require-relative (> 0.0.4)
28 | linecache19 (0.5.12)
29 | ruby_core_source (>= 0.1.4)
30 | puppet (2.7.3)
31 | facter (>= 1.5.1)
32 | rake (0.9.2)
33 | rbx-require-relative (0.0.5)
34 | rdoc (3.10)
35 | json (~> 1.4)
36 | rspec (2.6.0)
37 | rspec-core (~> 2.6.0)
38 | rspec-expectations (~> 2.6.0)
39 | rspec-mocks (~> 2.6.0)
40 | rspec-core (2.6.4)
41 | rspec-expectations (2.6.0)
42 | diff-lcs (~> 1.1.2)
43 | rspec-mocks (2.6.0)
44 | ruby-debug (0.10.4)
45 | columnize (>= 0.1)
46 | ruby-debug-base (~> 0.10.4.0)
47 | ruby-debug-base (0.10.4)
48 | linecache (>= 0.3)
49 | ruby-debug-base19 (0.11.25)
50 | columnize (>= 0.3.1)
51 | linecache19 (>= 0.5.11)
52 | ruby_core_source (>= 0.1.4)
53 | ruby-debug19 (0.11.6)
54 | columnize (>= 0.3.1)
55 | linecache19 (>= 0.5.11)
56 | ruby-debug-base19 (>= 0.11.19)
57 | ruby_core_source (0.1.5)
58 | archive-tar-minitar (>= 0.5.2)
59 |
60 | PLATFORMS
61 | ruby
62 |
63 | DEPENDENCIES
64 | activesupport (~> 2.1.0)
65 | appraisal!
66 | builder (>= 2.1.2)
67 | highline (>= 1.5.0)
68 | i18n (>= 0.5.0)
69 | jeweler (~> 1.6.2)
70 | puppet (= 2.7.3)
71 | rake
72 | rdoc
73 | rspec (~> 2.6.0)
74 | rspec-core (~> 2.6.0)
75 | ruby-debug
76 | ruby-debug19
77 |
--------------------------------------------------------------------------------
/gemfiles/2.2.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source :rubygems
4 |
5 | gem "puppet", "2.7.3"
6 | gem "highline", ">= 1.5.0"
7 | gem "builder", ">= 2.1.2"
8 | gem "activesupport", "~> 2.2.0"
9 | gem "i18n", ">= 0.5.0"
10 |
11 | group :development do
12 | gem "rake"
13 | gem "appraisal", :git=>"git://github.com/technicalpickles/appraisal.git"
14 | gem "rspec", "~> 2.8.0"
15 | gem "rspec-core", "~> 2.8.0"
16 | gem "test-unit"
17 | gem "jeweler", "~> 1.6.2"
18 | gem "rdoc"
19 | end
20 |
21 | group :debug do
22 | gem "ruby-debug", :platforms=>:ruby_18
23 | gem "ruby-debug19", :platforms=>:ruby_19
24 | end
25 |
26 |
--------------------------------------------------------------------------------
/gemfiles/2.2.gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/technicalpickles/appraisal.git
3 | revision: a755e5a086fa545f13c7465009e3fb8d4b4ff571
4 | specs:
5 | appraisal (0.3.8)
6 | bundler
7 | rake
8 |
9 | GEM
10 | remote: http://rubygems.org/
11 | specs:
12 | activesupport (2.2.3)
13 | archive-tar-minitar (0.5.2)
14 | builder (3.0.0)
15 | columnize (0.3.4)
16 | diff-lcs (1.1.3)
17 | facter (1.6.1)
18 | git (1.2.5)
19 | highline (1.6.2)
20 | i18n (0.6.0)
21 | jeweler (1.6.4)
22 | bundler (~> 1.0)
23 | git (>= 1.2.5)
24 | rake
25 | json (1.6.1)
26 | linecache (0.46)
27 | rbx-require-relative (> 0.0.4)
28 | linecache19 (0.5.12)
29 | ruby_core_source (>= 0.1.4)
30 | puppet (2.7.3)
31 | facter (>= 1.5.1)
32 | rake (0.9.2)
33 | rbx-require-relative (0.0.5)
34 | rdoc (3.10)
35 | json (~> 1.4)
36 | rspec (2.8.0)
37 | rspec-core (~> 2.8.0)
38 | rspec-expectations (~> 2.8.0)
39 | rspec-mocks (~> 2.8.0)
40 | rspec-core (2.8.0)
41 | rspec-expectations (2.8.0)
42 | diff-lcs (~> 1.1.2)
43 | rspec-mocks (2.8.0)
44 | ruby-debug (0.10.4)
45 | columnize (>= 0.1)
46 | ruby-debug-base (~> 0.10.4.0)
47 | ruby-debug-base (0.10.4)
48 | linecache (>= 0.3)
49 | ruby-debug-base19 (0.11.25)
50 | columnize (>= 0.3.1)
51 | linecache19 (>= 0.5.11)
52 | ruby_core_source (>= 0.1.4)
53 | ruby-debug19 (0.11.6)
54 | columnize (>= 0.3.1)
55 | linecache19 (>= 0.5.11)
56 | ruby-debug-base19 (>= 0.11.19)
57 | ruby_core_source (0.1.5)
58 | archive-tar-minitar (>= 0.5.2)
59 | test-unit (2.4.5)
60 |
61 | PLATFORMS
62 | ruby
63 |
64 | DEPENDENCIES
65 | activesupport (~> 2.2.0)
66 | appraisal!
67 | builder (>= 2.1.2)
68 | highline (>= 1.5.0)
69 | i18n (>= 0.5.0)
70 | jeweler (~> 1.6.2)
71 | puppet (= 2.7.3)
72 | rake
73 | rdoc
74 | rspec (~> 2.8.0)
75 | rspec-core (~> 2.8.0)
76 | ruby-debug
77 | ruby-debug19
78 | test-unit
79 |
--------------------------------------------------------------------------------
/gemfiles/2.3.11.gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/technicalpickles/appraisal.git
3 | revision: a755e5a086fa545f13c7465009e3fb8d4b4ff571
4 | specs:
5 | appraisal (0.3.8)
6 | bundler
7 | rake
8 |
9 | GEM
10 | remote: http://rubygems.org/
11 | specs:
12 | activesupport (2.3.11)
13 | archive-tar-minitar (0.5.2)
14 | builder (3.0.0)
15 | columnize (0.3.4)
16 | diff-lcs (1.1.3)
17 | facter (1.6.1)
18 | git (1.2.5)
19 | highline (1.6.2)
20 | i18n (0.6.0)
21 | jeweler (1.6.4)
22 | bundler (~> 1.0)
23 | git (>= 1.2.5)
24 | rake
25 | json (1.6.1)
26 | linecache (0.46)
27 | rbx-require-relative (> 0.0.4)
28 | linecache19 (0.5.12)
29 | ruby_core_source (>= 0.1.4)
30 | puppet (2.7.3)
31 | facter (>= 1.5.1)
32 | rake (0.9.2)
33 | rbx-require-relative (0.0.5)
34 | rdoc (3.10)
35 | json (~> 1.4)
36 | rspec (2.6.0)
37 | rspec-core (~> 2.6.0)
38 | rspec-expectations (~> 2.6.0)
39 | rspec-mocks (~> 2.6.0)
40 | rspec-core (2.6.4)
41 | rspec-expectations (2.6.0)
42 | diff-lcs (~> 1.1.2)
43 | rspec-mocks (2.6.0)
44 | ruby-debug (0.10.4)
45 | columnize (>= 0.1)
46 | ruby-debug-base (~> 0.10.4.0)
47 | ruby-debug-base (0.10.4)
48 | linecache (>= 0.3)
49 | ruby-debug-base19 (0.11.25)
50 | columnize (>= 0.3.1)
51 | linecache19 (>= 0.5.11)
52 | ruby_core_source (>= 0.1.4)
53 | ruby-debug19 (0.11.6)
54 | columnize (>= 0.3.1)
55 | linecache19 (>= 0.5.11)
56 | ruby-debug-base19 (>= 0.11.19)
57 | ruby_core_source (0.1.5)
58 | archive-tar-minitar (>= 0.5.2)
59 |
60 | PLATFORMS
61 | ruby
62 |
63 | DEPENDENCIES
64 | activesupport (= 2.3.11)
65 | appraisal!
66 | builder (>= 2.1.2)
67 | highline (>= 1.5.0)
68 | i18n (>= 0.5.0)
69 | jeweler (~> 1.6.2)
70 | puppet (= 2.7.3)
71 | rake
72 | rdoc
73 | rspec (~> 2.6.0)
74 | rspec-core (~> 2.6.0)
75 | ruby-debug
76 | ruby-debug19
77 |
--------------------------------------------------------------------------------
/gemfiles/2.3.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source :rubygems
4 |
5 | gem "puppet", "2.7.3"
6 | gem "highline", ">= 1.5.0"
7 | gem "builder", ">= 2.1.2"
8 | gem "activesupport", "~> 2.3.0"
9 | gem "i18n", ">= 0.5.0"
10 |
11 | group :development do
12 | gem "rake"
13 | gem "appraisal", :git=>"git://github.com/technicalpickles/appraisal.git"
14 | gem "rspec", "~> 2.8.0"
15 | gem "rspec-core", "~> 2.8.0"
16 | gem "test-unit"
17 | gem "jeweler", "~> 1.6.2"
18 | gem "rdoc"
19 | end
20 |
21 | group :debug do
22 | gem "ruby-debug", :platforms=>:ruby_18
23 | gem "ruby-debug19", :platforms=>:ruby_19
24 | end
25 |
26 |
--------------------------------------------------------------------------------
/gemfiles/2.3.gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/technicalpickles/appraisal.git
3 | revision: a755e5a086fa545f13c7465009e3fb8d4b4ff571
4 | specs:
5 | appraisal (0.3.8)
6 | bundler
7 | rake
8 |
9 | GEM
10 | remote: http://rubygems.org/
11 | specs:
12 | activesupport (2.3.14)
13 | archive-tar-minitar (0.5.2)
14 | builder (3.0.0)
15 | columnize (0.3.4)
16 | diff-lcs (1.1.3)
17 | facter (1.6.1)
18 | git (1.2.5)
19 | highline (1.6.2)
20 | i18n (0.6.0)
21 | jeweler (1.6.4)
22 | bundler (~> 1.0)
23 | git (>= 1.2.5)
24 | rake
25 | json (1.6.1)
26 | linecache (0.46)
27 | rbx-require-relative (> 0.0.4)
28 | linecache19 (0.5.12)
29 | ruby_core_source (>= 0.1.4)
30 | puppet (2.7.3)
31 | facter (>= 1.5.1)
32 | rake (0.9.2)
33 | rbx-require-relative (0.0.5)
34 | rdoc (3.10)
35 | json (~> 1.4)
36 | rspec (2.8.0)
37 | rspec-core (~> 2.8.0)
38 | rspec-expectations (~> 2.8.0)
39 | rspec-mocks (~> 2.8.0)
40 | rspec-core (2.8.0)
41 | rspec-expectations (2.8.0)
42 | diff-lcs (~> 1.1.2)
43 | rspec-mocks (2.8.0)
44 | ruby-debug (0.10.4)
45 | columnize (>= 0.1)
46 | ruby-debug-base (~> 0.10.4.0)
47 | ruby-debug-base (0.10.4)
48 | linecache (>= 0.3)
49 | ruby-debug-base19 (0.11.25)
50 | columnize (>= 0.3.1)
51 | linecache19 (>= 0.5.11)
52 | ruby_core_source (>= 0.1.4)
53 | ruby-debug19 (0.11.6)
54 | columnize (>= 0.3.1)
55 | linecache19 (>= 0.5.11)
56 | ruby-debug-base19 (>= 0.11.19)
57 | ruby_core_source (0.1.5)
58 | archive-tar-minitar (>= 0.5.2)
59 | test-unit (2.4.5)
60 |
61 | PLATFORMS
62 | ruby
63 |
64 | DEPENDENCIES
65 | activesupport (~> 2.3.0)
66 | appraisal!
67 | builder (>= 2.1.2)
68 | highline (>= 1.5.0)
69 | i18n (>= 0.5.0)
70 | jeweler (~> 1.6.2)
71 | puppet (= 2.7.3)
72 | rake
73 | rdoc
74 | rspec (~> 2.8.0)
75 | rspec-core (~> 2.8.0)
76 | ruby-debug
77 | ruby-debug19
78 | test-unit
79 |
--------------------------------------------------------------------------------
/gemfiles/3.0.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source :rubygems
4 |
5 | gem "puppet", "2.7.3"
6 | gem "highline", ">= 1.5.0"
7 | gem "builder", ">= 2.1.2"
8 | gem "activesupport", "~> 3.0.0"
9 | gem "i18n", ">= 0.5.0"
10 |
11 | group :development do
12 | gem "rake"
13 | gem "appraisal", :git=>"git://github.com/technicalpickles/appraisal.git"
14 | gem "rspec", "~> 2.8.0"
15 | gem "rspec-core", "~> 2.8.0"
16 | gem "test-unit"
17 | gem "jeweler", "~> 1.6.2"
18 | gem "rdoc"
19 | end
20 |
21 | group :debug do
22 | gem "ruby-debug", :platforms=>:ruby_18
23 | gem "ruby-debug19", :platforms=>:ruby_19
24 | end
25 |
26 |
--------------------------------------------------------------------------------
/gemfiles/3.0.gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/technicalpickles/appraisal.git
3 | revision: a755e5a086fa545f13c7465009e3fb8d4b4ff571
4 | specs:
5 | appraisal (0.3.8)
6 | bundler
7 | rake
8 |
9 | GEM
10 | remote: http://rubygems.org/
11 | specs:
12 | activesupport (3.0.10)
13 | archive-tar-minitar (0.5.2)
14 | builder (3.0.0)
15 | columnize (0.3.4)
16 | diff-lcs (1.1.3)
17 | facter (1.6.1)
18 | git (1.2.5)
19 | highline (1.6.2)
20 | i18n (0.6.0)
21 | jeweler (1.6.4)
22 | bundler (~> 1.0)
23 | git (>= 1.2.5)
24 | rake
25 | json (1.6.1)
26 | linecache (0.46)
27 | rbx-require-relative (> 0.0.4)
28 | linecache19 (0.5.12)
29 | ruby_core_source (>= 0.1.4)
30 | puppet (2.7.3)
31 | facter (>= 1.5.1)
32 | rake (0.9.2)
33 | rbx-require-relative (0.0.5)
34 | rdoc (3.10)
35 | json (~> 1.4)
36 | rspec (2.8.0)
37 | rspec-core (~> 2.8.0)
38 | rspec-expectations (~> 2.8.0)
39 | rspec-mocks (~> 2.8.0)
40 | rspec-core (2.8.0)
41 | rspec-expectations (2.8.0)
42 | diff-lcs (~> 1.1.2)
43 | rspec-mocks (2.8.0)
44 | ruby-debug (0.10.4)
45 | columnize (>= 0.1)
46 | ruby-debug-base (~> 0.10.4.0)
47 | ruby-debug-base (0.10.4)
48 | linecache (>= 0.3)
49 | ruby-debug-base19 (0.11.25)
50 | columnize (>= 0.3.1)
51 | linecache19 (>= 0.5.11)
52 | ruby_core_source (>= 0.1.4)
53 | ruby-debug19 (0.11.6)
54 | columnize (>= 0.3.1)
55 | linecache19 (>= 0.5.11)
56 | ruby-debug-base19 (>= 0.11.19)
57 | ruby_core_source (0.1.5)
58 | archive-tar-minitar (>= 0.5.2)
59 | test-unit (2.4.5)
60 |
61 | PLATFORMS
62 | ruby
63 |
64 | DEPENDENCIES
65 | activesupport (~> 3.0.0)
66 | appraisal!
67 | builder (>= 2.1.2)
68 | highline (>= 1.5.0)
69 | i18n (>= 0.5.0)
70 | jeweler (~> 1.6.2)
71 | puppet (= 2.7.3)
72 | rake
73 | rdoc
74 | rspec (~> 2.8.0)
75 | rspec-core (~> 2.8.0)
76 | ruby-debug
77 | ruby-debug19
78 | test-unit
79 |
--------------------------------------------------------------------------------
/gemfiles/3.1.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source :rubygems
4 |
5 | gem "puppet", "2.7.3"
6 | gem "highline", ">= 1.5.0"
7 | gem "builder", ">= 2.1.2"
8 | gem "activesupport", "~> 3.1.0"
9 | gem "i18n", ">= 0.5.0"
10 |
11 | group :development do
12 | gem "rake"
13 | gem "appraisal", :git=>"git://github.com/technicalpickles/appraisal.git"
14 | gem "rspec", "~> 2.8.0"
15 | gem "rspec-core", "~> 2.8.0"
16 | gem "test-unit"
17 | gem "jeweler", "~> 1.6.2"
18 | gem "rdoc"
19 | end
20 |
21 | group :debug do
22 | gem "ruby-debug", :platforms=>:ruby_18
23 | gem "ruby-debug19", :platforms=>:ruby_19
24 | end
25 |
26 |
--------------------------------------------------------------------------------
/gemfiles/3.1.gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/technicalpickles/appraisal.git
3 | revision: a755e5a086fa545f13c7465009e3fb8d4b4ff571
4 | specs:
5 | appraisal (0.3.8)
6 | bundler
7 | rake
8 |
9 | GEM
10 | remote: http://rubygems.org/
11 | specs:
12 | activesupport (3.1.1)
13 | multi_json (~> 1.0)
14 | archive-tar-minitar (0.5.2)
15 | builder (3.0.0)
16 | columnize (0.3.4)
17 | diff-lcs (1.1.3)
18 | facter (1.6.1)
19 | git (1.2.5)
20 | highline (1.6.2)
21 | i18n (0.6.0)
22 | jeweler (1.6.4)
23 | bundler (~> 1.0)
24 | git (>= 1.2.5)
25 | rake
26 | json (1.6.1)
27 | linecache (0.46)
28 | rbx-require-relative (> 0.0.4)
29 | linecache19 (0.5.12)
30 | ruby_core_source (>= 0.1.4)
31 | multi_json (1.0.3)
32 | puppet (2.7.3)
33 | facter (>= 1.5.1)
34 | rake (0.9.2)
35 | rbx-require-relative (0.0.5)
36 | rdoc (3.10)
37 | json (~> 1.4)
38 | rspec (2.8.0)
39 | rspec-core (~> 2.8.0)
40 | rspec-expectations (~> 2.8.0)
41 | rspec-mocks (~> 2.8.0)
42 | rspec-core (2.8.0)
43 | rspec-expectations (2.8.0)
44 | diff-lcs (~> 1.1.2)
45 | rspec-mocks (2.8.0)
46 | ruby-debug (0.10.4)
47 | columnize (>= 0.1)
48 | ruby-debug-base (~> 0.10.4.0)
49 | ruby-debug-base (0.10.4)
50 | linecache (>= 0.3)
51 | ruby-debug-base19 (0.11.25)
52 | columnize (>= 0.3.1)
53 | linecache19 (>= 0.5.11)
54 | ruby_core_source (>= 0.1.4)
55 | ruby-debug19 (0.11.6)
56 | columnize (>= 0.3.1)
57 | linecache19 (>= 0.5.11)
58 | ruby-debug-base19 (>= 0.11.19)
59 | ruby_core_source (0.1.5)
60 | archive-tar-minitar (>= 0.5.2)
61 | test-unit (2.4.5)
62 |
63 | PLATFORMS
64 | ruby
65 |
66 | DEPENDENCIES
67 | activesupport (~> 3.1.0)
68 | appraisal!
69 | builder (>= 2.1.2)
70 | highline (>= 1.5.0)
71 | i18n (>= 0.5.0)
72 | jeweler (~> 1.6.2)
73 | puppet (= 2.7.3)
74 | rake
75 | rdoc
76 | rspec (~> 2.8.0)
77 | rspec-core (~> 2.8.0)
78 | ruby-debug
79 | ruby-debug19
80 | test-unit
81 |
--------------------------------------------------------------------------------
/gemfiles/3.2.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source :rubygems
4 |
5 | gem "puppet", "2.7.3"
6 | gem "highline", ">= 1.5.0"
7 | gem "builder", ">= 2.1.2"
8 | gem "activesupport", "~> 3.2.0"
9 | gem "i18n", ">= 0.5.0"
10 |
11 | group :development do
12 | gem "rake"
13 | gem "appraisal", :git=>"git://github.com/technicalpickles/appraisal.git"
14 | gem "rspec", "~> 2.8.0"
15 | gem "rspec-core", "~> 2.8.0"
16 | gem "test-unit"
17 | gem "jeweler", "~> 1.6.2"
18 | gem "rdoc"
19 | end
20 |
21 | group :debug do
22 | gem "ruby-debug", :platforms=>:ruby_18
23 | gem "ruby-debug19", :platforms=>:ruby_19
24 | end
25 |
26 |
--------------------------------------------------------------------------------
/gemfiles/3.2.gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/technicalpickles/appraisal.git
3 | revision: a755e5a086fa545f13c7465009e3fb8d4b4ff571
4 | specs:
5 | appraisal (0.3.8)
6 | bundler
7 | rake
8 |
9 | GEM
10 | remote: http://rubygems.org/
11 | specs:
12 | activesupport (3.2.1)
13 | i18n (~> 0.6)
14 | multi_json (~> 1.0)
15 | archive-tar-minitar (0.5.2)
16 | builder (3.0.0)
17 | columnize (0.3.6)
18 | diff-lcs (1.1.3)
19 | facter (1.6.5)
20 | git (1.2.5)
21 | highline (1.6.11)
22 | i18n (0.6.0)
23 | jeweler (1.6.4)
24 | bundler (~> 1.0)
25 | git (>= 1.2.5)
26 | rake
27 | json (1.6.5)
28 | linecache (0.46)
29 | rbx-require-relative (> 0.0.4)
30 | linecache19 (0.5.12)
31 | ruby_core_source (>= 0.1.4)
32 | multi_json (1.0.4)
33 | puppet (2.7.3)
34 | facter (>= 1.5.1)
35 | rake (0.9.2.2)
36 | rbx-require-relative (0.0.5)
37 | rdoc (3.12)
38 | json (~> 1.4)
39 | rspec (2.8.0)
40 | rspec-core (~> 2.8.0)
41 | rspec-expectations (~> 2.8.0)
42 | rspec-mocks (~> 2.8.0)
43 | rspec-core (2.8.0)
44 | rspec-expectations (2.8.0)
45 | diff-lcs (~> 1.1.2)
46 | rspec-mocks (2.8.0)
47 | ruby-debug (0.10.4)
48 | columnize (>= 0.1)
49 | ruby-debug-base (~> 0.10.4.0)
50 | ruby-debug-base (0.10.4)
51 | linecache (>= 0.3)
52 | ruby-debug-base19 (0.11.25)
53 | columnize (>= 0.3.1)
54 | linecache19 (>= 0.5.11)
55 | ruby_core_source (>= 0.1.4)
56 | ruby-debug19 (0.11.6)
57 | columnize (>= 0.3.1)
58 | linecache19 (>= 0.5.11)
59 | ruby-debug-base19 (>= 0.11.19)
60 | ruby_core_source (0.1.5)
61 | archive-tar-minitar (>= 0.5.2)
62 | test-unit (2.4.5)
63 |
64 | PLATFORMS
65 | ruby
66 |
67 | DEPENDENCIES
68 | activesupport (~> 3.2.0)
69 | appraisal!
70 | builder (>= 2.1.2)
71 | highline (>= 1.5.0)
72 | i18n (>= 0.5.0)
73 | jeweler (~> 1.6.2)
74 | puppet (= 2.7.3)
75 | rake
76 | rdoc
77 | rspec (~> 2.8.0)
78 | rspec-core (~> 2.8.0)
79 | ruby-debug
80 | ruby-debug19
81 | test-unit
82 |
--------------------------------------------------------------------------------
/lib/shadow_puppet.rb:
--------------------------------------------------------------------------------
1 | require 'shadow_puppet/core_ext'
2 | require 'shadow_puppet/version'
3 |
4 | # Silence puppet's dependencies warnings like:
5 | # racc/parser.rb:27: warning: already initialized constant Racc_Runtime_Version
6 | # racc/parser.rb:28: warning: already initialized constant Racc_Runtime_Revision
7 | # racc/parser.rb:30: warning: already initialized constant Racc_Runtime_Core_Version_R
8 | # racc/parser.rb:31: warning: already initialized constant Racc_Runtime_Core_Revision_R
9 | # racc/parser.rb:35: warning: already initialized constant Racc_Runtime_Core_Revision_C
10 | # racc/parser.rb:39: warning: already initialized constant Racc_Main_Parsing_Routine
11 | # racc/parser.rb:40: warning: already initialized constant Racc_YY_Parse_Method
12 | # racc/parser.rb:41: warning: already initialized constant Racc_Runtime_Core_Version
13 | # racc/parser.rb:42: warning: already initialized constant Racc_Runtime_Core_Revision
14 | # racc/parser.rb:43: warning: already initialized constant Racc_Runtime_Type
15 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/type/tidy.rb:102: warning: class variable access from toplevel
16 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/type/tidy.rb:107: warning: class variable access from toplevel
17 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/type/tidy.rb:107: warning: class variable access from toplevel
18 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/type/tidy.rb:108: warning: class variable access from toplevel
19 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/type/tidy.rb:108: warning: class variable access from toplevel
20 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/type/tidy.rb:109: warning: class variable access from toplevel
21 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/type/tidy.rb:109: warning: class variable access from toplevel
22 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/type/tidy.rb:149: warning: class variable access from toplevel
23 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/provider/service/freebsd.rb:8: warning: class variable access from toplevel
24 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/provider/service/freebsd.rb:9: warning: class variable access from toplevel
25 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/provider/service/freebsd.rb:10: warning: class variable access from toplevel
26 | # /usr/lib/ruby/gems/1.9.1/gems/puppet-2.7.3/lib/puppet/provider/service/bsd.rb:11: warning: class variable access from toplevel
27 | # NOTE: Gem.source_index is deprecated, use Specification. It will be removed on or after 2011-11-01.
28 | # Gem.source_index called from /srv/app/current/vendor/plugins/moonshine/lib/moonshine/manifest/rails/rails.rb:223.
29 | # NOTE: Gem::SourceIndex#search is deprecated with no replacement. It will be removed on or after 2011-11-01.
30 | # Gem::SourceIndex#search called from /srv/app/current/vendor/plugins/moonshine/lib/moonshine/manifest/rails/rails.rb:223
31 | $VERBOSE = nil # comment this out to debug
32 | require 'puppet'
33 | require 'erb'
34 |
35 | require 'shadow_puppet/manifest'
36 |
37 | class ShadowPuppet::Manifest::Setup < ShadowPuppet::Manifest
38 | recipe :setup_directories
39 |
40 | def setup_directories()
41 | if Process.uid == 0
42 | file "/var/shadow_puppet",
43 | :ensure => "directory",
44 | :backup => false
45 | file "/etc/shadow_puppet",
46 | :ensure => "directory",
47 | :backup => false
48 | else
49 | file ENV["HOME"] + "/.shadow_puppet",
50 | :ensure => "directory",
51 | :backup => false
52 | file ENV["HOME"] + "/.shadow_puppet/var",
53 | :ensure => "directory",
54 | :backup => false,
55 | :require => file(ENV["HOME"] + "/.shadow_puppet")
56 | end
57 | end
58 | end
59 |
60 | setup = ShadowPuppet::Manifest::Setup.new
61 | setup.execute
62 |
--------------------------------------------------------------------------------
/lib/shadow_puppet/core_ext.rb:
--------------------------------------------------------------------------------
1 | require 'active_support'
2 | require 'active_support/version'
3 |
4 | # ActiveSupport 3 doesn't automatically load core_ext anymore
5 | if ActiveSupport::VERSION::MAJOR >= 3
6 | require 'active_support/deprecation'
7 | require 'active_support/core_ext'
8 | end
9 |
10 | ActiveSupport::Deprecation.silenced = true
11 |
12 | class Hash #:nodoc:
13 | def deep_symbolize_keys
14 | self.inject({}) { |result, (key, value)|
15 | value = value.deep_symbolize_keys if value.is_a?(Hash)
16 | result[(key.to_sym rescue key) || key] = value
17 | result
18 | }
19 | end
20 | end
21 |
22 | # backport inheritable accessors deprecated in 3.1 and removed in 3.2
23 | if (ActiveSupport::VERSION::MAJOR == 3 and ActiveSupport::VERSION::MINOR >= 1) or ActiveSupport::VERSION::MAJOR > 3
24 | class Class
25 | def class_inheritable_reader(*syms)
26 | syms.each do |sym|
27 | next if sym.is_a?(Hash)
28 | class_eval <<-EOS
29 | def self.#{sym} # def self.before_add_for_comments
30 | read_inheritable_attribute(:#{sym}) # read_inheritable_attribute(:before_add_for_comments)
31 | end # end
32 | #
33 | def #{sym} # def before_add_for_comments
34 | self.class.#{sym} # self.class.before_add_for_comments
35 | end # end
36 | EOS
37 | end
38 | end
39 |
40 | def class_inheritable_writer(*syms)
41 | options = syms.extract_options!
42 | syms.each do |sym|
43 | class_eval <<-EOS
44 | def self.#{sym}=(obj) # def self.color=(obj)
45 | write_inheritable_attribute(:#{sym}, obj) # write_inheritable_attribute(:color, obj)
46 | end # end
47 | #
48 | #{" #
49 | def #{sym}=(obj) # def color=(obj)
50 | self.class.#{sym} = obj # self.class.color = obj
51 | end # end
52 | " unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
53 | EOS
54 | end
55 | end
56 |
57 | def class_inheritable_accessor(*syms)
58 | class_inheritable_reader(*syms)
59 | class_inheritable_writer(*syms)
60 | end
61 |
62 | def inheritable_attributes
63 | @inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
64 | end
65 |
66 | def write_inheritable_attribute(key, value)
67 | if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
68 | @inheritable_attributes = {}
69 | end
70 | inheritable_attributes[key] = value
71 | end
72 |
73 | def read_inheritable_attribute(key)
74 | inheritable_attributes[key]
75 | end
76 |
77 | def reset_inheritable_attributes
78 | @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
79 | end
80 |
81 | private
82 | # Prevent this constant from being created multiple times
83 | EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)
84 |
85 | def inherited_with_inheritable_attributes(child)
86 | inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
87 |
88 | if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
89 | new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
90 | else
91 | new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
92 | memo.update(key => value.duplicable? ? value.dup : value)
93 | end
94 | end
95 |
96 | child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
97 | end
98 |
99 | alias inherited_without_inheritable_attributes inherited
100 | alias inherited inherited_with_inheritable_attributes
101 | end
102 | end
103 |
--------------------------------------------------------------------------------
/lib/shadow_puppet/manifest.rb:
--------------------------------------------------------------------------------
1 | module ShadowPuppet
2 | # A Manifest is an executable collection of Puppet Resources[http://reductivelabs.com/trac/puppet/wiki/TypeReference].
3 | #
4 | # ===Example
5 | #
6 | # class ManifestExample < ShadowPuppet::Manifest
7 | # recipe :sample
8 | # recipe :lamp, :ruby # queue calls to self.lamp and
9 | # # self.ruby when executing
10 | #
11 | # recipe :mysql, { # queue a call to self.mysql
12 | # :root_password => 'OMGSEKRET' # passing the provided hash
13 | # } # as an option
14 | #
15 | # def sample
16 | # exec :foo, :command => 'echo "foo" > /tmp/foo.txt'
17 | #
18 | # package :foo, :ensure => :installed
19 | #
20 | # file '/tmp/example.txt',
21 | # :ensure => :present,
22 | # :contents => Facter.to_hash_inspect,
23 | # :require => package(:foo)
24 | # end
25 | #
26 | # def lamp
27 | # # install a basic LAMP stack
28 | # end
29 | #
30 | # def ruby
31 | # # install a ruby interpreter and tools
32 | # end
33 | #
34 | # def mysql(options)
35 | # # install a mysql server and set the root password to options[:root_password]
36 | # end
37 | #
38 | # end
39 | #
40 | # To execute the above manifest, instantiate it and call execute on it:
41 | #
42 | # m = ManifestExample.new
43 | # m.execute
44 | #
45 | # As shown in the +sample+ method in ManifestExample above, instance
46 | # methods are created for each Puppet::Type available on your system. These
47 | # methods behave identally to the Puppet Resources methods. See here[http://reductivelabs.com/trac/puppet/wiki/TypeReference]
48 | # for documentation on these methods.
49 | #
50 | # To view a list of all defined methods on your system, run:
51 | #
52 | # ruby -rubygems -e 'require "shadow_puppet";puts ShadowPuppet::Manifest.puppet_type_methods'
53 | #
54 | # The use of methods (+sample+, +lamp+, +ruby+, and +mysql+ above) as a
55 | # container for resources facilitates recipie re-use through the use of Ruby
56 | # Modules. For example:
57 | #
58 | # module ApachePuppet
59 | # # Required options:
60 | # # domain
61 | # # path
62 | # def php_vhost(options)
63 | # #...
64 | # end
65 | # end
66 | #
67 | # class MyWebMainfest < ShadowPuppet::Manifest
68 | # include ApachePuppet
69 | # recipe :php_vhost, {
70 | # :domain => 'foo.com',
71 | # :path => '/var/www/apps/foo'
72 | # }
73 | # end
74 | class Manifest
75 |
76 | class_inheritable_accessor :recipes
77 | write_inheritable_attribute(:recipes, [])
78 | attr_reader :catalog
79 | class_inheritable_accessor :__config__
80 | write_inheritable_attribute(:__config__, Hash.new)
81 |
82 | # Initialize a new instance of this manifest. This can take a
83 | # config hash, which is immediately passed on to the configure
84 | # method
85 | def initialize(config = {})
86 | if Process.uid == 0
87 | Puppet[:confdir] = File.expand_path("/etc/shadow_puppet")
88 | Puppet[:vardir] = File.expand_path("/var/shadow_puppet")
89 | Puppet[:codedir] = File.expand_path("/etc/shadow_puppet/code")
90 | Puppet[:logdir] = File.expand_path("/var/log/shadow_puppet")
91 | else
92 | Puppet[:confdir] = File.expand_path("~/.shadow_puppet")
93 | Puppet[:vardir] = File.expand_path("~/.shadow_puppet/var")
94 | Puppet[:codedir] = File.expand_path("~/.shadow_puppet/code")
95 | Puppet[:logdir] = File.expand_path("~/shadow_puppet/log")
96 | end
97 | Puppet[:user] = Process.uid
98 | Puppet[:group] = Process.gid
99 | Puppet::Util::Log.newdestination(:console)
100 | Puppet[:diff_args] = "-u"
101 | Puppet.push_context(Puppet.base_context(Puppet.settings), "Update for application's settings")
102 |
103 | configure(config)
104 | @executed = false
105 | @catalog = Puppet::Resource::Catalog.new
106 | @catalog.host_config = false
107 | @catalog.name = self.name
108 | end
109 |
110 | # Declares that the named method or methods will be called whenever
111 | # execute is called on an instance of this class. If the last argument is
112 | # a Hash, this hash is passed as an argument to all provided methods.
113 | # If no options hash is provided, each method is passed the contents of
114 | # configuration[method].
115 | #
116 | # Subclasses of the Manifest class properly inherit the parent classes'
117 | # calls to recipe.
118 | def self.recipe(*methods)
119 | return nil if methods.nil? || methods == [] # TODO can probably replace with if methods.blank?
120 | methods.each do |meth|
121 | options = methods.extract_options!
122 | options = configuration[meth.to_sym] if options == {} # TODO can probably be replaced with options.blank?
123 | options ||= {}
124 | recipes << [meth.to_sym, options]
125 | end
126 | end
127 |
128 | # Access to a recipe of the class of this instance.
129 | #
130 | # class SampleManifest < ShadowPuppet::Manifest
131 | # def my_recipe
132 | # recipe :other_recipe
133 | # end
134 | # end
135 | def recipe(*methods)
136 | self.class.recipe *methods
137 | end
138 |
139 | # A HashWithIndifferentAccess describing any configuration that has been
140 | # performed on the class. Modify this hash by calling configure:
141 | #
142 | # class SampleManifest < ShadowPuppet::Manifest
143 | # configure(:name => 'test')
144 | # end
145 | #
146 | # >> SampleManifest.configuration
147 | # => {:name => 'test'}
148 | # #
149 | # Subclasses of the Manifest class properly inherit the parent classes'
150 | # configuration.
151 | def self.configuration
152 | __config__.with_indifferent_access
153 | end
154 |
155 | # Access to the configuration of the class of this instance.
156 | #
157 | # class SampleManifest < ShadowPuppet::Manifest
158 | # configure(:name => 'test')
159 | # end
160 | #
161 | # @manifest = SampleManifest.new
162 | # @manifest.configuration[:name] => "test"
163 | def configuration
164 | self.class.configuration
165 | end
166 |
167 | # Define configuration on this manifest. This is useful for storing things
168 | # such as hostnames, password, or usernames that may change between
169 | # different implementations of a shared manifest. Access this hash by
170 | # calling configuration:
171 | #
172 | # class SampleManifest < ShadowPuppet::Manifest
173 | # configure('name' => 'test')
174 | # end
175 | #
176 | # >> SampleManifest.configuration
177 | # => {:name => 'test'}
178 | # #
179 | # Subsequent calls to configure perform a deep_merge of the provided
180 | # hash into the pre-existing configuration.
181 | def self.configure(hash)
182 | __config__.replace(__config__.deep_symbolize_keys.deep_merge(hash.deep_symbolize_keys))
183 | end
184 |
185 | # Update the configuration of this manifest instance's class.
186 | #
187 | # class SampleManifest < ShadowPuppet::Manifest
188 | # configure({})
189 | # end
190 | #
191 | # @manifest = SampleManifest.new
192 | # @manifest.configure(:name => "test")
193 | # @manifest.configuration[:name] => "test"
194 | def configure(hash)
195 | self.class.configure(hash)
196 | end
197 | alias_method :configuration=, :configure
198 |
199 | #An array of all methods defined for creation of Puppet Resources
200 | def self.puppet_type_methods
201 | Puppet::Type.eachtype { |t| t.name }.keys.map { |n| n.to_s }.sort.inspect
202 | end
203 |
204 | def name
205 | @name ||= "#{self.class}##{self.object_id}"
206 | end
207 |
208 | #Create an instance method for every type that either creates or references
209 | #a resource
210 | def self.register_puppet_types
211 | Puppet::Type.loadall
212 | Puppet::Type.eachtype do |type|
213 | # remove the method rdoc placeholders
214 | remove_method(type.name) rescue nil
215 | define_method(type.name) do |*args|
216 | resource_or_reference(type, *args)
217 | end
218 | end
219 | end
220 | register_puppet_types
221 |
222 | # Returns true if this Manifest respond_to? all methods named by
223 | # calls to recipe, and if this Manifest has not been executed before.
224 | def executable?
225 | self.class.recipes.each do |meth,args|
226 | return false unless respond_to?(meth)
227 | end
228 | return false if executed?
229 | true
230 | end
231 |
232 | def missing_recipes
233 | missing = self.class.recipes.each do |meth,args|
234 | !respond_to?(meth)
235 | end
236 | end
237 |
238 | # Execute this manifest, applying all resources defined. Execute returns
239 | # true if successfull, and false if unsucessfull. By default, this
240 | # will only execute a manifest that has not already been executed?.
241 | # The +force+ argument, if true, removes this check.
242 | def execute(force=false)
243 | return false if executed? && !force
244 | evaluate_recipes
245 | transaction = apply
246 | rescue Exception => e
247 | false
248 | else
249 | not transaction.any_failed?
250 | ensure
251 | @executed = true
252 | end
253 |
254 | # Execute this manifest, applying all resources defined. Execute returns
255 | # true if successfull, and raises an exception if not. By default, this
256 | # will only execute a manifest that has not already been executed?.
257 | # The +force+ argument, if true, removes this check.
258 | def execute!(force=false)
259 | return false if executed? && !force
260 | evaluate_recipes
261 | transaction = apply
262 | rescue Exception => e
263 | raise e
264 | else
265 | not transaction.any_failed?
266 | ensure
267 | @executed = true
268 | end
269 |
270 | def graph_to(name, destination)
271 | evaluate_recipes
272 |
273 | relationship_graph = @catalog.relationship_graph
274 |
275 | graph = relationship_graph.to_dot_graph("name" => "#{name} Relationships".gsub(/\W+/, '_'))
276 | graph.options['label'] = "#{name} Relationships"
277 |
278 | # The graph ends up having all of the edges backwards
279 | graph.each_node do |node|
280 | next unless node.is_a?(DOT::DOTEdge)
281 | node.to, node.from = node.from, node.to
282 | end
283 |
284 | File.open(destination, "w") { |f|
285 | f.puts graph.to_s
286 | }
287 | end
288 |
289 | protected
290 |
291 | #Has this manifest instance been executed?
292 | def executed?
293 | @executed
294 | end
295 |
296 | private
297 |
298 | #Evaluate the methods calls queued in self.recipes
299 | def evaluate_recipes
300 | self.class.recipes.each do |meth, args|
301 | case arity = method(meth).arity
302 | when 1, -1
303 | send(meth, args)
304 | else
305 | send(meth)
306 | end
307 | end
308 | end
309 |
310 | # Create a catalog of all contained Puppet Resources and apply that
311 | # catalog to the currently running system
312 | def apply
313 | transaction = catalog.apply
314 | catalog.clear
315 | transaction
316 | end
317 |
318 | def resource_or_reference(type, *args)
319 | if args
320 | if args.flatten.size == 1
321 | reference(type, args.first)
322 | elsif resource = existing_resource(type.name, args.first)
323 | new_resource(type, args.first, args.last).parameters.each do |name, param|
324 | resource[name] = param.value if param.value
325 | end
326 | resource
327 | else
328 | add_resource(type, args.first, args.last)
329 | end
330 | end
331 | end
332 |
333 | def existing_resource(type, title)
334 | catalog.resources.detect { |r| r.type == type && r.title == title }
335 | end
336 |
337 | # Create a reference to another Puppet Resource.
338 | def reference(type, title, params = {})
339 | Puppet::Resource.new(type.name.to_s.capitalize, title.to_s)
340 | end
341 |
342 | # Creates a new Puppet Resource and adds it to the Catalog.
343 | def add_resource(type, title, params = {})
344 | catalog.add_resource(new_resource(type, title, params))
345 | end
346 |
347 | def new_resource(type, title, params = {})
348 | params.merge!({:title => title.to_s})
349 | params.merge!({:catalog => catalog})
350 | params.merge!({:path => ENV["PATH"]}) if type.name == :exec && params[:path].nil?
351 | params.merge!({:cwd => params[:cwd].to_s}) if params[:cwd]
352 | Puppet::Type.type(type.name).new(params)
353 | end
354 |
355 | end
356 | end
357 |
--------------------------------------------------------------------------------
/lib/shadow_puppet/test.rb:
--------------------------------------------------------------------------------
1 | module Puppet
2 | class Type
3 |
4 | # clearing out some puppet methods that we probably won't need for testing
5 | # that are also used in the params hash when defining the resource.
6 | undef path
7 | undef require
8 |
9 | # This allows access to resource options as methods on the resource.
10 | def method_missing name, *args
11 | if parameters.keys.include? name.to_sym
12 | parameters[name.to_sym].value
13 | end
14 | end
15 | end
16 | end
17 |
18 | module ShadowPuppet
19 | # To test manifests, access puppet resources using the plural form of the resource name.
20 | # This returns a hash of all resources of that type.
21 | #
22 | # manifest.execs
23 | # manifest.packages
24 | #
25 | # You can access resource options as methods on the resource
26 | #
27 | # manifest.files['/etc/motd'].content
28 | # manifest.execs['service ssh restart'].onlyif
29 | #
30 | # === Example
31 | #
32 | # Given this manifest:
33 | #
34 | # class TestManifest < ShadowPuppet::Manifest
35 | # def myrecipe
36 | # file '/etc/motd', :content => 'Welcome to the machine!', :mode => '644'
37 | # exec 'newaliases', :refreshonly => true
38 | # end
39 | # recipe :myrecipe
40 | # end
41 | #
42 | # A test for the manifest could look like this:
43 | #
44 | # manifest = TestManifest.new
45 | # manifest.myrecipe
46 | # assert_match /Welcome/, manifest.files['/etc/motd']
47 | # assert manifest.execs['newaliases'].refreshonly
48 | #
49 | class Manifest
50 | # Creates an instance method for every puppet type
51 | # that either creates or references a resource
52 | def self.register_puppet_types_for_testing
53 | Puppet::Type.loadall
54 | Puppet::Type.eachtype do |type|
55 | plural_type = type.name.to_s.downcase.pluralize
56 | #undefine the method rdoc placeholders
57 | undef_method(plural_type) rescue nil
58 | define_method(plural_type) do |*args|
59 | catalog.resources.select { |r| r.type == type.name }.inject({}) do |hash, resource|
60 | hash[resource.title] = resource; hash
61 | end
62 | end
63 | end
64 | end
65 | register_puppet_types_for_testing
66 |
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/lib/shadow_puppet/version.rb:
--------------------------------------------------------------------------------
1 | module ShadowPuppet
2 | VERSION = "0.10.3"
3 | end
4 |
--------------------------------------------------------------------------------
/rubocop-todo.yml:
--------------------------------------------------------------------------------
1 | # This configuration was generated by `rubocop --auto-gen-config`
2 | # on 2014-04-30 16:54:22 -0400 using RuboCop version 0.21.0.
3 | # The point is for the user to remove these configuration records
4 | # one by one as the offenses are removed from the code base.
5 | # Note that changes in the inspected code, or installation of new
6 | # versions of RuboCop, may require this file to be generated again.
7 |
8 | # Offense count: 2
9 | # Cop supports --auto-correct.
10 | Alias:
11 | Enabled: false
12 |
13 | # Offense count: 12
14 | # Cop supports --auto-correct.
15 | # Configuration parameters: EnforcedStyle, SupportedStyles.
16 | AlignParameters:
17 | Enabled: false
18 |
19 | # Offense count: 1
20 | AmbiguousOperator:
21 | Enabled: false
22 |
23 | # Offense count: 3
24 | # Cop supports --auto-correct.
25 | AndOr:
26 | Enabled: false
27 |
28 | # Offense count: 2
29 | # Configuration parameters: AllowSafeAssignment.
30 | AssignmentInCondition:
31 | Enabled: false
32 |
33 | # Offense count: 1
34 | BlockAlignment:
35 | Enabled: false
36 |
37 | # Offense count: 3
38 | # Cop supports --auto-correct.
39 | Blocks:
40 | Enabled: false
41 |
42 | # Offense count: 9
43 | # Cop supports --auto-correct.
44 | # Configuration parameters: EnforcedStyle, SupportedStyles.
45 | BracesAroundHashParameters:
46 | Enabled: false
47 |
48 | # Offense count: 1
49 | # Configuration parameters: EnforcedStyle, SupportedStyles.
50 | ClassAndModuleChildren:
51 | Enabled: false
52 |
53 | # Offense count: 1
54 | # Configuration parameters: CountComments.
55 | ClassLength:
56 | Max: 162
57 |
58 | # Offense count: 4
59 | # Cop supports --auto-correct.
60 | # Configuration parameters: PreferredMethods.
61 | CollectionMethods:
62 | Enabled: false
63 |
64 | # Offense count: 2
65 | # Configuration parameters: Keywords.
66 | CommentAnnotation:
67 | Enabled: false
68 |
69 | # Offense count: 1
70 | # Cop supports --auto-correct.
71 | DefWithParentheses:
72 | Enabled: false
73 |
74 | # Offense count: 18
75 | Documentation:
76 | Enabled: false
77 |
78 | # Offense count: 1
79 | # Cop supports --auto-correct.
80 | EmptyLines:
81 | Enabled: false
82 |
83 | # Offense count: 1
84 | EmptyLinesAroundAccessModifier:
85 | Enabled: false
86 |
87 | # Offense count: 7
88 | # Cop supports --auto-correct.
89 | EmptyLinesAroundBody:
90 | Enabled: false
91 |
92 | # Offense count: 1
93 | # Cop supports --auto-correct.
94 | EmptyLiteral:
95 | Enabled: false
96 |
97 | # Offense count: 88
98 | # Cop supports --auto-correct.
99 | # Configuration parameters: SupportedStyles.
100 | HashSyntax:
101 | EnforcedStyle: hash_rockets
102 |
103 | # Offense count: 1
104 | # Configuration parameters: MaxLineLength.
105 | IfUnlessModifier:
106 | Enabled: false
107 |
108 | # Offense count: 1
109 | # Cop supports --auto-correct.
110 | # Configuration parameters: SupportedStyles.
111 | IndentHash:
112 | EnforcedStyle: consistent
113 |
114 | # Offense count: 4
115 | # Cop supports --auto-correct.
116 | IndentationConsistency:
117 | Enabled: false
118 |
119 | # Offense count: 1
120 | # Cop supports --auto-correct.
121 | IndentationWidth:
122 | Enabled: false
123 |
124 | # Offense count: 17
125 | # Cop supports --auto-correct.
126 | LeadingCommentSpace:
127 | Enabled: false
128 |
129 | # Offense count: 73
130 | LineLength:
131 | Max: 134
132 |
133 | # Offense count: 1
134 | # Cop supports --auto-correct.
135 | # Configuration parameters: EnforcedStyle, SupportedStyles.
136 | MethodDefParentheses:
137 | Enabled: false
138 |
139 | # Offense count: 7
140 | # Configuration parameters: CountComments.
141 | MethodLength:
142 | Max: 18
143 |
144 | # Offense count: 2
145 | # Cop supports --auto-correct.
146 | Not:
147 | Enabled: false
148 |
149 | # Offense count: 2
150 | # Cop supports --auto-correct.
151 | # Configuration parameters: PreferredDelimiters.
152 | PercentLiteralDelimiters:
153 | Enabled: false
154 |
155 | # Offense count: 2
156 | # Cop supports --auto-correct.
157 | PerlBackrefs:
158 | Enabled: false
159 |
160 | # Offense count: 3
161 | # Cop supports --auto-correct.
162 | RedundantSelf:
163 | Enabled: false
164 |
165 | # Offense count: 2
166 | # Configuration parameters: MaxSlashes.
167 | RegexpLiteral:
168 | Enabled: false
169 |
170 | # Offense count: 3
171 | # Cop supports --auto-correct.
172 | RescueException:
173 | Enabled: false
174 |
175 | # Offense count: 3
176 | RescueModifier:
177 | Enabled: false
178 |
179 | # Offense count: 1
180 | # Cop supports --auto-correct.
181 | # Configuration parameters: AllowAsExpressionSeparator.
182 | Semicolon:
183 | Enabled: false
184 |
185 | # Offense count: 1
186 | ShadowingOuterLocalVariable:
187 | Enabled: false
188 |
189 | # Offense count: 6
190 | # Cop supports --auto-correct.
191 | SpaceAfterComma:
192 | Enabled: false
193 |
194 | # Offense count: 2
195 | # Cop supports --auto-correct.
196 | # Configuration parameters: EnforcedStyle, SupportedStyles.
197 | SpaceAroundEqualsInParameterDefault:
198 | Enabled: false
199 |
200 | # Offense count: 2
201 | # Cop supports --auto-correct.
202 | SpaceAroundOperators:
203 | Enabled: false
204 |
205 | # Offense count: 4
206 | # Cop supports --auto-correct.
207 | # Configuration parameters: EnforcedStyle, SupportedStyles.
208 | SpaceBeforeBlockBraces:
209 | Enabled: false
210 |
211 | # Offense count: 4
212 | # Cop supports --auto-correct.
213 | # Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
214 | SpaceInsideBlockBraces:
215 | Enabled: true
216 |
217 | # Offense count: 2
218 | # Cop supports --auto-correct.
219 | SpaceInsideBrackets:
220 | Enabled: false
221 |
222 | # Offense count: 18
223 | # Cop supports --auto-correct.
224 | # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles.
225 | SpaceInsideHashLiteralBraces:
226 | Enabled: false
227 |
228 | # Offense count: 1
229 | # Cop supports --auto-correct.
230 | SpecialGlobalVars:
231 | Enabled: false
232 |
233 | # Offense count: 138
234 | # Cop supports --auto-correct.
235 | # Configuration parameters: EnforcedStyle, SupportedStyles.
236 | StringLiterals:
237 | Enabled: false
238 |
239 | # Offense count: 3
240 | # Cop supports --auto-correct.
241 | # Configuration parameters: EnforcedStyle, SupportedStyles.
242 | TrailingBlankLines:
243 | Enabled: false
244 |
245 | # Offense count: 10
246 | # Cop supports --auto-correct.
247 | TrailingWhitespace:
248 | Enabled: false
249 |
250 | # Offense count: 1
251 | # Cop supports --auto-correct.
252 | # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, Whitelist.
253 | TrivialAccessors:
254 | Enabled: false
255 |
256 | # Offense count: 4
257 | UnusedBlockArgument:
258 | Enabled: false
259 |
260 | # Offense count: 4
261 | UnusedMethodArgument:
262 | Enabled: false
263 |
264 | # Offense count: 3
265 | UselessAssignment:
266 | Enabled: false
267 |
268 | # Offense count: 13
269 | Void:
270 | Enabled: false
271 |
272 | # Offense count: 1
273 | # Cop supports --auto-correct.
274 | WordArray:
275 | MinSize: 2
276 |
--------------------------------------------------------------------------------
/shadow_puppet.gemspec:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require 'shadow_puppet/version'
5 |
6 | Gem::Specification.new do |spec|
7 | spec.name = "shadow_puppet"
8 | spec.version = ShadowPuppet::VERSION
9 | spec.authors = ["Jesse Newland", "Josh Nichols", "Eric Lindvall",
10 | "Lee Jones", "Will Farrington", "dreamcat4",
11 | "Patrick Schless", "Ches Martin", "Rob Lingle",
12 | "Scott Fleckenstein", "Bryan Traywick"]
13 | spec.email = ["bryan@railsmachine.com"]
14 | spec.description = %q{A Ruby Puppet DSL}
15 | spec.summary = %q{A Ruby Puppet DSL}
16 | spec.homepage = "https://github.com/railsmachine/shadow_puppet/"
17 | spec.license = "LGPL"
18 |
19 | spec.files = `git ls-files`.split($/)
20 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22 | spec.require_paths = ["lib"]
23 |
24 | spec.add_runtime_dependency "puppet", "~> 4.4.1"
25 | spec.add_runtime_dependency "activesupport", ">= 2.2.0", "< 5.0"
26 |
27 | spec.add_development_dependency "bundler", "~> 1.3"
28 | spec.add_development_dependency "rake"
29 | spec.add_development_dependency "rspec", "~> 2.8.0"
30 | spec.add_development_dependency "rspec-core", "~> 2.8.0"
31 | spec.add_development_dependency "rubocop"
32 | end
33 |
--------------------------------------------------------------------------------
/spec/cli_spec.rb:
--------------------------------------------------------------------------------
1 | describe "ShadowPuppet's cli" do
2 | before(:all) do
3 | @root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
4 | @output = `#{@root}/bin/shadow_puppet #{@root}/spec/fixtures/cli_spec_manifest.rb echo=works`
5 | end
6 |
7 | it "accepts env variables on the end of the command line" do
8 | @output.should =~ /Exec\[from_env\]\/returns: works/
9 | end
10 |
11 | it "outputs the name of the manifest it's executing" do
12 | pending "can't yet determine how to do this"
13 | @output.should =~ /CliSpecManifest/
14 | end
15 |
16 | end
--------------------------------------------------------------------------------
/spec/core_ext_spec.rb:
--------------------------------------------------------------------------------
1 | describe Object do
2 | subject { Object.new }
3 |
4 | it { should respond_to(:present?) }
5 | it { should respond_to(:with_options) }
6 | end
7 |
8 | describe String do
9 | subject { "rake db:migrate spec" }
10 |
11 | it { should respond_to(:present?) }
12 | end
13 |
14 | describe Hash do
15 | subject { {:foo => :bar} }
16 | it { should respond_to(:reverse_merge) }
17 | it { should respond_to(:deep_merge) }
18 | it { should respond_to(:with_indifferent_access) }
19 | end
20 |
--------------------------------------------------------------------------------
/spec/fixtures/cli_spec_manifest.rb:
--------------------------------------------------------------------------------
1 | class CliSpecManifest < ShadowPuppet::Manifest
2 | def test_setting_env
3 | exec :from_env, :command => "echo #{ENV['echo']}", :logoutput => true
4 | exec :normal, :command => "echo test", :logoutput => true
5 | end
6 | recipe :test_setting_env
7 | end
--------------------------------------------------------------------------------
/spec/fixtures/manifests.rb:
--------------------------------------------------------------------------------
1 | class BlankManifest < ShadowPuppet::Manifest
2 | end
3 |
4 | class SuccessfulManifest < ShadowPuppet::Manifest
5 | recipe :foo
6 |
7 | def foo
8 | exec('foo', :command => 'true')
9 | end
10 | end
11 |
12 | class FailureManifest < SuccessfulManifest
13 | recipe :bar
14 |
15 | def bar
16 | exec('bar', :command => 'false')
17 | end
18 | end
19 |
20 | #this does nothing
21 | class NoOpManifest < ShadowPuppet::Manifest
22 | def foo
23 | exec('foo', :command => 'true')
24 | end
25 |
26 | def bar
27 | exec('bar', :command => 'true')
28 | end
29 | end
30 |
31 | #demonstrate the default method of satifying requirements: instance methods
32 | class RequiresMetViaMethods < ShadowPuppet::Manifest
33 | recipe :foo, :bar
34 |
35 | configure({ :foo => :bar , :nested_hash => { :nested_foo => :bar }, 'string' => 'value' })
36 |
37 | def foo
38 | exec('foo', :command => 'true')
39 | end
40 |
41 | def bar
42 | exec('bar', :command => 'true')
43 | end
44 | end
45 |
46 | class RequiresMetViaMethodsSubclass < RequiresMetViaMethods
47 | recipe :baz
48 |
49 | configure({ :baz => :bar, :nested_hash => { :nested_baz => :bar } })
50 |
51 | def baz
52 | exec('baz', :command => 'true')
53 | end
54 | end
55 |
56 | # Requirements can be handled by other recipes in the class
57 | class RequiresMetViaRecipeFromClassOfInstance < ShadowPuppet::Manifest
58 | def bar
59 | # other recipe stuff
60 | end
61 |
62 | def foo
63 | recipe :bar
64 | end
65 | recipe :foo
66 | end
67 |
68 | #requirements can also be handled by functions in external modules
69 | class ProvidedViaModules < ShadowPuppet::Manifest
70 | module FooRecipe
71 | def foo
72 | file('/tmp/moonshine_foo', :ensure => 'present', :content => 'foo')
73 | end
74 | end
75 |
76 | module BarRecipe
77 | def bar
78 | file('/tmp/moonshine_bar', :ensure => 'absent')
79 | end
80 | end
81 | include FooRecipe
82 | include BarRecipe
83 | recipe :foo, :bar
84 | end
85 |
86 | #requirements can also be handled by functions in external modules
87 | class PassingArguments < ShadowPuppet::Manifest
88 | def foo(options = {})
89 | file(options[:name], :ensure => 'present', :content => 'foo')
90 | end
91 | recipe :foo, :name => '/tmp/moonshine_foo'
92 | end
93 |
94 | # since self.respond_to?(:foo) == false, this raises an error when run
95 | class RequirementsNotMet < ShadowPuppet::Manifest
96 | recipe :foo, :bar
97 |
98 | # def foo
99 | # end
100 |
101 | def bar
102 | #this is okay
103 | end
104 | end
105 |
106 | class ConfigurationWithConvention < ShadowPuppet::Manifest
107 | configure(:foo => :bar)
108 | def foo(string)
109 | file('/tmp/moonshine_foo', :ensure => 'present', :content => string.to_s)
110 | end
111 | recipe :foo
112 | end
113 |
114 | # Test MoonshienSetupManifest
115 | class MoonshineSetupManifest < ShadowPuppet::Manifest
116 |
117 | configure(
118 | :deploy_to => "#{ENV['PWD']}/.shadow_puppet_test",
119 | :user => ENV['USER'],
120 | :group => (`uname -a`.match(/Darwin/) ? 'everyone' : ENV['USER'])
121 | )
122 |
123 | def directories
124 | deploy_to_array = configuration[:deploy_to].split('/')
125 | deploy_to_array.each_with_index do |dir, index|
126 | next if index == 0 || index >= (deploy_to_array.size-1)
127 | file '/'+deploy_to_array[1..index].join('/'), :ensure => :directory
128 | end
129 |
130 | dirs = [
131 | "#{configuration[:deploy_to]}",
132 | "#{configuration[:deploy_to]}/shared",
133 | "#{configuration[:deploy_to]}/shared/config",
134 | "#{configuration[:deploy_to]}/releases"
135 | ]
136 |
137 | dirs.each do |dir|
138 | file dir,
139 | :ensure => :directory,
140 | :owner => configuration[:user],
141 | :group => configuration[:group] || configuration[:user],
142 | :mode => '775'
143 | end
144 | end
145 | recipe :directories
146 | end
147 |
148 | # setting up a few different resource types to test the test helpers
149 | class TestHelpers < ShadowPuppet::Manifest
150 |
151 | def foo
152 | exec('foo', :command => 'true',:onlyif => 'test `hostname` == "foo"')
153 | package('bar',:ensure => :installed)
154 | file('/tmp/baz', :content => 'bar',:mode => '644',:owner => 'rails', :before => package('bar'))
155 | end
156 |
157 | end
158 |
159 | class DependencyTestManifest < ShadowPuppet::Manifest
160 | def test
161 | exec('foobar', :command => 'true', :before => exec('barbaz'))
162 | exec('trololol', :command => 'true', :alias => "winning")
163 | exec('barbaz', :command => 'true', :require => [exec('foobar'), exec('winning')])
164 |
165 | end
166 | recipe :test
167 | end
168 |
169 | class StupidTestManifest < ShadowPuppet::Manifest
170 | def my_recipe
171 | exec 'my_command',
172 | :command => 'true',
173 | :require => [ file('/tmp/foo'), exec('jk') ]
174 | file '/tmp/foo',
175 | :content => 'true'
176 | exec 'jk',
177 | :command => 'true'
178 | end
179 | recipe :my_recipe
180 | end
181 |
182 | class CwdCoercionTest < ShadowPuppet::Manifest
183 | def test
184 | tmp = Pathname.new('/tmp')
185 | exec 'true',
186 | :cwd => tmp
187 | file tmp.join('foo'),
188 | :ensure => :present
189 | end
190 | recipe :test
191 | end
192 |
193 | class DuplicateResourceTest < ShadowPuppet::Manifest
194 | def first_resource
195 | exec 'true',
196 | :cwd => Pathname.new('/tmp')
197 | end
198 |
199 | def second_resource
200 | exec 'true',
201 | :cwd => Pathname.new('/tmp'),
202 | :logoutput => true
203 | end
204 | end
205 |
206 | class MultipleRecipeConfigurationTest < ShadowPuppet::Manifest
207 | configure({
208 | :foo => {:man => 'chu'},
209 | :bar => {:food => 'yummy'}
210 | })
211 | recipe :foo, :bar
212 |
213 | def foo(options = {})
214 | exec 'foo', :command => 'true'
215 | end
216 |
217 | def bar(options = {})
218 | exec 'bar', :command => 'true'
219 | end
220 | end
221 |
--------------------------------------------------------------------------------
/spec/manifest_spec.rb:
--------------------------------------------------------------------------------
1 | describe "A manifest" do
2 |
3 | describe "when successful" do
4 |
5 | before(:each) do
6 | @manifest = SuccessfulManifest.new
7 | end
8 |
9 | it "returns true when executed" do
10 | @manifest.execute.should be_true
11 | end
12 |
13 | end
14 |
15 | describe "with resouces that fail" do
16 |
17 | before(:each) do
18 | @manifest = FailureManifest.new
19 | end
20 |
21 | it "returns false when executed" do
22 | @manifest.execute.should be_false
23 | end
24 |
25 | end
26 |
27 | describe "when blank" do
28 |
29 | before(:each) do
30 | @manifest = BlankManifest.new
31 | end
32 |
33 | it "does nothing" do
34 | @manifest.class.recipes.should == []
35 | end
36 |
37 | it "returns true when executed" do
38 | @manifest.execute.should be_true
39 | end
40 |
41 | end
42 |
43 | describe "without specified recipes" do
44 |
45 | before(:each) do
46 | @manifest = NoOpManifest.new
47 | end
48 |
49 | it "is executable by default" do
50 | @manifest.should be_executable
51 | end
52 |
53 | describe "when calling instance methods" do
54 |
55 | before(:each) do
56 | @manifest.foo
57 | end
58 |
59 | it "creates resources" do
60 | @manifest.execs.keys.sort.should == ['foo']
61 | end
62 |
63 | it "applies our customizations to resources" do
64 | @manifest.execs["foo"].path.should == ENV["PATH"].split(':')
65 | end
66 |
67 | describe "and then executing" do
68 |
69 | before(:each) do
70 | @manifest = @manifest.execute
71 | end
72 |
73 | it "returns true" do
74 | @manifest.should be_true
75 | end
76 |
77 | end
78 |
79 | end
80 |
81 | end
82 |
83 | describe "when recipes aren't fullfilled" do
84 |
85 | before(:each) do
86 | @manifest = RequirementsNotMet.new
87 | end
88 |
89 | it "returns false when executed" do
90 | @manifest.execute.should be_false
91 | end
92 |
93 | it "raises an error when executed!" do
94 | lambda {
95 | @manifest.execute!
96 | }.should raise_error(NameError)
97 | end
98 |
99 | end
100 |
101 | describe "in general" do
102 |
103 | before(:each) do
104 | @manifest = RequiresMetViaMethods.new
105 | end
106 |
107 | it "knows what it's supposed to do" do
108 | @manifest.class.recipes.should == [[:foo, {}], [:bar, {}]]
109 | end
110 |
111 | it "loading configuration on the class" do
112 | @manifest.class.configuration[:foo].should == :bar
113 | end
114 |
115 | it "can access the same configuration hash on the instance" do
116 | @manifest.configuration[:foo].should == :bar
117 | end
118 |
119 | it "can access configurations configured using symbols with symbols or strings" do
120 | @manifest.configuration[:foo].should == :bar
121 | @manifest.configuration['foo'].should == :bar
122 | end
123 |
124 |
125 | it "can access configurations configured using strings with symbols or strings" do
126 | @manifest.configuration['string'].should == 'value'
127 | @manifest.configuration[:string].should == 'value'
128 | end
129 |
130 | it "has a name" do
131 | @manifest.name.should == "#{@manifest.class}##{@manifest.object_id}"
132 | end
133 |
134 | describe 'when evaluated' do
135 |
136 | it "calls specified methods" do
137 | @manifest.should_receive(:foo)
138 | @manifest.should_receive(:bar)
139 | @manifest.send(:evaluate_recipes)
140 | end
141 |
142 | it "passes the configuration hash key named by each method if no options given" do
143 | @manifest = ConfigurationWithConvention.new
144 | @manifest.should_receive(:foo).with(:bar).exactly(1).times
145 | @manifest.send(:evaluate_recipes)
146 | end
147 |
148 | it "creates new resources" do
149 | @manifest.should_receive(:add_resource).with(Puppet::Type::Exec, 'foo', :command => 'true').exactly(1).times
150 | @manifest.should_receive(:add_resource).with(Puppet::Type::Exec, 'bar', :command => 'true').exactly(1).times
151 | @manifest.send(:evaluate_recipes)
152 | end
153 |
154 | it "creates new resources" do
155 | @manifest.send(:evaluate_recipes)
156 | @manifest.execs.keys.sort.should == ['bar', 'foo']
157 | end
158 |
159 | end
160 |
161 | describe "when executed" do
162 |
163 | it "calls evaluate_recipes and apply" do
164 | @manifest.should_receive(:evaluate_recipes, &@manifest.method(:evaluate_recipes))
165 | @manifest.should_receive(:apply, &@manifest.method(:apply))
166 | @manifest.execute
167 | end
168 |
169 | it "returns true" do
170 | @manifest.execute.should be_true
171 | end
172 |
173 | it "cannot be executed again" do
174 | @manifest.execute.should be_true
175 | @manifest.execute.should be_false
176 | end
177 |
178 | end
179 |
180 | describe "after execution" do
181 |
182 | before(:each) do
183 | @manifest = ProvidedViaModules.new
184 | @manifest.execute
185 | end
186 |
187 | it "allows creation of other similar resources" do
188 | m = PassingArguments.new
189 | m.execute.should be_true
190 | end
191 |
192 | end
193 |
194 | end
195 |
196 | describe "that subclasses an existing manifest" do
197 |
198 | before(:each) do
199 | @manifest = RequiresMetViaMethodsSubclass.new
200 | end
201 |
202 | it "inherits recipes from the parent class" do
203 | @manifest.class.recipes.map(&:first).should include(:foo, :bar)
204 | @manifest.class.recipes.first.first.should == :foo
205 | end
206 |
207 | it "appends recipes created in the subclass" do
208 | @manifest.class.recipes.map(&:first).should include(:baz)
209 | @manifest.class.recipes.last.first.should == :baz
210 | end
211 |
212 | it "merges it's configuration with that of the parent" do
213 | @manifest.class.configuration[:foo].should == :bar
214 | @manifest.class.configuration[:baz].should == :bar
215 | end
216 |
217 | it "deep_merges it's configuration with that of the parent" do
218 | @manifest.class.configuration[:nested_hash][:nested_baz].should == :bar
219 | @manifest.class.configuration[:nested_hash][:nested_foo].should == :bar
220 | @manifest.class.configuration['nested_hash']['nested_foo'].should == :bar
221 | end
222 |
223 | it "is able to add configuration parameters on the instance" do
224 | @manifest.configure 'boo' => :bar
225 | @manifest.configuration[:boo].should == :bar
226 | @manifest.class.configuration[:boo].should == :bar
227 | end
228 |
229 | end
230 | describe "that has recipes called from other recipes" do
231 | before(:each) do
232 | @manifest = RequiresMetViaRecipeFromClassOfInstance.new
233 | end
234 |
235 | it "is able to call a recipe of the class of this instance" do
236 | @manifest.execute.should be_true
237 | end
238 | end
239 |
240 | describe "when moonshine setup" do
241 |
242 | before(:each) do
243 | @manifest = MoonshineSetupManifest.new
244 | end
245 |
246 | it "include directories recipe" do
247 | @manifest.class.recipes.map(&:first).should include(:directories)
248 | end
249 |
250 | it "calls specified methods" do
251 | @manifest.should_receive(:directories)
252 | @manifest.send(:evaluate_recipes)
253 | end
254 |
255 | it "returns true when executed" do
256 | @manifest.execute.should be_true
257 | end
258 |
259 | end
260 |
261 | describe "when dependency test manifest" do
262 | before(:each) do
263 | @manifest = DependencyTestManifest.new
264 | end
265 |
266 | it "include directories recipe" do
267 | @manifest.class.recipes.map(&:first).should include(:test)
268 | end
269 |
270 | it "calls specified methods" do
271 | @manifest.should_receive(:test)
272 | @manifest.send(:evaluate_recipes)
273 | end
274 |
275 | it "returns true when executed" do
276 | @manifest.execute!.should be_true
277 | end
278 | end
279 |
280 | describe "when referencing files" do
281 | before(:each) do
282 | @manifest = StupidTestManifest.new
283 | end
284 |
285 | it "returns true when executed " do
286 | @manifest.execute!.should be_true
287 | end
288 | end
289 |
290 | describe "when passing a pathname to cwd" do
291 | before(:each) do
292 | @manifest = CwdCoercionTest.new
293 | end
294 |
295 | it "returns true when executed" do
296 | @manifest.execute!.should be_true
297 | end
298 | end
299 |
300 | describe "with multiple calls to the same resource" do
301 | before(:each) do
302 | @manifest = DuplicateResourceTest.new
303 | end
304 |
305 | it "creates the resource the first time" do
306 | @manifest.first_resource
307 | @manifest.execs["true"].cwd.should == '/tmp'
308 | @manifest.execs["true"].logoutput.should eql(:on_failure)
309 | end
310 |
311 | it "updates the resource subsequent times" do
312 | @manifest.first_resource
313 | @manifest.second_resource
314 | @manifest.execs["true"].cwd.should == '/tmp'
315 | @manifest.execs["true"].logoutput.should be_true
316 | end
317 |
318 | it "can execute successfully" do
319 | @manifest.first_resource
320 | @manifest.second_resource
321 | @manifest.execute!
322 | end
323 | end
324 |
325 | describe "when configure is used to configure multiple recipes" do
326 | before(:each) do
327 | @manifest = MultipleRecipeConfigurationTest.new
328 | end
329 |
330 | it "passes the appropriate options to each recipe" do
331 | @manifest.should_receive(:foo).with({'man' => 'chu'})
332 | @manifest.should_receive(:bar).with({'food' => 'yummy'})
333 | @manifest.send(:evaluate_recipes)
334 | end
335 |
336 | it "returns true when executed" do
337 | @manifest.execute!.should be_true
338 | end
339 | end
340 | end
341 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'bundler'
2 | Bundler.require(:default, :development)
3 |
4 | $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
5 | $LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib'))
6 |
7 | require 'shadow_puppet/core_ext'
8 | require 'shadow_puppet'
9 | require 'shadow_puppet/test'
10 |
11 | Dir.glob(File.join(File.expand_path(File.dirname(__FILE__)), 'fixtures', '*.rb')).each do |manifest|
12 | require manifest
13 | end
14 |
--------------------------------------------------------------------------------
/spec/test_spec.rb:
--------------------------------------------------------------------------------
1 | describe "ShadowPuppet's test helpers" do
2 |
3 | it "should be created when register_puppet_types_for_testing is called" do
4 | Puppet::Type.newtype(:dummy) {}
5 | Puppet::Type.type(:dummy).provide(:generic) {}
6 |
7 | BlankManifest.new.respond_to?(:dummies).should == false
8 | ShadowPuppet::Manifest.register_puppet_types_for_testing
9 | BlankManifest.new.respond_to?(:dummies).should == true
10 | end
11 |
12 | describe "when used in tests" do
13 |
14 | before do
15 | @manifest = TestHelpers.new
16 | @manifest.foo
17 | end
18 |
19 | it "should allow simple resource lookup" do
20 | @manifest.execs.keys.should == ['foo']
21 | @manifest.packages.keys.should == ['bar']
22 | @manifest.files.keys.should == ['/tmp/baz']
23 | @manifest.crons.keys.should == []
24 | end
25 |
26 | # making sure that properties such as, e.g the :onlyif condition of Exec[foo]
27 | # can be accessed simply as manifest.execs['foo'].onlyif rather than via the
28 | # param hash
29 | it "should allow referencing params directly" do
30 | @manifest.execs['foo'].command.should == 'true'
31 | end
32 |
33 | end
34 |
35 | end
36 |
--------------------------------------------------------------------------------
/spec/type_spec.rb:
--------------------------------------------------------------------------------
1 | describe "ShadowPuppet's type loading mechanism" do
2 | it "should create a new type helper methods when register_puppet_types is called" do
3 | Puppet::Type.newtype(:dummy_1) {}
4 | Puppet::Type.type(:dummy_1).provide(:generic) {}
5 |
6 | BlankManifest.new.respond_to?(:dummy_1).should == false
7 | ShadowPuppet::Manifest.register_puppet_types
8 | BlankManifest.new.respond_to?(:dummy_1).should == true
9 | end
10 | end
11 |
--------------------------------------------------------------------------------