foreman_hdm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ForemanHdm'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+ begin
37
+ require 'rubocop/rake_task'
38
+ RuboCop::RakeTask.new
39
+ rescue StandardError => _e
40
+ puts 'Rubocop not loaded.'
41
+ end
42
+
43
+ task :default do
44
+ Rake::Task['rubocop'].execute
45
+ end
46
+
47
+ begin
48
+ require 'github_changelog_generator/task'
49
+ rescue LoadError
50
+ # github_changelog_generator is an optional group
51
+ else
52
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
53
+ version = ForemanHdm::VERSION
54
+ config.future_release = "v#{version}" if /^\d+\.\d+.\d+$/.match?(version)
55
+ config.header = "# Changelog\n\nAll notable changes to this project will be documented in this file."
56
+ config.exclude_labels = %w[duplicate question invalid wontfix wont-fix skip-changelog]
57
+ config.user = 'betadots'
58
+ config.project = 'foreman_hdm'
59
+ end
60
+ end
@@ -0,0 +1,23 @@
1
+ module ForemanHdm
2
+ class KeysController < ::ApplicationController
3
+ before_action :find_host
4
+
5
+ def index
6
+ render json: proxy.keys(@host)
7
+ end
8
+
9
+ def show
10
+ render json: proxy.key(@host, params[:id])
11
+ end
12
+
13
+ private
14
+
15
+ def proxy
16
+ ::ProxyAPI::Hdm.new(url: @host.hdm_proxy.url)
17
+ end
18
+
19
+ def find_host
20
+ @host = ::Host.friendly.find(params[:host_id])
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module ProxyAPI
2
+ class Hdm < Resource
3
+ def initialize(args)
4
+ @url = "#{args[:url]}/hdm/"
5
+ super
6
+ end
7
+
8
+ def keys(host)
9
+ get("/nodes/#{host.fqdn}/keys")
10
+ end
11
+
12
+ def key(host, key)
13
+ get("/nodes/#{host.fqdn}/keys/#{key}")
14
+ end
15
+ end
16
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ ForemanHdm::Engine.routes.draw do
2
+ constraints(id: %r{[^/]+}, host_id: %r{[^/]+}) do
3
+ resources :hosts, only: [], controller: '/hosts' do
4
+ resources :keys, only: %i[index show]
5
+ end
6
+ end
7
+ end
8
+
9
+ Foreman::Application.routes.draw do
10
+ mount ForemanHdm::Engine, at: '/foreman_hdm'
11
+ end
@@ -0,0 +1,5 @@
1
+ class AddHdmProxyIdToHosts < ActiveRecord::Migration[6.1]
2
+ def change
3
+ add_column :hosts, :hdm_proxy_id, :integer
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ Feature.where(name: 'Hdm').first_or_create
@@ -0,0 +1,48 @@
1
+ module ForemanHdm
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ForemanHdm
4
+ engine_name 'foreman_hdm'
5
+ # register_gettext
6
+
7
+ config.autoload_paths += Dir["#{config.root}/app/controllers/concerns"]
8
+ config.autoload_paths += Dir["#{config.root}/app/helpers/concerns"]
9
+ config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
10
+ config.autoload_paths += Dir["#{config.root}/app/overrides"]
11
+
12
+ # Add any db migrations
13
+ initializer 'foreman_hdm.load_app_instance_data' do |app|
14
+ ForemanHdm::Engine.paths['db/migrate'].existent.each do |path|
15
+ app.config.paths['db/migrate'] << path
16
+ end
17
+ end
18
+
19
+ initializer 'foreman_hdm.register_plugin', :before => :finisher_hook do |_app|
20
+ Foreman::Plugin.register :foreman_hdm do
21
+ requires_foreman '>= 3.4.0'
22
+
23
+ # Add Global files for extending foreman-core components and routes
24
+ register_global_js_file 'global'
25
+
26
+ # Add permissions
27
+ security_block :foreman_hdm do
28
+ permission :view_foreman_hdm, { :'foreman_hdm/keys' => %i[index show] }
29
+ end
30
+
31
+ # Add a new role called 'Discovery' if it doesn't exist
32
+ role 'ForemanHdm', [:view_foreman_hdm]
33
+
34
+ smart_proxy_for Host, :hdm_proxy,
35
+ feature: 'Hdm',
36
+ label: N_('HDM Proxy'),
37
+ description: N_('Smart proxy to access HDM'),
38
+ api_description: N_('ID of HDM Proxy')
39
+ end
40
+ end
41
+
42
+ rake_tasks do
43
+ Rake::Task['db:seed'].enhance do
44
+ ForemanHdm::Engine.load_seed
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module ForemanHdm
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'foreman_hdm/engine'
2
+
3
+ module ForemanHdm
4
+ end
@@ -0,0 +1,39 @@
1
+ require 'rake/testtask'
2
+
3
+ # Tests
4
+ namespace :test do
5
+ desc 'Test ForemanHdm'
6
+ Rake::TestTask.new(:foreman_hdm) do |t|
7
+ test_dir = File.expand_path('../../test', __dir__)
8
+ t.libs << 'test'
9
+ t.libs << test_dir
10
+ t.pattern = "#{test_dir}/**/*_test.rb"
11
+ t.verbose = true
12
+ t.warning = false
13
+ end
14
+ end
15
+
16
+ namespace :foreman_hdm do
17
+ desc 'Lint with rubocop'
18
+ task rubocop: :environment do
19
+ begin
20
+ require 'rubocop/rake_task'
21
+ RuboCop::RakeTask.new(:rubocop_foreman_hdm) do |task|
22
+ task.patterns = ["#{ForemanHdm::Engine.root}/app/**/*.rb",
23
+ "#{ForemanHdm::Engine.root}/lib/**/*.rb",
24
+ "#{ForemanHdm::Engine.root}/test/**/*.rb"]
25
+ end
26
+ rescue StandardError
27
+ puts 'Rubocop not loaded.'
28
+ end
29
+
30
+ Rake::Task['rubocop_foreman_hdm'].invoke
31
+ end
32
+ end
33
+
34
+ Rake::Task[:test].enhance ['test:foreman_hdm']
35
+
36
+ load 'tasks/jenkins.rake'
37
+ if Rake::Task.task_defined?(:'jenkins:unit')
38
+ Rake::Task['jenkins:unit'].enhance ['test:foreman_hdm', 'foreman_hdm:rubocop']
39
+ end
data/locale/Makefile ADDED
@@ -0,0 +1,60 @@
1
+ #
2
+ # Makefile for PO merging and MO generation. More info in the README.
3
+ #
4
+ # make all-mo (default) - generate MO files
5
+ # make check - check translations using translate-tool
6
+ # make tx-update - download and merge translations from Transifex
7
+ # make clean - clean everything
8
+ #
9
+ DOMAIN = foreman_hdm
10
+ VERSION = $(shell ruby -e 'require "rubygems";spec = Gem::Specification::load(Dir.glob("../*.gemspec")[0]);puts spec.version')
11
+ POTFILE = $(DOMAIN).pot
12
+ MOFILE = $(DOMAIN).mo
13
+ POFILES = $(shell find . -name '$(DOMAIN).po')
14
+ MOFILES = $(patsubst %.po,%.mo,$(POFILES))
15
+ POXFILES = $(patsubst %.po,%.pox,$(POFILES))
16
+ EDITFILES = $(patsubst %.po,%.edit.po,$(POFILES))
17
+
18
+ %.mo: %.po
19
+ mkdir -p $(shell dirname $@)/LC_MESSAGES
20
+ msgfmt -o $(shell dirname $@)/LC_MESSAGES/$(MOFILE) $<
21
+
22
+ # Generate MO files from PO files
23
+ all-mo: $(MOFILES)
24
+
25
+ # Check for malformed strings
26
+ %.pox: %.po
27
+ msgfmt -c $<
28
+ pofilter --nofuzzy -t variables -t blank -t urls -t emails -t long -t newlines \
29
+ -t endwhitespace -t endpunc -t puncspacing -t options -t printf -t validchars --gnome $< > $@
30
+ cat $@
31
+ ! grep -q msgid $@
32
+
33
+ %.edit.po:
34
+ touch $@
35
+
36
+ check: $(POXFILES)
37
+
38
+ # Unify duplicate translations
39
+ uniq-po:
40
+ for f in $(shell find ./ -name "*.po") ; do \
41
+ msguniq $$f -o $$f ; \
42
+ done
43
+
44
+ tx-pull: $(EDITFILES)
45
+ tx pull -f
46
+ for f in $(EDITFILES) ; do \
47
+ sed -i 's/^\("Project-Id-Version: \).*$$/\1$(DOMAIN) $(VERSION)\\n"/' $$f; \
48
+ done
49
+
50
+ tx-update: tx-pull
51
+ @echo
52
+ @echo Run rake plugin:gettext[$(DOMAIN)] from the Foreman installation, then make -C locale mo-files to finish
53
+ @echo
54
+
55
+ mo-files: $(MOFILES)
56
+ git add $(POFILES) $(POTFILE) ../locale/*/LC_MESSAGES
57
+ git commit -m "i18n - pulling from tx"
58
+ @echo
59
+ @echo Changes commited!
60
+ @echo
@@ -0,0 +1,19 @@
1
+ # foreman_hdm
2
+ #
3
+ # This file is distributed under the same license as foreman_hdm.
4
+ #
5
+ #, fuzzy
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: version 0.0.1\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2014-08-20 08:46+0100\n"
11
+ "PO-Revision-Date: 2014-08-20 08:54+0100\n"
12
+ "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
13
+ "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
14
+ "Language: \n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
+
@@ -0,0 +1,19 @@
1
+ # foreman_hdm
2
+ #
3
+ # This file is distributed under the same license as foreman_hdm.
4
+ #
5
+ #, fuzzy
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: version 0.0.1\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2014-08-20 08:46+0100\n"
11
+ "PO-Revision-Date: 2014-08-20 08:46+0100\n"
12
+ "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
13
+ "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
14
+ "Language: \n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
19
+
data/locale/gemspec.rb ADDED
@@ -0,0 +1,2 @@
1
+ # Matches foreman_hdm.gemspec
2
+ _('TODO: Description of ForemanHdm.')
data/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "foreman_hdm",
3
+ "version": "1.0.0",
4
+ "description": "DESCRIPTION",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "lint": "tfm-lint --plugin -d /webpack",
8
+ "test": "tfm-test --config jest.config.js",
9
+ "test:watch": "tfm-test --plugin --watchAll",
10
+ "test:current": "tfm-test --plugin --watch",
11
+ "publish-coverage": "tfm-publish-coverage",
12
+ "stories": "tfm-stories --plugin",
13
+ "stories:build": "tfm-build-stories --plugin",
14
+ "create-react-component": "yo react-domain"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/theforeman/foreman_hdm.git"
19
+ },
20
+ "bugs": {
21
+ "url": "http://projects.theforeman.org/projects/foreman_hdm/issues"
22
+ },
23
+ "peerDependencies": {
24
+ "@theforeman/vendor": ">= 6.0.0"
25
+ },
26
+ "dependencies": {
27
+ "react-intl": "^2.8.0"
28
+ },
29
+ "devDependencies": {
30
+ "@babel/core": "^7.7.0",
31
+ "@sheerun/mutationobserver-shim": "^0.3.3",
32
+ "@theforeman/builder": "^6.0.0",
33
+ "@theforeman/eslint-plugin-foreman": "6.0.0",
34
+ "@theforeman/find-foreman": "^4.8.0",
35
+ "@theforeman/stories": "^7.0.0",
36
+ "@theforeman/test": "^8.0.0",
37
+ "@theforeman/vendor-dev": "^6.0.0",
38
+ "babel-eslint": "^10.0.3",
39
+ "eslint": "^6.7.2",
40
+ "prettier": "^1.19.1",
41
+ "stylelint-config-standard": "^18.0.0",
42
+ "stylelint": "^9.3.0"
43
+ }
44
+ }
@@ -0,0 +1,5 @@
1
+ FactoryBot.define do
2
+ factory :host do
3
+ name 'foreman_hdm'
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ # This calls the main test_helper in Foreman-core
2
+ require 'test_helper'
3
+
4
+ # Add plugin to FactoryBot's paths
5
+ FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
6
+ FactoryBot.reload
@@ -0,0 +1,11 @@
1
+ require 'test_plugin_helper'
2
+
3
+ class ForemanHdmTest < ActiveSupport::TestCase
4
+ setup do
5
+ User.current = User.find_by(login: 'admin')
6
+ end
7
+
8
+ test 'the truth' do
9
+ assert true
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { registerReducer } from 'foremanReact/common/MountingService';
3
+ import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
4
+ import { registerRoutes } from 'foremanReact/routes/RoutingService';
5
+ import Routes from './src/Router/routes'
6
+ import reducers from './src/reducers';
7
+ import HdmTab from './src/Extends/Host/HdmTab';
8
+ import "./src/Extends/index";
9
+
10
+ // register reducers
11
+ Object.entries(reducers).forEach(([key, reducer]) =>
12
+ registerReducer(key, reducer)
13
+ );
14
+
15
+ // register client routes
16
+ registerRoutes('PluginHdm', Routes);
17
+
18
+ // register fills for extending foreman core
19
+ // http://foreman.surge.sh/?path=/docs/introduction-slot-and-fill--page
20
+ // addGlobalFill('<slotId>', '<fillId>', <div key='plugin-template-example' />, 300);
21
+ addGlobalFill(
22
+ "host-details-page-tabs",
23
+ "HDM",
24
+ <HdmTab key="hdm-fill-root" />,
25
+ 490,
26
+ {
27
+ title: __('HDM'),
28
+ hideTab: ({hostDetails}) => (hostDetails.hdm_proxy == null)
29
+ },
30
+ );
@@ -0,0 +1,11 @@
1
+ // runs before each test to make sure console.error output will
2
+ // fail a test (i.e. default PropType missing). Check the error
3
+ // output and traceback for actual error.
4
+ global.console.error = (error, stack) => {
5
+ /* eslint-disable-next-line no-console */
6
+ if (stack) console.log(stack); // Prints out original stack trace
7
+ throw new Error(error);
8
+ };
9
+
10
+ // Increase jest timeout as some tests using multiple http mocks can time out on CI systems.
11
+ jest.setTimeout(10000);
data/webpack/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import componentRegistry from 'foremanReact/components/componentRegistry';
2
+
3
+ // register components for erb mounting
4
+ // componentRegistry.register({
5
+ // name: 'ExtendedEmptyState',
6
+ // type: ExtendedEmptyState,
7
+ // });
@@ -0,0 +1,82 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { STATUS } from 'foremanReact/constants';
4
+ import { Divider, EmptyStateIcon, Title } from '@patternfly/react-core';
5
+ import { ExclamationCircleIcon } from '@patternfly/react-icons';
6
+ import EmptyState from 'foremanReact/components/common/EmptyState/EmptyStatePattern';
7
+ import { global_danger_color_200 as dangerColor } from '@patternfly/react-tokens';
8
+ import Skeleton from 'react-loading-skeleton';
9
+ import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
10
+ import Hierarchy from './Hierarchy';
11
+
12
+ const Hierarchies = ({ hostname, keyName }) => {
13
+ let hierarchies = [];
14
+
15
+ if (keyName) {
16
+ const url = `${window.location.origin.toString()}/foreman_hdm/hosts/${hostname}/keys/${keyName}`;
17
+ const { response, status } = useAPI('get', url);
18
+
19
+ if (status === STATUS.PENDING) {
20
+ return <Skeleton count={5} />;
21
+ }
22
+
23
+ if (status === STATUS.ERROR) {
24
+ const description = __('Could not fetch HDM data.');
25
+ const icon = (
26
+ <EmptyStateIcon icon={ExclamationCircleIcon} color={dangerColor.value} />
27
+ );
28
+ return (
29
+ <EmptyState header={__('Error!')} icon={icon} description={description} />
30
+ );
31
+ }
32
+
33
+ if (Array.isArray(response)) {
34
+ hierarchies = response.map((item) => {
35
+ return (
36
+ <div keyName={item.hierarchy_name}>
37
+ <Hierarchy hierarchy={item} />
38
+ </div>
39
+ );
40
+ });
41
+ }
42
+ }
43
+
44
+ const header = () => {
45
+ if (keyName) {
46
+ const prefix = __("Key: ");
47
+ return (
48
+ <div>
49
+ <Title headingLevel="h2">
50
+ <b>{prefix}</b>
51
+ {keyName}
52
+ </Title>
53
+ <Divider className="pf-u-mb-md" />
54
+ </div>
55
+ );
56
+ } else {
57
+ const message = __("Please select a key from the list on the left.");
58
+ return (
59
+ <EmptyState header={__('No key selected')} description={message} />
60
+ );
61
+ }
62
+ }
63
+
64
+ return (
65
+ <div>
66
+ {header()}
67
+ {hierarchies}
68
+ </div>
69
+ );
70
+ };
71
+
72
+ Hierarchies.propTypes = {
73
+ hostname: PropTypes.string,
74
+ keyName: PropTypes.string
75
+ };
76
+
77
+ Hierarchies.defaultProps = {
78
+ hostname: "",
79
+ keyName: null
80
+ };
81
+
82
+ export default Hierarchies;
@@ -0,0 +1,70 @@
1
+ import React from 'react';
2
+ import { Accordion, AccordionItem, AccordionContent, AccordionToggle, AccordionExpandedContentBody, CodeBlock, CodeBlockCode, Title } from '@patternfly/react-core';
3
+
4
+ const Hierarchy = ({ hierarchy }) => {
5
+ const [expanded, setExpanded] = React.useState([]);
6
+
7
+ const toggle = id => {
8
+ const index = expanded.indexOf(id);
9
+ const newExpanded =
10
+ index >= 0 ? [...expanded.slice(0, index), ...expanded.slice(index + 1, expanded.length)] : [...expanded, id];
11
+ setExpanded(newExpanded);
12
+ };
13
+
14
+ const valueContent = (value) => {
15
+ if (value) {
16
+ return (
17
+ <CodeBlock>
18
+ <CodeBlockCode>{value}</CodeBlockCode>
19
+ </CodeBlock>
20
+ );
21
+ } else {
22
+ const message = __("No value present");
23
+ return (<div>{message}</div>);
24
+ }
25
+ };
26
+
27
+ const formattedPath = (file) => {
28
+ if (file.value) {
29
+ return (
30
+ <span>{file.path}</span>
31
+ );
32
+ } else {
33
+ return (
34
+ <em className="pf-u-disabled-color-100">{file.path}</em>
35
+ );
36
+ }
37
+ };
38
+
39
+ const accordionItems = hierarchy.files.map((file) => {
40
+ const value = valueContent(file.value);
41
+ const id = `${file.path}-toggle`;
42
+ const path = formattedPath(file);
43
+
44
+ return (
45
+ <AccordionItem>
46
+ <AccordionToggle
47
+ onClick={() => toggle(id)}
48
+ isExpanded={expanded.includes(id)}
49
+ id={id}
50
+ >
51
+ {path}
52
+ </AccordionToggle>
53
+ <AccordionContent id={`${id}-content`} isHidden={!expanded.includes(id)} isFixed>
54
+ {value}
55
+ </AccordionContent>
56
+ </AccordionItem>
57
+ );
58
+ });
59
+ return (
60
+ <div className="pf-u-mb-sm">
61
+ <Title headingLevel="h3">{hierarchy.hierarchy_name}</Title>
62
+
63
+ <Accordion isBordered>
64
+ {accordionItems}
65
+ </Accordion>
66
+ </div>
67
+ );
68
+ };
69
+
70
+ export default Hierarchy;