Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ class AnsibleDirectorApiController < ::Api::V2::BaseController
include ::Api::Version2
include ::Foreman::Controller::AutoCompleteSearch

include ::ForemanAnsibleDirector::RequestCtx::RequestContextHelper

around_action :attach_request_ctx

def attach_request_ctx
::ForemanAnsibleDirector::RequestCtx::RequestContext.with_context(
::ForemanAnsibleDirector::RequestCtx::RequestContext.new(request.request_id)
) do
@ctx = ctx
yield
end
end

def find_organization
@organization = Organization.current || find_optional_organization
if @organization.nil?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,73 +34,9 @@ def assignments
target_id: params[:target_id]
)
# TODO: Null check target
@assignments = target.resolved_ansible_content
end

# region ApiDoc: POST /api/v2/ansible_director/assignments
api :POST, '/v2/ansible_director/assignments', N_('Assign Ansible content to a target')
# TRANSLATORS: ApiDoc, do not translate!
description <<~DESC
Assign Ansible content from a source (e.g., a lifecycle environment) to a target.
DESC
param :assignment, Hash, desc: N_('Assignment definition'), required: true do
param :source, Hash, desc: N_('Source (provider of content)'), required: true do
param :type,
%w[ACR],
desc: N_('Type of source. Currently, only Ansible collection roles (ACR) are supported as sources.'),
example: 'ACR',
required: true
param :id,
:number,
desc: N_('ID of the source entity.'),
example: 5,
required: true
end
param :target, Hash, desc: N_('Target (receiver of content)'), required: true do
param :type,
%w[HOST HOSTGROUP],
desc: N_('Type of target. Both hosts (HOST) and hostgroups (HOSTGROUP) are supported as targets.'),
example: 'HOST',
required: true
param :id,
:number,
desc: N_('ID of the target entity.'),
example: 6,
required: true
end
end
# TRANSLATORS: ApiDoc, do not translate!
example <<~EXAMPLE
{
"assignment": {
"source": {
"type": "ACR",
"id": 109
},
"target": {
"type": "HOST",
"id": 1
}
}
}
EXAMPLE
# endregion
def assign
assignment = assignment_params

source = ::ForemanAnsibleDirector::AssignmentService.find_target(
target_type: assignment[:source][:type],
target_id: assignment[:source][:id]
)

target = ::ForemanAnsibleDirector::AssignmentService.find_target(
target_type: assignment[:target][:type],
target_id: assignment[:target][:id]
)

::ForemanAnsibleDirector::AssignmentService.create_assignment(
consumable: source,
assignable: target
@assignments, @resolved_assignments, @hierarchy = ::ForemanAnsibleDirector::AssignmentService.assignments_for(
target: target,
resolve: ::Foreman::Cast.to_bool(params[:resolve])
)
end

Expand Down Expand Up @@ -155,9 +91,15 @@ def assign
}
EXAMPLE
# endregion
def assign_bulk
def assign
target = ::ForemanAnsibleDirector::AssignmentService.find_target(
target_type: params[:target],
target_id: params[:target_id]
)

assignments = bulk_assignment_params
::ForemanAnsibleDirector::AssignmentService.create_bulk_assignments(
target: target,
assignments: assignments
)
end
Expand Down Expand Up @@ -194,10 +136,7 @@ def bulk_assignment_params
return [] if params[:assignments].empty?

params.require(:assignments).map do |assignment|
assignment.permit(
source: %i[type id],
target: %i[type id]
)
assignment.permit(:assignable_type, :assignable_namespace, :assignable_name, :assignable_role_name)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module ForemanAnsibleDirector
module Abstract
module ContentResolutionNode
def cr_immediate_predecessor
raise NotImplementedError
end

def cr_content_assignments
raise NotImplementedError
end

def cr_name
raise NotImplementedError
end

def cr_content_source
raise NotImplementedError
end
end
end
end
15 changes: 15 additions & 0 deletions app/lib/foreman_ansible_director/abstract/content_source.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module ForemanAnsibleDirector
module Abstract
module ContentSource
def cs_content_unit_versions
raise NotImplementedError
end

def cs_name
raise NotImplementedError
end
end
end
end
29 changes: 29 additions & 0 deletions app/lib/foreman_ansible_director/issues/base_issue.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module ForemanAnsibleDirector
module Issues
class BaseIssue
def initialize(**kwargs)
end

def title
raise NotImplementedError
end

def message
raise NotImplementedError
end

def render_for_response
{
title: title,
message: message,
}
end

def render_for_logs
render_for_response
end
end
end
end
9 changes: 9 additions & 0 deletions app/lib/foreman_ansible_director/issues/errors/base_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module ForemanAnsibleDirector
module Issues
module Errors
class BaseError < BaseIssue; end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module ForemanAnsibleDirector
module Issues
module Warnings
class BaseWarning < BaseIssue; end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

module ForemanAnsibleDirector
module Issues
module Warnings
class NoResolutionCandidateForCollectionRole < BaseWarning
def initialize(collection_namespace:,
collection_name:,
collection_role_identifier:,
content_source:,
assignment_id:)
@collection_namespace = collection_namespace
@collection_name = collection_name
@collection_role_identifier = collection_role_identifier
@content_source = content_source
@assignment_id = assignment_id
super
end

def title
fqrn = "#{@collection_namespace}.#{@collection_name}.#{@collection_role_identifier}"
"No resolution candidate for Ansible collection role: \"#{fqrn}\""
end

def message
<<~MESSAGE
No resolution candidate was found for the following Ansible collection role:
Collection namespace: #{@collection_namespace}
Collection name: #{@collection_name}
Role identifier: #{@collection_role_identifier}
Ensure the content source #{@content_source.cs_name} supplies this collection role.
Otherwise, it will be skipped.
MESSAGE
end

def render_for_response
super.merge({ assignment_id: @assignment_id })
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module ForemanAnsibleDirector
module Issues
module Warnings
class NoResolutionCandidateForRole < BaseWarning
def initialize(role_namespace:,
role_name:,
content_source:,
assignment_id:)
@role_namespace = role_namespace
@role_name = role_name
@assignment_id = assignment_id
super
end

def title
fqrn = "#{@role_namespace}.#{@role_name}"
"No resolution candidate for Ansible role: \"#{fqrn}\""
end

def message
<<~MESSAGE
No resolution candidate was found for the following Ansible role:
Role namespace: #{@collection_namespace}
Role name: #{@collection_name}
Ensure the content source #{@content_source.cs_name} supplies this role.
Otherwise, it will be skipped.
MESSAGE
end

def render_for_response
super.merge({ assignment_id: @assignment_id })
end
end
end
end
end
33 changes: 33 additions & 0 deletions app/lib/foreman_ansible_director/logging/loggable_model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module ForemanAnsibleDirector
module Logging
module LoggableModel
extend ActiveSupport::Concern

include ::ForemanAnsibleDirector::RequestCtx::RequestContextHelper

included do
after_create :log_creation
after_update :log_update
after_destroy :log_destroy
end

def loggable?
true
end

def log_creation
ctx&.add_created(self) if loggable?
end

def log_update
ctx&.add_updated(self) if !previous_changes.empty? && loggable?
end

def log_destroy
ctx&.add_deleted(self) if loggable?
end
end
end
end
10 changes: 0 additions & 10 deletions app/models/foreman_ansible_director/ansible_content_assignment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,5 @@
module ForemanAnsibleDirector
class AnsibleContentAssignment < ::ForemanAnsibleDirector::AnsibleDirectorModel
belongs_to :consumable, polymorphic: true
belongs_to :assignable, polymorphic: true

def content_unit_version
case consumable
when ::ForemanAnsibleDirector::AnsibleCollectionRole
consumable.ansible_collection_version # TODO: IMPORTANT! ACV should be referenced as content unit version!
when ::ForemanAnsibleDirector::AnsibleRole
consumable.content_unit_version
end
end
end
end

This file was deleted.

2 changes: 2 additions & 0 deletions app/models/foreman_ansible_director/ansible_director_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class AnsibleDirectorModel < ApplicationRecord
self.abstract_class = true
self.table_name_prefix = 'ad_'

include ::ForemanAnsibleDirector::Logging::LoggableModel

def self.table_name
table_name = model_name.route_key
"#{table_name_prefix}#{table_name}"
Expand Down
18 changes: 18 additions & 0 deletions app/models/foreman_ansible_director/concerns/host_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module ForemanAnsibleDirector
module Concerns
module HostExtensions
extend ActiveSupport::Concern
include ::ForemanAnsibleDirector::Abstract::ContentResolutionNode

included do
include ::ForemanAnsibleDirector::Concerns::ContentConsumer

Expand Down Expand Up @@ -31,6 +33,22 @@ def resolved_ansible_content
end
content.concat additions
end

def cr_immediate_predecessor
hostgroup
end

def cr_name
name
end

def cr_content_assignments
::ForemanAnsibleDirector::AnsibleContentAssignment.where(consumable: self)
end

def cr_content_source
ansible_lifecycle_environment
end
end
end
end
Loading