import hashlib
import subprocess
import build_artefact
import cmds
import util
_NAME_CHARS = ([chr(i) for i in range(ord('0'), ord('9'))]
+ [chr(i) for i in range(ord('a'), ord('z'))])
def hash_code_as_domain_name(n):
n = n % (2**63)
if n < 0:
n += 2**63
radix = len(_NAME_CHARS)
s = ""
for i in range(12):
d = n % radix
s = _NAME_CHARS[d] + s
n /= radix
return s
def hash_string(s):
r = long(hashlib.sha1(s).hexdigest(), 16)
if r < 0:
r += 2**63
return r
def hash_cmds(cmds):
hash_code = 0l
for cmd in cmds:
if isinstance(cmd, basestring):
hash_code ^= hash_string(cmd)
elif cmd['kind'] == 'LiteralFile':
hash_code ^= hash_string(cmd['content'])
hash_code ^= hash_string(cmd['to'])
hash_code ^= hash_string(cmd['filePermissions'])
hash_code ^= hash_string(cmd['owner'])
hash_code ^= hash_string(cmd['group'])
elif cmd['kind'] == 'CopyFile':
hash_code ^= hash_string(cmd['from'])
hash_code ^= hash_string(cmd['to'])
hash_code ^= hash_string(cmd['dirPermissions'])
hash_code ^= hash_string(cmd['filePermissions'])
hash_code ^= hash_string(cmd['owner'])
hash_code ^= hash_string(cmd['group'])
raise RuntimeError('CopyFile not supported in image')
elif cmd['kind'] == 'EnsureDir':
hash_code ^= hash_string(cmd['dir'])
hash_code ^= hash_string(cmd['dirPermissions'])
hash_code ^= hash_string(cmd['owner'])
hash_code ^= hash_string(cmd['group'])
else:
raise RuntimeError('Did not recognize image command kind: ' + cmd['kind'])
return hash_code
class PackerBuildArtefact(build_artefact.BuildArtefact):
def __init__(self, cmds):
self.cmds = cmds
self.cachedHashCode = None
self.packerProcess = None
def builderHashCode(self):
raise NotImplementedError("%s has no override" % self.__class__.__name__)
def hashCode(self):
if self.cachedHashCode is None:
self.cachedHashCode = self.builderHashCode() ^ hash_cmds(self.cmds)
return self.cachedHashCode
def _configFileName(self):
return self.name() + '.packer.json'
def _config(self):
provs = [
{
'type': 'shell',
'execute_command': "{{ .Vars }} sudo -HE /bin/bash '{{ .Path }}'",
'inline': cmds.compile_command_to_bash(cmd),
}
for cmd in self.cmds
]
return util.jsonstr({'builders': [self.builder()], 'provisioners': provs})
def name(self):
return 'micromanage-%s' % hash_code_as_domain_name(self.hashCode())
def getOutputFiles(self, dirpath):
return ['%s/%s' % (dirpath, self._configFileName())]
def outputFiles(self, dirpath):
config = self._config()
dirfilename = '%s/%s' % (dirpath, self._configFileName())
with open(dirfilename, 'w') as f:
f.write(config)
def doBuild(self, dirpath):
filename = self._configFileName()
if self.packerProcess is not None:
raise RuntimeError('Packer build already in progress.')
command = ['packer', 'build', filename]
logfilename = '%s/%s.log' % (dirpath, filename)
print '%s > %s' % (' '.join(command), logfilename)
with open(logfilename, 'w') as logfile:
self.packerProcess = subprocess.Popen(command, stdout=logfile, cwd=dirpath)
def wait(self, dirpath):
if self.packerProcess is None:
raise RuntimeError('No Packer build in progress.')
exitcode = self.packerProcess.wait()
if exitcode != 0:
print 'Packer error: Exit code was %d' % exitcode
filename = self._configFileName()
logfilename = '%s/%s.log' % (dirpath, filename)
with open(logfilename, 'r') as logfile:
print logfile.read()
raise RuntimeError('Error from packer.')