promotion 1.0.7
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/.yardopts +1 -0
- data/CHANGELOG +0 -0
- data/README +256 -0
- data/VERSION +1 -0
- data/bin/devolve +59 -0
- data/bin/evolve +60 -0
- data/bin/promote +67 -0
- data/deploy.xml +35 -0
- data/ext/promotion/extconf.rb +19 -0
- data/lib/promotion/application.rb +49 -0
- data/lib/promotion/config.rb +55 -0
- data/lib/promotion/enforcer.rb +322 -0
- data/lib/promotion/erb/crontab.erb +19 -0
- data/lib/promotion/erb/profile.erb +17 -0
- data/lib/promotion/erb/rc.conf.local.erb +20 -0
- data/lib/promotion/erb/sudoers.erb +22 -0
- data/lib/promotion/evolver.rb +126 -0
- data/lib/promotion/generator.rb +207 -0
- data/lib/promotion.rb +8 -0
- data/promotion.xsd +323 -0
- metadata +97 -0
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'rexml/document'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Promotion # :nodoc:
|
6
|
+
|
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
|
14
|
+
|
15
|
+
# Returns the XML specification for the selected application
|
16
|
+
attr_accessor :specs
|
17
|
+
|
18
|
+
# Creates a new Generator.
|
19
|
+
#
|
20
|
+
# Note that if any generated file contains a line starting with the +marker+
|
21
|
+
# (eg. "#---promotion---"), we preserve the contents up to that point, and replace
|
22
|
+
# the contents from that point onwards.
|
23
|
+
def initialize()
|
24
|
+
@specs = []
|
25
|
+
@marker = "#---promotion---" # add this marker to a config file to preserve the contents before it
|
26
|
+
end
|
27
|
+
|
28
|
+
# Gathers the specs for all applications in the staging area, then generate:
|
29
|
+
# - /etc/profile
|
30
|
+
# - /etc/rc.conf.local
|
31
|
+
# - /etc/sudoers
|
32
|
+
# - /var/cron/tabs/*
|
33
|
+
def start()
|
34
|
+
$log.info("\n#{'_'*40}\nGenerating common environment files")
|
35
|
+
gather_specs()
|
36
|
+
generate_standard("Profile")
|
37
|
+
generate_standard("Rc_conf")
|
38
|
+
generate_sudoers()
|
39
|
+
generate_crontab()
|
40
|
+
end
|
41
|
+
|
42
|
+
# Gathers specs from all deployment descriptors in folders with the staging area
|
43
|
+
def gather_specs()
|
44
|
+
begin
|
45
|
+
search = Folders::Staging + "/*/" + Files::Spec
|
46
|
+
$log.debug("Searching for application specs with Dir[#{search}]")
|
47
|
+
specfiles = Dir[search]
|
48
|
+
specfiles.each { |filename|
|
49
|
+
specfile = File.new(filename, File::RDONLY)
|
50
|
+
doc = REXML::Document.new(specfile)
|
51
|
+
@specs << doc.root
|
52
|
+
specfile.close()
|
53
|
+
}
|
54
|
+
return(specs)
|
55
|
+
rescue => e
|
56
|
+
$log.error("Error while gathering application specs\n#{e.message}" + e.backtrace.join("\n"))
|
57
|
+
exit 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Convenience method to create an ERB generator based on the template corresponding
|
62
|
+
# to a name. For example, if +sym+ is "Profile", we use the filename Templates::Profile
|
63
|
+
# as the template.
|
64
|
+
#
|
65
|
+
# Returns the ERB generator
|
66
|
+
def generator_for(sym)
|
67
|
+
begin
|
68
|
+
templateFilename = File.expand_path(Templates.const_get(sym), Folders::Templates)
|
69
|
+
raise "Missing template for #{sym}" unless File.exists?(templateFilename)
|
70
|
+
$log.debug("Generating with template #{templateFilename}")
|
71
|
+
templateFile = File.new(templateFilename, File::RDONLY)
|
72
|
+
template = templateFile.read
|
73
|
+
ensure
|
74
|
+
templateFile.close() unless templateFile.nil? || templateFile.closed?
|
75
|
+
end
|
76
|
+
generator = ERB.new(template, nil, '<>')
|
77
|
+
return(generator)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Convenience method to generate content for a system file, preserving the content
|
81
|
+
# up to the +marker+, and appending the generated content.
|
82
|
+
#
|
83
|
+
# Returns a string of the new content for the file
|
84
|
+
def contents_for(sym, generator, definedBinding)
|
85
|
+
generatedContents = generator.result(definedBinding)
|
86
|
+
begin
|
87
|
+
originalContents = IO.readlines(Files.const_get(sym),"")[0].split(@marker)[0]
|
88
|
+
rescue
|
89
|
+
originalContents = ""
|
90
|
+
end
|
91
|
+
newContents = [originalContents, @marker, generatedContents].join("\n")
|
92
|
+
return(newContents)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Sometimes we need to run a process on the new content in a temporary file
|
96
|
+
# instead of writing the content directly to a new file (eg. visudo)
|
97
|
+
def write_temp_file_for(sym, newContents)
|
98
|
+
begin
|
99
|
+
tempFilename = Files.const_get(sym)+".tmp"
|
100
|
+
tempFile = File.new(tempFilename, File::WRONLY | File::CREAT | File::TRUNC)
|
101
|
+
tempFile.puts(newContents)
|
102
|
+
ensure
|
103
|
+
tempFile.close unless tempFile.nil? || tempFile.closed?
|
104
|
+
end
|
105
|
+
return(tempFilename)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Write the approved contents to the system file
|
109
|
+
def write_file_for(sym, newContents)
|
110
|
+
begin
|
111
|
+
generatedFilename = Files.const_get(sym)
|
112
|
+
generatedFile = File.new(generatedFilename, File::WRONLY | File::CREAT | File::TRUNC)
|
113
|
+
generatedFile.puts(newContents)
|
114
|
+
$log.info("Generated #{sym} written to #{generatedFilename}.")
|
115
|
+
ensure
|
116
|
+
generatedFile.close unless generatedFile.nil? || generatedFile.closed?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Convenience method for standard configuration files:
|
121
|
+
# - creates a generator using the relevant template
|
122
|
+
# - generate the new contents
|
123
|
+
# - write the contents to the system file
|
124
|
+
# This is useful when the system file can be written directly without an intermediate
|
125
|
+
# checking step (such as visudo).
|
126
|
+
def generate_standard(sym)
|
127
|
+
begin
|
128
|
+
generator = generator_for(sym)
|
129
|
+
newContents = contents_for(sym, generator, binding)
|
130
|
+
write_file_for(sym, newContents)
|
131
|
+
rescue => e
|
132
|
+
$log.error("Error occurred while generating #{sym}\n#{e.message}" + e.backtrace.join("\n"))
|
133
|
+
exit 1
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# The sudoers file must be verified by the visudo command
|
138
|
+
def generate_sudoers()
|
139
|
+
begin
|
140
|
+
generator = generator_for("Sudoers")
|
141
|
+
newContents = contents_for("Sudoers", generator, binding)
|
142
|
+
tempFilename = write_temp_file_for("Sudoers", newContents)
|
143
|
+
$log.info("Checking temporary sudoers written to #{tempFilename}.")
|
144
|
+
visudoResults = `#{Files::Visudo} -c -f #{tempFilename}`
|
145
|
+
if visudoResults =~ /parsed OK/
|
146
|
+
$log.info("visudo confirms that sudoers syntax is correct.")
|
147
|
+
else
|
148
|
+
$log.error(visudoResults)
|
149
|
+
raise
|
150
|
+
end
|
151
|
+
write_file_for("Sudoers", newContents)
|
152
|
+
FileUtils.rm_f(tempFilename)
|
153
|
+
$log.info("New sudoers written to #{Files::Sudoers}")
|
154
|
+
rescue => e
|
155
|
+
$log.error("Error occurred while generating sudoers\n#{e.message}" + e.backtrace.join("\n"))
|
156
|
+
exit 1
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# The crontab for each user must be deployed using the crontab tool so this
|
161
|
+
# method cannot use the convenience methods because the filenames are user-specific
|
162
|
+
def generate_crontab()
|
163
|
+
begin
|
164
|
+
schedules = {} # keyed on user, value is array of schedule elements
|
165
|
+
@specs.each { |spec|
|
166
|
+
spec.elements.each("/Specification/Crontab/Schedule") { |sched|
|
167
|
+
user = sched.attributes["User"]
|
168
|
+
schedules[user] = [] unless schedules.has_key?(user)
|
169
|
+
schedules[user] << sched
|
170
|
+
}
|
171
|
+
}
|
172
|
+
generator = generator_for("Crontab")
|
173
|
+
schedules.each { |user, crontablist|
|
174
|
+
generatedContents = generator.result(binding)
|
175
|
+
userCrontab = File.expand_path(user, Folders::Crontabs)
|
176
|
+
begin
|
177
|
+
originalContents = IO.readlines(userCrontab,"")[0].split(@marker)[0]
|
178
|
+
rescue
|
179
|
+
originalContents = ""
|
180
|
+
end
|
181
|
+
newContents = [originalContents, @marker, generatedContents].join("\n")
|
182
|
+
begin
|
183
|
+
tempFilename = user + ".tmp"
|
184
|
+
tempFile = File.new(tempFilename, File::WRONLY | File::CREAT | File::TRUNC)
|
185
|
+
tempFile.puts(newContents)
|
186
|
+
ensure
|
187
|
+
tempFile.close unless tempFile.nil? || tempFile.closed?
|
188
|
+
end
|
189
|
+
$log.info("Checking temporary crontab written to #{tempFilename}.")
|
190
|
+
crontabResults = `#{Files::Crontab} -u #{user} #{tempFilename}`
|
191
|
+
if crontabResults == ""
|
192
|
+
$log.info("crontab confirms that crontab syntax is correct for user #{user}.")
|
193
|
+
else
|
194
|
+
$log.error(crontabResults)
|
195
|
+
raise
|
196
|
+
end
|
197
|
+
FileUtils.rm_f(tempFilename)
|
198
|
+
$log.info("New crontab installed for user #{user}")
|
199
|
+
}
|
200
|
+
rescue => e
|
201
|
+
$log.error("Error occurred while generating crontab\n#{e.message}" + e.backtrace.join("\n"))
|
202
|
+
exit 1
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
end
|
data/lib/promotion.rb
ADDED
data/promotion.xsd
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
2
|
+
|
3
|
+
<xsd:schema targetNamespace="http://finalstep.com.au/promotion/v100"
|
4
|
+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
5
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
6
|
+
xmlns="http://finalstep.com.au/promotion/v100">
|
7
|
+
|
8
|
+
<xsd:element name="Specification">
|
9
|
+
<xsd:complexType>
|
10
|
+
<xsd:sequence>
|
11
|
+
<xsd:element ref="Description" />
|
12
|
+
<xsd:element ref="Groups" minOccurs="0" maxOccurs="1" />
|
13
|
+
<xsd:element ref="Users" minOccurs="0" maxOccurs="1" />
|
14
|
+
<xsd:element ref="Folders" minOccurs="0" maxOccurs="unbounded" />
|
15
|
+
<xsd:element ref="Files" minOccurs="0" maxOccurs="unbounded" />
|
16
|
+
<xsd:element ref="Allfiles" minOccurs="0" maxOccurs="unbounded" />
|
17
|
+
<xsd:element ref="Zipfile" minOccurs="0" maxOccurs="unbounded" />
|
18
|
+
<xsd:element ref="Sudoers" minOccurs="0" maxOccurs="1" />
|
19
|
+
<xsd:element ref="Daemon" minOccurs="0" maxOccurs="1" />
|
20
|
+
<xsd:element ref="Environment" minOccurs="0" maxOccurs="1" />
|
21
|
+
<xsd:element ref="Crontab" minOccurs="0" maxOccurs="1" />
|
22
|
+
<xsd:element ref="Database" minOccurs="0" maxOccurs="1" />
|
23
|
+
</xsd:sequence>
|
24
|
+
<xsd:attribute name="Name" type="xsd:NMTOKEN" use="required" />
|
25
|
+
<xsd:attribute name="Title" type="xsd:string" use="required" />
|
26
|
+
</xsd:complexType>
|
27
|
+
</xsd:element>
|
28
|
+
|
29
|
+
<xsd:element name="Description">
|
30
|
+
<xsd:complexType mixed="true" />
|
31
|
+
</xsd:element>
|
32
|
+
|
33
|
+
<xsd:element name="Groups">
|
34
|
+
<xsd:complexType>
|
35
|
+
<xsd:sequence>
|
36
|
+
<xsd:element ref="Group" minOccurs="1" maxOccurs="unbounded" />
|
37
|
+
</xsd:sequence>
|
38
|
+
</xsd:complexType>
|
39
|
+
</xsd:element>
|
40
|
+
|
41
|
+
<xsd:element name="Group">
|
42
|
+
<xsd:complexType>
|
43
|
+
<xsd:attribute name="Name" type="xsd:NMTOKEN" use="required" />
|
44
|
+
<xsd:attribute name="Gid" type="xsd:integer" use="required" />
|
45
|
+
</xsd:complexType>
|
46
|
+
</xsd:element>
|
47
|
+
|
48
|
+
<xsd:element name="Users">
|
49
|
+
<xsd:complexType>
|
50
|
+
<xsd:sequence>
|
51
|
+
<xsd:element ref="User" minOccurs="1" maxOccurs="unbounded" />
|
52
|
+
</xsd:sequence>
|
53
|
+
</xsd:complexType>
|
54
|
+
</xsd:element>
|
55
|
+
|
56
|
+
<xsd:element name="User">
|
57
|
+
<xsd:complexType>
|
58
|
+
<xsd:attribute name="Uid" type="xsd:integer" use="required" />
|
59
|
+
<xsd:attribute name="Name" type="xsd:NMTOKEN" use="required" />
|
60
|
+
<xsd:attribute name="Gid" type="xsd:integer" use="required" />
|
61
|
+
<xsd:attribute name="Class" type="xsd:NMTOKEN" use="required" />
|
62
|
+
<xsd:attribute name="Home" type="xsd:string" use="required" />
|
63
|
+
<xsd:attribute name="Gecos" type="xsd:string" use="required" />
|
64
|
+
<xsd:attribute name="Shell" type="SHELL" use="required" />
|
65
|
+
<xsd:attribute name="Groups" type="xsd:string" use="optional" />
|
66
|
+
</xsd:complexType>
|
67
|
+
</xsd:element>
|
68
|
+
|
69
|
+
<xsd:element name="Folders">
|
70
|
+
<xsd:complexType>
|
71
|
+
<xsd:sequence>
|
72
|
+
<xsd:element ref="Folder" minOccurs="1" maxOccurs="unbounded" />
|
73
|
+
</xsd:sequence>
|
74
|
+
<xsd:attributeGroup ref="FolderAttributes" />
|
75
|
+
</xsd:complexType>
|
76
|
+
</xsd:element>
|
77
|
+
|
78
|
+
<xsd:element name="Folder">
|
79
|
+
<xsd:complexType>
|
80
|
+
<xsd:simpleContent>
|
81
|
+
<xsd:extension base="xsd:normalizedString">
|
82
|
+
<xsd:attributeGroup ref="FolderAttributes" />
|
83
|
+
<xsd:attribute name="Clear" type="xsd:boolean" use="optional" />
|
84
|
+
</xsd:extension>
|
85
|
+
</xsd:simpleContent>
|
86
|
+
</xsd:complexType>
|
87
|
+
</xsd:element>
|
88
|
+
|
89
|
+
<xsd:element name="Files">
|
90
|
+
<xsd:complexType>
|
91
|
+
<xsd:sequence>
|
92
|
+
<xsd:element ref="File" minOccurs="1" maxOccurs="unbounded" />
|
93
|
+
<xsd:element ref="Link" minOccurs="0" maxOccurs="unbounded" />
|
94
|
+
</xsd:sequence>
|
95
|
+
<xsd:attributeGroup ref="FileAttributes"/>
|
96
|
+
</xsd:complexType>
|
97
|
+
</xsd:element>
|
98
|
+
|
99
|
+
<xsd:element name="Allfiles">
|
100
|
+
<xsd:complexType>
|
101
|
+
<xsd:simpleContent>
|
102
|
+
<xsd:extension base="xsd:normalizedString">
|
103
|
+
<xsd:attributeGroup ref="FileAttributes"/>
|
104
|
+
</xsd:extension>
|
105
|
+
</xsd:simpleContent>
|
106
|
+
</xsd:complexType>
|
107
|
+
</xsd:element>
|
108
|
+
|
109
|
+
<xsd:element name="File">
|
110
|
+
<xsd:complexType>
|
111
|
+
<xsd:simpleContent>
|
112
|
+
<xsd:extension base="xsd:normalizedString">
|
113
|
+
<xsd:attributeGroup ref="FileAttributes" />
|
114
|
+
<xsd:attribute name="Empty" type="xsd:boolean" use="optional" />
|
115
|
+
<xsd:attribute name="Overwrite" type="xsd:boolean" default="true" />
|
116
|
+
<xsd:attribute name="Backup" type="xsd:boolean" default="false" />
|
117
|
+
</xsd:extension>
|
118
|
+
</xsd:simpleContent>
|
119
|
+
</xsd:complexType>
|
120
|
+
</xsd:element>
|
121
|
+
|
122
|
+
<xsd:element name="Link">
|
123
|
+
<xsd:complexType>
|
124
|
+
<xsd:simpleContent>
|
125
|
+
<xsd:extension base="xsd:normalizedString">
|
126
|
+
<xsd:attributeGroup ref="FolderAttributes"/>
|
127
|
+
<xsd:attribute name="Target" type="xsd:normalizedString"/>
|
128
|
+
</xsd:extension>
|
129
|
+
</xsd:simpleContent>
|
130
|
+
</xsd:complexType>
|
131
|
+
</xsd:element>
|
132
|
+
|
133
|
+
<xsd:element name="Zipfile">
|
134
|
+
<xsd:complexType>
|
135
|
+
<xsd:sequence>
|
136
|
+
<xsd:element ref="Zip" minOccurs="1" maxOccurs="1"/>
|
137
|
+
<xsd:element ref="Source" minOccurs="1" maxOccurs="unbounded" />
|
138
|
+
</xsd:sequence>
|
139
|
+
</xsd:complexType>
|
140
|
+
</xsd:element>
|
141
|
+
|
142
|
+
<xsd:element name="Zip">
|
143
|
+
<xsd:complexType>
|
144
|
+
<xsd:simpleContent>
|
145
|
+
<xsd:extension base="xsd:normalizedString">
|
146
|
+
<xsd:attributeGroup ref="FolderAttributes" />
|
147
|
+
</xsd:extension>
|
148
|
+
</xsd:simpleContent>
|
149
|
+
</xsd:complexType>
|
150
|
+
</xsd:element>
|
151
|
+
|
152
|
+
<xsd:element name="Source">
|
153
|
+
<xsd:complexType>
|
154
|
+
<xsd:simpleContent>
|
155
|
+
<xsd:extension base="xsd:normalizedString" />
|
156
|
+
</xsd:simpleContent>
|
157
|
+
</xsd:complexType>
|
158
|
+
</xsd:element>
|
159
|
+
|
160
|
+
<xsd:element name="Sudoers">
|
161
|
+
<xsd:complexType>
|
162
|
+
<xsd:sequence>
|
163
|
+
<xsd:element ref="UserPrivilege" minOccurs="1" maxOccurs="unbounded" />
|
164
|
+
</xsd:sequence>
|
165
|
+
</xsd:complexType>
|
166
|
+
</xsd:element>
|
167
|
+
|
168
|
+
<xsd:element name="UserPrivilege">
|
169
|
+
<xsd:complexType>
|
170
|
+
<xsd:simpleContent>
|
171
|
+
<xsd:extension base="xsd:string">
|
172
|
+
<xsd:attribute name="User" type="xsd:NMTOKEN" use="required" />
|
173
|
+
<xsd:attribute name="Password" type="xsd:boolean" use="optional" default="false"/>
|
174
|
+
<xsd:attribute name="Runas" type="xsd:NMTOKEN" use="optional" />
|
175
|
+
</xsd:extension>
|
176
|
+
</xsd:simpleContent>
|
177
|
+
</xsd:complexType>
|
178
|
+
</xsd:element>
|
179
|
+
|
180
|
+
<xsd:element name="Daemon">
|
181
|
+
<xsd:complexType>
|
182
|
+
<xsd:attributeGroup ref="ExecutionAttributes"/>
|
183
|
+
</xsd:complexType>
|
184
|
+
</xsd:element>
|
185
|
+
|
186
|
+
<xsd:element name="Command">
|
187
|
+
<xsd:simpleType>
|
188
|
+
<xsd:restriction base="xsd:string" />
|
189
|
+
</xsd:simpleType>
|
190
|
+
</xsd:element>
|
191
|
+
|
192
|
+
<xsd:element name="Environment">
|
193
|
+
<xsd:complexType>
|
194
|
+
<xsd:sequence>
|
195
|
+
<xsd:choice>
|
196
|
+
<xsd:element ref="Variable" minOccurs="0" maxOccurs="unbounded" />
|
197
|
+
<xsd:element ref="Alias" minOccurs="0" maxOccurs="unbounded" />
|
198
|
+
</xsd:choice>
|
199
|
+
</xsd:sequence>
|
200
|
+
</xsd:complexType>
|
201
|
+
</xsd:element>
|
202
|
+
|
203
|
+
<xsd:element name="Variable">
|
204
|
+
<xsd:complexType>
|
205
|
+
<xsd:simpleContent>
|
206
|
+
<xsd:extension base="xsd:string">
|
207
|
+
<xsd:attribute name="Name" type="xsd:NMTOKEN" use="required" />
|
208
|
+
<xsd:attribute name="Comment" type="xsd:string" use="optional" />
|
209
|
+
</xsd:extension>
|
210
|
+
</xsd:simpleContent>
|
211
|
+
</xsd:complexType>
|
212
|
+
</xsd:element>
|
213
|
+
|
214
|
+
<xsd:element name="Alias">
|
215
|
+
<xsd:complexType>
|
216
|
+
<xsd:simpleContent>
|
217
|
+
<xsd:extension base="xsd:string">
|
218
|
+
<xsd:attribute name="Name" type="xsd:NMTOKEN" use="required" />
|
219
|
+
<xsd:attribute name="Comment" type="xsd:string" use="optional" />
|
220
|
+
</xsd:extension>
|
221
|
+
</xsd:simpleContent>
|
222
|
+
</xsd:complexType>
|
223
|
+
</xsd:element>
|
224
|
+
|
225
|
+
<xsd:element name="Crontab">
|
226
|
+
<xsd:complexType>
|
227
|
+
<xsd:sequence>
|
228
|
+
<xsd:element ref="Schedule" minOccurs="1" maxOccurs="unbounded" />
|
229
|
+
</xsd:sequence>
|
230
|
+
</xsd:complexType>
|
231
|
+
</xsd:element>
|
232
|
+
|
233
|
+
<xsd:element name="Schedule">
|
234
|
+
<xsd:complexType>
|
235
|
+
<xsd:sequence>
|
236
|
+
<xsd:element ref="Command" minOccurs="1" maxOccurs="1" />
|
237
|
+
</xsd:sequence>
|
238
|
+
<xsd:attribute name="User" type="xsd:NMTOKEN" use="required" />
|
239
|
+
<xsd:attribute name="Minute" type="MINUTE" use="optional" />
|
240
|
+
<xsd:attribute name="Hour" type="HOUR" use="optional" />
|
241
|
+
<xsd:attribute name="DayOfMonth" type="DAYOFMONTH" use="optional" />
|
242
|
+
<xsd:attribute name="Month" type="MONTH" use="optional" />
|
243
|
+
<xsd:attribute name="DayOfWeek" type="DAYOFWEEK" use="optional" />
|
244
|
+
<xsd:attribute name="Comment" type="xsd:string" use="optional" />
|
245
|
+
</xsd:complexType>
|
246
|
+
</xsd:element>
|
247
|
+
|
248
|
+
<xsd:element name="Database">
|
249
|
+
<xsd:complexType>
|
250
|
+
<xsd:simpleContent>
|
251
|
+
<xsd:extension base="xsd:string">
|
252
|
+
<xsd:attribute name="database" type="xsd:string" use="optional"/>
|
253
|
+
</xsd:extension>
|
254
|
+
</xsd:simpleContent>
|
255
|
+
</xsd:complexType>
|
256
|
+
</xsd:element>
|
257
|
+
|
258
|
+
<xsd:attributeGroup name="FolderAttributes">
|
259
|
+
<xsd:attribute name="Group" type="xsd:NMTOKEN" use="optional" default="bin"/>
|
260
|
+
<xsd:attribute name="Owner" type="xsd:NMTOKEN" use="optional" default="root"/>
|
261
|
+
<xsd:attribute name="Mode" type="MODE" use="optional" default="0640"/>
|
262
|
+
</xsd:attributeGroup>
|
263
|
+
|
264
|
+
<xsd:attributeGroup name="FileAttributes">
|
265
|
+
<xsd:attribute name="Source" type="xsd:string" use="optional" />
|
266
|
+
<xsd:attributeGroup ref="FolderAttributes"/>
|
267
|
+
</xsd:attributeGroup>
|
268
|
+
|
269
|
+
<xsd:attributeGroup name="ExecutionAttributes">
|
270
|
+
<xsd:attribute name="Priority" type="xsd:nonNegativeInteger" use="optional" />
|
271
|
+
<xsd:attribute name="Flags" type="xsd:string" use="required" />
|
272
|
+
<xsd:attribute name="User" type="xsd:string" use="required" />
|
273
|
+
</xsd:attributeGroup>
|
274
|
+
|
275
|
+
<xsd:simpleType name="MODE">
|
276
|
+
<xsd:restriction base="xsd:string">
|
277
|
+
<xsd:pattern value="[0-7]{4}"/>
|
278
|
+
</xsd:restriction>
|
279
|
+
</xsd:simpleType>
|
280
|
+
|
281
|
+
<xsd:simpleType name="SHELL">
|
282
|
+
<xsd:restriction base="xsd:string">
|
283
|
+
<xsd:enumeration value="/sbin/nologin" />
|
284
|
+
<xsd:enumeration value="/bin/ksh" />
|
285
|
+
</xsd:restriction>
|
286
|
+
</xsd:simpleType>
|
287
|
+
|
288
|
+
<xsd:simpleType name="MINUTE">
|
289
|
+
<xsd:restriction base="xsd:integer">
|
290
|
+
<xsd:minInclusive value="0"/>
|
291
|
+
<xsd:maxInclusive value="59"/>
|
292
|
+
</xsd:restriction>
|
293
|
+
</xsd:simpleType>
|
294
|
+
|
295
|
+
<xsd:simpleType name="HOUR">
|
296
|
+
<xsd:restriction base="xsd:integer">
|
297
|
+
<xsd:minInclusive value="0"/>
|
298
|
+
<xsd:maxInclusive value="23"/>
|
299
|
+
</xsd:restriction>
|
300
|
+
</xsd:simpleType>
|
301
|
+
|
302
|
+
<xsd:simpleType name="DAYOFMONTH">
|
303
|
+
<xsd:restriction base="xsd:integer">
|
304
|
+
<xsd:minInclusive value="1"/>
|
305
|
+
<xsd:maxInclusive value="31"/>
|
306
|
+
</xsd:restriction>
|
307
|
+
</xsd:simpleType>
|
308
|
+
|
309
|
+
<xsd:simpleType name="MONTH">
|
310
|
+
<xsd:restriction base="xsd:integer">
|
311
|
+
<xsd:minInclusive value="1"/>
|
312
|
+
<xsd:maxInclusive value="12"/>
|
313
|
+
</xsd:restriction>
|
314
|
+
</xsd:simpleType>
|
315
|
+
|
316
|
+
<xsd:simpleType name="DAYOFWEEK">
|
317
|
+
<xsd:restriction base="xsd:integer">
|
318
|
+
<xsd:minInclusive value="0"/>
|
319
|
+
<xsd:maxInclusive value="7"/>
|
320
|
+
</xsd:restriction>
|
321
|
+
</xsd:simpleType>
|
322
|
+
|
323
|
+
</xsd:schema>
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: promotion
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 7
|
10
|
+
version: 1.0.7
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Richard Kernahan
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-09-19 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: "\t\tThe Promotion tool is designed to make it easy and quick to deploy an application\n\
|
22
|
+
\t\tinto production. Originally built for use with OpenBSD, it can be used on an *nix\n\
|
23
|
+
\t\tsystem by adjusting a few paths (in <code>config.rb</code>).\n\n\
|
24
|
+
\t\tTo deploy or install an application you just need to copy a few files into place, right?\n\
|
25
|
+
\t\tWell, the folders need to be there first of course, oh and the permissions need to be set,\n\
|
26
|
+
\t\tand I guess we need the right users set up before file ownerships can be set correctly,\n\
|
27
|
+
\t\twhich means we need groups before that ... ok, so there is more to it than copying a few files.\n\n\
|
28
|
+
\t\tThere are also system-wide settings that may need to be modified to support an application,\n\
|
29
|
+
\t\tsuch as environment variables in <code>/etc/profile</code>, <code>/etc/sudoers</code>,\n\
|
30
|
+
\t\tstartup scripts in <code>/etc/rc.conf.local</code>, and <code>/var/cron/tabs/*</code> cron jobs.\n\n\
|
31
|
+
\t\tPromotion handles all of this based on an XML deployment descriptor for each application,\n\
|
32
|
+
\t\tallowing rapid, reliable redeployment with a single line command. It also manages database\n\
|
33
|
+
\t\tschema migration.\n"
|
34
|
+
email: rec.dev@finalstep.com.au
|
35
|
+
executables: []
|
36
|
+
|
37
|
+
extensions:
|
38
|
+
- ext/promotion/extconf.rb
|
39
|
+
extra_rdoc_files: []
|
40
|
+
|
41
|
+
files:
|
42
|
+
- bin/promote
|
43
|
+
- bin/evolve
|
44
|
+
- bin/devolve
|
45
|
+
- ext/promotion/extconf.rb
|
46
|
+
- lib/promotion.rb
|
47
|
+
- lib/promotion/application.rb
|
48
|
+
- lib/promotion/config.rb
|
49
|
+
- lib/promotion/enforcer.rb
|
50
|
+
- lib/promotion/evolver.rb
|
51
|
+
- lib/promotion/generator.rb
|
52
|
+
- lib/promotion/erb/crontab.erb
|
53
|
+
- lib/promotion/erb/profile.erb
|
54
|
+
- lib/promotion/erb/rc.conf.local.erb
|
55
|
+
- lib/promotion/erb/sudoers.erb
|
56
|
+
- .yardopts
|
57
|
+
- deploy.xml
|
58
|
+
- promotion.xsd
|
59
|
+
- CHANGELOG
|
60
|
+
- README
|
61
|
+
- VERSION
|
62
|
+
homepage: http://rubygems.org/gems/promotion
|
63
|
+
licenses: []
|
64
|
+
|
65
|
+
post_install_message: "\n Executables have been installed in /usr/local/sbin:\n * promote\n * evolve\n * devolve\n "
|
66
|
+
rdoc_options: []
|
67
|
+
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
hash: 3
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
requirements: []
|
89
|
+
|
90
|
+
rubyforge_project: promotion
|
91
|
+
rubygems_version: 1.8.24
|
92
|
+
signing_key:
|
93
|
+
specification_version: 3
|
94
|
+
summary: Promotion makes it possible to repeatedly deploy an application in a fast and reliable way.
|
95
|
+
test_files: []
|
96
|
+
|
97
|
+
has_rdoc: "true"
|