promotion 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG CHANGED
@@ -1,7 +1,13 @@
1
+
2
+ == Version 1.3.0
3
+ - Added generation of newsyslog.conf
4
+ - Added some more examples into the shell generated by mkdeploy
5
+ - eliminated ERB, replacing it with plain text generation
6
+
1
7
  == Version 1.2.1
2
8
  - Added Clear attribute to Folder.
3
- - Fixed crontab generation. crontabs are generated completely - prior
4
- contents is not preserved (due to the nature of crontabs)
9
+ - Fixed crontab generation. crontabs are generated completely - prior
10
+ contents is not preserved (due to the nature of crontabs)
5
11
 
6
12
  == Version 1.2.0
7
13
  - Make Promotion a better gem citizen by making the installation of executables
data/bin/mkdeploy CHANGED
@@ -29,6 +29,7 @@ template =<<-EOT
29
29
  <Files Owner="root" Group="wheel" Mode="0644" Source="conf">
30
30
  <File>/etc/my.cnf</File>
31
31
  <File>/etc/myapp.conf</File>
32
+ <Link Owner="root" Group="bin" Mode="0640" Target="/home/myapp/bak">/var/myappbak</Link>
32
33
  </Files>
33
34
  <Files Owner="root" Group="wheel" Mode="0750" Source="sbin">
34
35
  <File>/usr/local/sbin/up!</File>
@@ -36,7 +37,14 @@ template =<<-EOT
36
37
  <File>/usr/local/sbin/push!</File>
37
38
  </Files>
38
39
  <Allfiles Group="bin" Mode="0644" Owner="root" Source="conf">/var/axonsec/conf</Allfiles>
40
+ <Sudoers>
41
+ <UserPrivilege Password="false" Runas="admin" User="richard">/sbin/halt -p</UserPrivilege>
42
+ </Sudoers>
39
43
  <Daemon Flags="" Priority="10" User="_myapp" />
44
+ <Environment>
45
+ <Variable Comment="Set up for Java" Name="JAVA_HOME">/usr/local/lib/jdk-1.7.0</Variable>
46
+ <Alias Name="ll">ls -la </Alias>
47
+ </Environment>
40
48
  <Crontab>
41
49
  <Schedule User="root" Hour="2" Minute="7"
42
50
  Comment="Backup the entire database at 2:07am each morning">
@@ -44,6 +52,8 @@ template =<<-EOT
44
52
  </Schedule>
45
53
  </Crontab>
46
54
  <Database>/usr/local/bin/mysql</Database>
55
+ <Newsyslog Owner="_myapp" Group="_myapp" Mode="0640" Count="10" When="D02"
56
+ Zip="false" Binary="true">/var/myapp/logs/myapp.log</Newsyslog>
47
57
  </Specification>
48
58
  EOT
49
59
 
@@ -27,9 +27,8 @@ class Application
27
27
  # Promotes an application into production.
28
28
  def promote()
29
29
  enforcer = Promotion::Enforcer.new(@appname)
30
- generator = Promotion::Generator.new()
31
30
  enforcer.start()
32
- generator.start()
31
+ Generator::start()
33
32
  $log.info("Application #{@appname} successfully promoted.")
34
33
  end
35
34
 
@@ -22,6 +22,8 @@ module Files
22
22
  Sudoers = "/etc/sudoers"
23
23
  # system path to rc.conf.local
24
24
  Rc_conf = "/etc/rc.conf.local"
25
+ # system path to newsyslog.conf
26
+ Newsyslog = "/etc/newsyslog.conf"
25
27
  # path to useradd executable
26
28
  Useradd = "/usr/sbin/useradd"
27
29
  # path to groupadd executable
@@ -36,13 +38,14 @@ end
36
38
 
37
39
  # A section of the configuration file that contains ERB template filenames
38
40
  module Templates
39
- # filename for Profile template
41
+ # filename for template for /etc/profile
40
42
  Profile = "profile.erb"
41
- # filename for Rc_conf template
43
+ # filename for template for /etc/rc.conf.local
42
44
  Rc_conf = "rc.conf.local.erb"
43
- # filename for Sudoers template
45
+ # filename for template for /etc/sudoers
44
46
  Sudoers = "sudoers.erb"
45
47
  # filename for Crontab template
46
48
  Crontab = "crontab.erb"
49
+ # filename for template for /etc/newsyslog.conf
50
+ Newsyslog = "newsyslog.conf.erb"
47
51
  end
48
-
@@ -1,56 +1,36 @@
1
- require 'erb'
2
1
  require 'rexml/document'
3
- require 'fileutils'
2
+ require 'promotion/generator/profile'
3
+ require 'promotion/generator/rcconf'
4
+ require 'promotion/generator/newsyslog'
5
+ require 'promotion/generator/sudoers'
6
+ require 'promotion/generator/crontab'
4
7
 
5
- module Promotion # :nodoc:
8
+ module Promotion
9
+ module Generator
6
10
 
7
- # Generator generates the system-wide files that can only be assembled by taking into
8
- # account all of the applications that are deployed (ie. found in the staging area).
9
- #
10
- # Such files as /etc/profile, /etc/sudoers, and crontabs might receive contributions
11
- # from a several apps. For example, /etc/profile might need to set an environment variable
12
- # like JAVA_HOME and MYAPP_BASE.
13
- class Generator
11
+ Marker = "#---promotion---" # add this marker to a config file to preserve the contents before it
14
12
 
15
- TEMPLATES = File.expand_path("erb", File.dirname(__FILE__))
16
-
17
- # Returns the XML specification for the selected application
18
- attr_accessor :specs
19
-
20
- # Creates a new Generator.
21
- #
22
- # Note that if any generated file contains a line starting with the +marker+
23
- # (eg. "#---promotion---"), we preserve the contents up to that point, and replace
24
- # the contents from that point onwards.
25
- def initialize()
26
- @specs = []
27
- @marker = "#---promotion---" # add this marker to a config file to preserve the contents before it
28
- end
29
-
30
- # Gathers the specs for all applications in the staging area, then generate:
31
- # - /etc/profile
32
- # - /etc/rc.conf.local
33
- # - /etc/sudoers
34
- # - /var/cron/tabs/*
35
- def start()
13
+ def self.start()
36
14
  $log.info("\n#{'_'*40}\nGenerating common environment files")
37
- gather_specs()
38
- generate_standard("Profile")
39
- generate_standard("Rc_conf")
40
- generate_sudoers()
41
- generate_crontab()
42
- end
15
+ specs = self.gather_specs()
16
+ Profile.generate(specs)
17
+ Rcconf.generate(specs)
18
+ Newsyslog.generate(specs)
19
+ Sudoers.generate(specs)
20
+ Crontab.generate(specs)
21
+ end
43
22
 
44
23
  # Gathers specs from all deployment descriptors in folders with the staging area
45
- def gather_specs()
24
+ def self.gather_specs()
25
+ specs = []
46
26
  begin
47
27
  search = Folders::Staging + "/*/" + Files::Spec
48
28
  $log.debug("Searching for application specs with Dir[#{search}]")
49
29
  specfiles = Dir[search]
50
30
  specfiles.each { |filename|
51
31
  specfile = File.new(filename, File::RDONLY)
52
- doc = REXML::Document.new(specfile)
53
- @specs << doc.root
32
+ doc = ::REXML::Document.new(specfile)
33
+ specs << doc.root
54
34
  specfile.close()
55
35
  }
56
36
  return(specs)
@@ -60,145 +40,28 @@ class Generator
60
40
  end
61
41
  end
62
42
 
63
- # Convenience method to create an ERB generator based on the template corresponding
64
- # to a name. For example, if +sym+ is "Profile", we use the filename Templates::Profile
65
- # as the template.
66
- #
67
- # Returns the ERB generator
68
- def generator_for(sym)
43
+ # Write the approved contents to the system file
44
+ # or a temporary file instead if temp is true.
45
+ def self.write_file_for(sym, newContents, temp=false)
69
46
  begin
70
- templateFilename = File.expand_path(Templates.const_get(sym), TEMPLATES)
71
- raise "Missing template for #{sym}" unless File.exists?(templateFilename)
72
- $log.debug("Generating with template #{templateFilename}")
73
- templateFile = File.new(templateFilename, File::RDONLY)
74
- template = templateFile.read
47
+ filename = Files.const_get(sym) + (temp ? ".tmp" : "")
48
+ f = File.new(filename, File::WRONLY | File::CREAT | File::TRUNC)
49
+ f.puts(newContents)
50
+ $log.info("Generated #{sym} written to #{filename}.") unless temp
75
51
  ensure
76
- templateFile.close() unless templateFile.nil? || templateFile.closed?
52
+ f.close unless f.nil? || f.closed?
77
53
  end
78
- generator = ERB.new(template, nil, '<>')
79
- return(generator)
54
+ return(filename)
80
55
  end
81
56
 
82
- # Convenience method to generate content for a system file, preserving the content
83
- # up to the +marker+, and appending the generated content.
84
- #
85
- # Returns a string of the new content for the file
86
- def contents_for(sym, generator, definedBinding)
87
- generatedContents = generator.result(definedBinding)
57
+ # Returns a string of the new content for the file up to the +marker+
58
+ def self.original_contents_for(sym)
88
59
  begin
89
- originalContents = IO.readlines(Files.const_get(sym),"")[0].split(@marker)[0]
60
+ originalContents = IO.readlines(Files.const_get(sym),"")[0].split(Marker)[0]
90
61
  rescue
91
62
  originalContents = ""
92
63
  end
93
- newContents = [originalContents, @marker, generatedContents].join("\n")
94
- return(newContents)
95
- end
96
-
97
- # Sometimes we need to run a process on the new content in a temporary file
98
- # instead of writing the content directly to a new file (eg. visudo)
99
- def write_temp_file_for(sym, newContents)
100
- begin
101
- tempFilename = Files.const_get(sym)+".tmp"
102
- tempFile = File.new(tempFilename, File::WRONLY | File::CREAT | File::TRUNC)
103
- tempFile.puts(newContents)
104
- ensure
105
- tempFile.close unless tempFile.nil? || tempFile.closed?
106
- end
107
- return(tempFilename)
108
- end
109
-
110
- # Write the approved contents to the system file
111
- def write_file_for(sym, newContents)
112
- begin
113
- generatedFilename = Files.const_get(sym)
114
- generatedFile = File.new(generatedFilename, File::WRONLY | File::CREAT | File::TRUNC)
115
- generatedFile.puts(newContents)
116
- $log.info("Generated #{sym} written to #{generatedFilename}.")
117
- ensure
118
- generatedFile.close unless generatedFile.nil? || generatedFile.closed?
119
- end
120
- end
121
-
122
- # Convenience method for standard configuration files:
123
- # - creates a generator using the relevant template
124
- # - generate the new contents
125
- # - write the contents to the system file
126
- # This is useful when the system file can be written directly without an intermediate
127
- # checking step (such as visudo).
128
- def generate_standard(sym)
129
- begin
130
- generator = generator_for(sym)
131
- newContents = contents_for(sym, generator, binding)
132
- write_file_for(sym, newContents)
133
- rescue => e
134
- $log.error("Error occurred while generating #{sym}\n#{e.message}" + e.backtrace.join("\n"))
135
- exit 1
136
- end
137
- end
138
-
139
- # The sudoers file must be verified by the visudo command
140
- def generate_sudoers()
141
- begin
142
- generator = generator_for("Sudoers")
143
- newContents = contents_for("Sudoers", generator, binding)
144
- tempFilename = write_temp_file_for("Sudoers", newContents)
145
- $log.info("Checking temporary sudoers written to #{tempFilename}.")
146
- visudoResults = `#{Files::Visudo} -c -f #{tempFilename}`
147
- if visudoResults =~ /parsed OK/
148
- $log.info("visudo confirms that sudoers syntax is correct.")
149
- else
150
- $log.error(visudoResults)
151
- raise
152
- end
153
- write_file_for("Sudoers", newContents)
154
- FileUtils.rm_f(tempFilename)
155
- $log.info("New sudoers written to #{Files::Sudoers}")
156
- rescue => e
157
- $log.error("Error occurred while generating sudoers\n#{e.message}" + e.backtrace.join("\n"))
158
- exit 1
159
- end
160
- end
161
-
162
- # The crontab for each user must be deployed using the crontab tool so this
163
- # method cannot use the convenience methods because the filenames are user-specific
164
- def generate_crontab()
165
- begin
166
- schedules = {} # keyed on user, value is array of schedule elements
167
- @specs.each { |spec|
168
- spec.elements.each("/Specification/Crontab/Schedule") { |sched|
169
- user = sched.attributes["User"]
170
- schedules[user] = [] unless schedules.has_key?(user)
171
- schedules[user] << sched
172
- }
173
- }
174
- generator = generator_for("Crontab")
175
- schedules.each { |user, crontablist|
176
- generatedContents = generator.result(binding)
177
- userCrontab = File.expand_path(user, Folders::Crontabs)
178
- begin
179
- tempFilename = user + ".tmp"
180
- tempFile = File.new(tempFilename, File::WRONLY | File::CREAT | File::TRUNC)
181
- tempFile.puts(generatedContents)
182
- # previous cron jobs are *NOT* preserved - the tab is competely generated!
183
- # otherwise we'll double the crontab each time we run.
184
- ensure
185
- tempFile.close unless tempFile.nil? || tempFile.closed?
186
- end
187
- $log.info("Checking temporary crontab written to #{tempFilename}.")
188
- crontabResults = `#{Files::Crontab} -u #{user} #{tempFilename}`
189
- if crontabResults == ""
190
- $log.info("crontab confirms that crontab syntax is correct for user #{user}.")
191
- else
192
- $log.error(crontabResults)
193
- raise
194
- end
195
- FileUtils.rm_f(tempFilename)
196
- $log.info("New crontab installed for user #{user}")
197
- }
198
- rescue => e
199
- $log.error("Error occurred while generating crontab\n#{e.message}" + e.backtrace.join("\n"))
200
- exit 1
201
- end
64
+ return(originalContents)
202
65
  end
203
66
 
204
67
  end
data/promotion.xsd CHANGED
@@ -20,6 +20,7 @@
20
20
  <xsd:element ref="Environment" minOccurs="0" maxOccurs="1" />
21
21
  <xsd:element ref="Crontab" minOccurs="0" maxOccurs="1" />
22
22
  <xsd:element ref="Database" minOccurs="0" maxOccurs="1" />
23
+ <xsd:element ref="Newsyslog" minOccurs="0" maxOccurs="unbounded" />
23
24
  </xsd:sequence>
24
25
  <xsd:attribute name="Name" type="xsd:NMTOKEN" use="required" />
25
26
  <xsd:attribute name="Title" type="xsd:string" use="required" />
@@ -255,6 +256,23 @@
255
256
  </xsd:complexType>
256
257
  </xsd:element>
257
258
 
259
+ <xsd:element name="Newsyslog">
260
+ <xsd:complexType>
261
+ <xsd:simpleContent>
262
+ <xsd:extension base="xsd:normalizedString">
263
+ <xsd:attributeGroup ref="FileAttributes" />
264
+ <xsd:attribute name="Count" type="xsd:integer" use="optional" default="10" />
265
+ <xsd:attribute name="Size" type="xsd:integer" use="optional" />
266
+ <xsd:attribute name="When" type="xsd:string" use="optional" default="24" />
267
+ <!-- treat as $-format if starts with D,W,M; treat as integer if integer
268
+ else treat as @-format if digits with a 'T' -->
269
+ <xsd:attribute name="Zip" type="xsd:boolean" use="optional" default="true"/>
270
+ <xsd:attribute name="Binary" type="xsd:boolean" use="optional" default="false"/>
271
+ </xsd:extension>
272
+ </xsd:simpleContent>
273
+ </xsd:complexType>
274
+ </xsd:element>
275
+
258
276
  <xsd:attributeGroup name="FolderAttributes">
259
277
  <xsd:attribute name="Group" type="xsd:NMTOKEN" use="optional" default="bin"/>
260
278
  <xsd:attribute name="Owner" type="xsd:NMTOKEN" use="optional" default="root"/>
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: promotion
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 2
9
- - 1
10
- version: 1.2.1
8
+ - 3
9
+ - 0
10
+ version: 1.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Richard Kernahan
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-09-25 00:00:00 Z
18
+ date: 2012-09-26 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: log4r