From 926d4581ca1bc3a7fcf6af5b052a2cac1a16f2e0 Mon Sep 17 00:00:00 2001 From: Dhamo1107 Date: Thu, 3 Jul 2025 20:50:05 +0530 Subject: [PATCH 1/3] Added changes for container project create & delete functionalities --- .../container_project_controller.rb | 86 +++++++++++++++++++ .../button/new_container_project.rb | 5 ++ .../toolbar/container_projects_center.rb | 27 ++++++ .../container-project-form.schema.js | 56 ++++++++++++ .../container-project-form/index.jsx | 39 +++++++++ .../packs/component-definitions-common.js | 2 + app/views/container_project/new.html.haml | 3 + config/routes.rb | 2 + 8 files changed, 220 insertions(+) create mode 100644 app/helpers/application_helper/button/new_container_project.rb create mode 100644 app/javascript/components/container-project-form/container-project-form.schema.js create mode 100644 app/javascript/components/container-project-form/index.jsx create mode 100644 app/views/container_project/new.html.haml diff --git a/app/controllers/container_project_controller.rb b/app/controllers/container_project_controller.rb index 71e0e3ddf95..975069c244c 100644 --- a/app/controllers/container_project_controller.rb +++ b/app/controllers/container_project_controller.rb @@ -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) @@ -30,6 +88,34 @@ def textual_group_list end helper_method :textual_group_list + def form_params + options = {} + options[:name] = params[:name] if params[:name] + options[:ems_id] = params[:ems_id] if params[:ems_id] + options + end + + 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 => [ diff --git a/app/helpers/application_helper/button/new_container_project.rb b/app/helpers/application_helper/button/new_container_project.rb new file mode 100644 index 00000000000..b60cf5d77ec --- /dev/null +++ b/app/helpers/application_helper/button/new_container_project.rb @@ -0,0 +1,5 @@ +class ApplicationHelper::Button::NewContainerProject < ApplicationHelper::Button::ButtonNewDiscover + def disabled? + super || ManageIQ::Providers::Openshift::ContainerManager.count == 0 + end +end diff --git a/app/helpers/application_helper/toolbar/container_projects_center.rb b/app/helpers/application_helper/toolbar/container_projects_center.rb index 832b7241588..37c9526162c 100644 --- a/app/helpers/application_helper/toolbar/container_projects_center.rb +++ b/app/helpers/application_helper/toolbar/container_projects_center.rb @@ -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, diff --git a/app/javascript/components/container-project-form/container-project-form.schema.js b/app/javascript/components/container-project-form/container-project-form.schema.js new file mode 100644 index 00000000000..953b4214950 --- /dev/null +++ b/app/javascript/components/container-project-form/container-project-form.schema.js @@ -0,0 +1,56 @@ +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, + helperText: __('Must be lowercase alphanumeric with hyphens, start and end with alphanumeric'), + }, + { + 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, + helperText: __('Select the container management provider'), + }, + ], +}); + +export default createSchema; diff --git a/app/javascript/components/container-project-form/index.jsx b/app/javascript/components/container-project-form/index.jsx new file mode 100644 index 00000000000..3905eb34fad --- /dev/null +++ b/app/javascript/components/container-project-form/index.jsx @@ -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 ( + + + + ); +}; + +export default ContainerProjectForm; diff --git a/app/javascript/packs/component-definitions-common.js b/app/javascript/packs/component-definitions-common.js index 164edaa3905..16e7375cc65 100644 --- a/app/javascript/packs/component-definitions-common.js +++ b/app/javascript/packs/component-definitions-common.js @@ -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'; @@ -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); diff --git a/app/views/container_project/new.html.haml b/app/views/container_project/new.html.haml new file mode 100644 index 00000000000..03c3cddf376 --- /dev/null +++ b/app/views/container_project/new.html.haml @@ -0,0 +1,3 @@ += render :partial => "layouts/flash_msg" +.col-md-12 + = react('ContainerProjectForm') diff --git a/config/routes.rb b/config/routes.rb index ce1fd14bf9a..5837911fff4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -854,6 +854,8 @@ show show_list tagging_edit + delete_container_projects + new ], :post => %w[ button From 0b146fe9aee33b288d9639d704c0d29110adca30 Mon Sep 17 00:00:00 2001 From: Dhamo1107 Date: Thu, 3 Jul 2025 20:55:15 +0530 Subject: [PATCH 2/3] Removed unused method --- app/controllers/container_project_controller.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/controllers/container_project_controller.rb b/app/controllers/container_project_controller.rb index 975069c244c..1274eef2439 100644 --- a/app/controllers/container_project_controller.rb +++ b/app/controllers/container_project_controller.rb @@ -88,13 +88,6 @@ def textual_group_list end helper_method :textual_group_list - def form_params - options = {} - options[:name] = params[:name] if params[:name] - options[:ems_id] = params[:ems_id] if params[:ems_id] - options - end - def process_container_projects(container_projects, task) return if container_projects.empty? From 2154b792cd4d23bf414111dacd5e9dc7dc881e37 Mon Sep 17 00:00:00 2001 From: Dhamo1107 Date: Thu, 3 Jul 2025 21:42:37 +0530 Subject: [PATCH 3/3] Removed helper texts in form --- .../container-project-form/container-project-form.schema.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/javascript/components/container-project-form/container-project-form.schema.js b/app/javascript/components/container-project-form/container-project-form.schema.js index 953b4214950..7547af84c9d 100644 --- a/app/javascript/components/container-project-form/container-project-form.schema.js +++ b/app/javascript/components/container-project-form/container-project-form.schema.js @@ -36,7 +36,6 @@ const createSchema = (emsId, setState) => ({ isRequired: true, label: __('Container Project Name'), maxLength: 63, - helperText: __('Must be lowercase alphanumeric with hyphens, start and end with alphanumeric'), }, { component: componentTypes.SELECT, @@ -48,7 +47,6 @@ const createSchema = (emsId, setState) => ({ isRequired: true, includeEmpty: true, loadOptions: loadProviders, - helperText: __('Select the container management provider'), }, ], });