Skip to content
Open
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
79 changes: 79 additions & 0 deletions app/controllers/container_project_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,70 @@ class ContainerProjectController < ApplicationController
include Mixins::ContainersCommonMixin
include Mixins::DashboardViewMixin
include Mixins::BreadcrumbsMixin
include Mixins::GenericListMixin
include Mixins::GenericShowMixin
include Mixins::GenericSessionMixin
include Mixins::GenericButtonMixin
include Mixins::GenericFormMixin

before_action :check_privileges
before_action :get_session_data
after_action :cleanup_action
after_action :set_session_data

def button
case params[:pressed]
when "container_project_new"
javascript_redirect(:action => "new")
when 'container_project_delete'
delete_container_projects
end
end

def new
assert_privileges("container_project_new")
@in_a_form = true
drop_breadcrumb(
:name => _("Add New Container Project"),
:url => "/container_project/new"
)
end

def delete_container_projects
assert_privileges("container_project_delete")
container_projects = find_records_with_rbac(ContainerProject, checked_or_params)
container_projects_to_delete = []

container_projects.each do |container_project|
# Check if container project has any running pods or services
if container_project.container_groups.present? || container_project.container_services.present?
add_flash(_("Container Project \"%{name}\" cannot be removed because it contains running workloads") %
{:name => container_project.name}, :warning)
else
container_projects_to_delete.push(container_project)
end
end

process_container_projects(container_projects_to_delete, "destroy") unless container_projects_to_delete.empty?

# refresh the list if applicable
if @lastaction == "show_list" # list of Container Projects
show_list
render_flash
@refresh_partial = "layouts/gtl"
elsif %w[show show_dashboard].include?(@lastaction)
if flash_errors? # either show the errors and stay on the 'show'
render_flash
else # or (if we deleted what we were showing) we redirect to the listing
flash_to_session
javascript_redirect(previous_breadcrumb_url)
end
else # nested list of Container Projects
flash_to_session
redirect_to(last_screen_url)
end
end

def show_list
@showtype = "main"
process_show_list(:named_scope => :active)
Expand All @@ -30,6 +88,27 @@ def textual_group_list
end
helper_method :textual_group_list

def process_container_projects(container_projects, task)
return if container_projects.empty?

if task == "destroy"
container_projects.each do |container_project|
audit = {
:event => "container_project_record_delete_initiated",
:message => "[#{container_project.name}] Record delete initiated",
:target_id => container_project.id,
:target_class => "ContainerProject",
:userid => session[:userid]
}
AuditEvent.success(audit)
container_project.delete_container_project_queue(session[:userid])
end
add_flash(n_("Delete initiated for %{number} Container Project.",
"Delete initiated for %{number} Container Projects.",
container_projects.length) % {:number => container_projects.length})
end
end

def breadcrumbs_options
{
:breadcrumbs => [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ApplicationHelper::Button::NewContainerProject < ApplicationHelper::Button::ButtonNewDiscover
def disabled?
super || ManageIQ::Providers::Openshift::ContainerManager.count == 0
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
class ApplicationHelper::Toolbar::ContainerProjectsCenter < ApplicationHelper::Toolbar::Basic
button_group('container_project_vmdb', [
select(
:container_project_vmdb_choice,
nil,
t = N_('Configuration'),
t,
:items => [
button(
:container_project_new,
'pficon pficon-add-circle-o fa-lg',
t = N_('Add New Container Project'),
t,
:klass => ApplicationHelper::Button::NewContainerProject),
button(
:container_project_delete,
'pficon pficon-delete fa-lg',
N_('Delete selected Container Projects'),
N_('Delete Container Projects'),
:url_parms => "main_div",
:send_checked => true,
:confirm => N_("Warning: The selected Container Projects will be permanently deleted!"),
:enabled => false,
:onwhen => "1+"),
]
),
])

button_group('container_project_policy', [
select(
:container_project_policy_choice,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { componentTypes, validatorTypes } from '@@ddf';

// Load container managers that support container projects
const providerUrl = '/api/providers?expand=resources&attributes=id,name,type&filter[]=type=ManageIQ::Providers::Openshift::ContainerManager';
const loadProviders = () => API.get(providerUrl).then(({ resources }) =>
resources.map(({ id, name }) => ({ label: name, value: id }))
).catch((error) => {
console.error('Error loading providers:', error);
return [];
});

// Validation for container project name (Kubernetes naming rules)
const validateProjectName = {
type: validatorTypes.PATTERN,
pattern: /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/,
message: __('Name must consist of lower case alphanumeric characters or \'-\', start with an alphanumeric character, and end with an alphanumeric character'),
};

const validateProjectNameLength = {
type: validatorTypes.MAX_LENGTH,
threshold: 63,
message: __('Name must be no more than 63 characters'),
};

const createSchema = (emsId, setState) => ({
fields: [
{
component: componentTypes.TEXT_FIELD,
id: 'name',
name: 'name',
validate: [
{ type: validatorTypes.REQUIRED },
validateProjectName,
validateProjectNameLength,
],
isRequired: true,
label: __('Container Project Name'),
maxLength: 63,
},
{
component: componentTypes.SELECT,
id: 'ems_id',
name: 'ems_id',
label: __('Container Provider'),
validate: [{ type: validatorTypes.REQUIRED }],
onChange: (emsId) => setState((state) => ({ ...state, emsId })),
isRequired: true,
includeEmpty: true,
loadOptions: loadProviders,
},
],
});

export default createSchema;
39 changes: 39 additions & 0 deletions app/javascript/components/container-project-form/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState } from 'react';
import { Grid } from 'carbon-components-react';
import MiqFormRenderer from '@@ddf';
import miqRedirectBack from '../../helpers/miq-redirect-back';
import createSchema from './container-project-form.schema';

const ContainerProjectForm = () => {
const [{ emsId }, setState] = useState({ emsId: null });

const onSubmit = (values) => {
miqSparkleOn();
const request = API.post('/api/container_projects', values);
request.then(() => {
const message = sprintf(__('Add of Container Project "%s" has been successfully queued.'), values.name);
miqRedirectBack(message, 'success', '/container_project/show_list');
}).catch(miqSparkleOff);
};

const onCancel = () => {
const message = __('Creation of new Container Project was canceled by the user.');
miqRedirectBack(message, 'warning', '/container_project/show_list');
};

return (
<Grid>
<MiqFormRenderer
initialValues={{}}
schema={createSchema(emsId, setState)}
onSubmit={onSubmit}
onCancel={onCancel}
buttonsLabels={{
submitLabel: __('Add'),
}}
/>
</Grid>
);
};

export default ContainerProjectForm;
2 changes: 2 additions & 0 deletions app/javascript/packs/component-definitions-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import CloudVolumeActions from '../components/cloud-volume-actions-form';
import CloudVolumeBackupForm from '../components/cloud-volume-backup-form';
import CloudVolumeForm from '../components/cloud-volume-form';
import ContainerDashboardCards from '../components/ems_container_dashboard';
import ContainerProjectForm from '../components/container-project-form';
import ContainerProjects from '../components/container-projects';
import CopyCatalogForm from '../components/copy-catalog-form/copy-catalog-form';
import CopyDashboardForm from '../components/copy-dashboard-form/copy-dashboard-form';
Expand Down Expand Up @@ -216,6 +217,7 @@ ManageIQ.component.addReact('CloudVolumeActions', CloudVolumeActions);
ManageIQ.component.addReact('CloudVolumeBackupForm', CloudVolumeBackupForm);
ManageIQ.component.addReact('CloudVolumeForm', CloudVolumeForm);
ManageIQ.component.addReact('ContainerDashboardCards', ContainerDashboardCards);
ManageIQ.component.addReact('ContainerProjectForm', ContainerProjectForm);
ManageIQ.component.addReact('ContainerProjects', ContainerProjects);
ManageIQ.component.addReact('CopyCatalogForm', CopyCatalogForm);
ManageIQ.component.addReact('CopyDashboardForm', CopyDashboardForm);
Expand Down
3 changes: 3 additions & 0 deletions app/views/container_project/new.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= render :partial => "layouts/flash_msg"
.col-md-12
= react('ContainerProjectForm')
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,8 @@
show
show_list
tagging_edit
delete_container_projects
new
],
:post => %w[
button
Expand Down
Loading