1# Copyright 2015 Thanh Ha 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations 13# under the License. 14 15from functools import wraps 16import logging 17import sys 18 19import xml.etree.ElementTree as XML 20 21from jenkins_jobs.errors import InvalidAttributeError 22from jenkins_jobs.errors import JenkinsJobsException 23from jenkins_jobs.errors import MissingAttributeError 24from jenkins_jobs.modules import hudson_model 25 26import pkg_resources 27 28 29def build_trends_publisher(plugin_name, xml_element, data): 30 """Helper to create various trend publishers.""" 31 32 def append_thresholds(element, data, only_totals): 33 """Appends the status thresholds.""" 34 35 for status in ["unstable", "failed"]: 36 status_data = data.get(status, {}) 37 38 limits = [ 39 ("total-all", "TotalAll"), 40 ("total-high", "TotalHigh"), 41 ("total-normal", "TotalNormal"), 42 ("total-low", "TotalLow"), 43 ] 44 45 if only_totals is False: 46 limits.extend( 47 [ 48 ("new-all", "NewAll"), 49 ("new-high", "NewHigh"), 50 ("new-normal", "NewNormal"), 51 ("new-low", "NewLow"), 52 ] 53 ) 54 55 for key, tag_suffix in limits: 56 tag_name = status + tag_suffix 57 XML.SubElement(element, tag_name).text = str(status_data.get(key, "")) 58 59 # Tuples containing: setting name, tag name, default value 60 settings = [ 61 ("healthy", "healthy", ""), 62 ("unhealthy", "unHealthy", ""), 63 ("health-threshold", "thresholdLimit", "low"), 64 ("plugin-name", "pluginName", plugin_name), 65 ("default-encoding", "defaultEncoding", ""), 66 ("can-run-on-failed", "canRunOnFailed", False), 67 ("use-stable-build-as-reference", "useStableBuildAsReference", False), 68 ("use-previous-build-as-reference", "usePreviousBuildAsReference", False), 69 ("use-delta-values", "useDeltaValues", False), 70 ("thresholds", "thresholds", {}), 71 ("should-detect-modules", "shouldDetectModules", False), 72 ("dont-compute-new", "dontComputeNew", True), 73 ("do-not-resolve-relative-paths", "doNotResolveRelativePaths", False), 74 ("pattern", "pattern", ""), 75 ] 76 77 thresholds = ["low", "normal", "high"] 78 79 for key, tag_name, default in settings: 80 xml_config = XML.SubElement(xml_element, tag_name) 81 config_value = data.get(key, default) 82 83 if key == "thresholds": 84 append_thresholds( 85 xml_config, config_value, data.get("dont-compute-new", True) 86 ) 87 elif key == "health-threshold" and config_value not in thresholds: 88 raise JenkinsJobsException( 89 "health-threshold must be one of %s" % ", ".join(thresholds) 90 ) 91 else: 92 if isinstance(default, bool): 93 xml_config.text = str(config_value).lower() 94 else: 95 xml_config.text = str(config_value) 96 97 98def config_file_provider_builder(xml_parent, data): 99 """Builder / Wrapper helper""" 100 xml_files = XML.SubElement(xml_parent, "managedFiles") 101 102 files = data.get("files", []) 103 for file in files: 104 xml_file = XML.SubElement( 105 xml_files, "org.jenkinsci.plugins." "configfiles.buildwrapper.ManagedFile" 106 ) 107 mapping = [ 108 ("file-id", "fileId", None), 109 ("target", "targetLocation", ""), 110 ("variable", "variable", ""), 111 ("replace-tokens", "replaceTokens", False), 112 ] 113 convert_mapping_to_xml(xml_file, file, mapping, fail_required=True) 114 115 116def config_file_provider_settings(xml_parent, data): 117 SETTINGS_TYPES = ["file", "cfp"] 118 settings = { 119 "default-settings": "jenkins.mvn.DefaultSettingsProvider", 120 "settings": "jenkins.mvn.FilePathSettingsProvider", 121 "config-file-provider-settings": "org.jenkinsci.plugins.configfiles.maven.job.MvnSettingsProvider", 122 "default-global-settings": "jenkins.mvn.DefaultGlobalSettingsProvider", 123 "global-settings": "jenkins.mvn.FilePathGlobalSettingsProvider", 124 "config-file-provider-global-settings": "org.jenkinsci.plugins.configfiles.maven.job." 125 "MvnGlobalSettingsProvider", 126 } 127 128 if "settings" in data: 129 # Support for Config File Provider 130 settings_file = str(data["settings"]) 131 settings_type = data.get("settings-type", "file") 132 133 # For cfp versions <2.10.0 we are able to detect cfp via the config 134 # settings name. 135 text = "org.jenkinsci.plugins.configfiles.maven.MavenSettingsConfig" 136 if settings_file.startswith(text): 137 settings_type = "cfp" 138 139 if settings_type == "file": 140 lsettings = XML.SubElement( 141 xml_parent, "settings", {"class": settings["settings"]} 142 ) 143 XML.SubElement(lsettings, "path").text = settings_file 144 elif settings_type == "cfp": 145 lsettings = XML.SubElement( 146 xml_parent, 147 "settings", 148 {"class": settings["config-file-provider-settings"]}, 149 ) 150 XML.SubElement(lsettings, "settingsConfigId").text = settings_file 151 else: 152 raise InvalidAttributeError("settings-type", settings_type, SETTINGS_TYPES) 153 else: 154 XML.SubElement(xml_parent, "settings", {"class": settings["default-settings"]}) 155 156 if "global-settings" in data: 157 # Support for Config File Provider 158 global_settings_file = str(data["global-settings"]) 159 global_settings_type = data.get("global-settings-type", "file") 160 161 # For cfp versions <2.10.0 we are able to detect cfp via the config 162 # settings name. 163 text = "org.jenkinsci.plugins.configfiles.maven." "GlobalMavenSettingsConfig" 164 if global_settings_file.startswith(text): 165 global_settings_type = "cfp" 166 167 if global_settings_type == "file": 168 gsettings = XML.SubElement( 169 xml_parent, "globalSettings", {"class": settings["global-settings"]} 170 ) 171 XML.SubElement(gsettings, "path").text = global_settings_file 172 elif global_settings_type == "cfp": 173 gsettings = XML.SubElement( 174 xml_parent, 175 "globalSettings", 176 {"class": settings["config-file-provider-global-settings"]}, 177 ) 178 XML.SubElement(gsettings, "settingsConfigId").text = global_settings_file 179 else: 180 raise InvalidAttributeError( 181 "settings-type", global_settings_type, SETTINGS_TYPES 182 ) 183 else: 184 XML.SubElement( 185 xml_parent, "globalSettings", {"class": settings["default-global-settings"]} 186 ) 187 188 189def copyartifact_build_selector(xml_parent, data, select_tag="selector"): 190 191 select = data.get("which-build", "last-successful") 192 selectdict = { 193 "last-successful": "StatusBuildSelector", 194 "last-completed": "LastCompletedBuildSelector", 195 "specific-build": "SpecificBuildSelector", 196 "last-saved": "SavedBuildSelector", 197 "upstream-build": "TriggeredBuildSelector", 198 "permalink": "PermalinkBuildSelector", 199 "workspace-latest": "WorkspaceSelector", 200 "build-param": "ParameterizedBuildSelector", 201 "downstream-build": "DownstreamBuildSelector", 202 "multijob-build": "MultiJobBuildSelector", 203 } 204 if select not in selectdict: 205 raise InvalidAttributeError("which-build", select, selectdict.keys()) 206 permalink = data.get("permalink", "last") 207 permalinkdict = { 208 "last": "lastBuild", 209 "last-stable": "lastStableBuild", 210 "last-successful": "lastSuccessfulBuild", 211 "last-failed": "lastFailedBuild", 212 "last-unstable": "lastUnstableBuild", 213 "last-unsuccessful": "lastUnsuccessfulBuild", 214 } 215 if permalink not in permalinkdict: 216 raise InvalidAttributeError("permalink", permalink, permalinkdict.keys()) 217 if select == "multijob-build": 218 selector = XML.SubElement( 219 xml_parent, 220 select_tag, 221 {"class": "com.tikal.jenkins.plugins.multijob." + selectdict[select]}, 222 ) 223 else: 224 selector = XML.SubElement( 225 xml_parent, 226 select_tag, 227 {"class": "hudson.plugins.copyartifact." + selectdict[select]}, 228 ) 229 mapping = [] 230 if select == "specific-build": 231 mapping.append(("build-number", "buildNumber", "")) 232 if select == "last-successful": 233 mapping.append(("stable", "stable", False)) 234 if select == "upstream-build": 235 mapping.append( 236 ("fallback-to-last-successful", "fallbackToLastSuccessful", False) 237 ) 238 if select == "permalink": 239 mapping.append(("", "id", permalinkdict[permalink])) 240 if select == "build-param": 241 mapping.append(("param", "parameterName", "")) 242 if select == "downstream-build": 243 mapping.append(("upstream-project-name", "upstreamProjectName", "")) 244 mapping.append(("upstream-build-number", "upstreamBuildNumber", "")) 245 convert_mapping_to_xml(selector, data, mapping, fail_required=False) 246 247 248def findbugs_settings(xml_parent, data): 249 # General Options 250 mapping = [ 251 ("rank-priority", "isRankActivated", False), 252 ("include-files", "includePattern", ""), 253 ("exclude-files", "excludePattern", ""), 254 ] 255 convert_mapping_to_xml(xml_parent, data, mapping, fail_required=True) 256 257 258def get_value_from_yaml_or_config_file(key, section, data, jjb_config): 259 return jjb_config.get_plugin_config(section, key, data.get(key, "")) 260 261 262def cloudformation_region_dict(): 263 region_dict = { 264 "us-east-1": "US_East_Northern_Virginia", 265 "us-west-1": "US_WEST_Northern_California", 266 "us-west-2": "US_WEST_Oregon", 267 "eu-central-1": "EU_Frankfurt", 268 "eu-west-1": "EU_Ireland", 269 "ap-southeast-1": "Asia_Pacific_Singapore", 270 "ap-southeast-2": "Asia_Pacific_Sydney", 271 "ap-northeast-1": "Asia_Pacific_Tokyo", 272 "sa-east-1": "South_America_Sao_Paulo", 273 } 274 return region_dict 275 276 277def cloudformation_init(xml_parent, data, xml_tag): 278 cloudformation = XML.SubElement( 279 xml_parent, 280 "com.syncapse.jenkinsci." "plugins.awscloudformationwrapper." + xml_tag, 281 ) 282 return XML.SubElement(cloudformation, "stacks") 283 284 285def cloudformation_stack(xml_parent, stack, xml_tag, stacks, region_dict): 286 if "name" not in stack or stack["name"] == "": 287 raise MissingAttributeError("name") 288 step = XML.SubElement( 289 stacks, "com.syncapse.jenkinsci.plugins." "awscloudformationwrapper." + xml_tag 290 ) 291 292 if xml_tag == "SimpleStackBean": 293 mapping = [("prefix", "isPrefixSelected", False)] 294 else: 295 parameters_value = ",".join(stack.get("parameters", [])) 296 mapping = [ 297 ("description", "description", ""), 298 ("", "parameters", parameters_value), 299 ("timeout", "timeout", "0"), 300 ("sleep", "sleep", "0"), 301 ("recipe", "cloudFormationRecipe", None), 302 ] 303 304 cloudformation_stack_mapping = [ 305 ("name", "stackName", None), 306 ("access-key", "awsAccessKey", None), 307 ("secret-key", "awsSecretKey", None), 308 ("region", "awsRegion", None, region_dict), 309 ] 310 for map in mapping: 311 cloudformation_stack_mapping.append(map) 312 313 convert_mapping_to_xml( 314 step, stack, cloudformation_stack_mapping, fail_required=True 315 ) 316 317 318def include_exclude_patterns(xml_parent, data, yaml_prefix, xml_elem_name): 319 xml_element = XML.SubElement(xml_parent, xml_elem_name) 320 XML.SubElement(xml_element, "includePatterns").text = ",".join( 321 data.get(yaml_prefix + "-include-patterns", []) 322 ) 323 XML.SubElement(xml_element, "excludePatterns").text = ",".join( 324 data.get(yaml_prefix + "-exclude-patterns", []) 325 ) 326 327 328def artifactory_deployment_patterns(xml_parent, data): 329 include_exclude_patterns( 330 xml_parent, data, "deployment", "artifactDeploymentPatterns" 331 ) 332 333 334def artifactory_env_vars_patterns(xml_parent, data): 335 include_exclude_patterns(xml_parent, data, "env-vars", "envVarsPatterns") 336 337 338def artifactory_optional_props(xml_parent, data, target): 339 optional_str_props = [ 340 ("scopes", "scopes"), 341 ("violationRecipients", "violation-recipients"), 342 ("blackDuckAppName", "black-duck-app-name"), 343 ("blackDuckAppVersion", "black-duck-app-version"), 344 ("blackDuckReportRecipients", "black-duck-report-recipients"), 345 ("blackDuckScopes", "black-duck-scopes"), 346 ] 347 348 for (xml_prop, yaml_prop) in optional_str_props: 349 XML.SubElement(xml_parent, xml_prop).text = data.get(yaml_prop, "") 350 351 common_bool_props = [ 352 # yaml property name, xml property name, default value 353 ("deploy-artifacts", "deployArtifacts", True), 354 ("discard-old-builds", "discardOldBuilds", False), 355 ("discard-build-artifacts", "discardBuildArtifacts", False), 356 ("publish-build-info", "deployBuildInfo", False), 357 ("env-vars-include", "includeEnvVars", False), 358 ("run-checks", "runChecks", False), 359 ("include-publish-artifacts", "includePublishArtifacts", False), 360 ("license-auto-discovery", "licenseAutoDiscovery", True), 361 ("enable-issue-tracker-integration", "enableIssueTrackerIntegration", False), 362 ("aggregate-build-issues", "aggregateBuildIssues", False), 363 ("black-duck-run-checks", "blackDuckRunChecks", False), 364 ( 365 "black-duck-include-published-artifacts", 366 "blackDuckIncludePublishedArtifacts", 367 False, 368 ), 369 ( 370 "auto-create-missing-component-requests", 371 "autoCreateMissingComponentRequests", 372 True, 373 ), 374 ( 375 "auto-discard-stale-component-requests", 376 "autoDiscardStaleComponentRequests", 377 True, 378 ), 379 ( 380 "filter-excluded-artifacts-from-build", 381 "filterExcludedArtifactsFromBuild", 382 False, 383 ), 384 ] 385 convert_mapping_to_xml(xml_parent, data, common_bool_props, fail_required=True) 386 387 if "wrappers" in target: 388 wrapper_bool_props = [ 389 ("enable-resolve-artifacts", "enableResolveArtifacts", False), 390 ("disable-license-auto-discovery", "disableLicenseAutoDiscovery", False), 391 ("record-all-dependencies", "recordAllDependencies", False), 392 ] 393 convert_mapping_to_xml(xml_parent, data, wrapper_bool_props, fail_required=True) 394 395 if "publishers" in target: 396 publisher_bool_props = [ 397 ("even-if-unstable", "evenIfUnstable", False), 398 ("pass-identified-downstream", "passIdentifiedDownstream", False), 399 ( 400 "allow-promotion-of-non-staged-builds", 401 "allowPromotionOfNonStagedBuilds", 402 False, 403 ), 404 ] 405 convert_mapping_to_xml( 406 xml_parent, data, publisher_bool_props, fail_required=True 407 ) 408 409 410def artifactory_common_details(details, data): 411 mapping = [("name", "artifactoryName", ""), ("url", "artifactoryUrl", "")] 412 convert_mapping_to_xml(details, data, mapping, fail_required=True) 413 414 415def artifactory_repository(xml_parent, data, target): 416 if "release" in target: 417 release_mapping = [ 418 ("deploy-release-repo-key", "keyFromText", ""), 419 ("deploy-release-repo-key", "keyFromSelect", ""), 420 ("deploy-dynamic-mode", "dynamicMode", False), 421 ] 422 convert_mapping_to_xml(xml_parent, data, release_mapping, fail_required=True) 423 424 if "snapshot" in target: 425 snapshot_mapping = [ 426 ("deploy-snapshot-repo-key", "keyFromText", ""), 427 ("deploy-snapshot-repo-key", "keyFromSelect", ""), 428 ("deploy-dynamic-mode", "dynamicMode", False), 429 ] 430 convert_mapping_to_xml(xml_parent, data, snapshot_mapping, fail_required=True) 431 432 433def append_git_revision_config(parent, config_def): 434 params = XML.SubElement(parent, "hudson.plugins.git.GitRevisionBuildParameters") 435 436 try: 437 # If git-revision is a boolean, the get() will 438 # throw an AttributeError 439 combine_commits = str(config_def.get("combine-queued-commits", False)).lower() 440 except AttributeError: 441 combine_commits = "false" 442 443 XML.SubElement(params, "combineQueuedCommits").text = combine_commits 444 445 446def test_fairy_common(xml_element, data): 447 xml_element.set("plugin", "TestFairy") 448 valid_max_duration = ["10m", "60m", "300m", "1440m"] 449 valid_interval = [1, 2, 5] 450 valid_video_quality = ["high", "medium", "low"] 451 452 mappings = [ 453 # General 454 ("apikey", "apiKey", None), 455 ("appfile", "appFile", None), 456 ("tester-groups", "testersGroups", ""), 457 ("notify-testers", "notifyTesters", True), 458 ("autoupdate", "autoUpdate", True), 459 # Session 460 ("max-duration", "maxDuration", "10m", valid_max_duration), 461 ("record-on-background", "recordOnBackground", False), 462 ("data-only-wifi", "dataOnlyWifi", False), 463 # Video 464 ("video-enabled", "isVideoEnabled", True), 465 ("screenshot-interval", "screenshotInterval", 1, valid_interval), 466 ("video-quality", "videoQuality", "high", valid_video_quality), 467 # Metrics 468 ("cpu", "cpu", True), 469 ("memory", "memory", True), 470 ("logs", "logs", True), 471 ("network", "network", False), 472 ("phone-signal", "phoneSignal", False), 473 ("wifi", "wifi", False), 474 ("gps", "gps", False), 475 ("battery", "battery", False), 476 ("opengl", "openGl", False), 477 # Advanced options 478 ("advanced-options", "advancedOptions", ""), 479 ] 480 convert_mapping_to_xml(xml_element, data, mappings, fail_required=True) 481 482 483def trigger_get_parameter_order(registry, plugin): 484 logger = logging.getLogger("%s:trigger_get_parameter_order" % __name__) 485 486 if ( 487 str( 488 registry.jjb_config.get_plugin_config(plugin, "param_order_from_yaml", True) 489 ).lower() 490 == "false" 491 ): 492 logger.warning( 493 "Using deprecated order for parameter sets in %s. It is " 494 "recommended that you update your job definition instead of " 495 "enabling use of the old hardcoded order", 496 plugin, 497 ) 498 499 # deprecated order 500 return [ 501 "predefined-parameters", 502 "git-revision", 503 "property-file", 504 "current-parameters", 505 "node-parameters", 506 "svn-revision", 507 "restrict-matrix-project", 508 "node-label-name", 509 "node-label", 510 "boolean-parameters", 511 ] 512 513 return None 514 515 516def trigger_project(tconfigs, project_def, registry, param_order=None): 517 518 info = registry.get_plugin_info("parameterized-trigger") 519 plugin_version = pkg_resources.parse_version(info.get("version", str(sys.maxsize))) 520 521 logger = logging.getLogger("%s:trigger_project" % __name__) 522 pt_prefix = "hudson.plugins.parameterizedtrigger." 523 if param_order: 524 parameters = param_order 525 else: 526 parameters = project_def.keys() 527 528 for param_type in parameters: 529 param_value = project_def.get(param_type) 530 if param_value is None: 531 continue 532 533 if param_type == "predefined-parameters": 534 params = XML.SubElement(tconfigs, pt_prefix + "PredefinedBuildParameters") 535 properties = XML.SubElement(params, "properties") 536 properties.text = param_value 537 elif param_type == "git-revision" and param_value: 538 if "combine-queued-commits" in project_def: 539 logger.warning( 540 "'combine-queued-commit' has moved to reside under " 541 "'git-revision' configuration, please update your " 542 "configs as support for this will be removed." 543 ) 544 git_revision = { 545 "combine-queued-commits": project_def["combine-queued-commits"] 546 } 547 else: 548 git_revision = project_def["git-revision"] 549 append_git_revision_config(tconfigs, git_revision) 550 elif param_type == "property-file": 551 params = XML.SubElement(tconfigs, pt_prefix + "FileBuildParameters") 552 property_file_mapping = [ 553 ("property-file", "propertiesFile", None), 554 ("fail-on-missing", "failTriggerOnMissing", False), 555 ] 556 557 if plugin_version >= pkg_resources.parse_version("2.35.2"): 558 property_file_mapping.append( 559 ("property-multiline", "textParamValueOnNewLine", False) 560 ) 561 562 convert_mapping_to_xml( 563 params, project_def, property_file_mapping, fail_required=True 564 ) 565 if "file-encoding" in project_def: 566 XML.SubElement(params, "encoding").text = project_def["file-encoding"] 567 if "use-matrix-child-files" in project_def: 568 # TODO: These parameters only affect execution in 569 # publishers of matrix projects; we should warn if they are 570 # used in other contexts. 571 use_matrix_child_files_mapping = [ 572 ("use-matrix-child-files", "useMatrixChild", None), 573 ("matrix-child-combination-filter", "combinationFilter", ""), 574 ("only-exact-matrix-child-runs", "onlyExactRuns", False), 575 ] 576 convert_mapping_to_xml( 577 params, 578 project_def, 579 use_matrix_child_files_mapping, 580 fail_required=True, 581 ) 582 elif param_type == "current-parameters" and param_value: 583 XML.SubElement(tconfigs, pt_prefix + "CurrentBuildParameters") 584 elif param_type == "node-parameters" and param_value: 585 XML.SubElement(tconfigs, pt_prefix + "NodeParameters") 586 elif param_type == "svn-revision" and param_value: 587 param = XML.SubElement( 588 tconfigs, pt_prefix + "SubversionRevisionBuildParameters" 589 ) 590 XML.SubElement(param, "includeUpstreamParameters").text = str( 591 project_def.get("include-upstream", False) 592 ).lower() 593 elif param_type == "restrict-matrix-project" and param_value: 594 subset = XML.SubElement( 595 tconfigs, pt_prefix + "matrix.MatrixSubsetBuildParameters" 596 ) 597 XML.SubElement(subset, "filter").text = project_def[ 598 "restrict-matrix-project" 599 ] 600 elif param_type == "node-label-name" or param_type == "node-label": 601 tag_name = ( 602 "org.jvnet.jenkins.plugins.nodelabelparameter." 603 "parameterizedtrigger.NodeLabelBuildParameter" 604 ) 605 if tconfigs.find(tag_name) is not None: 606 # already processed and can only have one 607 continue 608 params = XML.SubElement(tconfigs, tag_name) 609 name = XML.SubElement(params, "name") 610 if "node-label-name" in project_def: 611 name.text = project_def["node-label-name"] 612 label = XML.SubElement(params, "nodeLabel") 613 if "node-label" in project_def: 614 label.text = project_def["node-label"] 615 elif param_type == "boolean-parameters" and param_value: 616 params = XML.SubElement(tconfigs, pt_prefix + "BooleanParameters") 617 config_tag = XML.SubElement(params, "configs") 618 param_tag_text = pt_prefix + "BooleanParameterConfig" 619 params_list = param_value 620 for name, value in params_list.items(): 621 param_tag = XML.SubElement(config_tag, param_tag_text) 622 mapping = [("", "name", name), ("", "value", value or False)] 623 convert_mapping_to_xml( 624 param_tag, project_def, mapping, fail_required=True 625 ) 626 627 628def trigger_threshold( 629 parent_element, element_name, threshold_name, supported_thresholds=None 630): 631 """Generate a resultThreshold XML element for build/join triggers""" 632 element = XML.SubElement(parent_element, element_name) 633 634 try: 635 threshold = hudson_model.THRESHOLDS[threshold_name.upper()] 636 except KeyError: 637 if not supported_thresholds: 638 supported_thresholds = hudson_model.THRESHOLDS.keys() 639 raise JenkinsJobsException( 640 "threshold must be one of %s" % ", ".join(supported_thresholds) 641 ) 642 XML.SubElement(element, "name").text = threshold["name"] 643 XML.SubElement(element, "ordinal").text = threshold["ordinal"] 644 XML.SubElement(element, "color").text = threshold["color"] 645 return element 646 647 648def convert_mapping_to_xml(parent, data, mapping, fail_required=True): 649 """Convert mapping to XML 650 651 fail_required affects the last parameter of the mapping field when it's 652 parameter is set to 'None'. When fail_required is True then a 'None' value 653 represents a required configuration so will raise a MissingAttributeError 654 if the user does not provide the configuration. 655 656 If fail_required is False parameter is treated as optional. Logic will skip 657 configuring the XML tag for the parameter. We recommend for new plugins to 658 set fail_required=True and instead of optional parameters provide a default 659 value for all parameters that are not required instead. 660 661 valid_options provides a way to check if the value the user input is from a 662 list of available options. When the user pass a value that is not supported 663 from the list, it raise an InvalidAttributeError. 664 665 valid_dict provides a way to set options through their key and value. If 666 the user input corresponds to a key, the XML tag will use the key's value 667 for its element. When the user pass a value that there are no keys for, 668 it raise an InvalidAttributeError. 669 """ 670 for elem in mapping: 671 (optname, xmlname, val) = elem[:3] 672 val = data.get(optname, val) 673 674 valid_options = [] 675 valid_dict = {} 676 if len(elem) == 4: 677 if type(elem[3]) is list: 678 valid_options = elem[3] 679 if type(elem[3]) is dict: 680 valid_dict = elem[3] 681 682 # Use fail_required setting to allow support for optional parameters 683 if val is None and fail_required is True: 684 raise MissingAttributeError(optname) 685 686 # if no value is provided then continue else leave it 687 # up to the user if they want to use an empty XML tag 688 if val is None and fail_required is False: 689 continue 690 691 if valid_dict: 692 if val not in valid_dict: 693 raise InvalidAttributeError(optname, val, valid_dict.keys()) 694 695 if valid_options: 696 if val not in valid_options: 697 raise InvalidAttributeError(optname, val, valid_options) 698 699 if type(val) == bool: 700 val = str(val).lower() 701 702 if val in valid_dict: 703 XML.SubElement(parent, xmlname).text = str(valid_dict[val]) 704 else: 705 XML.SubElement(parent, xmlname).text = str(val) 706 707 708def jms_messaging_common(parent, subelement, data): 709 """JMS common helper function 710 711 Pass the XML parent and the specific subelement from the builder or the 712 publisher. 713 714 data is passed to mapper helper function to map yaml fields to XML fields 715 """ 716 namespace = XML.SubElement(parent, subelement) 717 718 if "override-topic" in data: 719 overrides = XML.SubElement(namespace, "overrides") 720 XML.SubElement(overrides, "topic").text = str(data.get("override-topic", "")) 721 722 mapping = [ 723 # option, xml name, default value 724 ("provider-name", "providerName", ""), 725 ("msg-type", "messageType", "CodeQualityChecksDone"), 726 ("msg-props", "messageProperties", ""), 727 ("msg-content", "messageContent", ""), 728 ] 729 convert_mapping_to_xml(namespace, data, mapping, fail_required=True) 730 731 732def check_mutual_exclusive_data_args(data_arg_position, *args): 733 mutual_exclusive_args = set(args) 734 735 def validator(f): 736 @wraps(f) 737 def wrap(*args): 738 actual_args = set(args[data_arg_position].keys()) 739 if len(actual_args & mutual_exclusive_args) > 1: 740 raise JenkinsJobsException( 741 "Args: {} in {} are mutual exclusive. Please define one of it.".format( 742 mutual_exclusive_args, f.__name__ 743 ) 744 ) 745 return f(*args) 746 747 return wrap 748 749 return validator 750