Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #34121 - Add/Delete a single Ansible role to Host(group) #10

Merged
merged 5 commits into from May 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/hammer_cli_foreman_ansible.rb
Expand Up @@ -8,6 +8,7 @@ module HammerCLIForemanAnsible
require 'hammer_cli_foreman_ansible/i18n'
require 'hammer_cli_foreman_ansible/ansible'
require 'hammer_cli_foreman_ansible/ansible_roles'
require 'hammer_cli_foreman_ansible/associated_ansible_role'
require 'hammer_cli_foreman_ansible/host'
require 'hammer_cli_foreman_ansible/hostgroup'

Expand Down
85 changes: 85 additions & 0 deletions lib/hammer_cli_foreman_ansible/associated_ansible_role.rb
@@ -0,0 +1,85 @@
module HammerCLIForemanAnsible
module AssociatedAnsibleRole
# This method fetches all associated resource (ansible roles) ids
# to send them back via :update endpoint of the main resource (host/hostgroup)
# We use this array of ids to add or remove direct associations
# This method is used to 'reset' associated ids via HammerCLIForeman::[Add|Remove]AssociatedCommand
def get_current_ids
roles = HammerCLIForeman.record_to_common_format(resource.call(association_name(true), { id: get_identifier }))
return ids_to_keep_for_removal(roles) if self.class.command_names.include?('remove')

ids_to_keep_for_addition(roles)
end

# Since there are no option_sources available to deal with such cases (AssociatedCommand)
# Make sure we get id of the provided role before we try to get already associated ones
def get_new_ids
associated_identifiers = get_associated_identifiers
@associated_identifier = get_associated_identifier

ids = get_current_ids.map(&:to_s)

required_ids = associated_identifiers.nil? ? [] : associated_identifiers.map(&:to_s)
required_ids << @associated_identifier.to_s unless @associated_identifier.nil?

ids = if self.class.command_names.include?('remove')
ids.delete_if { |id| required_ids.include? id }
else
ids + required_ids
end
ids.uniq
end

private

# Treat inherited as we do in UI:
# - Don't allow to directly associate them (the role is not available to select)
# - If the same role was assigned AND inherited then treat it as it was just assigned (keep it)
# - If the role is indirectly associated only then don't associate it directly unless --force provided
def ids_to_keep_for_addition(roles)
roles.map do |role|
# Keep directly associated roles
next role['id'] if role['directly_assigned']

# Host groups can have not inherited and not directly assigned roles in the response.
# This means those roles are from hosts, skip them.
# Hosts cannot have such case.

# Pre-check to force stop the command if we're trying to add an already inherited role
# (in case we don't have it directly associated as well)
if @associated_identifier == role['id'] && role['inherited']
next role['id'] if option_force?

msg = _(
'Ansible role %{name} is already inherited from a host group. Please use %{option} for direct association.'
) % { name: role['name'], option: '--force' }
raise ArgumentError, msg
end

# We skip not directly assigned and inherited
# Also skip not inherited for host groups
nil
end.compact
end

# Treat inherited as we do in UI:
# - Don't allow to remove them (the role is not available to select)
# - If the same role was assigned AND inherited then treat it as it was just assigned (keep or remove it)
# - If the role was inherited only then don't remove it
def ids_to_keep_for_removal(roles)
roles.map do |role|
# Keep or remove later directly associated roles
next role['id'] if role['directly_assigned']

# Pre-check to force stop the command if we're trying to remove not directly assigned role
if role['id'] == @associated_identifier
raise ArgumentError, _('Ansible role %s is not assigned directly and cannot be removed.') % role['name']
end

# We skip not directly assigned and inherited
# Also skip not inherited for host groups
nil
end.compact
end
end
end
44 changes: 43 additions & 1 deletion lib/hammer_cli_foreman_ansible/host.rb
Expand Up @@ -11,7 +11,10 @@ class AnsibleRolesCommand < HammerCLIForeman::Command
class ListCommand < HammerCLIForeman::ListCommand
action :ansible_roles

output HammerCLIForemanAnsible::AnsibleRolesCommand::ListCommand.output_definition
output(HammerCLIForemanAnsible::AnsibleRolesCommand::ListCommand.output_definition) do
field :inherited, _('Inherited'), Fields::Boolean
field :directly_assigned, _('Directly assigned'), Fields::Boolean
end

build_options
end
Expand All @@ -35,6 +38,45 @@ class AssignRolesCommand < HammerCLIForeman::Command

build_options
end

class AddAnsibleRoleCommand < HammerCLIForeman::AddAssociatedCommand
prepend HammerCLIForemanAnsible::AssociatedAnsibleRole

command_name 'add'
associated_resource :ansible_roles
desc _('Associate an Ansible role')

option '--force', :flag, _('Associate the Ansible role even if it already is associated indirectly')

success_message _('Ansible role has been associated.')
failure_message _('Could not associate the Ansible role')

validate_options do
any(:option_name, :option_id).required
any(:option_ansible_role_name, :option_ansible_role_id).required
end

build_options
end

class RemoveAnsibleRoleCommand < HammerCLIForeman::RemoveAssociatedCommand
prepend HammerCLIForemanAnsible::AssociatedAnsibleRole

command_name 'remove'
associated_resource :ansible_roles
desc _('Disassociate an Ansible role')

success_message _('Ansible role has been disassociated.')
failure_message _('Could not disassociate the Ansible role')

validate_options do
any(:option_name, :option_id).required
any(:option_ansible_role_name, :option_ansible_role_id).required
end

build_options
end

autoload_subcommands
end
end
Expand Down
43 changes: 42 additions & 1 deletion lib/hammer_cli_foreman_ansible/hostgroup.rb
Expand Up @@ -11,7 +11,10 @@ class AnsibleRolesCommand < HammerCLIForeman::Command
class ListCommand < HammerCLIForeman::ListCommand
action :ansible_roles

output HammerCLIForemanAnsible::AnsibleRolesCommand::ListCommand.output_definition
output(HammerCLIForemanAnsible::AnsibleRolesCommand::ListCommand.output_definition) do
field :inherited, _('Inherited'), Fields::Boolean
field :directly_assigned, _('Directly assigned'), Fields::Boolean
end

build_options
end
Expand All @@ -36,6 +39,44 @@ class AssignRolesCommand < HammerCLIForeman::Command
build_options
end

class AddAnsibleRoleCommand < HammerCLIForeman::AddAssociatedCommand
prepend HammerCLIForemanAnsible::AssociatedAnsibleRole

command_name 'add'
associated_resource :ansible_roles
desc _('Associate an Ansible role')

option '--force', :flag, _('Associate the Ansible role even if it already is associated indirectly')

success_message _('Ansible role has been associated.')
failure_message _('Could not associate the Ansible role')

validate_options do
any(:option_name, :option_title, :option_id).required
any(:option_ansible_role_name, :option_ansible_role_id).required
end

build_options
end

class RemoveAnsibleRoleCommand < HammerCLIForeman::RemoveAssociatedCommand
prepend HammerCLIForemanAnsible::AssociatedAnsibleRole

command_name 'remove'
associated_resource :ansible_roles
desc _('Disassociate an Ansible role')

success_message _('Ansible role has been disassociated.')
failure_message _('Could not disassociate the Ansible role')

validate_options do
any(:option_name, :option_title, :option_id).required
any(:option_ansible_role_name, :option_ansible_role_id).required
end

build_options
end

autoload_subcommands
end
end
Expand Down