Allow use of aliases defined previously inside included files

Anchors and aliases were expanded internally within JJB's yaml loading
calls so they were limited to individual documents. Now, included files
will have access to aliases of anchors already defined at previously
processed files.

Example:

- default:
    name: default-timeout-wrapper
    timeout: &timeout
      fail: true
      elastic-percentage: 150
      elastic-default-timeout: 90
      type: elastic

- wrapper: !include include002_1.yaml.inc

Previously was not possible to use '*timeout' alias inside
include002_1.yaml.inc file

Closes-Story: 2000173

Change-Id: Ic031ddbb0310bd11748183fbde9502735c3b7169
This commit is contained in:
Victor Seva 2015-02-20 12:37:29 +01:00
parent 6c6370bbff
commit 1191dcfccf
7 changed files with 75 additions and 6 deletions

View File

@ -319,9 +319,8 @@ For example:
The `anchors and aliases`_ are expanded internally within JJB's yaml loading The `anchors and aliases`_ are expanded internally within JJB's yaml loading
calls, and are limited to individual documents. That means you use the same calls and are not limited to individual documents. That means you can't use
anchor name in separate files without collisions, but also means that you must the same anchor name in included files without collisions.
define the anchor in the same file that you intend to reference it.
A simple example can be seen in the specs `full length example`_ with the A simple example can be seen in the specs `full length example`_ with the
following being more representative of usage within JJB: following being more representative of usage within JJB:

View File

@ -129,7 +129,31 @@ class OrderedConstructor(BaseConstructor):
data.update(mapping) data.update(mapping)
class LocalLoader(OrderedConstructor, yaml.Loader): class LocalAnchorLoader(yaml.Loader):
"""Subclass for yaml.Loader which keeps Alias between calls"""
anchors = {}
def __init__(self, *args, **kwargs):
super(LocalAnchorLoader, self).__init__(*args, **kwargs)
self.anchors = LocalAnchorLoader.anchors
@classmethod
def reset_anchors(cls):
cls.anchors = {}
# override the default composer to skip resetting the anchors at the
# end of the current document
def compose_document(self):
# Drop the DOCUMENT-START event.
self.get_event()
# Compose the root node.
node = self.compose_node(None, None)
# Drop the DOCUMENT-END event.
self.get_event()
return node
class LocalLoader(OrderedConstructor, LocalAnchorLoader):
"""Subclass for yaml.Loader which handles the local tags 'include', """Subclass for yaml.Loader which handles the local tags 'include',
'include-raw' and 'include-raw-escaped' to specify a file to include data 'include-raw' and 'include-raw-escaped' to specify a file to include data
from and whether to parse it as additional yaml, treat it as a data blob from and whether to parse it as additional yaml, treat it as a data blob
@ -228,4 +252,5 @@ class LocalLoader(OrderedConstructor, yaml.Loader):
def load(stream, **kwargs): def load(stream, **kwargs):
LocalAnchorLoader.reset_anchors()
return yaml.load(stream, functools.partial(LocalLoader, **kwargs)) return yaml.load(stream, functools.partial(LocalLoader, **kwargs))

View File

@ -16,9 +16,9 @@
import os import os
from testtools import ExpectedException from testtools import ExpectedException
from testtools.matchers import MismatchError
from testtools import TestCase from testtools import TestCase
from testscenarios.testcase import TestWithScenarios from testscenarios.testcase import TestWithScenarios
from yaml.composer import ComposerError
from jenkins_jobs import builder from jenkins_jobs import builder
from tests.base import get_scenarios, JsonTestCase, YamlTestCase from tests.base import get_scenarios, JsonTestCase, YamlTestCase
@ -40,7 +40,8 @@ class TestCaseLocalYamlInclude(TestWithScenarios, TestCase, JsonTestCase):
def test_yaml_snippet(self): def test_yaml_snippet(self):
if os.path.basename(self.in_filename).startswith("exception_"): if os.path.basename(self.in_filename).startswith("exception_"):
with ExpectedException(MismatchError): with ExpectedException(ComposerError,
"^found duplicate anchor .*"):
super(TestCaseLocalYamlInclude, self).test_yaml_snippet() super(TestCaseLocalYamlInclude, self).test_yaml_snippet()
else: else:
super(TestCaseLocalYamlInclude, self).test_yaml_snippet() super(TestCaseLocalYamlInclude, self).test_yaml_snippet()

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="hudson.scm.NullSCM"/>
<builders/>
<publishers/>
<buildWrappers>
<hudson.plugins.build__timeout.BuildTimeoutWrapper>
<timeoutMinutes>3</timeoutMinutes>
<failBuild>true</failBuild>
<writingDescription>false</writingDescription>
<timeoutPercentage>150</timeoutPercentage>
<timeoutMinutesElasticDefault>90</timeoutMinutesElasticDefault>
<timeoutType>elastic</timeoutType>
</hudson.plugins.build__timeout.BuildTimeoutWrapper>
</buildWrappers>
</project>

View File

@ -0,0 +1,16 @@
# vim: sw=4 ts=4 et
- default:
name: default-timeout-wrapper
timeout: &timeout
fail: true
elastic-percentage: 150
elastic-default-timeout: 90
type: elastic
# include that uses timeout alias
- wrapper: !include include002_1.yaml.inc
- job:
name: test-job-1
wrappers:
!include include002.yaml.inc

View File

@ -0,0 +1 @@
- timeout-wrapper

View File

@ -0,0 +1,3 @@
name: timeout-wrapper
wrappers:
- timeout: *timeout