Don't store references to secret objects from jobs

This removes direct references to secret objects, instead resolving
them at run time.  They are also resolved and decrypted (but not
stored) at configuration time for validation purposes.

This is part of a change series to re-use configuration objects
between layouts to save memory.

Change-Id: I004f381d9df8c165b04ba540527d42ed74ee8fe1
This commit is contained in:
James E. Blair 2018-02-20 14:57:25 -08:00
parent ec1f54deb3
commit f37568cc10
3 changed files with 27 additions and 19 deletions

View File

@ -121,7 +121,7 @@ class TestJob(BaseTestCase):
# Apply the diablo variant
diablo = model.Job('py27')
diablo.timeout = 40
job.applyVariant(diablo)
job.applyVariant(diablo, self.layout)
self.assertEqual(40, job.timeout)
self.assertEqual(['py27-pre'],
@ -140,7 +140,7 @@ class TestJob(BaseTestCase):
good_final = model.Job('py27')
good_final.voting = False
job.applyVariant(good_final)
job.applyVariant(good_final, self.layout)
self.assertFalse(job.voting)
bad_final = model.Job('py27')
@ -148,7 +148,7 @@ class TestJob(BaseTestCase):
with testtools.ExpectedException(
Exception,
"Unable to modify final job"):
job.applyVariant(bad_final)
job.applyVariant(bad_final, self.layout)
def test_job_inheritance_job_tree(self):
pipeline = model.Pipeline('gate', self.layout)

View File

@ -629,12 +629,9 @@ class JobParser(object):
"Unable to use secret %s. Secrets must be "
"defined in the same project in which they "
"are used" % secret_name)
# If the secret declares a different name, set it on the decrypted
# copy of the secret object
decrypted_secret = secret.decrypt(
job.source_context.project.private_key)
decrypted_secret.name = secret_name
secrets.append(decrypted_secret)
# Decrypt a copy of the secret to verify it can be done
secret.decrypt(job.source_context.project.private_key)
secrets.append((secret.name, secret_name))
# A job in an untrusted repo that uses secrets requires
# special care. We must note this, and carry this flag

View File

@ -698,6 +698,7 @@ class PlaybookContext(object):
self.path = path
self.roles = roles
self.secrets = secrets
self.decrypted_secrets = []
def __repr__(self):
return '<PlaybookContext %s %s>' % (self.source_context,
@ -721,12 +722,21 @@ class PlaybookContext(object):
self.secrets)
return r
def freezeSecrets(self, layout):
secrets = []
for (secret_name, secret_alias) in self.secrets:
secret = layout.secrets.get(secret_name)
decrypted_secret = secret.decrypt(
self.source_context.project.private_key)
decrypted_secret.name = secret_alias
secrets.append(decrypted_secret)
self.decrypted_secrets = secrets
def toDict(self):
# Render to a dict to use in passing json to the executor
secrets = {}
for secret in self.secrets:
secret_data = copy.deepcopy(secret.secret_data)
secrets[secret.name] = secret_data
for secret in self.decrypted_secrets:
secrets[secret.name] = secret.secret_data
return dict(
connection=self.source_context.project.connection_name,
project=self.source_context.project.name,
@ -1036,7 +1046,7 @@ class Job(object):
setattr(job, k, copy.deepcopy(self._get(k)))
return job
def freezePlaybooks(self, pblist):
def freezePlaybooks(self, pblist, layout):
"""Take a list of playbooks, and return a copy of it updated with this
job's roles.
@ -1046,10 +1056,11 @@ class Job(object):
for old_pb in pblist:
pb = old_pb.copy()
pb.roles = self.roles
pb.freezeSecrets(layout)
ret.append(pb)
return tuple(ret)
def applyVariant(self, other):
def applyVariant(self, other, layout):
"""Copy the attributes which have been set on the other job to this
job."""
if not isinstance(other, Job):
@ -1107,13 +1118,13 @@ class Job(object):
self.addRoles(other.roles)
if other._get('run') is not None:
other_run = self.freezePlaybooks(other.run)
other_run = self.freezePlaybooks(other.run, layout)
self.run = other_run
if other._get('pre_run') is not None:
other_pre_run = self.freezePlaybooks(other.pre_run)
other_pre_run = self.freezePlaybooks(other.pre_run, layout)
self.pre_run = self.pre_run + other_pre_run
if other._get('post_run') is not None:
other_post_run = self.freezePlaybooks(other.post_run)
other_post_run = self.freezePlaybooks(other.post_run, layout)
self.post_run = other_post_run + self.post_run
self.updateVariables(other.variables, other.host_variables,
other.group_variables)
@ -2820,7 +2831,7 @@ class Layout(object):
frozen_job = variant.copy()
frozen_job.setBase()
else:
frozen_job.applyVariant(variant)
frozen_job.applyVariant(variant, item.layout)
frozen_job.name = variant.name
frozen_job.name = jobname
# Whether the change matches any of the project pipeline
@ -2828,7 +2839,7 @@ class Layout(object):
matched = False
for variant in job_list.jobs[jobname]:
if variant.changeMatches(change):
frozen_job.applyVariant(variant)
frozen_job.applyVariant(variant, item.layout)
matched = True
self.log.debug("Pipeline variant %s matched %s",
repr(variant), change)