Class: DevStructure::Blueprint

Inherits:
Hash
  • Object
show all
Includes:
DevStructure, Puppet
Defined in:
lib/devstructure/blueprint.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, hash) ⇒ Blueprint

Returns a new instance of Blueprint.



11
12
13
14
15
16
17
# File 'lib/devstructure/blueprint.rb', line 11

def initialize(name, hash)
  @name = name
  self["name"] = name
  super nil
  clear
  hash.each { |k, v| self[k.to_s] = v }
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(symbol, *args) ⇒ Object



28
29
30
# File 'lib/devstructure/blueprint.rb', line 28

def method_missing(symbol, *args)
  self[symbol.to_s]
end

Instance Attribute Details

#nameObject

Returns the value of attribute name.



19
20
21
# File 'lib/devstructure/blueprint.rb', line 19

def name
  @name
end

Instance Method Details

#-(other) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/devstructure/blueprint.rb', line 40

def -(other)
  b = Marshal.load(Marshal.dump(self))

  # First pass removes all duplicates except managers.
  other.walk(
    :package => lambda { |manager, command, package, version|
      return if b.packages[package]
      b.packages[manager]["_packages"].delete package
    }
  )

  # Second pass removes managers that manage no packages.
  #   For RubyGems we have to dig a little deeper to see what is managed
  #   by rubygems-update.
  b.walk(
    :package => lambda { |manager, command, package, version|
      return unless b.packages[package]
      test = if package =~ /^rubygems(\d+\.\d+)$/
        b.packages["rubygems-update"]
      else
        b.packages[package]
      end
      if test
        case test["_packages"].length
        when 0
        when 1
          return unless test["_packages"]["rubygems-update"]
        else
          return
        end
      end
      p = b.packages[manager]["_packages"]
      p.delete package
      b.packages.delete manager if 0 == p.length
    }
  )

  # Third pass adds back special dependencies like ruby*-dev.
  b.walk(
    :after => lambda { |manager, command|
      case manager

      # Match a version of ruby*-dev to a version of rubygems*.
      when /^rubygems(\d+\.\d+)$/
        b.packages["apt"]["_packages"]["ruby#{$1}-dev"] =
          packages["apt"]["_packages"]["ruby#{$1}-dev"]

      end
    }
  )

  b
end

#chefObject

Raises:

  • (NotImplementedError)


255
256
257
258
# File 'lib/devstructure/blueprint.rb', line 255

def chef
  puts "# No soup for you."
  raise NotImplementedError, "Contractor::Blueprint#chef", caller
end

#disclaimerObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/devstructure/blueprint.rb', line 94

def disclaimer
  puts <<EOF
#
# #{owner}'s #{name}
# http#{token ? "s" : ""}://devstructure.com/blueprints/#{owner}/#{name}
EOF
  if token
    puts <<EOF
#
# This blueprint is private.  You may share it by adding trusted users
# or by sharing this secret URL:
#   https://devstructure.com/blueprints/#{owner}/#{name}/#{token}
EOF
  end
  puts <<EOF
#
# This file was automatically generated by ruby-devstructure(7).
#
EOF
end

#filesObject



32
33
34
# File 'lib/devstructure/blueprint.rb', line 32

def files
  self["blueprint"]["_files"]
end

#packagesObject



36
37
38
# File 'lib/devstructure/blueprint.rb', line 36

def packages
  self["blueprint"]["_packages"]
end

#puppetObject



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/devstructure/blueprint.rb', line 153

def puppet
  disclaimer
  puts "class #{@name} {"
  puts Exec.defaults(:path => ENV["PATH"].split(":"))

  # Packages.
  walk(
    :before => lambda { |manager, command|
      p = packages[manager]["_packages"]
      return if 0 == p.length || 1 == p.length && p[manager]
      puts "\n\t# Packages that depend on #{manager}."
      case manager
      when /rubygems|rip/
        puts "\tpackage {"
      when /ruby|python/
        puts "\texec {"
      else
        puts "\tpackage {"
      end
    },
    :package => lambda { |manager, command, package, version|
      command =~ /gem(\d+\.\d+) install/
      case manager
      when "apt"
        unless manager == package
          puts Package.partial(package, :ensure => version)
        end
      when "rubygems-update"
        unless manager == package
          puts Package.partial(package,
            :require => [Package["ruby#{$1}-dev"], Exec["update_rubygems"]],
            :provider => :gem,
            :ensure => version
          )
        end
      when /rubygems|rip/
        options = {
          :require => Package["rubygems#{$1}", "ruby#{$1}-dev"],
          :provider => :gem,
          :ensure => version
        }
        options[:require] += [Package[manager]] unless manager == package
        puts Package.partial(package, options)
      when /python/
        puts Exec.partial(sprintf("#{command}\n", package),
          :require => Package[manager, "python-setuptools"]
        )

      # TODO Expand Java, Erlang, etc.

      else
        puts Exec.partial(sprintf("#{command}\n", package, version),
          :require => Package[manager]
        )
      end
    },
    :after => lambda { |manager, command|
      p = packages[manager]["_packages"]
      return if 0 == p.length || 1 == p.length && manager == p.keys.first
      puts "\t}"

      # Update RubyGems if we're through with gems in Debian's location.
      return unless manager =~ /^rubygems\d+\.\d+$/
      return unless p["rubygems-update"]
      puts Exec.complete("update_rubygems",
        :subscribe => Package["rubygems-update"],
        :path => %w(/usr/bin /var/lib/gems/1.8/bin),
        :refreshonly => true
      )

    }
  )

  # System files.
  if 0 < files.length
    puts "\n\t# System files depend on all packages."
    puts Package.defaults(
      :before => files.sort.map { |pathname, content|
        Puppet::File[pathname]
      }
    )
    puts "\tfile {"
    files.sort.each do |pathname, content|
      if Hash == content.class
        if content["_target"]
          puts Puppet::File.partial(pathname, :ensure => content["_target"])
          next
        elsif content["_base64"]
          content = Base64.decode64(content["_base64"])
        end
      end
      puts Puppet::File.partial(pathname,
        :content => content,
        :ensure => :present
      )
    end
    puts "\t}"
  end

  puts "\n}"
end

#save(ds = nil) ⇒ Object



21
22
23
24
# File 'lib/devstructure/blueprint.rb', line 21

def save(ds=nil)
  ds ||= API.new
  ds.blueprints(owner).post(name, self)
end

#shObject



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/devstructure/blueprint.rb', line 115

def sh
  disclaimer

  # Packages.
  walk(
    :package => lambda { |manager, command, package, version|
      return if manager == package
      printf "#{command}\n", package, version
      if "rubygems-update" == package && command =~ /gem(\d+\.\d+) install/
        puts "PATH=\"$PATH:/var/lib/gems/#{$1}/bin\" update_rubygems"
      end
    }
  )

  # System files.
  files.sort.each do |pathname, content|
    if Hash == content.class
      if content["_target"]
        puts "ln -s #{content["_target"]} #{pathname}"
        next
      elsif content["_base64"]
        content = content["_base64"]
        command = "base64 --decode"
      end
    else
      content.gsub!(/\\/, "\\\\\\\\")
      content.gsub!(/\$/, "\\$")
      command = "cat"
    end
    eof = "EOF"
    eof << "EOF" while content =~ /^#{eof}$/
    puts "#{command} >#{pathname} <<#{eof}"
    puts content
    puts eof
  end

end

#walk(options = {}) ⇒ Object

Walk a package tree and execute callbacks along the way. Callbacks are listed in the options hash. These are supported:

:before => lambda { |manager, command| }
  Executed before a manager's dependencies are enumerated.
:package => lambda { |manager, command, package, version| }
  Executed when a package is enumerated.
:after => lambda { |manager, command| }
  Executed after a manager's dependencies are enumerated.


268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/devstructure/blueprint.rb', line 268

def walk(options={})

  # Start with packages installed with apt.
  options[:manager] ||= "apt"

  # Handle managers.
  if options[:before].respond_to?(:call)
    options[:before].call options[:manager],
      packages[options[:manager]]["_command"]
  end

  # Callback for each package that depends on this manager.  Note which
  # ones themselves are managers.
  managers = []
  packages[options[:manager]]["_packages"].sort.each do |package, version|

    # Handle packages.
    if options[:package].respond_to?(:call)
      [version].flatten.each do |v|
        options[:package].call options[:manager],
          packages[options[:manager]]["_command"], package, v
      end
    end

    if options[:manager] != package && packages[package]
      managers << package
    end
  end

  # Handle managers.
  if options[:after].respond_to?(:call)
    options[:after].call options[:manager],
      packages[options[:manager]]["_command"]
  end

  # Recurse into each manager that was just installed.  This is done
  # after the completed round because there could have been other
  # dependencies at that level aside from the manager.
  managers.each do |manager|
    walk options.merge(:manager => manager)
  end

end