From 72d72d9d489c79ac1f4924bb57b1d5722dd77eb1 Mon Sep 17 00:00:00 2001 From: Dan Macpherson Date: Fri, 4 Apr 2025 17:16:31 +1000 Subject: [PATCH] Adding variant functionality to patterns browser --- .../medical-diagnosis-amx/_index.adoc | 7 +- .../multicloud-gitops-Portworx/_index.md | 1 + .../multicloud-gitops-amx-rhoai/_index.adoc | 9 +- .../multicloud-gitops-amx/_index.adoc | 7 +- .../multicloud-gitops-qat/_index.adoc | 9 +- .../_index.adoc | 12 +- .../multicloud-gitops-sgx/_index.adoc | 8 +- layouts/partials/menu-patterns-browser.html | 9 +- layouts/partials/patterns-browser.html | 2 + static/js/patterns-browser-v2.js | 189 +++++++++++++----- 10 files changed, 179 insertions(+), 74 deletions(-) diff --git a/content/patterns/medical-diagnosis-amx/_index.adoc b/content/patterns/medical-diagnosis-amx/_index.adoc index 03cfbef90..ba5590885 100644 --- a/content/patterns/medical-diagnosis-amx/_index.adoc +++ b/content/patterns/medical-diagnosis-amx/_index.adoc @@ -3,15 +3,16 @@ title: Intel AMX accelerated Medical Diagnosis date: 2023-10-10 validated: false summary: This pattern is based on a demo implementation of an automated data pipeline for chest X-ray analysis previously developed by Red Hat. The pattern is modified to utilize Intel AMX feature. -products: +rh_products: - Red Hat OpenShift Container Platform - Red Hat OpenShift Serverless - Red Hat OpenShift Data Foundation -- Red Hat OpenShift Node Feature Discovery -- 4th Gen Intel Xeon Scalable processors with Intel Advanced Matrix Extensions (Intel AMX) +partners: +- Intel industries: - medical aliases: /medical-diagnosis-amx/ +variant_of: medical-diagnosis pattern_logo: medical-diagnosis.png links: install: getting-started diff --git a/content/patterns/multicloud-gitops-Portworx/_index.md b/content/patterns/multicloud-gitops-Portworx/_index.md index ad415462e..5b1397fae 100644 --- a/content/patterns/multicloud-gitops-Portworx/_index.md +++ b/content/patterns/multicloud-gitops-Portworx/_index.md @@ -11,6 +11,7 @@ partners: industries: - General aliases: /multicloud-gitops-Portworx/ +variant_of: multicloud-gitops pattern_logo: multicloud-gitops-Portworx.png links: install: getting-started diff --git a/content/patterns/multicloud-gitops-amx-rhoai/_index.adoc b/content/patterns/multicloud-gitops-amx-rhoai/_index.adoc index 5f6403371..f551bac49 100644 --- a/content/patterns/multicloud-gitops-amx-rhoai/_index.adoc +++ b/content/patterns/multicloud-gitops-amx-rhoai/_index.adoc @@ -3,15 +3,16 @@ title: Intel AMX accelerated Multicloud GitOps with Openshift AI date: 2024-02-27 validated: false summary: This is extension of Multicloud GitOps pattern with Red Hat Openshift AI component to show the value of using Intel AMX. -products: +rh_products: - Red Hat OpenShift Container Platform - Red Hat Advanced Cluster Management - Red Hat Openshift AI -- OpenVINO Toolkit Operator -- 5th Gen Intel Xeon Scalable processors with Intel Advanced Matrix Extensions (Intel AMX) +partners: +- Intel industries: - General aliases: /multicloud-gitops-amx-rhoai/ +variant_of: multicloud-gitops # uncomment once this exists # pattern_logo: multicloud-gitops.png pattern_logo: amx-intel-ai.png @@ -105,7 +106,7 @@ This solution also uses a variety of _observability tools_ including the Prometh // RHODS pattern description The basic {mcg-pattern} has been extended to highlight the *{intel-5th-gen-xeon-processors}* capabilities, offering developers a streamlined pathway to accelerate their workloads through the integration of cutting-edge *{intel-amx}*, providing efficiency and performance optimization in AI workloads. -The basic pattern has been extended with two components: Openshift AI and OpenVINO Toolkit Operator. +The basic pattern has been extended with two components: Openshift AI and OpenVINO Toolkit Operator. * Openshift AI, serves as a robust AI/ML platform for the creation of AI-driven applications and provides a collaborative environment for data scientists and developers that helps to move easily from experiment to production. It offers Jupyter application with selection of notebook servers, equipped with pre-configured environments and necessary support and optimizations (such as CUDA, PyTorch, Tensorflow, HabanaAI, etc.). diff --git a/content/patterns/multicloud-gitops-amx/_index.adoc b/content/patterns/multicloud-gitops-amx/_index.adoc index e101e9783..b005e17c3 100644 --- a/content/patterns/multicloud-gitops-amx/_index.adoc +++ b/content/patterns/multicloud-gitops-amx/_index.adoc @@ -3,14 +3,15 @@ title: Intel AMX accelerated Multicloud GitOps date: 2023-10-05 validated: false summary: This is extension of Multicloud GitOps pattern with additional application to show the value of using Intel AMX. -products: +rh_products: - Red Hat OpenShift Container Platform - Red Hat Advanced Cluster Management -- Red Hat Node Feature Discovery -- 4th Gen Intel Xeon Scalable processors with Intel Advanced Matrix Extensions (Intel AMX) +partners: +- Intel industries: - General aliases: /multicloud-gitops-amx/ +variant_of: multicloud-gitops # uncomment once this exists # pattern_logo: multicloud-gitops.png pattern_logo: amx-intel-ai.png diff --git a/content/patterns/multicloud-gitops-qat/_index.adoc b/content/patterns/multicloud-gitops-qat/_index.adoc index 99c270b92..e9c725fb4 100644 --- a/content/patterns/multicloud-gitops-qat/_index.adoc +++ b/content/patterns/multicloud-gitops-qat/_index.adoc @@ -3,16 +3,15 @@ title: Intel QAT accelerated Multicloud GitOps date: 2023-10-05 validated: false summary: This is extension of Multicloud GitOps pattern with additional application to show the value of using Intel QAT. -products: +rh_products: - Red Hat OpenShift Container Platform - Red Hat Advanced Cluster Management -- Red Hat Node Feature Discovery -- 4th Gen Intel Xeon Scalable processors with Intel QuickAssist Technology (Intel QAT) -- Sail Operator (Istio Service Mesh) -- Intel Device Plugin Operator +partners: +- Intel industries: - General aliases: /multicloud-gitops-qat/ +variant_of: multicloud-gitops # uncomment once this exists # pattern_logo: multicloud-gitops.png pattern_logo: intel-qat.png diff --git a/content/patterns/multicloud-gitops-sgx-hello-world/_index.adoc b/content/patterns/multicloud-gitops-sgx-hello-world/_index.adoc index d8efa5e86..355477376 100644 --- a/content/patterns/multicloud-gitops-sgx-hello-world/_index.adoc +++ b/content/patterns/multicloud-gitops-sgx-hello-world/_index.adoc @@ -3,15 +3,15 @@ title: Intel SGX protected application in Multicloud GitOps date: 2024-02-20 validated: false summary: This is an extension of Multicloud GitOps pattern with additional application using Intel SGX. -products: +rh_products: - Red Hat OpenShift Container Platform - Red Hat Advanced Cluster Management -- Red Hat Node Feature Discovery -- Intel Device Plugins Operator -- 5th Gen Intel Xeon Scalable processors with Intel Software Guard Extensions (Intel SGX) +partners: +- Intel industries: - General aliases: /multicloud-gitops-sgx-hello-world/ +variant_of: multicloud-gitops # uncomment once this exists # pattern_logo: multicloud-gitops.png pattern_logo: sgx-intel-ai.png @@ -45,7 +45,7 @@ Based on the requirements of a specific implementation, certain details might di Background:: Organizations are aiming to develop, deploy, and operate applications on an open hybrid cloud in a stable, simple, and secure way. This hybrid strategy includes multi-cloud deployments where workloads might be running on multiple clusters and on multiple clouds, private or public. This strategy requires an infrastructure-as-code approach: GitOps. GitOps uses Git repositories as a single source of truth to deliver infrastructure-as-code. Submitted code checks the continuous integration (CI) process, while the continuous delivery (CD) process checks and applies requirements for things like security, infrastructure-as-code, or any other boundaries set for the application framework. All changes to code are tracked, making updates easy while also providing version control should a rollback be needed. -Moreover, organizations are looking for solutions that are secure for AI, ML, data processing etc. It is the case especially for cloud computing, which uses heavily multi-tenancy and multiple processes runs on single bare-metal machine and they do not know who might be their neighbors and what are their intentions. +Moreover, organizations are looking for solutions that are secure for AI, ML, data processing etc. It is the case especially for cloud computing, which uses heavily multi-tenancy and multiple processes runs on single bare-metal machine and they do not know who might be their neighbors and what are their intentions. Memory encryption technologies can protect well the data and separate application from other ones run on the same machine - it is possible using *{intel-5th-gen-xeon-processors}* with *Intel Software Guard Extensions*. [id="about-solution"] @@ -190,7 +190,7 @@ NFD manages the detection of hardware features and configuration in an OpenShift In the logs of `hello-world-sgx` pod, there is an information that "Gramine is starting" and it executes the application by printing "HelloWorld!". -This pattern demonstrates basic capabilities of running docker applications inside SGX enclaves. Based on this pattern other applications can be secured with {intel-sgx} and Gramine Shielded Containers in a similar way as presented here. +This pattern demonstrates basic capabilities of running docker applications inside SGX enclaves. Based on this pattern other applications can be secured with {intel-sgx} and Gramine Shielded Containers in a similar way as presented here. //figure 7 originally .Logs from `hello-world-sgx` pod diff --git a/content/patterns/multicloud-gitops-sgx/_index.adoc b/content/patterns/multicloud-gitops-sgx/_index.adoc index feb869706..ab9936482 100644 --- a/content/patterns/multicloud-gitops-sgx/_index.adoc +++ b/content/patterns/multicloud-gitops-sgx/_index.adoc @@ -3,15 +3,15 @@ title: Intel SGX protected Vault for Multicloud GitOps date: 2024-02-09 validated: false summary: This is an extension of the Multicloud GitOps pattern with an additional application to show the value of using Intel SGX. -products: +rh_products: - Red Hat OpenShift Container Platform - Red Hat Advanced Cluster Management -- Red Hat Node Feature Discovery -- Intel Device Plugins -- 5th Gen Intel Xeon Scalable processors with Intel Security Guard Extensions (Intel SGX) +partners: +- Intel industries: - General aliases: /multicloud-gitops-sgx/ +variant_of: multicloud-gitops # uncomment once this exists # pattern_logo: multicloud-gitops.png pattern_logo: sgx-intel-ai.png diff --git a/layouts/partials/menu-patterns-browser.html b/layouts/partials/menu-patterns-browser.html index 28bf647b1..d6c848bff 100644 --- a/layouts/partials/menu-patterns-browser.html +++ b/layouts/partials/menu-patterns-browser.html @@ -7,13 +7,14 @@

@@ -32,7 +33,7 @@

+
+
diff --git a/static/js/patterns-browser-v2.js b/static/js/patterns-browser-v2.js index 74c75a45f..f46ba01ee 100644 --- a/static/js/patterns-browser-v2.js +++ b/static/js/patterns-browser-v2.js @@ -42,7 +42,7 @@ class Filter { } class FilteredPatterns { - // Class for filtered the patterns based on a Filter object. Also sorts the + // Class for filtering the patterns based on a Filter object. Also sorts the // patterns based on sort_value. constructor(patterns_filter, patterns, sort_value) { @@ -74,6 +74,20 @@ class FilteredPatterns { }; if (patternPassed == true) { filteredPatterns.push(this.patterns[item]); }; }; + + // This is a special filter for the variant parent. If variant_of is set, + // then filter by patterns that are variants of the parent. + var variant_of = getVariantParent(); + if (variant_of != null) { + var variantFilter = []; + for (item = 0; item < filteredPatterns.length; item++) { + if (variant_of == filteredPatterns[item].Params.variant_of) { + variantFilter.push(filteredPatterns[item]); + } + } + filteredPatterns = variantFilter + } + this.sort_filtered_patterns(filteredPatterns) return filteredPatterns } @@ -126,6 +140,61 @@ function getParams() { return enabledParams; } +function getVariantParent() { + // Gets the ID of the currently chosen variant parent. If the parent variant + // hasn't been set, return null. + const url = window.location.search; + const urlParams = new URLSearchParams(url); + if (urlParams.has("variant_of")) { + return urlParams.get("variant_of"); + } else { + return null; + } +} + +function containsVariants(current_pattern, patterns) { + // Check an array of patterns (patterns) to see if a pattern (current_pattern) + // is a parent. Returns the number of variants for the parent. + var pattern_id = current_pattern.Link.split("/").filter(n => n)[1]; + var variant_count = 0; + patterns.forEach(function (item, index) { + if (pattern_id == item.Params.variant_of) { + variant_count += 1; + } + }); + return variant_count; +} + +function outputVariantCount(variant_count, pattern_id) { + // Displays the number of variants on the parent pattern's card in the + // bottom corner. + var url = new URL(window.location.href); + url.searchParams.set('variant_of', pattern_id) + var result = ""; + if (variant_count == 1) { + result = (``); + } else if (variant_count > 1) { + result = (``); + } + return result; +} + +function renderVariantStatus(patterns) { + // Render a notice on top of the patterns browser to indicate which variant + // parent has been selected. Also provide a link back to the standard patterns + // browser withou tthe variant set. + var url = new URL(window.location.href); + url.searchParams.delete('variant_of') + const parent_variant = getVariantParent(); + if (parent_variant) { + const parent_pattern = patterns.find((pattern) => pattern.Link.split("/").filter(n => n)[1] == parent_variant); + const status = document.getElementById("variant-status"); + status.innerHTML = (` +

Showing variant patterns for ${parent_pattern.Name}.

+

Click here to browse all patterns

`); + } +} + function cleanString(string) { // Provide a string that can be used as a HTML id i.e. no spaces. return string.replace(/ /g, "-") @@ -198,13 +267,13 @@ function checkCategoryObject(patternTerms, filterTerms, filter_type) { function renderSpinner() { // HTML for the loading spinner - return '
' + - '
' + - '' + - '' + - '' + - '
' + - '
'; + return (`div class="pf-l-bullseye"> +
+ + + +
+
`); } function renderFilterItem(type, name, linkTitle, enabledItem) { @@ -214,10 +283,11 @@ function renderFilterItem(type, name, linkTitle, enabledItem) { } else { checkedProp = "" } - var filterItemHtml = ''; + var filterItemHtml = (``); return filterItemHtml; } @@ -229,13 +299,13 @@ function renderFilterButtons(filterButtonTypes, name, enabledFilters) { for (item = 0; item < filterButtonTypes.length; item++) { selected = ""; if (enabledFilters == filterButtonTypes[item].toLowerCase()) { - selected = " pf-m-selected"; + selected = "pf-m-selected"; }; - filterButtonsHtml += '
' + - '' + - '
' + filterButtonsHtml += (`
+ +
`) } filterButtonsHtml += '

' return filterButtonsHtml; @@ -264,35 +334,34 @@ function renderLabel(tier, tier_categories) { // HTML to render the pattern tier label if (tier != undefined) { var color= tier_categories.filter_list.find(item => item.Name === tier); - var renderedLabelHtml = '' + - '' + - '' + capitalizeFirstLetter(tier) + '' + - capitalizeFirstLetter(tier) + - '' + - ''; + var renderedLabelHtml = (` + + ${capitalizeFirstLetter(tier)} + ${capitalizeFirstLetter(tier)} + + `); return renderedLabelHtml; } else { return ""; }; } -function renderCard(pattern, tier_categories) { +function renderCard(pattern, tier_categories, variant_count) { // HTML for each pattern card - var renderCardHtml = ''; + var pattern_id = pattern.Link.split("/").filter(n => n)[1]; + var renderCardHtml = (``); return renderCardHtml; } @@ -319,30 +388,50 @@ function renderFilteredCards(patterns, filter_categories) { // Render the filtered cards for (item = 0; item < filteredPatterns.length; item++) { + var variant_count = containsVariants(filteredPatterns[item], patterns); const element = document.getElementById("patternCards"); - element.innerHTML += renderCard(filteredPatterns[item], filter_categories.tier); + element.innerHTML += renderCard(filteredPatterns[item], filter_categories.tier, variant_count); }; + // Render the variant status + renderVariantStatus(patterns); + // Display the number of patterns var totalPatternsCount = patterns.length var filteredPatternsCount = filteredPatterns.length const counter = document.getElementById("pattern-counter"); counter.innerHTML = filteredPatternsCount + " of " + totalPatternsCount + " patterns displayed"; + + // Update the counters for checked filters + updateFilterCounters(); +} + +function updateFilterCounters() { + const elementIds = ["TiersItems", "IndustriesItems", "RhProductsItems", "PartnersItems"] + for (const elementId of elementIds) { + const element = document.getElementById(elementId); + const elementCounter = document.getElementById(elementId + "Counter"); + var count = element.querySelectorAll('input[type="checkbox"]:checked').length; + if (count > 0) { + elementCounter.innerHTML = (`${count}`); + } else { + elementCounter.innerHTML = ""; + } + } } function filterSelection(filter) { // Filter the patterns const patternsData = getData() patternsData.then(output => { - renderFilteredCards(output.patterns, output.filter_categories) updateURL(output.filter_categories) + renderFilteredCards(output.patterns, output.filter_categories) }); } function changeFilterType(id) { // Change Function to change the filter type when the user clicks an // AND / OR operator button - var filterType = id.split(":"); var filter_category = filterType[0].replace('_button',''); const patternsData = getData(); @@ -354,13 +443,17 @@ function changeFilterType(id) { } const selectButton = document.getElementById(id); selectButton.classList.add("pf-m-selected"); - renderFilteredCards(output.patterns, output.filter_categories) updateURL(output.filter_categories) + renderFilteredCards(output.patterns, output.filter_categories) }); } function updateURL(filter_categories){ // Update the URL with the current params based on the checkboxes + // Snce parent_variant is a special type of filter, it's not stored as a part + // of the checbox filters. So if it is set, we need to store it so we can + // reuse it once we regenerate the URL. + const parent_variant = getVariantParent(); var updatedUrl = new URL(window.location.pathname, window.location.origin); const patternsFilter = new Filter(filter_categories); var enabledParams = {}; @@ -372,12 +465,18 @@ function updateURL(filter_categories){ updatedUrl.searchParams.append(key, found.Name); } } + // Determine if we'r eusing an "add" or "or" filter. for (var type_key in patternsFilter.filter_types) { var type_value = patternsFilter.filter_types[type_key]; if (type_value == "or") { updatedUrl.searchParams.append(type_key + "_filter", "or"); } } + // Add the parent_variant we stored earlier (if it was set). + if (parent_variant) { + updatedUrl.searchParams.append('variant_of', parent_variant); + } + // Update the URL. history.pushState({}, null, updatedUrl.href); } @@ -389,8 +488,8 @@ function clearFilters(){ filter_checkboxes[item].checked = false; } patternsData.then(output => { - renderFilteredCards(output.patterns, output.filter_categories); updateURL(output.filter_categories); + renderFilteredCards(output.patterns, output.filter_categories); }); }