foreman_remote_execution 1.4.1 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +11 -9
  3. data/.rubocop_todo.yml +146 -116
  4. data/app/controllers/api/v2/job_invocations_controller.rb +6 -1
  5. data/app/controllers/api/v2/job_templates_controller.rb +0 -1
  6. data/app/controllers/job_invocations_controller.rb +6 -1
  7. data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
  8. data/app/models/concerns/foreman_remote_execution/errors_flattener.rb +3 -0
  9. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +1 -1
  10. data/app/models/concerns/foreman_remote_execution/subnet_extensions.rb +1 -1
  11. data/app/models/foreign_input_set.rb +1 -1
  12. data/app/models/job_invocation.rb +1 -1
  13. data/app/models/job_invocation_composer.rb +2 -2
  14. data/app/models/job_invocation_task_group.rb +1 -1
  15. data/app/models/job_template.rb +3 -3
  16. data/app/models/job_template_effective_user.rb +1 -1
  17. data/app/models/remote_execution_feature.rb +1 -1
  18. data/app/models/target_remote_execution_proxy.rb +1 -1
  19. data/app/models/targeting.rb +2 -2
  20. data/app/models/targeting_host.rb +1 -1
  21. data/app/models/template_input.rb +1 -1
  22. data/app/models/template_invocation.rb +1 -1
  23. data/app/models/template_invocation_input_value.rb +1 -1
  24. data/db/migrate/20150612121541_add_job_template_to_template.rb +1 -1
  25. data/db/migrate/20150616080015_create_template_input.rb +1 -1
  26. data/db/migrate/20150708133241_add_targeting.rb +1 -1
  27. data/db/migrate/20150708133242_add_invocation.rb +1 -1
  28. data/db/migrate/20150708133305_add_template_invocation.rb +1 -1
  29. data/db/migrate/20150812110800_add_resolved_at_to_targeting.rb +1 -1
  30. data/db/migrate/20150812145900_add_last_task_id_to_job_invocation.rb +1 -1
  31. data/db/migrate/20150826191632_create_target_remote_execution_proxies.rb +1 -1
  32. data/db/migrate/20150827144500_change_targeting_search_query_type.rb +1 -1
  33. data/db/migrate/20150827152730_add_options_to_template_input.rb +1 -1
  34. data/db/migrate/20150903192731_add_execution_to_interface.rb +2 -2
  35. data/db/migrate/20150923125825_add_job_invocation_task_group.rb +1 -1
  36. data/db/migrate/20151013135415_add_pub_key_to_smart_proxy.rb +1 -1
  37. data/db/migrate/20151022105508_rename_last_task_id_column.rb +1 -1
  38. data/db/migrate/20151116105412_add_triggering_to_job_invocation.rb +1 -1
  39. data/db/migrate/20151120171100_add_effective_user_to_template_invocation.rb +1 -1
  40. data/db/migrate/20151124162300_create_job_template_effective_users.rb +1 -1
  41. data/db/migrate/20151203100824_add_description_to_job_invocation.rb +1 -1
  42. data/db/migrate/20151215114631_add_host_id_to_template_invocation.rb +2 -2
  43. data/db/migrate/20151217092555_migrate_to_task_groups.rb +2 -2
  44. data/db/migrate/20160108134600_create_template_input_sets.rb +1 -1
  45. data/db/migrate/20160108141144_make_job_name_default_to_something.rb +1 -1
  46. data/db/migrate/20160111113032_upcase_ssh_feature.rb +2 -2
  47. data/db/migrate/20160113161916_add_run_host_job_task_id_to_template_invocation.rb +1 -1
  48. data/db/migrate/20160113162007_expand_all_template_invocations.rb +3 -3
  49. data/db/migrate/20160114120200_rename_job_categories.rb +1 -1
  50. data/db/migrate/20160114125628_rename_job_name_to_job_category.rb +1 -1
  51. data/db/migrate/20160118124600_create_remote_execution_features.rb +1 -1
  52. data/db/migrate/20160125155108_make_job_template_name_unique.rb +1 -1
  53. data/db/migrate/20160127134031_add_advanced_to_template_input.rb +1 -1
  54. data/db/migrate/20160127162711_reword_puppet_template_description.rb +1 -1
  55. data/db/migrate/20160203104056_add_concurrency_options_to_job_invocation.rb +1 -1
  56. data/db/migrate/20160926225841_update_template_input_value.rb +1 -1
  57. data/db/migrate/20170110145641_add_host_action_button_to_remote_execution_feature.rb +1 -1
  58. data/db/migrate/20170613101039_add_timeout_to_job_templates_and_job_invocations.rb +1 -1
  59. data/db/migrate/20180110104432_rename_template_invocation_permission.rb +25 -0
  60. data/db/migrate/20180112125015_fix_taxable_taxonomies_job_template.rb +14 -0
  61. data/db/seeds.d/90-bookmarks.rb +1 -1
  62. data/foreman_remote_execution.gemspec +2 -1
  63. data/lib/foreman_remote_execution/engine.rb +3 -3
  64. data/lib/foreman_remote_execution/version.rb +1 -1
  65. data/test/benchmark/run_hosts_job_benchmark.rb +10 -10
  66. data/test/benchmark/targeting_benchmark.rb +6 -6
  67. data/test/factories/foreman_remote_execution_factories.rb +14 -14
  68. data/test/functional/api/v2/foreign_input_sets_controller_test.rb +10 -15
  69. data/test/functional/api/v2/job_invocations_controller_test.rb +19 -8
  70. data/test/functional/api/v2/job_templates_controller_test.rb +12 -16
  71. data/test/functional/api/v2/remote_execution_features_controller_test.rb +3 -4
  72. data/test/functional/api/v2/template_inputs_controller_test.rb +8 -13
  73. data/test/test_plugin_helper.rb +4 -4
  74. data/test/unit/actions/run_hosts_job_test.rb +3 -3
  75. data/test/unit/concerns/exportable_test.rb +1 -1
  76. data/test/unit/concerns/foreman_tasks_cleaner_extensions_test.rb +2 -2
  77. data/test/unit/concerns/host_extensions_test.rb +20 -20
  78. data/test/unit/concerns/nic_extensions_test.rb +1 -1
  79. data/test/unit/input_template_renderer_test.rb +86 -86
  80. data/test/unit/job_invocation_composer_test.rb +17 -16
  81. data/test/unit/job_invocation_test.rb +10 -10
  82. data/test/unit/job_template_effective_user_test.rb +2 -2
  83. data/test/unit/job_template_importer_test.rb +3 -3
  84. data/test/unit/job_template_test.rb +15 -15
  85. data/test/unit/remote_execution_feature_test.rb +3 -3
  86. data/test/unit/remote_execution_provider_test.rb +15 -15
  87. data/test/unit/targeting_test.rb +3 -3
  88. data/test/unit/template_input_test.rb +1 -1
  89. data/test/unit/template_invocation_input_value_test.rb +17 -17
  90. metadata +19 -77
  91. data/doc/.gitignore +0 -7
  92. data/doc/Gemfile +0 -7
  93. data/doc/Rakefile +0 -41
  94. data/doc/_config.yml +0 -33
  95. data/doc/plugins/alert_block.rb +0 -27
  96. data/doc/plugins/div_tag.rb +0 -24
  97. data/doc/plugins/graphviz.rb +0 -121
  98. data/doc/plugins/plantuml.rb +0 -84
  99. data/doc/plugins/play.rb +0 -13
  100. data/doc/plugins/tags.rb +0 -137
  101. data/doc/plugins/toc.rb +0 -19
  102. data/doc/source/.nojekyll +0 -0
  103. data/doc/source/404.md +0 -6
  104. data/doc/source/_includes/footer.html +0 -21
  105. data/doc/source/_includes/header.html +0 -59
  106. data/doc/source/_includes/tocify.html +0 -6
  107. data/doc/source/_layouts/default.html +0 -9
  108. data/doc/source/_layouts/page.html +0 -25
  109. data/doc/source/atom.xml +0 -32
  110. data/doc/source/design/index.md +0 -1322
  111. data/doc/source/design/wireframes.pdf +0 -0
  112. data/doc/source/index.md +0 -18
  113. data/doc/source/static/css/bootstrap-responsive.min.css +0 -9
  114. data/doc/source/static/css/bootstrap.min.css +0 -866
  115. data/doc/source/static/css/jquery.tocify.css +0 -128
  116. data/doc/source/static/css/style.css +0 -285
  117. data/doc/source/static/css/syntax.css +0 -60
  118. data/doc/source/static/images/foreman.png +0 -0
  119. data/doc/source/static/images/glyphicons-halflings-white.png +0 -0
  120. data/doc/source/static/images/glyphicons-halflings.png +0 -0
  121. data/doc/source/static/js/bootstrap.min.js +0 -7
  122. data/doc/source/static/js/jquery-ui-1.9.2.custom.min.js +0 -6
  123. data/doc/source/static/js/jquery.js +0 -2
  124. data/doc/source/static/js/jquery.tocify.min.js +0 -3
  125. data/doc/source/static/js/scroll.js +0 -24
@@ -1,13 +0,0 @@
1
- require 'pp'
2
- require 'pry'
3
-
4
- module Jekyll
5
-
6
- class Play < Generator
7
- def generate(site)
8
- # pp site
9
- # binding.pry
10
- end
11
-
12
- end
13
- end
@@ -1,137 +0,0 @@
1
- require 'nuggets/range/quantile'
2
- require 'erb'
3
-
4
- module Jekyll
5
-
6
- class Tagger < Generator
7
-
8
- safe true
9
-
10
- attr_accessor :site
11
-
12
- @types = [:page, :feed]
13
-
14
- class << self; attr_accessor :types, :site; end
15
-
16
- def generate(site)
17
- self.class.site = self.site = site
18
-
19
- generate_tag_pages
20
- add_tag_cloud
21
- end
22
-
23
- private
24
-
25
- # Generates a page per tag and adds them to all the pages of +site+.
26
- # A <tt>tag_page_layout</tt> have to be defined in your <tt>_config.yml</tt>
27
- # to use this.
28
- def generate_tag_pages
29
- active_tags.each { |tag, posts| new_tag(tag, posts) }
30
- end
31
-
32
- def new_tag(tag, posts)
33
- self.class.types.each do |type|
34
- if layout = site.config["tag_#{type}_layout"]
35
- data = { 'layout' => layout, 'posts' => posts.sort.reverse!, 'tag' => tag, 'title' => tag }
36
-
37
- name = yield data if block_given?
38
- name ||= tag
39
-
40
- tag_dir = site.config["tag_#{type}_dir"]
41
- tag_dir = File.join(tag_dir, (pretty? ? name : ''))
42
-
43
- page_name = "#{pretty? ? 'index' : name}#{site.layouts[data['layout']].ext}"
44
-
45
- site.pages << TagPage.new(
46
- site, site.source, tag_dir, page_name, data
47
- )
48
- end
49
- end
50
- end
51
-
52
- def add_tag_cloud(num = 5, name = 'tag_data')
53
- s, t = site, { name => calculate_tag_cloud(num) }
54
- s.respond_to?(:add_payload) ? s.add_payload(t) : s.config.update(t)
55
- end
56
-
57
- # Calculates the css class of every tag for a tag cloud. The possible
58
- # classes are: set-1..set-5.
59
- #
60
- # [[<TAG>, <CLASS>], ...]
61
- def calculate_tag_cloud(num = 5)
62
- range = 0
63
-
64
- tags = active_tags.map do |tag, posts|
65
- [tag.to_s, range < (size = posts.size) ? range = size : size]
66
- end
67
-
68
- range = 1..range
69
-
70
- tags.sort!.map! { |tag, size| [tag, range.quantile(size, num)] }
71
- end
72
-
73
- def active_tags
74
- return site.tags unless site.config['ignored_tags']
75
- site.tags.reject { |t| site.config['ignored_tags'].include? t[0] }
76
- end
77
-
78
- def pretty?
79
- @pretty ||= (site.permalink_style == :pretty || site.config['tag_permalink_style'] == 'pretty')
80
- end
81
-
82
- end
83
-
84
- class TagPage < Page
85
-
86
- def initialize(site, base, dir, name, data = {})
87
- self.content = data.delete('content') || ''
88
- self.data = data
89
-
90
- super(site, base, dir[-1, 1] == '/' ? dir : '/' + dir, name)
91
- end
92
-
93
- def read_yaml(*)
94
- # Do nothing
95
- end
96
-
97
- end
98
-
99
- module Filters
100
-
101
- def tag_cloud(site)
102
- active_tag_data.map do |tag, set|
103
- tag_link(tag, tag_url(tag), :class => "set-#{set} label label-default")
104
- end.join(' ')
105
- end
106
-
107
- def tag_link(tag, url = tag_url(tag), html_opts = nil)
108
- html_opts &&= ' ' << html_opts.map { |k, v| %Q{#{k}="#{v}"} }.join(' ')
109
- %Q{<a href="#{url}"#{html_opts}>#{tag}</a>}
110
- end
111
-
112
- def tag_url(tag, type = :page, site = Tagger.site)
113
- # FIXME generate full url for atom.xml page
114
- url = File.join('', site.config["tag_#{type}_dir"], ERB::Util.u(tag))
115
- site.permalink_style == :pretty || site.config['tag_permalink_style'] == 'pretty' ? url : url << '.html'
116
- end
117
-
118
- def tags(obj)
119
- tags = obj['tags'].dup
120
- tags.map! { |t| t.first } if tags.first.is_a?(Array)
121
- tags.map! { |t| tag_link(t, tag_url(t), rel: 'tag', class: 'label label-default') if t.is_a?(String) }.compact!
122
- tags.join(' ')
123
- end
124
-
125
- def keywords(obj)
126
- return '' if not obj['tags']
127
- tags = obj['tags'].dup
128
- tags.join(',')
129
- end
130
-
131
- def active_tag_data(site = Tagger.site)
132
- return site.config['tag_data'] unless site.config['ignored_tags']
133
- site.config['tag_data'].reject { |tag, set| site.config['ignored_tags'].include? tag }
134
- end
135
- end
136
-
137
- end
@@ -1,19 +0,0 @@
1
- module Jekyll
2
- module FancyToCFilter
3
- def fancytoc(input)
4
- converter = @context.registers[:site].converters.find { |c| c.is_a? Jekyll::Converters::Markdown }
5
- extensions = converter.instance_variable_get(:@parser).instance_variable_get(:@redcarpet_extensions)
6
- toc_generator = Redcarpet::Markdown.new(Redcarpet::Render::HTML_TOC, extensions)
7
- toc = toc_generator.render(input)
8
-
9
- <<-HTML unless toc.empty?
10
- <div class="toc well" data-spy="affix" data-offset-top="0" data-offset-bottom="0">
11
- <h4>Table of content</h4>
12
- #{toc}
13
- </div>
14
- HTML
15
- end
16
- end
17
- end
18
-
19
- Liquid::Template.register_filter(Jekyll::FancyToCFilter)
File without changes
@@ -1,6 +0,0 @@
1
- ---
2
- layout: page
3
- title: "404"
4
- ---
5
-
6
- We are sorry, page You are looking for does not exist :'(
@@ -1,21 +0,0 @@
1
- <div class="footer">
2
- <div class="container">
3
- <p>This web site is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_GB">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>. Source available: <a xmlns:dct="http://purl.org/dc/terms/" href="https://github.com/theforeman/theforeman.org" rel="dct:source">github/theforeman/theforeman.org</a>.</p>
4
- <a href="https://plus.google.com/102496134326414788199" rel="publisher">Google+ community</a>
5
- </div>
6
- </div>
7
-
8
- <script type="text/javascript">
9
- var _gaq = _gaq || [];
10
- _gaq.push(['_setAccount', 'UA-2134730-5']);
11
- _gaq.push(['_trackPageview']);
12
-
13
- (function() {
14
- var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
15
- ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
16
- var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
17
- })();
18
- </script>
19
-
20
- </body>
21
- </html>
@@ -1,59 +0,0 @@
1
- <!doctype html>
2
- <html itemscope itemtype="http://schema.org/Organization" lang="en">
3
- <head>
4
- <base href="{{ site.baseurl }}">
5
- {% if page.sitename != nil %}
6
- {% assign sitename = page.sitename %}
7
- {% else %}
8
- {% assign sitename = site.sitename %}
9
- {% endif %}
10
-
11
- {% if page.pagename != nil %}
12
- {% assign pagename = true %}
13
- {% endif %}
14
- <title>{{ sitename }}{% if pagename %} :: {{ page.pagename }}{% endif %}</title>
15
-
16
- <script type="text/javascript" src="{{ site.baseurl }}static/js/jquery.js"></script>
17
- <script type="text/javascript" src="{{ site.baseurl }}static/js/bootstrap.min.js"></script>
18
- <script type="text/javascript" src="{{ site.baseurl }}static/js/jquery-ui-1.9.2.custom.min.js"></script>
19
- <script type="text/javascript" src="{{ site.baseurl }}static/js/jquery.tocify.min.js"></script>
20
- <script type="text/javascript" src="{{ site.baseurl }}static/js/scroll.js"></script>
21
-
22
- <link rel="stylesheet" media="all" href="{{ site.baseurl }}static/css/bootstrap.min.css"/>
23
- <link rel="stylesheet" media="all" href="{{ site.baseurl }}static/css/bootstrap-responsive.min.css"/>
24
- <link rel="stylesheet" media="all" href="{{ site.baseurl }}static/css/jquery.tocify.css"/>
25
- <link rel="stylesheet" media="all" href="{{ site.baseurl }}static/css/syntax.css" />
26
- <link rel="stylesheet" media="all" href="{{ site.baseurl }}static/css/style.css"/>
27
- <meta name="apple-mobile-web-app-capable" content="yes">
28
- <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0;" />
29
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
30
- </head>
31
-
32
- <body>
33
- <div class="navbar navbar-fixed-top">
34
- <div class="navbar-inner">
35
- <div class="container">
36
- <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
37
- <span class="icon-bar"></span>
38
- <span class="icon-bar"></span>
39
- <span class="icon-bar"></span>
40
- </button>
41
- <img alt="Foreman" class="logo" src="{{ site.baseurl }}static/images/foreman.png" />
42
- <div class="brand">
43
- <a href="{{ site.baseurl }}">Foreman</a>
44
- <a class="subtitle" href="{{ site.baseurl }}">Remote Execution</a>
45
- </div>
46
- <div class="nav-collapse collapse">
47
- <ul class="nav">
48
- <li><a href="{{ site.baseurl }}">About</a></li>
49
- <li><a href="{{ site.baseurl }}design/">Design</a></li>
50
- </ul>
51
- </div>
52
- </div>
53
- </div>
54
- </div>
55
- <script>
56
- $(function(){
57
- $('.nav > li a[href="'+window.location.pathname+'"]').parents('.nav > li').addClass('active')
58
- })
59
- </script>
@@ -1,6 +0,0 @@
1
- <script type="text/javascript">
2
- $(function() {
3
- //Calls the tocify method on your HTML div.
4
- $("#toc").tocify({context: '#doc', scrollTo: '60', selectors: "h2,h3,h4"});
5
- });
6
- </script>
@@ -1,9 +0,0 @@
1
- {% include header.html %}
2
- <div id="wrap">
3
- <div id="main">
4
- <div id="content" class="container">
5
- {{ content }}
6
- </div>
7
- </div>
8
- </div>
9
- {% include footer.html %}
@@ -1,25 +0,0 @@
1
- ---
2
- pagename: Foreman Remote Execution
3
- ---
4
- {% include header.html %}
5
-
6
- <div id="main">
7
- <div id="content" class="container">
8
- <div class="row">
9
- <div id="toc" class="span3">
10
- </div>
11
- <div id="doc" class="offset3 span9">
12
- {{ content }}
13
- </div>
14
- </div>
15
- </div>
16
- </div>
17
-
18
- <script type="text/javascript">
19
- $(function() {
20
- //Calls the tocify method on your HTML div.
21
- $("#toc").tocify({context: '#doc', scrollTo: '60', selectors: "h1,h2,h3"});
22
- });
23
- </script>
24
-
25
- {% include footer.html %}
@@ -1,32 +0,0 @@
1
- ---
2
- # Front matter comment to ensure Jekyll properly reads file.
3
- ---
4
-
5
- <?xml version="1.0" encoding="utf-8"?>
6
- <feed xmlns="http://www.w3.org/2005/Atom">
7
-
8
- <title><![CDATA[{{ site.title }}]]></title>
9
- <link href="{{ site.url }}/atom.xml" rel="self"/>
10
- <link href="{{ site.url }}/"/>
11
- <updated>{{ site.time | date_to_xmlschema }}</updated>
12
- <id>{{ site.url }}/</id>
13
- <author>
14
- <name><![CDATA[{{ site.author | strip_html }}]]></name>
15
- {% if site.email %}<email><![CDATA[{{ site.email }}]]></email>{% endif %}
16
- </author>
17
-
18
- {% for post in site.posts %}
19
- <entry>
20
- <title type="html"><![CDATA[{{ post.title | cdata_escape }}]]></title>
21
- <link href="{{ site.url }}{{ post.url }}"/>
22
- <updated>{{ post.date | date_to_xmlschema }}</updated>
23
- <id>{{ site.url }}{{ post.id }}</id>
24
- <!--<content type="html">-->
25
- <!--&lt;!&ndash; FIXME full urls&ndash;&gt;-->
26
- <!--<![CDATA[-->
27
- <!--Tags: {{ post | tags }}-->
28
- <!--]]>-->
29
- <!--</content>-->
30
- </entry>
31
- {% endfor %}
32
- </feed>
@@ -1,1322 +0,0 @@
1
- ---
2
- layout: page
3
- title: Design
4
- countheads: true
5
- toc: true
6
- comments: true
7
- ---
8
-
9
- Remote Execution Technology
10
- ===========================
11
-
12
- User Stories
13
- ------------
14
-
15
- - As a user I want to run jobs in parallel across large number of
16
- hosts
17
-
18
- - As a user I want to run jobs on a host in a different network
19
- segment (the host doesn't see the Foreman server/the Foreman server
20
- doesn't see the host directly)
21
-
22
- - As a user I want to manage a host without installing an agent on it
23
- (just plain old ssh)
24
-
25
- - As a community user I want to already existing remote execution
26
- technologies in combination with the Foreman
27
-
28
- Design
29
- ------
30
-
31
- Although specific providers are mentioned in the design, it's used
32
- mainly for distinguishing different approaches to the remote execution
33
- than to choose specific technologies
34
-
35
- ### Ssh Single Host Push
36
-
37
- {% plantuml %}
38
- actor User
39
- participant "Foreman Server" as Foreman
40
- participant "Foreman Proxy" as Proxy
41
- participant "Host" as Host
42
-
43
- autonumber
44
- User -> Foreman : JobInvocation
45
- Foreman -> Proxy : ProxyCommand
46
- Proxy -> Host : SshScript
47
- Activate Host
48
- Host --> Proxy : ProgressReport[1, Running]
49
- Host --> Proxy : ProgressReport[2, Running]
50
- Proxy --> Foreman : AccumulatedProgressReport[1, Running]
51
- Host --> Proxy : ProgressReport[3, Running]
52
- Host --> Proxy : ProgressReport[4, Finished]
53
- Deactivate Host
54
- Proxy --> Foreman : AccumulatedProgressReport[2, Finished]
55
- {% endplantuml %}
56
-
57
- JobInvocation: see see [scheduling](design#job-invocation)
58
-
59
- ProxyCommand:
60
-
61
- * host: host.example.com
62
- * provider: ssh
63
- * input: "yum install -y vim-X11"
64
-
65
- SSHScript:
66
-
67
- * host: host.example.com
68
- * input: "yum install -y vim-X11"
69
-
70
- ProgressReport[1, Running]:
71
-
72
- * output: "Resolving depednencies"
73
-
74
- ProgressReport[2, Running]:
75
-
76
- * output: "installing libXt"
77
-
78
- AccumulatedProgressReport[1, Running]:
79
-
80
- * output: { stdout: "Resolving depednencies\ninstalling libXt" }
81
-
82
- ProgressReport[3, Running]:
83
-
84
- * output: "installing vim-X11"
85
-
86
- ProgressReport[4, Finished]:
87
-
88
- * output: "operation finished successfully"
89
- * exit_code: 0
90
-
91
- AccumulatedProgressReport[2, Finished]:
92
-
93
- * output: { stdout: "installing vim-X11\noperation finished successfully", exit_code: 0 }
94
- * success: true
95
-
96
- ### Ssh Single Host Check-in
97
-
98
- This case allows to handle the case, when the host is offline by the
99
- time of job invocation: the list of jobs for the host is stored on the
100
- Foreman server side for running once the host is online.
101
-
102
- This approach is not limited to the ssh provider only.
103
-
104
- {% plantuml %}
105
- actor User
106
- participant "Foreman Server" as Foreman
107
- participant "Foreman Proxy" as Proxy
108
- participant "Host" as Host
109
-
110
- autonumber
111
- User -> Foreman : JobInvocation
112
- Host -> Proxy : CheckIn
113
- Proxy -> Foreman : CheckIn
114
- Foreman -> Proxy : ProxyCommand
115
- Proxy -> Host : SshScript
116
- {% endplantuml %}
117
-
118
- ### Ssh Multi Host
119
-
120
- {% plantuml %}
121
- actor User
122
- participant "Foreman Server" as Foreman
123
- participant "Foreman Proxy" as Proxy
124
- participant "Host 1" as Host1
125
- participant "Host 2" as Host2
126
-
127
- autonumber
128
- User -> Foreman : JobInvocation
129
- Foreman -> Proxy : ProxyCommand[host1]
130
- Foreman -> Proxy : ProxyCommand[host2]
131
- Proxy -> Host1 : SSHScript
132
- Proxy -> Host2 : SSHScript
133
- {% endplantuml %}
134
-
135
- ProxyCommand[host1]:
136
-
137
- * host: host-1.example.com
138
- * provider: ssh
139
- * input: "yum install -y vim-X11"
140
-
141
- ProxyCommand[host2]:
142
-
143
- * host: host-2.example.com
144
- * provider: ssh
145
- * input: "yum install -y vim-X11"
146
-
147
- {% info_block %}
148
- we might want to optimize the communication between server and
149
- the proxy (sending collection of ProxyCommands in bulk, as well as
150
- the AccumulatedProgerssReports). That would could also be utilized
151
- by the Ansible implementation, where there might be optimization
152
- on the invoking the ansible commands at once (the same might apply
153
- to mcollective). On the other hand, this is more an optimization,
154
- not required to be implemented from the day one: but it's good to have
155
- this in mind
156
- {% endinfo_block %}
157
-
158
- ### MCollective Single Host
159
-
160
- {% plantuml %}
161
- actor User
162
- participant "Foreman Server" as Foreman
163
- participant "Foreman Proxy" as Proxy
164
- participant "AMQP" as AMQP
165
- participant "Host" as Host
166
-
167
- autonumber
168
- User -> Foreman : JobInvocation
169
- Foreman -> Proxy : ProxyCommand
170
- Proxy -> AMQP : MCOCommand
171
- AMQP -> Host : MCOCommand
172
- Activate Host
173
- Host --> AMQP : ProgressReport[Finished]
174
- Deactivate Host
175
- AMQP --> Proxy : ProgressReport[Finished]
176
- Proxy --> Foreman : AccumulatedProgressReport[Finished]
177
- {% endplantuml %}
178
-
179
- JobInvocation:
180
-
181
- * hosts: [host.example.com]
182
- * template: install-packages-mco
183
- * input: { packages: ['vim-X11'] }
184
-
185
- ProxyCommand:
186
-
187
- * host: host.example.com
188
- * provider: mcollective
189
- * input: { agent: package, args: { package => 'vim-X11' } }
190
-
191
- MCOCommand:
192
-
193
- * host: host.example.com
194
- * input: { agent: package, args: { package => 'vim-X11' } }
195
-
196
- ProgressReport[Finished]:
197
-
198
- * output: [ {"name":"vim-X11","tries":1,"version":"7.4.160-1","status":0,"release":"1.el7"},
199
- {"name":"libXt","tries":1,"version":"1.1.4-6","status":0,"release":"1.el7"} ]
200
-
201
- AccumulatedProgressReport[Finished]:
202
-
203
- * output: [ {"name":"vim-X11","tries":1,"version":"7.4.160-1","status":0,"release":"1.el7"},
204
- {"name":"libXt","tries":1,"version":"1.1.4-6","status":0,"release":"1.el7"} ]
205
- * success: true
206
-
207
- ### Ansible Single Host
208
-
209
- {% plantuml %}
210
- actor User
211
- participant "Foreman Server" as Foreman
212
- participant "Foreman Proxy" as Proxy
213
- participant "Host" as Host
214
-
215
- autonumber
216
- User -> Foreman : JobInvocation
217
- Foreman -> Proxy : ProxyCommand
218
- Proxy -> Host : AnsibleCommand
219
- Activate Host
220
- Host --> Proxy : ProgressReport[Finished]
221
- Deactivate Host
222
- Proxy --> Foreman : AccumulatedProgressReport[Finished]
223
-
224
- {% endplantuml %}
225
-
226
- JobInvocation:
227
-
228
- * hosts: [host.example.com]
229
- * template: install-packages-ansible
230
- * input: { packages: ['vim-X11'] }
231
-
232
- ProxyCommand:
233
-
234
- * host: host.example.com
235
- * provider: ansible
236
- * input: { module: yum, args: { name: 'vim-X11', state: installed } }
237
-
238
- AnsibleCommand:
239
-
240
- * host: host.example.com
241
- * provider: ansible
242
- * input: { module: yum, args: { name: 'vim-X11', state: installed } }
243
-
244
- ProgressReport[Finished]:
245
-
246
- * output: { changed: true,
247
- rc: 0,
248
- results: ["Resolving depednencies\ninstalling libXt\ninstalling vim-X11\noperation finished successfully"] }
249
-
250
- AccumulatedProgressReport[Finished]:
251
-
252
- * output: { changed: true,
253
- rc: 0,
254
- results: ["Resolving depednencies\ninstalling libXt\ninstalling vim-X11\noperation finished successfully"] }
255
- * success: true
256
-
257
-
258
- Job Preparation
259
- ===============
260
-
261
- User Stories
262
- ------------
263
-
264
- - As a user I want to be able to create a template to run some command for a given remote execution provider for a specific job
265
-
266
- - As a user these job templates should be audited and versioned
267
-
268
- - As a user I want to be able to define inputs into the template that consist of user input at execution time. I should be able to use these inputs within my template.
269
-
270
- - As a user I want to be able to define an input for a template that uses a particular fact about the host being executed on at execution time.
271
-
272
- - As a user I want to be able to define an input for a template that uses a particular smart variable that is resolved at execution time.
273
-
274
- - As a user I want to be able to define a description of each input in order to help describe the format and meaning of an input.
275
-
276
- - As a user I want to be able to specify default number of tries per job template.
277
-
278
- - As a user I want to be able to specify default retry interval per job template.
279
-
280
- - As a user I want to be able to specify default splay time per job template.
281
-
282
- - As a user I want to setup default timeout per job template.
283
-
284
- - As a user I want to preview a rendered job template for a host (providing needed inputs)
285
-
286
- Scenarios
287
- ---------
288
- **Creating a job template**
289
-
290
- 1. given I'm on new template form
291
- 1. I select from a list of existing job names or fill in a new job name
292
- 1. I select some option to add an input
293
- 1. Give the input a name
294
- 1. Select the type 'user input'
295
- 1. Give the input a description (space separated package list)
296
- 1. I select from a list of known providers (ssh, mco, salt, ansible)
297
- 1. I am shown an example of how to use the input in the template
298
- 1. I am able to see some simple example for the selected provider??
299
- 1. I fill in the template
300
- 1. I select one or more organizations and locations (if enabled)
301
- 1. I click save
302
-
303
-
304
- **Creating a smart variable based input**
305
-
306
- 1. given i am creating or editing a job template
307
- 1. I select to add a new input
308
- 1. Give the input a name
309
- 1. Define a smart variable name
310
-
311
- Design
312
- ------
313
-
314
- {% plantuml %}
315
-
316
- class JobTemplate {
317
- name:string
318
- job_name: string
319
- retry_count: integer
320
- retry_interval: integer
321
- splay: integer
322
- provider_type: string
323
- ==
324
- has_and_belongs_to_many :taxonomies
325
- has_many :inputs
326
- has_many :audits
327
- }
328
-
329
- class ConfigTemplateInput {
330
- name: string
331
- required: bool
332
- input_type: USER_INPUT | FACT | SMART_VARIABLE
333
- fact_name: string
334
- smart_variable_name: string
335
- description: string
336
- ==
337
- has_one :job_template
338
- }
339
-
340
- ConfigTemplate "1" -- "N" ConfigTemplateInput
341
- {% endplantuml %}
342
-
343
-
344
- Job Invocation
345
- ==============
346
-
347
- User Stories
348
- ------------
349
-
350
- - As a user I would like to invoke a job on a single host
351
-
352
- - As a user I would like to invoke a job on a set of hosts, based on
353
- search filter
354
-
355
- - As a user I want to be able to reuse existing bookmarks for job
356
- invocation
357
-
358
- - As a user, when setting a job in future, I want to decide if the
359
- search criteria should be evaluated now or on the execution time
360
-
361
- - As a user I want to reuse the target of previous jobs for next execution
362
-
363
- - As a CLI user I want to be able to invoke a job via hammer CLI
364
-
365
- - As a user, I want to be able to invoke the job on a specific set of hosts
366
- (by using checkboxes in the hosts table)
367
-
368
- - As a user, when planning future job execution, I want to see a
369
- warning with the info about unreachable hosts
370
-
371
- - As a user I want to be able to override default values like (number
372
- of tries, retry interval, splay time, timeout, effective user...) when I plan an execution of command.
373
-
374
- - As a user I expect to see a the description of an input whenever i am being requested to
375
- provide the value for the input.
376
-
377
- - As a user I want to be able to re-invoke the jobs based on
378
- success/failure of previous task
379
-
380
- Scenarios
381
- ---------
382
-
383
- **Fill in target for a job**
384
-
385
- 1. when I'm on job invocation form
386
- 1. then I can specify the target of the job using the scoped search
387
- syntax
388
- 1. the target might influence the list of providers available for the
389
- invocation: although, in delayed execution and dynamic targeting the
390
- current list of providers based on the hosts might not be final and
391
- we should count on that.
392
-
393
- **Fill in template inputs for a job**
394
-
395
- 1. given I'm on job invocation form
396
- 1. when I choose the job to execute
397
- 1. then I'm given a list of providers that I have enabled and has a
398
- template available for the job
399
- 1. and each provider allows to choose which template to use for this
400
- invocation (if more templates for the job and provider are available)
401
- 1. and every template has input fields generated based on the input
402
- defined on the template (such as list of packages for install package
403
- job)
404
-
405
- **See the calculated template inputs for a job**
406
-
407
- 1. given I'm on job invocation form
408
- 1. when I choose the job to execute
409
- 1. and I'm using a template with inputs calculated base on fact data
410
- template available for the job
411
- 1. then the preview of the current value for this input should be displayed
412
- 1. but for the execution the value that the fact has by the time of
413
- execution will be used.
414
-
415
- **Fill in job description for the execution**
416
-
417
- 1. given I'm on job invocation form
418
- 1. there should be a field for task description, that will be used for
419
- listing the jobs
420
- 1. the description value should be pregenerated based on the job name
421
- and specified input (something like "Package install: zsh")
422
-
423
- **Fill in execution properties of the job**
424
-
425
- 1. when I'm on job invocation form
426
- 1. I can override the default values for number of tries, retry
427
- interval, splay time, timeout, effective user...
428
- 1. the overrides are common for all the templates
429
-
430
- **Set the execution time into future** (see [scheduling](design#scheduling)
431
- for more scenarios)
432
-
433
- 1. when I'm on a job invocation form
434
- 1. then I can specify the time to start the execution at (now by
435
- default)
436
- 1. and I can specify if the targeting should be calculated now or
437
- postponed to the execution time
438
-
439
- **Run a job from host detail**
440
-
441
- 1. given I'm on a host details page
442
- 1. when I click "Run job"
443
- 1. then a user dialog opens with job invocation form, with pre-filled
444
- targeting pointing to this particular host
445
-
446
- **Run a job from host index**
447
-
448
- 1. given I'm on a host index page
449
- 1. when I click "Run job"
450
- 1. then a user dialog opens with job invocation form, with prefiled
451
- targeting using the same search that was used in the host index page
452
-
453
- **Invoke a job with single remote execution provider**
454
-
455
- 1. given I have only one provider available in my installation
456
- 1. and I'm on job invocation form
457
- 1. when I choose the job to execute
458
- 1. then only the template for this provider is available to run and
459
- asking for user inputs
460
-
461
- **Invoke a job with hammer**
462
-
463
- 1. given I'm using CLI
464
- 1. then I can run a job with ability to specify:
465
- - targeting with scoped search or bookmark_id
466
- - job name to run
467
- - templates to use for the job
468
- - inputs on per-template basis
469
- - execution properties as overrides for the defaults coming from the template
470
- - ``start_at`` value for execution in future
471
- - in case of the start_at value, if the targeting should be static
472
- vs. dynamic
473
- - whether to wait for the job or exit after invocation (--async
474
- option)
475
-
476
- **Re-invoke a job**
477
-
478
- 1. given I'm in job details page
479
- 1. when I choose re-run
480
- 1. then a user dialog opens with job invocation form, with prefiled
481
- targeting parameters from the previous execution
482
- 1 and I can override all the values (including targeting, job,
483
- templates and inputs)
484
-
485
- **Re-invoke a job for failed hosts**
486
-
487
- 1. given I'm in job details page
488
- 1. when I choose re-run
489
- 1. then a user dialog opens with job invocation form, with prefiled
490
- targeting parameters from the previous execution
491
- 1 and I can override all the values (including targeting, job,
492
- templates and inputs)
493
- 1. I can choose in the targeting to only run on hosts that failed with
494
- the job previously
495
-
496
- **Edit a bookmark referenced by pending job invocation**
497
-
498
- 1. given I have a pending execution task which targeting was created
499
- from a bookmark
500
- 2. when I edit the bookmark
501
- 3. then I should be notified about the existence of the pending tasks
502
- with ability to update the targeting (or cancel and recreate the
503
- invocation)
504
-
505
- **Email notification: opt in**
506
-
507
- 1. given I haven't configured to send email notifications about my executions
508
- 1. then the job invocation should have the 'send email notification'
509
- turned off by default
510
-
511
- **Email notification: opt out**
512
-
513
- 1. given I haven't configured to send email notifications about my executions
514
- 1. then the job invocation should have the 'send email notification'
515
- turned off by default
516
-
517
- Design
518
- ------
519
-
520
- Class diagram of Foreman classes
521
-
522
- {% plantuml %}
523
-
524
- class Bookmark {
525
- name:string
526
- query:string
527
- controller:string
528
- public:bool
529
- owner_id:integer
530
- owner_type:string
531
- }
532
-
533
- class Targeting {
534
- query: string
535
- dynamic: bool
536
- }
537
-
538
- class Host
539
- class User
540
-
541
- class TemplateInvocation {
542
- inputs
543
- }
544
-
545
- class JobInvocation {
546
- tries
547
- retry_interval
548
- splay
549
- concurrency
550
- effective_user
551
- email_notification: bool
552
- }
553
-
554
- class JobTask {
555
- start_at: datetime
556
- }
557
-
558
- Bookmark "1" - "N" Targeting
559
- Targeting "M" - "N" Host : (polymorphic)
560
- Targeting "N" -- "1" User
561
- JobInvocation "1" -- "1" Targeting
562
- JobInvocation "1" -- "N" TemplateInvocation
563
- TemplateInvocation "N" -- "1" JobTemplate
564
- JobInvocation "1" -- "N" JobTask
565
-
566
- {% endplantuml %}
567
-
568
- Query is copied to Targeting, we don't want to propagate any later
569
- changes to Bookmark to already planned job executions.
570
-
571
- We can store link to original bookmark to be able to
572
- compare changes later.
573
-
574
- For JobInvocation we forbid later editing of Targeting.
575
-
576
- Open questions
577
- --------------
578
-
579
- * should we unify the common inputs in all templates to specify them
580
- only once or scoping the input by template?
581
-
582
- * Maybe an inputs catalog (with both defined name and semantic) might
583
- help with keeping the inputs consistent across templates/providers
584
-
585
-
586
- Job Execution
587
- =============
588
-
589
- User Stories
590
- ------------
591
-
592
- - As a user I want to be able to cancel job which hasn't been started yet.
593
-
594
- - As a user I want to be able to cancel job which is in progress
595
- (if supported by specific provider…)
596
-
597
- - As a user I want job execution to fail after timeout limit.
598
-
599
- - As a user I want to job execution to be re-tried
600
- based on the tries and retry interval values given in the invocation
601
-
602
- - As a user I want to job execution on multiple hosts to be spread
603
- using the splay time value: the execution of the jobs will be spread
604
- randomly across the time interval
605
-
606
- - As a user I want to job execution on multiple hosts to be limited
607
- by a concurrency level: the number of concurrently running jobs will
608
- not exceed the limit.
609
-
610
- - As a user I want the job execution to be performed as a user that
611
- was specified on the job invocation
612
-
613
- - As a user I want an ability to retry the job execution when the host
614
- checks in (support of hosts that are offline by the time the
615
- execution).
616
-
617
- Scenarios
618
- ---------
619
-
620
- **Cancel pending bulk task: all at once**
621
-
622
- 1. given I've set a job to run in future on multiple hosts
623
- 1. when I click 'cancel' on the corresponding bulk task
624
- 1. then the whole task should be canceled (including all the sub-tasks
625
- on all the hosts)
626
-
627
- **Cancel pending bulk task: task on specific host**
628
-
629
- 1. given I've set a job to run in future on multiple hosts
630
- 1. when I show the task representation on a host details page
631
- 1. when I click 'cancel' on the task
632
- 1. then I should be offered whether I should cancel just this
633
- instance or the whole bulk task on all hosts
634
-
635
- **Fail after timeout**
636
-
637
- 1. given I've invoked a job
638
- 1. when the job fails to start in given specified timeout
639
- 1. then the job should be marked as failed due to timeout
640
-
641
- **Retried task**
642
-
643
- 1. given I've invoked a job
644
- 1. when the job fails to start at first attemt
645
- 1. then the executor should wait for retry_timeout period
646
- 1. and it should reiterate with the attempt based on the tries number
647
- 1. and I should see the information about the number of retries
648
-
649
- Design
650
- ------
651
-
652
- Class diagram for jobs running on multiple hosts
653
-
654
- {% plantuml %}
655
-
656
-
657
- class Host {
658
- get_provider(type)
659
- }
660
-
661
- class BulkJobTask {
662
- state: $TaskState
663
- start_at: datetime
664
- started_at: datetime
665
- ended_at datetime
666
- cancel()
667
- }
668
-
669
- class JobTask {
670
- retry: integer
671
- retry_interval: integer
672
- timeout: integer
673
- splay: integer
674
- concurrency: integer
675
- type: string
676
- state: $TaskState
677
- start_at: datetime
678
- started_at: datetime
679
- tried_count: integer
680
- ended_at datetime
681
- {abstract} support_cancel?()
682
- {abstract} proxy_endpoint()
683
- cancel()
684
- }
685
-
686
- abstract class ProxyCommand {
687
- }
688
-
689
- class SSHProxyCommand {
690
- {static} support_cancel?()
691
- proxy_endpoint():string
692
- }
693
-
694
- class MCollectiveProxyCommand {
695
- {static} support_cancel?()
696
- proxy_endpoint():string
697
- }
698
-
699
- BulkJobTask "N" - "1" JobInvocation
700
- BulkJobTask "1" -- "N" JobTask
701
- TemplateInvocation "N" - "1" JobInvocation
702
- TemplateInvocation "1" --- "N" JobTask
703
- JobTask "1" -- "1" ProxyCommand
704
- JobTask "N" -- "1" Host
705
-
706
- ProxyCommand <|-- SSHProxyCommand
707
- ProxyCommand <|-- MCollectiveProxyCommand
708
-
709
- {% endplantuml %}
710
-
711
- Class diagram for jobs running a single host
712
-
713
- {% plantuml %}
714
-
715
- class Host {
716
- get_provider(type)
717
- }
718
-
719
- class JobTask {
720
- retry: integer
721
- retry_interval: integer
722
- timeout: integer
723
- splay: integer
724
- concurrency: integer
725
- type: string
726
- state: $TaskState
727
- started_at: datetime
728
- start_at: datetime
729
- tried_count: integer
730
- ended_at datetime
731
- cancel()
732
- {abstract} support_cancel?()
733
- {abstract} proxy_endpoint()
734
- plan()
735
- cancel()
736
- }
737
-
738
- class ProxyCommand {
739
- }
740
-
741
- JobTask "N" - "1" JobInvocation
742
- TemplateInvocation "N" - "1" JobInvocation
743
- TemplateInvocation "1" - "N" JobTask
744
- JobTask "1" -- "1" ProxyCommand
745
- JobTask "1" -- "1" Host
746
- {% endplantuml %}
747
-
748
- Reporting
749
- =========
750
-
751
- User Stories
752
- ------------
753
-
754
- - As a user I would like to monitor the current state of the job
755
- running against a single host, including the output and exit status
756
-
757
- - As a user I would like to monitor the status of bulk job,
758
- including the number of successful, failed and pending tasks
759
-
760
- - As a user I would like to see the history of all job run on a
761
- host
762
-
763
- - As a user I would like to see the history of all tasks that I've
764
- invoked
765
-
766
- - As a user I would like to be able to get an email notification with
767
- execution report
768
-
769
- Scenarios
770
- ---------
771
-
772
- **Track the job running on a set of hosts**
773
-
774
- 1. given I've set a job to run in future on multiple hosts
775
- 1. then I can watch the progress of the job (number of
776
- successful/failed/pending tasks)
777
- 1. and I can get to the list of jobs per host
778
- 1. and I'm able to filter on the host that it was run against and
779
- state
780
-
781
- **Track the job running on a single host**
782
-
783
- 1. given I've set a job to run on a specific host
784
- 1. when I show the task representation page
785
- 1. then I can watch the progress of the job (updated log), status
786
-
787
- **History of jobs run on a host**
788
-
789
- 1. given I'm on host jobs page
790
- 1. when I can see all the jobs run against the host
791
- 1. and I'm able to filter on the host that it was run against and
792
- state, owner etc.
793
-
794
- **History of invoked jobs**
795
-
796
- 1. given I'm on job invocation history page
797
- 1. when I can see all the jobs invoked in the system
798
- 1. scoped by a taxonomy (based on the hosts the jobs were run against)
799
- 1. and I'm able to filter on the host that it was run against and
800
- state, owner etc.
801
-
802
- **Email notification: send after finish**
803
-
804
- 1. given I've invoked a job with email notification turned on
805
- 1. when the job finishes
806
- 1. then I should get the email with report from the job after it finishes
807
-
808
- Design
809
- ------
810
-
811
- Class diagram for jobs running on multiple hosts
812
-
813
- {% plantuml %}
814
-
815
-
816
- class Host {
817
- }
818
-
819
- class JobInvocation {
820
- email_notification: bool
821
- }
822
-
823
- class BulkJobTask {
824
- state: $TaskState
825
- start_at: datetime
826
- started_at: datetime
827
- ended_at datetime
828
- }
829
-
830
- class JobTask {
831
- type: string
832
- state: $TaskState
833
- start_at: datetime
834
- started_at: datetime
835
- ended_at datetime
836
- tried_count: integer
837
- command: string
838
- output: string
839
- exit_code: string
840
- }
841
-
842
- BulkJobTask "N" - "1" JobInvocation
843
- BulkJobTask "1" -- "N" JobTask
844
- JobTask "1" -- "1" Host
845
-
846
- {% endplantuml %}
847
-
848
- Scheduling
849
- ==========
850
-
851
- User Stories
852
- ------------
853
-
854
- - As a user I want to be able go execute a job at future time
855
-
856
- - As a user I want to set the job to reoccur with specified
857
- frequency
858
-
859
- Scenarios
860
- ---------
861
-
862
- **Job set for the future**
863
-
864
- 1. given I've invoked a job at future time
865
- 1. when the time comes
866
- 1. the job gets executed
867
-
868
- **Creating reoccurring job**
869
-
870
- 1. given I'm in job invocation form
871
- 1. when I check 'reoccurring job'
872
- 1. then I can set the frequency and valid until date
873
-
874
- **Showing the tasks with reoccurring logic**
875
-
876
- 1. when I list the jobs
877
- 1. I can see the information about the reoccurring logic at every job
878
- 1. and I can filter the jobs for those with the reoccurring logic
879
-
880
- **Canceling the reoccurring job**
881
-
882
- 1. given I have reoccurring job configured
883
- 1. when I cancel the next instance of the job
884
- 1. then I'm offered to cancel the reoccurring of the job in the future
885
-
886
- Design
887
- ------
888
-
889
- {% plantuml %}
890
-
891
- class Schedule {
892
- start_at: datetime
893
- end_at: datetime
894
- cronline: string
895
- }
896
-
897
- class JobTask {
898
- }
899
-
900
- JobTask "N" -- "1" JobInvocation
901
- JobInvocation "1" -- "1" Schedule
902
-
903
- {% endplantuml %}
904
-
905
- Developer API
906
- =============
907
-
908
- User Stories
909
- ------------
910
-
911
- - As a Foreman developer, I want to be able to use remote execution
912
- plugin to help with other Foreman features such as:
913
- - puppet run
914
- - grubby reprovision
915
- - content actions (package install/update/remove/downgrade, group
916
- install/uninstall, package profile refresh)
917
- - subscription actions (refresh)
918
- - OpenSCAP content update
919
-
920
- Scenarios
921
- ---------
922
-
923
- **Defining a predefined job without provided inputs**
924
-
925
- 1. given I'm a Foreman developer
926
- 1. and I want to expose 'puppet run' feature to the user
927
- 1. then define the 'Puppet Run' as predefined job in the code
928
- 1. and specify the default job name to be used for the mapping
929
-
930
- **Defining a predefined job with provided inputs**
931
-
932
- 1. given I'm a Katello developer
933
- 1. and I want to expose 'package install' feature to the user
934
- 1. then I define the 'Package Install' predefined job with list of
935
- packages as provided input in the code
936
- 1. and I specify default job name to be used for the mapping
937
- 1. and I specify default mapping of the provided inputs to template
938
- inputs
939
-
940
- **Preseeding the predefined jobs**
941
-
942
- 1. given I've defined the 'Package Install' predefined job
943
- 1. when the seed script is run as part of the Foreman installation
944
- 1. the systems tries to create the default mapping from the predefined job to
945
- the existing templates based on the developer-provided defaults
946
-
947
- **Configuring the predefined jobs mapping**
948
-
949
- 1. given I'm the administrator of the Foreman instance
950
- 1. then I can see all the predefined jobs mapping
951
- 1. when I edit existing mapping
952
- 1. then I can choose job name, template and provided input -> template
953
- inputs mapping
954
-
955
- **Configuring the predefined jobs mapping with organizations**
956
-
957
- 1. given I'm the administrator of the Foreman instance
958
- 1. then I can scope the mapping of the predefined job to a specific
959
- organization
960
- 1. and the system doesn't let me to create two mappings for the same
961
- predefined job and provider visible in one organization
962
-
963
- **Using the predefined jobs without provided inputs**
964
-
965
- 1. given I'm a Foreman user
966
- 1. when I'm on host details page
967
- 1. and I press 'Puppet Run'
968
- 1. the job is invoked on the host based on the predefined mapping
969
-
970
- **Using the predefined jobs with provided inputs**
971
-
972
- 1. given I'm a Katello user
973
- 1. when I'm on host applicable errata list
974
- 1. and I select a set of errata to install on the host
975
- 1. and I click 'Install errata'
976
- 1. the job will be invoked to install the packages belonging to this
977
- errata
978
-
979
- **Using the predefined jobs with customization**
980
-
981
- 1. given I'm a Katello user
982
- 1. when I'm on host applicable errata list
983
- 1. and I select a set of errata to install on the host
984
- 1. and I click 'Install errata (customize)'
985
- 1. then the job invocation form will be opened with pre-filled values
986
- based on the mapping
987
- 1. and I can update the values, including setting the start_at time or
988
- reoccurring logic
989
-
990
- Design
991
- ------
992
-
993
- {% plantuml %}
994
-
995
- class PredefinedJob {
996
- predefined_job_name: string
997
- ==
998
- has_and_belongs_to_many :taxonomies
999
- }
1000
-
1001
- class PredefinedJobInputMapping {
1002
- provided_input_name: string
1003
- }
1004
-
1005
- PredefinedJob "1" -- "N" PredefinedJobInputMapping
1006
- PredefinedJobInputMapping "N" -- "1" ConfigTemplateInput
1007
- PredefinedJob "M" -- "N" JobTemplate
1008
- note on link #red: 1:1 per organization and provider
1009
-
1010
- {% endplantuml %}
1011
-
1012
-
1013
- Security
1014
- ========
1015
-
1016
- User Stories
1017
- ------------
1018
-
1019
- - As a user I want to be able to plan job invocation for any host that I
1020
- can view (view_host permission).
1021
-
1022
- - As a user I want to be able to plan a job invocation of job that I can
1023
- view (view_job permission)
1024
-
1025
- - As a user I want to restrict other users which combination of host and job
1026
- name they can execute (execute permission on job_task resource).
1027
-
1028
- - As a user I want to be warned if I planned job invocation on hosts on
1029
- which the execution of this job is not allowed to me.
1030
-
1031
- - As a user I want to see refused job invocations (based on permissions) as
1032
- failed when they are executed.
1033
-
1034
- - As a user I want to set limit filter with execute permission by host attributes
1035
- such as hostgroup, environment, fqdn, id, lifecycle environment (if applicable),
1036
- content view (if applicable).
1037
-
1038
- - As a user I want to specify effective_user for JobInvocation if at least one
1039
- provider supports it.
1040
-
1041
- - As a user I want to restrict other users to execute job under specific user
1042
- as a part of filter condition. If the provider does not allow this, execution
1043
- should be refused.
1044
-
1045
- - As a job template provider I want to be able to specify default effective user
1046
-
1047
- Scenarios
1048
- ---------
1049
-
1050
- **Allow user A to invoke package installation on host B**
1051
-
1052
- 1. given user A can view all hosts and job templates
1053
- 1. when he invoke package installation job on host B
1054
- 1. then his job task fails because he does not have execution
1055
- permission for such job task
1056
-
1057
- **Allow user A to run package installation on host B**
1058
-
1059
- 1. given I've permissions to assign other user permissions
1060
- 1. and user A can view all hosts and job templates
1061
- 1. and user A can create job invocations
1062
- 1. when I grant user A execution permission on resource JobTask
1063
- 1. and I set related filter condition to "host_name = B and job_name = package_install"
1064
- 1. and user A invokes package install execution on hosts B and C
1065
- 1. then the job gets executed successfully on host B
1066
- 1. and job execution will fail on host C
1067
-
1068
- **User can set effective user**
1069
-
1070
- 1. given the provider of job template supports changing effective user
1071
- 1. when user invokes a job
1072
- 1. then he can set effective user under which job is executed on target host
1073
-
1074
- **User can disallow running job as different effective user**
1075
-
1076
- 1. given I've permissions to assign other user permissions
1077
- 1. and user A can view all hosts and job templates
1078
- 1. and user A can create job invocations
1079
- 1. when I grant user A execution permission on resource JobTask
1080
- 1. and I set related filter condition to "effective_user = user_a"
1081
- 1. and user A invokes job execution with effective user set to different user (e.g. root)
1082
- 1. then the job execution fails
1083
-
1084
- New permissions introduced
1085
- --------------------------
1086
-
1087
- - JobInvocation
1088
- - Create
1089
- - View
1090
- - Cancel
1091
- - Edit (Schedule, never can change targetting)
1092
- - JobTask
1093
- - Execute
1094
- - (filter can be: effective_user = 'joe' and host_id = 1 or host_id = 2 and script_name = 'foobar')
1095
-
1096
-
1097
- Design
1098
- ------
1099
-
1100
- {% plantuml %}
1101
-
1102
- class JobTemplate {
1103
- effective_user: string
1104
- }
1105
-
1106
- class JobInvocation {
1107
- effective_user: string
1108
- }
1109
-
1110
- {% endplantuml %}
1111
-
1112
- Katello Client Utilities
1113
- ========================
1114
-
1115
- Design
1116
- ------
1117
-
1118
- katello-agent provides three main functions aside from remote management:
1119
-
1120
- * package profile yum plugin - pushes a new package profile after any yum transaction
1121
- * Split out into its own package (yum-plugin-katello-profile)
1122
- * enabled repository monitoring
1123
- * monitors /etc/yum.repos.d/redhat.repo file for changes and sends newly enabled repos whenever it does change
1124
- * Split out into its own package (katello-errata-profile) with a service to do the same
1125
- * On the capsule, goferd runs to recieve commands to sync repositories, possible solutions:
1126
- * katello-agent can remain (but possibly renamed), with a lot of the existing functionality removed
1127
- * pulp changes to a rest api method for initiating capsule syncs, katello needs to store some auth credentials per capsule
1128
-
1129
- Orchestration
1130
- =============
1131
-
1132
- User Stories
1133
- ------------
1134
-
1135
- - As a user I want to group a number of jobs together and treat them
1136
- as an executable unit. (i.e. run this script to stop the app, install
1137
- these errata, reboot the system)
1138
-
1139
- - As a user I want to run a set of jobs in a rolling fashion.
1140
- (i.e.,patch server 1, reboot it, if it succeeds, proceed to server 2
1141
- & repeat. Otherwise raise exception)
1142
-
1143
- - As a user I want to define a rollback job in case the execution
1144
- fails
1145
-
1146
- - As a sysadmin I would like to orchestrate several actions across a
1147
- collection of machines. (e.g. install a DB on this machine, and pass
1148
- the ip address into an install of a web server on another machine)
1149
-
1150
- Design
1151
- ------
1152
-
1153
- - TBD after the simple support is implemented, possible cooperation with
1154
- multi-host deployments feature
1155
-
1156
- - Some of the features might be solved by advanced remote execution
1157
- technology integration (such as ansible playbook)
1158
-
1159
-
1160
- Design: the whole picture
1161
- ======================
1162
-
1163
- {% plantuml %}
1164
- class Host {
1165
- get_provider(type)
1166
- }
1167
-
1168
- package "Job Preparation" {
1169
- class JobTemplate {
1170
- name: string
1171
- job_name: string
1172
- retry_count: integer
1173
- retry_interval: integer
1174
- splay: integer
1175
- provider_type: string
1176
- effective_user: string
1177
- ==
1178
- has_and_belongs_to_many :taxonomies
1179
- has_many :inputs
1180
- has_many :audits
1181
- }
1182
-
1183
- class ConfigTemplateInput {
1184
- name: string
1185
- required: bool
1186
- input_type: USER_INPUT | FACT | SMART_VARIABLE
1187
- fact_name: string
1188
- smart_variable_name: string
1189
- description: string
1190
- ==
1191
- has_one :job_template
1192
- }
1193
-
1194
- JobTemplate "1" -- "N" ConfigTemplateInput
1195
- }
1196
-
1197
- package "Job Invocation" {
1198
- class Bookmark {
1199
- name:string
1200
- query:string
1201
- controller:string
1202
- public:bool
1203
- owner_id:integer
1204
- owner_type:string
1205
- }
1206
-
1207
- class Targeting {
1208
- query: string
1209
- dynamic: bool
1210
- }
1211
-
1212
- class TemplateInvocation {
1213
- inputs
1214
- }
1215
-
1216
- class JobInvocation {
1217
- tries
1218
- retry_interval
1219
- splay
1220
- concurrency
1221
- email_notification: bool
1222
- effective_user: string
1223
- }
1224
-
1225
- class User
1226
-
1227
- Bookmark "1" -DOWN- "N" Targeting
1228
- Targeting "M" -DOWN- "N" Host
1229
- Targeting "N" -UP- "1" User
1230
- JobInvocation "1" -LEFT- "1" Targeting
1231
- JobInvocation "1" -DOWN- "N" TemplateInvocation
1232
- TemplateInvocation "N" -LEFT- "1" JobTemplate
1233
-
1234
- }
1235
-
1236
- package "Scheduling" {
1237
- class Schedule {
1238
- start_at: datetime
1239
- end_at: datetime
1240
- cronline: string
1241
- }
1242
-
1243
- JobInvocation "1" -UP- "0..1" Schedule
1244
- }
1245
-
1246
- package "Execution" {
1247
- class BulkJobTask {
1248
- state: $TaskState
1249
- start_at: datetime
1250
- started_at: datetime
1251
- ended_at datetime
1252
- cancel()
1253
- }
1254
-
1255
- class JobTask {
1256
- state: $TaskState
1257
- start_at: datetime
1258
- started_at: datetime
1259
- tried_count: integer
1260
- ended_at datetime
1261
- retry: integer
1262
- retry_interval: integer
1263
- timeout: integer
1264
- splay: integer
1265
- concurrency: integer
1266
- provider: string
1267
-
1268
- command: string
1269
- output: string
1270
- exit_code: string
1271
-
1272
- {abstract} support_cancel?()
1273
- {abstract} proxy_endpoint()
1274
- cancel()
1275
- }
1276
-
1277
- abstract class ProxyCommand {
1278
- }
1279
-
1280
- class SSHProxyCommand {
1281
- {static} support_cancel?()
1282
- proxy_endpoint():string
1283
- }
1284
-
1285
- class MCollectiveProxyCommand {
1286
- {static} support_cancel?()
1287
- proxy_endpoint():string
1288
- }
1289
-
1290
- BulkJobTask "N" -LEFT- "1" JobInvocation
1291
- BulkJobTask "1" -- "N" JobTask
1292
- TemplateInvocation "1" -- "N" JobTask
1293
- JobTask "1" -RIGHT- "1" ProxyCommand
1294
- JobTask "N" -UP- "1" Host
1295
- }
1296
-
1297
-
1298
- ProxyCommand <|-- SSHProxyCommand
1299
- ProxyCommand <|-- MCollectiveProxyCommand
1300
-
1301
- package "Developer API" {
1302
- class PredefinedJob {
1303
- predefined_job_name: string
1304
- ==
1305
- has_and_belongs_to_many :taxonomies
1306
- }
1307
-
1308
- class PredefinedJobInputMapping {
1309
- provided_input_name: string
1310
- }
1311
-
1312
- PredefinedJob "1" -- "N" PredefinedJobInputMapping
1313
- PredefinedJobInputMapping "N" -RIGHT- "1" ConfigTemplateInput
1314
- PredefinedJob "M" -RIGHT- "N" JobTemplate
1315
- }
1316
-
1317
- {% endplantuml %}
1318
-
1319
- Wireframes
1320
- ===========
1321
-
1322
- Here are [wireframes PDF](/foreman_remote_execution/design/wireframes.pdf) from 2015-08-14 which we follow where underlaying backends allow us.