promotion 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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