diff --git a/packages/manager/src/features/Linodes/LinodesCreate/utilities.test.ts b/packages/manager/src/features/Linodes/LinodesCreate/utilities.test.ts
index 0dd7619430b..8a18835cce1 100644
--- a/packages/manager/src/features/Linodes/LinodesCreate/utilities.test.ts
+++ b/packages/manager/src/features/Linodes/LinodesCreate/utilities.test.ts
@@ -50,12 +50,7 @@ describe('trimOneClickFromLabel', () => {
});
describe('filterOneClickApps', () => {
- const baseApps = {
- 1: 'Base App 1',
- 2: 'Base App 2',
- 3: 'Base App 3',
- 4: 'Base App 4',
- };
+ const baseAppIds = [1, 2, 3, 4];
const newApps = {
5: 'New App 1',
6: 'New App 2',
@@ -78,7 +73,7 @@ describe('filterOneClickApps', () => {
it('filters OneClickApps and trims labels, excluding StackScripts with Helpers', () => {
// feeding 4 Ids (1,2,3,4) getting 3 back
const filteredOCAsWithHelpersLabel = filterOneClickApps({
- baseApps,
+ baseAppIds,
newApps,
queryResults: queryResultsWithHelpers,
});
@@ -86,7 +81,7 @@ describe('filterOneClickApps', () => {
// feeding 4 Ids (5,6,7,8) getting 4 back
const filteredOCAsWithoutHelpersLabel = filterOneClickApps({
- baseApps,
+ baseAppIds,
newApps,
queryResults: queryResultsWithoutHelpers,
});
@@ -97,7 +92,7 @@ describe('filterOneClickApps', () => {
it('handles empty queryResults', () => {
const emptyQueryResults: StackScript[] = [];
const filteredOCAs = filterOneClickApps({
- baseApps,
+ baseAppIds,
newApps,
queryResults: emptyQueryResults,
});
diff --git a/packages/manager/src/features/Linodes/LinodesCreate/utilities.tsx b/packages/manager/src/features/Linodes/LinodesCreate/utilities.tsx
index 73b5d99dba4..68144a1aed7 100644
--- a/packages/manager/src/features/Linodes/LinodesCreate/utilities.tsx
+++ b/packages/manager/src/features/Linodes/LinodesCreate/utilities.tsx
@@ -51,7 +51,7 @@ export const trimOneClickFromLabel = (stackScript: StackScript) => {
};
interface FilteredOCAs {
- baseApps: Record;
+ baseAppIds: number[];
newApps: Record | never[];
queryResults: StackScript[];
}
@@ -64,18 +64,17 @@ interface FilteredOCAs {
* @returns an array of OCA StackScripts
*/
export const filterOneClickApps = ({
- baseApps,
+ baseAppIds,
newApps,
queryResults,
}: FilteredOCAs) => {
- const allowedApps = Object.keys({ ...baseApps, ...newApps });
+ const allowedAppIds = [...baseAppIds, ...Object.keys(newApps).map(Number)];
+
// Don't display One-Click Helpers to the user
// Filter out any apps that we don't have info for
const filteredApps: StackScript[] = queryResults.filter(
(app: StackScript) => {
- return (
- !app.label.match(/helpers/i) && allowedApps.includes(String(app.id))
- );
+ return !app.label.match(/helpers/i) && allowedAppIds.includes(app.id);
}
);
return filteredApps.map((app) => trimOneClickFromLabel(app));
diff --git a/packages/manager/src/features/OneClickApps/oneClickApps.ts b/packages/manager/src/features/OneClickApps/oneClickApps.ts
index d1c14d8fe88..ff23648aa79 100644
--- a/packages/manager/src/features/OneClickApps/oneClickApps.ts
+++ b/packages/manager/src/features/OneClickApps/oneClickApps.ts
@@ -1,2500 +1,8 @@
-import { oneClickAppFactory } from 'src/factories/stackscripts';
+import { oneClickApps as newOneClickApps } from './oneClickAppsv2';
import type { OCA } from './types';
/**
* @deprecated See oneClickAppsv2.ts
*/
-export const oneClickApps: OCA[] = [
- {
- alt_description: 'Free open source control panel with a mobile app.',
- alt_name: 'Free infrastructure control panel',
- categories: ['Control Panels'],
- colors: {
- end: 'a3a3a3',
- start: '20a53a',
- },
- description: `Feature-rich alternative control panel for users who need critical control panel functionality but don’t need to pay for more niche premium features. aaPanel is open source and consistently maintained with weekly updates.`,
- logo_url: 'aapanel.svg',
- name: 'aaPanel',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/aapanel/',
- title: 'Deploy aaPanel through the Linode Marketplace',
- },
- ],
- summary:
- 'Popular open source free control panel with robust features and a mobile app.',
- website: 'https://www.aapanel.com/reference.html',
- },
- {
- alt_description:
- 'Free accounting software. QuickBooks alternative for freelancers and small businesses.',
- alt_name: 'Open source accounting software',
- categories: ['Productivity'],
- colors: {
- end: '55588b',
- start: '6ea152',
- },
- description: `Akaunting is a universal accounting software that helps small businesses run more efficiently. Track expenses, generate reports, manage your books, and get the other essential features to run your business from a single dashboard.`,
- logo_url: 'akaunting.svg',
- name: 'Akaunting',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/akaunting/',
- title: 'Deploy Akaunting through the Linode Marketplace',
- },
- ],
- summary:
- 'Free and open source accounting software you can use in your browser.',
- website: 'https://akaunting.com',
- },
- {
- alt_description:
- 'Free high-performance media streaming, including livestreaming.',
- alt_name: 'Free media streaming app',
- categories: ['Media and Entertainment'],
- colors: {
- end: '0a0a0a',
- start: 'df0718',
- },
- description: `Self-hosted free version to optimize and record video streaming for webinars, gaming, and more.`,
- logo_url: 'antmediaserver.svg',
- name: 'Ant Media Server: Community Edition',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/antmediaserver/',
- title: 'Deploy Ant Media Server through the Linode Marketplace',
- },
- ],
- summary: 'A reliable, flexible and scalable video streaming solution.',
- website: 'https://antmedia.io/',
- },
- {
- alt_description:
- 'Low latency live streaming including WebRTC streaming, CMAF, and HLS.',
- alt_name: 'Media streaming app',
- categories: ['Media and Entertainment'],
- colors: {
- end: '0a0a0a',
- start: 'df0718',
- },
- description: `Ant Media Server makes it easy to set up a video streaming platform with ultra low latency. The Enterprise edition supports WebRTC Live Streaming in addition to CMAF and HLS streaming. Set up live restreaming to social media platforms to reach more viewers.`,
- logo_url: 'antmediaserver.svg',
- name: 'Ant Media Server: Enterprise Edition',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/antmediaenterpriseserver/',
- title:
- 'Deploy Ant Media Enterprise Edition through the Linode Marketplace',
- },
- ],
- summary: 'Highly scalable and feature-rich live video streaming platform.',
- website: 'https://antmedia.io/',
- },
- {
- alt_description:
- 'Open-source workflow management platform for data engineering pipelines.',
- alt_name: 'Workflow management platform',
- categories: ['Development'],
- colors: {
- end: 'E43921',
- start: '00C7D4',
- },
- description: `Programmatically author, schedule, and monitor workflows with a Python-based tool. Airflow provides full insight into the status and logs of your tasks, all in a modern web application.`,
- logo_url: 'apacheairflow.svg',
- name: 'Apache Airflow',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/apache-airflow/',
- title: 'Deploy Apache Airflow through the Linode Marketplace',
- },
- ],
- summary:
- 'Open source workflow management platform for data engineering pipelines.',
- website: 'https://airflow.apache.org/',
- },
- {
- alt_description: 'Open source real-time data stream management cluster.',
- alt_name: 'Data stream publisher-subscriber cluster',
- categories: ['Development'],
- colors: {
- end: '5CA2A2',
- start: '00C7D4',
- },
- description: `Apache Kafka supports a wide range of applications from log aggregation to real-time analytics. Kafka provides a foundation for building data pipelines, event-driven architectures, or stream processing applications.`,
- logo_url: 'apachekafka.svg',
- name: 'Apache Kafka Cluster',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/apache-kafka/',
- title: 'Deploy an Apache Kafka cluster through the Linode Marketplace',
- },
- ],
- summary: 'Open source data streaming.',
- website: 'https://kafka.apache.org/',
- },
- {
- alt_description:
- 'A self-hosted backend-as-a-service platform that provides developers with all the core APIs required to build any application.',
- alt_name: 'Self-hosted backend-as-a-service',
- categories: ['Development'],
- colors: {
- end: 'f02e65',
- start: 'f02e65',
- },
- description: `A self-hosted Firebase alternative for web, mobile & Flutter developers.`,
- logo_url: 'appwrite.svg',
- name: 'Appwrite',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/appwrite/',
- title: 'Deploy Appwrite through the Linode Marketplace',
- },
- ],
- summary:
- 'Appwrite is an open-source, cross-platform and technology-agnostic alternative to Firebase, providing all the core APIs necessary for web, mobile and Flutter development.',
- website: 'https://appwrite.io/',
- },
- {
- alt_description: 'Free internet radio station management and hosting.',
- alt_name: 'Online radio station builder',
- categories: ['Media and Entertainment'],
- colors: {
- end: '0b1b64',
- start: '1f8df5',
- },
- description: `All aspects of running a radio station in one web interface so you can start your own station. Manage media, create playlists, and interact with listeners on one free platform.`,
- logo_url: 'azuracast.svg',
- name: 'Azuracast',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/azuracast/',
- title: 'Deploy AzuraCast through the Linode Marketplace',
- },
- ],
- summary: 'Open source, self-hosted web radio tool.',
- website: 'https://www.azuracast.com/',
- },
- {
- alt_description: 'Free penetration testing tool using client-side vectors.',
- alt_name: 'Penetration testing tool for security research',
- categories: ['Security'],
- colors: {
- end: '000f21',
- start: '4a80a9',
- },
- description: `Test the security posture of a client or application using client-side vectors, all powered by a simple API. This project is developed solely for lawful research and penetration testing.`,
- logo_url: 'beef.svg',
- name: 'BeEF',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/beef/',
- title: 'Deploy BeEF through the Linode Marketplace',
- },
- ],
- summary:
- 'Browser Exploitation Framework (BeEF) is an open source web browser penetration tool.',
- website: 'https://github.com/beefproject/beef',
- },
- {
- alt_description:
- 'Application builder for forms, portals, admin panels, and more.',
- alt_name: 'Low-code application builder',
- categories: ['Development'],
- colors: {
- end: '000000',
- start: '9981f5',
- },
- description:
- 'Budibase is a modern, open source low-code platform for building modern business applications in minutes. Build, design and automate business apps, such as: admin panels, forms, internal tools, client portals and more. Before Budibase, it could take developers weeks to build simple CRUD apps; with Budibase, building CRUD apps takes minutes. When self-hosting please follow best practices for securing, updating and backing up your server.',
- logo_url: 'budibase.svg',
- name: 'Budibase',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/budibase/',
- title: 'Deploy Budibase through the Linode Marketplace',
- },
- ],
- summary: 'Low-code platform for building modern business applications.',
- website: 'https://docs.budibase.com/docs',
- },
- {
- alt_description:
- 'Image hosting and sharing alternative to Google Photos and Flickr.',
- alt_name: 'Photo library and image library',
- categories: ['Media and Entertainment'],
- colors: {
- end: '8e44ad',
- start: '23a8e0',
- },
- description: `Chevereto is a full-featured image sharing solution that acts as an alternative to services like Google Photos or Flickr. Optimize image hosting by using external cloud storage (like Linode’s S3-compatible Object Storage) and connect to Chevereto using API keys.`,
- logo_url: 'chevereto.svg',
- name: 'Chevereto',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/chevereto/',
- title: 'Deploy Chevereto through the Linode Marketplace',
- },
- ],
- summary:
- 'Self-host your own open source image library to easily upload, collaborate, and share images on your terms.',
- website: 'https://v3-docs.chevereto.com/',
- },
- {
- alt_description:
- 'Host multiple apps on one server and control panel, including WordPress, GitLab, and Nextcloud.',
- alt_name: 'Cloud app and website control panel',
- categories: ['Website'],
- colors: {
- end: '212121',
- start: '03a9f4',
- },
- description: `Turnkey solution for running apps like WordPress, Rocket.Chat, NextCloud, GitLab, and OpenVPN.`,
- logo_url: 'cloudron.svg',
- name: 'Cloudron',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/cloudron/',
- title: 'Deploy Cloudron through the Linode Marketplace',
- },
- ],
- summary:
- 'End-to-end deployment and automatic updates for a range of essential applications.',
- website: 'https://docs.cloudron.io',
- },
- {
- alt_description:
- 'SQL and NoSQL database interface and monitoring for MySQL, PostgreSQL, and more.',
- alt_name: 'Database monitoring',
- categories: ['Databases'],
- colors: {
- end: '3f434c',
- start: '0589de',
- },
- description: `All-in-one interface for scripting and monitoring databases, including MySQL, MariaDB, Percona, PostgreSQL, Galera Cluster and more. Easily deploy database instances, manage with an included CLI, and automate performance monitoring.`,
- logo_url: 'clustercontrol.svg',
- name: 'ClusterControl',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/clustercontrol/',
- title: 'Deploy ClusterControl through the Linode Marketplace',
- },
- ],
- summary:
- 'All-in-one database deployment, management, and monitoring system.',
- website: 'https://docs.severalnines.com/docs/clustercontrol/',
- },
- {
- alt_description:
- 'Highly available, five-node enterprise NoSQL database cluster.',
- alt_name: 'Couchbase Enterprise Server Cluster',
- categories: ['Databases'],
- colors: {
- end: 'EC1018',
- start: '333333',
- },
- description: `Couchbase Enterprise Server is a high-performance NoSQL database, built for scale. Couchbase Server is designed with memory-first architecture, built-in cache and workload isolation.`,
- logo_url: 'couchbase.svg',
- name: 'Couchbase Cluster',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/couchbase/',
- title:
- 'Deploy a Couchbase Enterprise Server cluster through the Linode Marketplace',
- },
- ],
- summary: 'NoSQL production database cluster.',
- website: 'https://www.couchbase.com/',
- },
- {
- alt_description:
- 'Linux-based web hosting control panel for managing websites, servers, databases, and more.',
- alt_name: 'Web server automation and control panel',
- categories: ['Control Panels'],
- colors: {
- end: '141d25',
- start: 'ff6c2c',
- },
- description: `The cPanel & WHM® Marketplace App streamlines publishing and managing a website on your Linode. cPanel & WHM is a Linux® based web hosting control panel and platform that helps you create and manage websites, servers, databases and more with a suite of hosting automation and optimization tools.`,
- logo_url: 'cpanel.svg',
- name: 'cPanel',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/cpanel/',
- title: 'Deploy cPanel through the Linode Marketplace',
- },
- ],
- summary:
- 'The leading hosting automation platform that has simplified site and server management for 20 years.',
- website: 'https://www.cpanel.net/',
- },
- {
- alt_description:
- 'Web hosting control panel for managing websites, including WordPress.',
- alt_name: 'Web hosting control panel',
- categories: ['Control Panels'],
- colors: {
- end: '33cccc',
- start: '3d596d',
- },
- description: `Reduce setup time required to host websites and applications, including popular tools like OpenLiteSpeed WordPress.`,
- logo_url: 'cyberpanel.svg',
- name: 'CyberPanel',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/cyberpanel/',
- title: 'Deploy CyberPanel through the Linode Marketplace',
- },
- ],
- summary: 'Next-generation hosting control panel by OpenLiteSpeed.',
- website: 'https://docs.litespeedtech.com/cloud/images/cyberpanel/',
- },
- {
- alt_description: 'Open source community forum alternative to Reddit.',
- alt_name: 'Chat forum',
- categories: ['Media and Entertainment'],
- colors: {
- end: 'eae692',
- start: '13b3ed',
- },
- description: `Launch a sleek forum with robust integrations to popular tools like Slack and WordPress to start more conversations.`,
- logo_url: 'discourse.svg',
- name: 'Discourse',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/discourse/',
- title: 'Deploy Discourse through the Linode Marketplace',
- },
- ],
- summary:
- 'Open source community and discussion forum for customers, teams, fans, and more.',
- website: 'https://www.discourse.org/',
- },
- {
- alt_description: 'Fast Python development with best practices.',
- alt_name: 'Python framework',
- categories: ['Development'],
- colors: {
- end: '136149',
- start: '0a2e1f',
- },
- description: `Django is a web development framework for the Python programming language. It enables rapid development, while favoring pragmatic and clean design.`,
- logo_url: 'django.svg',
- name: 'Django',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/django/',
- title: 'Deploy Django through the Linode Marketplace',
- },
- ],
- summary: `A framework for simplifying the process of building your web applications more quickly and with less code.`,
- website: 'https://www.djangoproject.com/',
- },
- {
- alt_description:
- 'Popular container tool to build cloud-native applications.',
- alt_name: 'Container builder',
- categories: ['Development'],
- colors: {
- end: '1e65c9',
- start: '2496ed',
- },
- description: `Docker is a tool that enables you to create, deploy, and manage lightweight, stand-alone packages that contain everything needed to run an application (code, libraries, runtime, system settings, and dependencies).`,
- logo_url: 'docker.svg',
- name: 'Docker',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/docker/',
- title: 'Deploy Docker through the Linode Marketplace',
- },
- ],
- summary: `Securely build, share and run modern applications anywhere.`,
- website: 'https://www.docker.com/',
- },
- {
- alt_description: 'Secure website CMS.',
- alt_name: 'CMS: content management system',
- categories: ['Website'],
- colors: {
- end: '1b64a5',
- start: '0678be',
- },
- description: `Drupal is a content management system (CMS) designed for building custom websites for personal and business use. Built for high performance and scalability, Drupal provides the necessary tools to create rich, interactive community websites with forums, user blogs, and private messaging. Drupal also has support for personal publishing projects and can power podcasts, blogs, and knowledge-based systems, all within a single, unified platform.`,
- logo_url: 'drupal.svg',
- name: 'Drupal',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/drupal/',
- title: 'Deploy Drupal through the Linode Marketplace',
- },
- ],
- summary: `Powerful content management system built on PHP and supported by a database engine.`,
- website: 'https://www.drupal.org/',
- },
- {
- alt_description:
- 'Flexible control panel to simplify SSL certificates and push code from GitHub.',
- alt_name: 'Server control panel',
- categories: ['Control Panels'],
- colors: {
- end: '000000',
- start: '059669',
- },
- description: `Deploy Node.js, Ruby, Python, PHP, Go, and Java applications via an intuitive control panel. Easily set up free SSL certificates, run commands with an in-browser terminal, and push your code from Github to accelerate development.`,
- logo_url: 'easypanel.svg',
- name: 'Easypanel',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/easypanel/',
- title: 'Deploy Easypanel through the Linode Marketplace',
- },
- ],
- summary: 'Modern server control panel based on Docker.',
- website: 'https://easypanel.io/',
- },
- {
- alt_description: 'File storage alternative to Dropbox and Google Drive.',
- alt_name: 'File sharing',
- categories: ['Productivity'],
- colors: {
- end: '0168ad',
- start: '3e8cc1',
- },
- description: `File synchronization across multiple users’ computers and other devices to keep everyone working without interruption.`,
- logo_url: 'filecloud.svg',
- name: 'FileCloud',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/filecloud/',
- title: 'Deploy FileCloud through the Linode Marketplace',
- },
- ],
- summary: 'Enterprise file sharing to manage and sync from any device.',
- website: 'https://www.getfilecloud.com',
- },
- {
- alt_description: 'Fast Python development with best practices.',
- alt_name: 'Python framework',
- categories: ['Development'],
- colors: {
- end: '1e2122',
- start: '363b3d',
- },
- description: `Flask is a lightweight WSGI web application framework written in Python. It is designed to make getting started quick and easy, with the ability to scale up to complex applications.`,
- logo_url: 'flask.svg',
- name: 'Flask',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/flask/',
- title: 'Deploy Flask through the Linode Marketplace',
- },
- ],
- summary: `A quick light-weight web framework for Python that includes several utilities and libraries you can use to create a web application.`,
- website: 'https://www.palletsprojects.com/p/flask/',
- },
- {
- alt_description: 'Free alternative to Trello and Asana.',
- alt_name: 'Kanban board project management tool',
- categories: ['Productivity'],
- colors: {
- end: '1d52ad',
- start: '2997f8',
- },
- description: `Create boards, assign tasks, and keep projects moving with a free and robust alternative to tools like Trello and Asana.`,
- logo_url: 'focalboard.svg',
- name: 'Focalboard',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/focalboard/',
- title: 'Deploy Focalboard through the Linode Marketplace',
- },
- ],
- summary: 'Free open source project management tool.',
- website: 'https://www.focalboard.com/',
- },
- {
- alt_description: 'SQL database.',
- alt_name: 'SQL database',
- categories: ['Databases'],
- colors: {
- end: '000000',
- start: 'EC7704',
- },
- description: `Galera provides a performant multi-master/active-active database solution with synchronous replication, to achieve high availability.`,
- logo_url: 'galeramarketplaceocc.svg',
- name: 'Galera Cluster',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/galera-cluster/',
- title: 'Deploy Galera Cluster through the Linode Marketplace',
- },
- ],
- summary: `Multi-master MariaDB database cluster.`,
- website: 'https://galeracluster.com/',
- },
- {
- alt_description: 'Open source, self-hosted Git management tool.',
- alt_name: 'Git repository hosting',
- categories: ['Development'],
- colors: {
- end: '34495e',
- start: '609926',
- },
- description: `Self-hosted Git service built and maintained by a large developer community.`,
- logo_url: 'gitea.svg',
- name: 'Gitea',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/gitea/',
- title: 'Deploy Gitea through the Linode Marketplace',
- },
- ],
- summary: 'Git with a cup of tea - A painless self-hosted Git service.',
- website: 'https://gitea.io/',
- },
- {
- alt_description: 'Popular Git management tool.',
- alt_name: 'Git repository hosting',
- categories: ['Development'],
- colors: {
- end: '21153e',
- start: '48357d',
- },
- description: `GitLab is a complete solution for all aspects of your software development. At its core, GitLab serves as your centralized Git repository. GitLab also features built-in tools that represent every task in your development workflow, from planning to testing to releasing.
- Self-hosting your software development with GitLab offers total control of your codebase. At the same time, its familiar interface will ease collaboration for you and your team. GitLab is the most popular self-hosted Git repository, so you'll benefit from a robust set of integrated tools and an active community.`,
- logo_url: 'gitlab.svg',
- name: 'GitLab',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/gitlab/',
- title: 'Deploy GitLab through the Linode Marketplace',
- },
- ],
- summary:
- 'More than a self-hosted Git repository: use GitLab to manage all the stages of your DevOps life cycle.',
- website: 'https://about.gitlab.com/',
- },
- {
- alt_description:
- 'No-code platform for Kubernetes developers and operators.',
- alt_name: 'Go Paddle',
- categories: ['Development'],
- colors: {
- end: '252930',
- start: '3a5bfd',
- },
- description: `Provision multicloud clusters, containerize applications, and build DevOps pipelines. Gopaddle’s suite of templates and integrations helps eliminate manual errors and automate Kubernetes application releases.`,
- logo_url: 'gopaddle.svg',
- name: 'Gopaddle',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/gopaddle/',
- title: 'Deploy Gopaddle through the Linode Marketplace',
- },
- ],
- summary:
- 'Simple low-code platform for Kubernetes developers and operators.',
- website: 'https://gopaddle.io/',
- },
- {
- alt_description: 'Open source, highly available, shared filesystem.',
- alt_name: 'GlusterFS',
- categories: ['Development'],
- colors: {
- end: '784900',
- start: 'D4AC5C',
- },
- description:
- 'GlusterFS is an open source, software scalable network filesystem. This app deploys three GlusterFS servers and three GlusterFS clients.',
- logo_url: 'glusterfs.svg',
- name: 'GlusterFS Cluster',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/glusterfs-cluster/',
- title: 'Deploy GlusterFS Cluster through the Linode Marketplace',
- },
- ],
- summary: 'Open source network filesystem.',
- website: 'https://www.gluster.org/',
- },
- {
- alt_description: 'Markdown-based website CMS.',
- alt_name: 'CMS: content management system',
- categories: ['Website'],
- colors: {
- end: 'b987cf',
- start: '1a0629',
- },
- description: `Build websites on a CMS that prioritizes speed and simplicity over customization and integration support. Create your content in Markdown and take advantage of powerful taxonomy to customize relationships between pages and other content.`,
- logo_url: 'grav.svg',
- name: 'Grav',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/grav/',
- title: 'Deploy Grav through the Linode Marketplace',
- },
- ],
- summary: 'Modern and open source flat-file content management system.',
- website: 'https://getgrav.org/',
- },
- {
- alt_description: 'Desktop cloud hosting.',
- alt_name: 'Virtual desktop',
- categories: ['Development'],
- colors: {
- end: '213121',
- start: '304730',
- },
- description: `Access your desktop from any device with a browser to keep your desktop hosted in the cloud.`,
- logo_url: 'guacamole.svg',
- name: 'Guacamole',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/guacamole/',
- title: 'Deploy Apache Guacamole through the Linode Marketplace',
- },
- ],
- summary: 'Free open source clientless remote desktop gateway.',
- website: 'https://guacamole.apache.org/',
- },
- {
- alt_description: 'Web Application Firewall.',
- alt_name: 'Community WAF',
- categories: ['Security'],
- colors: {
- end: '00C1A9',
- start: '22324F',
- },
- description: `Harden your web applications and APIs against OWASP Top 10 attacks. Haltdos makes it easy to manage WAF settings and review logs in an intuitive web-based GUI.`,
- logo_url: 'haltdos.svg',
- name: 'HaltDOS Community WAF',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/haltdos-community-waf/',
- title: 'Deploy Haltdos Community WAF through the Linode Marketplace',
- },
- ],
- summary: 'User-friendly web application firewall.',
- website: 'https://www.haltdos.com/',
- },
- {
- alt_description: 'Container registry for Kubernetes.',
- alt_name: 'Container registry for Kubernetes.',
- categories: ['Development'],
- colors: {
- end: '4495d7',
- start: '60b932',
- },
- description: `Open source registry for images and containers. Linode recommends using Harbor with Linode Kubernetes Engine (LKE).`,
- logo_url: 'harbor.svg',
- name: 'Harbor',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/harbor/',
- title: 'Deploy Harbor through the Linode Marketplace',
- },
- ],
- summary: 'Cloud native container registry for Kubernetes and more.',
- website: 'https://goharbor.io/docs',
- },
- {
- alt_description:
- 'HashiCorp containerization tool to use instead of or with Kubernetes',
- alt_name: 'Container scheduler and orchestrator',
- categories: ['Development'],
- colors: {
- end: '545556',
- start: '60dea9',
- },
- description:
- 'A simple and flexible scheduler and orchestrator to deploy and manage containers and non-containerized applications across on-prem and clouds at scale.',
- logo_url: 'nomad.svg',
- name: 'HashiCorp Nomad Cluster',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/hashicorp-nomad-cluster',
- title: 'Deploy HashiCorp Nomad Cluster through the Linode Marketplace',
- },
- ],
- summary: 'Flexible scheduling and orchestration for diverse workloads.',
- website: 'https://www.nomadproject.io/docs',
- },
- {
- alt_description:
- 'HashiCorp Nomad clients for horizontally scaling a Nomad One-Click Cluster',
- alt_name: 'Container scheduler and orchestrator',
- categories: ['Development'],
- colors: {
- end: '545556',
- start: '60dea9',
- },
- description:
- 'A simple deployment of multiple clients to horizontally scale an existing Nomad One-Click Cluster.',
- logo_url: 'nomad.svg',
- name: 'HashiCorp Nomad Clients Cluster',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/hashicorp-nomad-clients-cluster',
- title:
- 'Deploy HashiCorp Nomad Clients Cluster through the Linode Marketplace',
- },
- ],
- summary: 'Flexible scheduling and orchestration for diverse workloads.',
- website: 'https://www.nomadproject.io/docs',
- },
- {
- alt_description:
- 'HashiCorp containerization tool to use instead of or with Kubernetes',
- alt_name: 'Container scheduler and orchestrator',
- categories: ['Development'],
- colors: {
- end: '545556',
- start: '60dea9',
- },
- description:
- 'A simple and flexible scheduler and orchestrator to deploy and manage containers and non-containerized applications across on-prem and clouds at scale.',
- logo_url: 'nomad.svg',
- name: 'HashiCorp Nomad',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/hashicorp-nomad',
- title: 'Deploy HashiCorp Nomad through the Linode Marketplace',
- },
- ],
- summary: 'Flexible scheduling and orchestration for diverse workloads.',
- website: 'https://www.nomadproject.io/docs',
- },
- {
- alt_description: 'HashiCorp password and secrets management storage.',
- alt_name: 'Security secrets management',
- categories: ['Security'],
- colors: {
- end: '545556',
- start: 'ffd712',
- },
- description:
- 'HashiCorp Vault is an open source, centralized secrets management system. It provides a secure and reliable way of storing and distributing secrets like API keys, access tokens, and passwords.',
- logo_url: 'vault.svg',
- name: 'HashiCorp Vault',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/hashicorp-vault',
- title: 'Deploy HashiCorp Vault through the Linode Marketplace',
- },
- ],
- summary: 'An open source, centralized secrets management system.',
- website: 'https://www.vaultproject.io/docs',
- },
- {
- alt_description:
- 'Retool open-source alternative, with low-code UI components.',
- alt_name: 'Low-code development platform',
- categories: ['Security'],
- colors: {
- end: 'FF58BE',
- start: '654AEC',
- },
- description:
- 'Illa Builder is a Retool open-source alternative, with low-code UI components for self-hosting the development of internal tools.',
- logo_url: 'illabuilder.svg',
- name: 'Illa Builder',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/illa-builder',
- title: 'Deploy Illa Builder through the Linode Marketplace',
- },
- ],
- summary: 'An open-source, low-code development platform.',
- website: 'https://github.com/illacloud/illa-builder',
- },
- {
- alt_description: 'CI/CD tool to delegate automation tasks and jobs.',
- alt_name: 'Free automation tool',
- categories: ['Development'],
- colors: {
- end: 'd24939',
- start: 'd33833',
- },
- description: `Jenkins is an open source automation tool which can build, test, and deploy your infrastructure.`,
- logo_url: 'jenkins.svg',
- name: 'Jenkins',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/jenkins/',
- title: 'Deploy Jenkins through the Linode Marketplace',
- },
- ],
- summary: `A tool that gives you access to a massive library of plugins to support automation in your project's lifecycle.`,
- website: 'https://jenkins.io/',
- },
- {
- alt_description: 'Enterprise-ready backups tool.',
- alt_name: 'Server backups management and control panel',
- categories: ['Control Panels'],
- colors: {
- end: '1f2c38',
- start: 'ff6c2c',
- },
- description: `Powerful and customizable backups for several websites and data all in the same interface. JetBackup integrates with any control panel via API, and has native support for cPanel and DirectAdmin. Easily backup your data to storage you already use, including Linode’s S3-compatible Object Storage.`,
- logo_url: 'jetbackup.svg',
- name: 'JetBackup',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/jetbackup/',
- title: 'Deploy JetBackup through the Linode Marketplace',
- },
- ],
- summary:
- 'Advanced customizable backups to integrate with your preferred control panel.',
- website: 'https://docs.jetapps.com/',
- },
- {
- alt_description: 'Open source video conferencing alternative to Zoom.',
- alt_name: 'Video chat and video conferencing',
- categories: ['Media and Entertainment'],
- colors: {
- end: '949699',
- start: '1d76ba',
- },
- description: `Secure, stable, and free alternative to popular video conferencing services. Use built-in features to limit meeting access with passwords or stream on YouTube so anyone can attend.`,
- logo_url: 'jitsi.svg',
- name: 'Jitsi',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/jitsi/',
- title: 'Deploy Jitsi through the Linode Marketplace',
- },
- ],
- summary: 'Free, open source video conferencing and communication platform.',
- website: 'https://jitsi.org/',
- },
- {
- alt_description:
- 'Open source video conferencing cluster, alternative to Zoom.',
- alt_name: 'Video chat and video conferencing cluster',
- categories: ['Media and Entertainment'],
- colors: {
- end: '949699',
- start: '1d76ba',
- },
- description: `Secure, stable, and free alternative to popular video conferencing services. This app deploys four networked Jitsi nodes.`,
- logo_url: 'jitsi.svg',
- name: 'Jitsi Cluster',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/jitsi-cluster/',
- title: 'Deploy Jitsi Cluster through the Linode Marketplace',
- },
- ],
- summary: 'Free, open source video conferencing and communication platform.',
- website: 'https://jitsi.org/',
- },
- {
- alt_description: 'Secure website CMS.',
- alt_name: 'CMS: content management system',
- categories: ['Website'],
- colors: {
- end: '5090cd',
- start: 'f2a13e',
- },
- description: `Free open source CMS optimized for building custom functionality and design.`,
- logo_url: 'joomla.svg',
- name: 'Joomla',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/joomla/',
- title: 'Deploy Joomla through the Linode Marketplace',
- },
- ],
- summary: 'Flexible and security-focused content management system.',
- website: 'https://www.joomla.org/',
- },
- {
- alt_description:
- 'Digital note-taking application alternative to Evernote and OneNote.',
- alt_name: 'Multimedia note-taking and digital notebook',
- categories: ['Website'],
- colors: {
- end: '509df9',
- start: '043872',
- },
- description: `Capture your thoughts and securely access them from any device with a highly customizable note-taking software.`,
- logo_url: 'joplin.svg',
- name: 'Joplin',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/joplin/',
- title: 'Deploy Joplin through the Linode Marketplace',
- },
- ],
- summary: 'Open source multimedia note-taking app.',
- website: 'https://joplinapp.org/',
- },
- {
- alt_description: 'Data science notebook.',
- alt_name: 'Data science and machine learning development environment.',
- categories: ['Productivity'],
- colors: {
- end: '9e9e9e',
- start: 'f37626',
- },
- description:
- 'JupyterLab is a cutting-edge web-based, interactive development environment, geared towards data science, machine learning and other scientific computing workflows.',
- logo_url: 'jupyter.svg',
- name: 'JupyterLab',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/jupyterlab/',
- title: 'Deploy JupyterLab through the Linode Marketplace',
- },
- ],
- summary: 'Data science development environment.',
- website: 'https://jupyter.org',
- },
- {
- alt_description:
- 'Security research and testing platform with hundreds of tools for reverse engineering, penetration testing, and more.',
- alt_name: 'Security research',
- categories: ['Security'],
- colors: {
- end: '2fa1bc',
- start: '267ff7',
- },
- description: `Kali Linux is an open source, Debian-based Linux distribution that has become an industry-standard tool for penetration testing and security audits. Kali includes hundreds of free tools for reverse engineering, penetration testing and more. Kali prioritizes simplicity, making security best practices more accessible to everyone from cybersecurity professionals to hobbyists.`,
- logo_url: 'kalilinux.svg',
- name: 'Kali Linux',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/kali-linux/',
- title: 'Deploy Kali Linux through the Linode Marketplace',
- },
- ],
- summary:
- 'Popular Linux distribution and tool suite for penetration testing and security research.',
- website: 'https://www.kali.org/',
- },
- {
- alt_description: 'Drag and drop website CMS.',
- alt_name: 'CMS: content management system',
- categories: ['Website'],
- colors: {
- end: '4395ff',
- start: '0166ff',
- },
- description: `Use Kepler Builder to easily design and build sites in WordPress - no coding or design knowledge necessary.`,
- logo_url: 'keplerbuilder.svg',
- name: 'Kepler Builder',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/kepler/',
- title: 'Deploy Kepler through the Linode Marketplace',
- },
- ],
- summary: 'Powerful drag & drop WordPress website builder.',
- website: 'https://kepler.app/',
- },
- {
- alt_description: 'Essential software stack for Linux applications.',
- alt_name: 'Web stack',
- categories: ['Stacks'],
- colors: {
- end: 'bfa477',
- start: '3c4043',
- },
- description: `The LAMP stack consists of the Linux operating system, the Apache HTTP Server, the MySQL relational database management system, and the PHP programming language. This software environment is a foundation for popular PHP application
- frameworks like WordPress, Drupal, and Laravel. Upload your existing PHP application code to your new app or use a PHP framework to write a new application on the Linode.`,
- logo_url: 'lamp_flame.svg',
- name: 'LAMP',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/lamp-stack/',
- title: 'Deploy a LAMP Stack through the Linode Marketplace',
- },
- ],
- summary: `Build PHP-based applications with the LAMP software stack: Linux, Apache, MySQL, and PHP.`,
- },
- {
- alt_description: 'Essential software stack for Linux applications.',
- alt_name: 'Web stack',
- categories: ['Stacks'],
- colors: {
- end: '005138',
- start: '2e7d32',
- },
- description: `LEMP provides a platform for applications that is compatible with the LAMP stack for nearly all applications; however, because NGINX is able to serve more pages at once with a more predictable memory usage profile, it may be more suited to high demand situations.`,
- logo_url: 'lemp.svg',
- name: 'LEMP',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/lemp-stack/',
- title: 'Deploy a LEMP Stack through the Linode Marketplace',
- },
- ],
- summary: `The LEMP stack replaces the Apache web server component with NGINX (“Engine-X”), providing the E in the acronym: Linux, NGINX, MySQL/MariaDB, PHP.`,
- },
- {
- alt_description:
- 'LinuxGSM is a command line utility that simplifies self-hosting multiplayer game servers.',
- alt_name: 'Multiplayer Game Servers',
- categories: ['Games'],
- colors: {
- end: 'F6BD0C',
- start: '000000',
- },
- description: `Self hosted multiplayer game servers.`,
- logo_url: 'linuxgsm.svg',
- name: 'LinuxGSM',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/linuxgsm/',
- title: 'Deploy LinuxGSM through the Linode Marketplace',
- },
- ],
- summary: 'Simple command line multiplayer game servers.',
- website: 'https://docs.linuxgsm.com',
- },
- {
- alt_description: 'Optimized control panel server.',
- alt_name: 'Web server control panel',
- categories: ['Website'],
- colors: {
- end: '6e92c7',
- start: '353785',
- },
- description: `High-performance LiteSpeed web server equipped with WHM/cPanel and WHM LiteSpeed Plugin.`,
- logo_url: 'litespeedcpanel.svg',
- name: 'LiteSpeed cPanel',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/litespeed-cpanel/',
- title: 'Deploy LiteSpeed cPanel through the Linode Marketplace',
- },
- ],
- summary: 'Next-generation web server with cPanel and WHM.',
- website: 'https://docs.litespeedtech.com/cp/cpanel/',
- },
- {
- alt_description: 'Audio and video streaming with E2E data encryption.',
- alt_name: 'Live streaming',
- categories: ['Media and Entertainment'],
- colors: {
- end: '4d8eff',
- start: '346ee0',
- },
- description: `Stream live audio or video while maximizing customer engagement with advanced built-in features. Liveswitch provides real-time monitoring, audience polling, and end-to-end (E2E) data encryption.`,
- logo_url: 'liveswitch.svg',
- name: 'LiveSwitch',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/liveswitch/',
- title: 'Deploy LiveSwitch through the Linode Marketplace',
- },
- ],
- summary: 'High quality and reliable interactive live streaming.',
- website: 'https://www.liveswitch.io/',
- },
- {
- alt_description: 'FFmpeg encoder plugins.',
- alt_name: 'Premium video encoding',
- categories: ['Media and Entertainment'],
- colors: {
- end: '041125',
- start: '6DBA98',
- },
- description: `MainConcept FFmpeg Plugins Demo is suited for both VOD and live production workflows, with advanced features such as Hybrid GPU acceleration and xHE-AAC audio format.`,
- logo_url: 'mainconcept.svg',
- name: 'MainConcept FFmpeg Plugins Demo',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/mainconcept-ffmpeg-plugins-demo/',
- title:
- 'Deploy MainConcept FFmpeg Plugins Demo through the Linode Marketplace',
- },
- ],
- summary:
- 'MainConcept FFmpeg Plugins Demo contains advanced video encoding tools.',
- website: 'https://www.mainconcept.com/ffmpeg',
- },
- {
- alt_description: 'Live video encoding engine.',
- alt_name: 'Real time video encoding',
- categories: ['Media and Entertainment'],
- colors: {
- end: '041125',
- start: '6DBA98',
- },
- description: `MainConcept Live Encoder Demo is a powerful all-in-one encoding engine designed to simplify common broadcast and OTT video workflows.`,
- logo_url: 'mainconcept.svg',
- name: 'MainConcept Live Encoder Demo',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/mainconcept-live-encoder-demo/',
- title:
- 'Deploy MainConcept Live Encoder Demo through the Linode Marketplace',
- },
- ],
- summary: 'MainConcept Live Encoder is a real time video encoding engine.',
- website: 'https://www.mainconcept.com/live-encoder',
- },
- {
- alt_description: 'Panasonic camera format encoder.',
- alt_name: 'Media encoding into professional file formats.',
- categories: ['Media and Entertainment'],
- colors: {
- end: '041125',
- start: '6DBA98',
- },
- description: `MainConcept P2 AVC ULTRA Transcoder Demo is an optimized Docker container for file-based transcoding of media files into professional Panasonic camera formats like P2 AVC-Intra, P2 AVC LongG and AVC-intra RP2027.v1 and AAC High Efficiency v2 formats into an MP4 container.`,
- logo_url: 'mainconcept.svg',
- name: 'MainConcept P2 AVC ULTRA Transcoder Demo',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/mainconcept-p2-avc-ultra-demo/',
- title:
- 'Deploy MainConcept P2 AVC ULTRA Transcoder Demo through the Linode Marketplace',
- },
- ],
- summary:
- 'MainConcept P2 AVC ULTRA Transcoder is a Docker container for file-based transcoding of media files into professional Panasonic camera formats.',
- website: 'https://www.mainconcept.com/transcoders',
- },
- {
- alt_description: 'Sony camera format encoder.',
- alt_name: 'Media encoding into professional file formats.',
- categories: ['Media and Entertainment'],
- colors: {
- end: '041125',
- start: '6DBA98',
- },
- description: `MainConcept XAVC Transcoder Demo is an optimized Docker container for file-based transcoding of media files into professional Sony camera formats like XAVC-Intra, XAVC Long GOP and XAVC-S.`,
- logo_url: 'mainconcept.svg',
- name: 'MainConcept XAVC Transcoder Demo',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/mainconcept-xavc-transcoder-demo/',
- title:
- 'Deploy MainConcept XAVC Transcoder Demo through the Linode Marketplace',
- },
- ],
- summary:
- 'MainConcept XAVC Transcoder is a Docker container for file-based transcoding of media files into professional Sony camera formats.',
- website: 'https://www.mainconcept.com/transcoders',
- },
- {
- alt_description: 'Sony XDCAM format encoder.',
- alt_name: 'Media encoding into professional file formats.',
- categories: ['Media and Entertainment'],
- colors: {
- end: '041125',
- start: '6DBA98',
- },
- description: `MainConcept XDCAM Transcoder Demo is an optimized Docker container for file-based transcoding of media files into professional Sony camera formats like XDCAM HD, XDCAM EX, XDCAM IMX and DVCAM (XDCAM DV).`,
- logo_url: 'mainconcept.svg',
- name: 'MainConcept XDCAM Transcoder Demo',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/mainconcept-xdcam-transcoder-demo/',
- title:
- 'Deploy MainConcept XDCAM Transcoder Demo through the Linode Marketplace',
- },
- ],
- summary:
- 'MainConcept XDCAM Transcoder is a Docker container for file-based transcoding of media files into professional Sony camera formats.',
- website: 'https://www.mainconcept.com/transcoders',
- },
- {
- alt_description: 'Open source Twitter alternative.',
- alt_name: 'Open source social media',
- categories: ['Media and Entertainment'],
- colors: {
- end: '563ACC',
- start: '6364FF',
- },
- description: `Mastodon is an open-source and decentralized micro-blogging platform, supporting federation and public access to the server.`,
- logo_url: 'mastodon.svg',
- name: 'Mastodon',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/mastodon/',
- title: 'Deploy Mastodon through the Linode Marketplace',
- },
- ],
- summary:
- 'Mastodon is an open-source and decentralized micro-blogging platform.',
- website: 'https://docs.joinmastodon.org/',
- },
- {
- alt_description: 'Angular and Node.js stack.',
- alt_name: 'Web framework',
- categories: ['Development'],
- colors: {
- end: '686868',
- start: '323232',
- },
- description: `MEAN is a full-stack JavaScript-based framework which accelerates web application development much faster than other frameworks. All involved technologies are well-established, offer robust feature sets, and are well-supported by their maintaining organizations. These characteristics make them a great choice for your applications.`,
- logo_url: 'mean.svg',
- name: 'MEAN',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/mean-stack/',
- title: 'Deploy a MEAN Stack through the Linode Marketplace',
- },
- ],
- summary: `A MEAN (MongoDB, Express, Angular, Node.js) stack is a free and open-source web software bundle used to build modern web applications.`,
- website: 'http://meanjs.org/',
- },
- {
- alt_description: 'React and Node.js stack.',
- alt_name: 'Web stack',
- categories: [],
- colors: {
- end: '256291',
- start: '30383a',
- },
- description: `MERN is a full stack platform that contains everything you need to build a web application: MongoDB, a document database used to persist your application's data; Express, which serves as the web application framework; React, used to build your application's user interfaces;
- and Node.js, which serves as the run-time environment for your application. All of these technologies are well-established, offer robust feature sets, and are well-supported by their maintaining organizations. These characteristics make them a great choice for your applications. Upload your
- existing MERN website code to your new Linode, or use MERN's scaffolding tool to start writing new web applications on the Linode.`,
- logo_url: 'mern.svg',
- name: 'MERN',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/mern-stack/',
- title: 'Deploy a MERN Stack through the Linode Marketplace',
- },
- ],
- summary: `Build production-ready apps with the MERN stack: MongoDB, Express, React, and Node.js.`,
- },
- {
- alt_description: 'Drag and drop website CMS.',
- alt_name: 'Website builder',
- categories: ['Development'],
- colors: {
- end: '4592ff',
- start: '4592ff',
- },
- description: `Microweber is an easy Drag and Drop website builder and a powerful CMS of a new generation, based on the PHP Laravel Framework.`,
- logo_url: 'microweber.svg',
- name: 'Microweber',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/microweber/',
- title: 'Deploy Microweber through the Linode Marketplace',
- },
- ],
- summary: `Drag and drop CMS and website builder.`,
- website: 'https://microweber.com/',
- },
- {
- alt_description: 'Classic open world survival crafting game.',
- alt_name: 'World building game',
- categories: ['Games'],
- colors: {
- end: 'd0c8c4',
- start: '97948f',
- },
- description: `With over 100 million users around the world, Minecraft is the most popular online game of all time. Less of a game and more of a lifestyle choice, you and other players are free to build and explore in a 3D generated world made up of millions of mineable blocks. Collect resources by leveling mountains,
- taming forests, and venturing out to sea. Choose a home from the varied list of biomes like ice worlds, flower plains, and jungles. Build ancient castles or modern mega cities, and fill them with redstone circuit contraptions and villagers. Fight off nightly invasions of Skeletons, Zombies, and explosive
- Creepers, or adventure to the End and the Nether to summon the fabled End Dragon and the chaotic Wither. If that is not enough, Minecraft is also highly moddable and customizable. You decide the rules when hosting your own Minecraft server for you and your friends to play together in this highly addictive game.`,
- logo_url: 'minecraft.svg',
- name: 'Minecraft: Java Edition',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/minecraft/',
- title: 'Deploy a Minecraft Server through the Linode Marketplace',
- },
- ],
- summary: `Build, explore, and adventure in your own 3D generated world.`,
- website: 'https://www.minecraft.net/',
- },
- {
- alt_description: 'Open source course builder and education tool.',
- alt_name: 'Online course CMS',
- categories: ['Website'],
- colors: {
- end: '494949',
- start: 'ff7800',
- },
- description: `Robust open-source learning platform enabling online education for more than 200 million users around the world. Create personalized learning environments within a secure and integrated system built for all education levels with an intuitive interface, drag-and-drop features, and accessible documentation.`,
- logo_url: 'moodle.svg',
- name: 'Moodle',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/moodle/',
- title: 'Deploy Moodle through the Linode Marketplace',
- },
- ],
- summary:
- 'World’s most popular learning management system built and maintained by an active developer community.',
- website: 'https://docs.moodle.org/',
- },
- {
- alt_description: 'SQL database.',
- alt_name: 'SQL database',
- categories: ['Databases'],
- colors: {
- end: '8a9177',
- start: '1d758f',
- },
- description: `MySQL, or MariaDB for Linux distributions, is primarily used for web and server applications, including as a component of the industry-standard LAMP and LEMP stacks.`,
- logo_url: 'mysql.svg',
- name: 'MySQL/MariaDB',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/mysql/',
- title: 'Deploy MySQL/MariaDB through the Linode Marketplace',
- },
- ],
- summary: `World's most popular open source database.`,
- website: 'https://www.mysql.com/',
- },
- {
- alt_description: `Microservice centeric stream processing.`,
- alt_name: 'Microservice messaging bus',
- categories: ['Development'],
- colors: {
- end: '000000',
- start: '0086FF',
- },
- description:
- 'NATS is a distributed PubSub technology that enables applications to securely communicate across any combination of cloud vendors, on-premise, edge, web and mobile, and devices.',
- logo_url: 'nats.svg',
- name: 'NATS Single Node',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/nats-single-node/',
- title: 'Deploy NATS single node through the Linode Marketplace',
- },
- ],
- summary: 'Cloud native application messaging service.',
- website: 'https://nats.io',
- },
- {
- alt_description:
- 'File storage alternative to Dropbox and office suite alternative to Microsoft Office.',
- alt_name: 'File storage management & business tool suite',
- categories: ['Productivity'],
- colors: {
- end: '2a2a36',
- start: '16a5f3',
- },
- description: `Nextcloud AIO stands for Nextcloud All In One, and provides easy deployment and maintenance for popular Nextcloud tools. AIO includes Nextcloud, Nextcloud Office, OnlyOffice, and high-performance backend features.`,
- logo_url: 'nextcloud.svg',
- name: 'Nextcloud',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/nextcloud/',
- title: 'Deploy Nextcloud through the Linode Marketplace',
- },
- ],
- summary: `A safe home for all your data.`,
- },
- {
- alt_description:
- 'File storage and sharing alternative to Dropbox and Google Drive.',
- alt_name: 'File sharing',
- categories: ['Productivity'],
- colors: {
- end: '252730',
- start: '1f4c8f',
- },
- description: `Securely share and collaborate Linode S3 object storage files/folders with your internal or external users such as customers, partners, vendors, etc with fine access control and a simple interface. Nirvashare easily integrates with many external identity providers such as Active Directory, GSuite, AWS SSO, KeyClock, etc.`,
- logo_url: 'nirvashare.svg',
- name: 'NirvaShare',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/nirvashare/',
- title: 'Deploy NirvaShare through the Linode Marketplace',
- },
- ],
- summary:
- 'Secure file sharing for better collaboration with employees, partners, vendors, and more.',
- website: 'https://nirvashare.com/setup-guide/',
- },
- {
- alt_description:
- 'Versatile cross-platform JavaScript run-time (runtime) environment.',
- alt_name: 'JavaScript environment',
- categories: ['Development'],
- colors: {
- end: '333333',
- start: '3d853c',
- },
- description: `NodeJS is a free, open-source, and cross-platform JavaScript run-time environment that lets developers write command line tools and server-side scripts outside of a browser.`,
- logo_url: 'nodejs.svg',
- name: 'NodeJS',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/nodejs/',
- title: 'Deploy NodeJS through the Linode Marketplace',
- },
- ],
- summary:
- 'Popular and versatile open source JavaScript run-time environment.',
- website: 'https://nodejs.org/',
- },
- {
- alt_description:
- 'Open source marketing and business platform with a CRM and email marketing.',
- alt_name: 'Marketing tool suite',
- categories: ['Productivity'],
- colors: {
- end: '027e84',
- start: '55354c',
- },
- description: `Odoo is a free and comprehensive business app suite of tools that seamlessly integrate. Choose what you need to manage your business on a single platform, including a CRM, email marketing tools, essential project management functions, and more.`,
- logo_url: 'odoo.svg',
- name: 'Odoo',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/odoo/',
- title: 'Deploy Odoo through the Linode Marketplace',
- },
- ],
- summary:
- 'Open source, all-in-one business app suite with more than 7 million users.',
- website: 'https://www.odoo.com/',
- },
- {
- alt_description: 'Office Suite',
- alt_name: 'Office Docs',
- categories: ['Productivity'],
- colors: {
- end: 'ff6f3d',
- start: 'ffa85b',
- },
- description: `Create and collaborate on text documents, spreadsheets, and presentations compatible with popular file types including .docx, .xlsx, and more. Additional features include real-time editing, paragraph locking while co-editing, and version history.`,
- logo_url: 'onlyoffice.svg',
- name: 'ONLYOFFICE Docs',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/onlyoffice/',
- title: 'Deploy ONLYOFFICE Docs through the Linode Marketplace',
- },
- ],
- summary: 'Open source comprehensive office suite.',
- website: 'https://www.onlyoffice.com/',
- },
- {
- alt_description: 'Fast Python development with best practices.',
- alt_name: 'Python framework',
- categories: ['Development'],
- colors: {
- end: '5cbf8a',
- start: '318640',
- },
- description: `Simple deployment for OLS web server, Python LSAPI, and CertBot.`,
- logo_url: 'openlitespeeddjango.svg',
- name: 'OpenLiteSpeed Django',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/openlitespeed-django/',
- title: 'Deploy OpenLiteSpeed Django through the Linode Marketplace',
- },
- ],
- summary: 'OLS web server with Django development framework.',
- website: 'https://docs.litespeedtech.com/cloud/images/django/',
- },
- {
- alt_description:
- 'Versatile cross-platform JavaScript run-time (runtime) environment.',
- alt_name: 'JavaScript environment',
- categories: ['Development'],
- colors: {
- end: '33cccc',
- start: '3d596d',
- },
- description: `High-performance open source web server with Node and CertBot, in addition to features like HTTP/3 support and easy SSL setup.`,
- logo_url: 'openlitespeednodejs.svg',
- name: 'OpenLiteSpeed NodeJS',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/openlitespeed-nodejs/',
- title: 'Deploy OpenLiteSpeed Node.js through the Linode Marketplace',
- },
- ],
- summary: 'OLS web server with NodeJS JavaScript runtime environment.',
- website: 'https://docs.litespeedtech.com/cloud/images/nodejs/',
- },
- {
- alt_description: 'Ruby web application framework with development tools.',
- alt_name: 'Ruby web application framework.',
- categories: ['Development'],
- colors: {
- end: 'd94b7a',
- start: '8e1a4a',
- },
- description: `Easy setup to run Ruby apps in the cloud and take advantage of OpenLiteSpeed server features like SSL, HTTP/3 support, and RewriteRules.`,
- logo_url: 'openlitespeedrails.svg',
- name: 'OpenLiteSpeed Rails',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/openlitespeed-rails/',
- title: 'Deploy OpenLiteSpeed Rails through the Linode Marketplace ',
- },
- ],
- summary: 'OLS web server with Ruby and CertBot.',
- website: 'https://docs.litespeedtech.com/cloud/images/rails/',
- },
- {
- alt_description: 'Popular website content management system.',
- alt_name: 'CMS: content management system',
- categories: ['Website'],
- colors: {
- end: '3d596d',
- start: '33cccc',
- },
- description: `Accelerated and scalable hosting for WordPress. Includes OpenLiteSpeed, PHP, MySQL Server, WordPress, and LiteSpeed Cache.`,
- logo_url: 'openlitespeedwordpress.svg',
- name: 'OpenLiteSpeed WordPress',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/openlitespeed-wordpress/',
- title: 'Deploy OpenLiteSpeed Wordpress through the Linode Marketplace',
- },
- ],
- summary: 'Blazing fast, open source alternative to LiteSpeed Web Server.',
- website: 'https://openlitespeed.org/',
- },
- {
- alt_description: 'Popular virtual private network.',
- alt_name: 'Free VPN',
- categories: ['Security'],
- colors: {
- end: '193766',
- start: 'ea7e20',
- },
- description: `OpenVPN is a widely trusted, free, and open-source virtual private network application. OpenVPN creates network tunnels between groups of computers that are not on the same local network, and it uses OpenSSL to encrypt your traffic.`,
- logo_url: 'openvpn.svg',
- name: 'OpenVPN',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/openvpn/',
- title: 'Deploy OpenVPN through the Linode Marketplace',
- },
- ],
- summary: `Open-source virtual private network (VPN) application. OpenVPN securely connects your computer to your servers, or to the public Internet.`,
- website: 'https://openvpn.net/',
- },
- {
- alt_description: 'Video and audio live streaming alternative to Twitch.',
- alt_name: 'Live streaming app',
- categories: ['Media and Entertainment'],
- colors: {
- end: '2086e1',
- start: '7871ff',
- },
- description: `A live streaming and chat server for use with existing popular broadcasting software.`,
- logo_url: 'owncast.svg',
- name: 'Owncast',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/owncast/',
- title: 'Deploy Owncast through the Linode Marketplace',
- },
- ],
- summary:
- 'The standalone “Twitch in a Box” open source streaming and chat solution.',
- website: 'https://owncast.online/',
- },
- {
- alt_description: 'Self-hosted file sharing and collaboration platform.',
- alt_name: 'Collabrative file sharing',
- categories: ['Productivity'],
- colors: {
- end: '041e42',
- start: '041e42',
- },
- description: `LAMP-stack-based server application that allows you to access your files from anywhere in a secure way.`,
- logo_url: 'owncloud.svg',
- name: 'ownCloud',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/owncloud/',
- title: 'Deploy ownCloud through the Linode Marketplace',
- },
- ],
- summary:
- 'Dropbox and OneDrive alternative that lets you remain in control of your files.',
- website: 'https://doc.owncloud.com/docs/next/',
- },
- {
- alt_description: 'Password Manager',
- alt_name: 'Passbolt',
- categories: ['Security'],
- colors: {
- end: 'D40101',
- start: '171717',
- },
- description: `Passbolt is an open-source password manager designed for teams and businesses. It allows users to securely store, share and manage passwords.`,
- logo_url: 'passbolt.svg',
- name: 'Passbolt',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/passbolt/',
- title: 'Deploy Passbolt through the Linode Marketplace',
- },
- ],
- summary: 'Open-source password manager for teams and businesses.',
- website: 'https://www.passbolt.com/',
- },
- {
- alt_description: 'Password Manager',
- alt_name: 'Pass Key',
- categories: ['Security'],
- colors: {
- end: '3A5EFF',
- start: '709cff',
- },
- description: `Self-host a password manager designed to simplify and secure your digital life. Passky is a streamlined version of paid password managers designed for everyone to use.`,
- logo_url: 'passky.svg',
- name: 'Passky',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/passky/',
- title: 'Deploy Passky through the Linode Marketplace',
- },
- ],
- summary: 'Simple open source password manager.',
- website: 'https://passky.org/',
- },
- {
- alt_description: 'Open source project management tool.',
- alt_name: 'Ticket management project management tool',
- categories: ['Productivity'],
- colors: {
- end: '0a0a0a',
- start: '4cff4c',
- },
- description: `Open source alternative to paid ticket management solutions with essential features including a streamlined task list, project and client management, and ticket prioritization.`,
- logo_url: 'peppermint.svg',
- name: 'Peppermint',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/peppermint/',
- title: 'Deploy Peppermint through the Linode Marketplace',
- },
- ],
- summary: 'Simple yet scalable open source ticket management.',
- website: 'https://peppermint.sh/',
- },
- {
- alt_description:
- 'Web interface for MySQL/MariaDB operations and server administration.',
- alt_name: 'SQL database GUI',
- categories: ['Databases'],
- colors: {
- end: '6c78af',
- start: 'f89d10',
- },
- description: `Intuitive web interface for MySQL and MariaDB operations, including importing/exporting data, administering multiple servers, and global database search.`,
- logo_url: 'phpmyadmin.svg',
- name: 'phpMyAdmin',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/phpmyadmin/',
- title: 'Deploy phpMyAdmin through the Linode Marketplace',
- },
- ],
- summary: 'Popular free administration tool for MySQL and MariaDB.',
- website: 'https://www.phpmyadmin.net/',
- },
- {
- alt_description: 'Popular DNS privacy sinkhole.',
- alt_name: 'Network ad blocking',
- categories: ['Security'],
- colors: {
- end: 'f60d1a',
- start: '96060c',
- },
- description: `Protect your network and devices from unwanted content. Avoid ads in non-browser locations with a free, lightweight, and comprehensive privacy solution you can self-host.`,
- logo_url: 'pihole.svg',
- name: 'Pi-hole',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/pihole/',
- title: 'Deploy Pi-hole through the Linode Marketplace',
- },
- ],
- summary: 'Free, open source, and highly scalable DNS sinkhole.',
- website: 'https://pi-hole.net/',
- },
- {
- alt_description: 'Popular WordPress server management.',
- alt_name: 'WordPress control panel',
- categories: ['Control Panels'],
- colors: {
- end: '4b5868',
- start: '53bce6',
- },
- description: `Plesk is a leading WordPress and website management platform and control panel. Plesk lets you build and manage multiple websites from a single dashboard to configure web services, email, and other applications. Plesk features hundreds of extensions, plus a complete WordPress toolkit. Use the Plesk One-Click App to manage websites hosted on your Linode.`,
- logo_url: 'plesk.svg',
- name: 'Plesk',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/plesk/',
- title: 'Deploy Plesk through the Linode Marketplace',
- },
- ],
- summary:
- 'A secure, scalable, and versatile website and WordPress management platform.',
- website: 'https://www.plesk.com/',
- },
- {
- alt_description:
- 'Video / media library storage and sharing across TVs, phones, computers, and more.',
- alt_name: 'Media server',
- categories: [],
- colors: {
- end: '332c37',
- start: 'e5a00d',
- },
- description: `Organize, stream, and share your media library with friends, in addition to free live TV in 220+ countries.`,
- logo_url: 'plex.svg',
- name: 'Plex',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/plex/',
- title: 'Deploy Plex Media Server through the Linode Marketplace',
- },
- ],
- summary:
- 'Media server and streaming service to stay entertained across devices.',
- website: 'https://www.plex.tv/',
- },
- {
- alt_description: 'MySQL alternative for SQL database.',
- alt_name: 'SQL database',
- categories: ['Databases'],
- colors: {
- end: '254078',
- start: '326690',
- },
- description: `PostgreSQL is a popular open source relational database system that provides many advanced configuration options that can help optimize your database’s performance in a production environment.`,
- logo_url: 'postgresql.svg',
- name: 'PostgreSQL',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/postgresql/',
- title: 'Deploy PostgreSQL through the Linode Marketplace',
- },
- ],
- summary: `The PostgreSQL relational database system is a powerful, scalable, and standards-compliant open-source database platform.`,
- website: 'https://www.postgresql.org/',
- },
- {
- alt_description: 'MySQL alternative for SQL database.',
- alt_name: 'SQL database',
- categories: ['Databases'],
- colors: {
- end: '254078',
- start: '326690',
- },
- description: `PostgreSQL is a popular open source relational database system that provides many advanced configuration options that can help optimize your database’s performance in a production environment.`,
- logo_url: 'postgresqlmarketplaceocc.svg',
- name: 'PostgreSQL Cluster',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/postgresql-cluster/',
- title: 'Deploy PostgreSQL Cluster through the Linode Marketplace',
- },
- ],
- summary: `The PostgreSQL relational database system is a powerful, scalable, and standards-compliant open-source database platform.`,
- website: 'https://www.postgresql.org/',
- },
- {
- alt_description: 'Virtual private network for businesses and teams.',
- alt_name: 'Enterprise VPN',
- categories: ['Security'],
- colors: {
- end: '2e72d2',
- start: '2e4153',
- },
- description: `User-friendly VPN for both individual and commercial use. Choose from three pricing plans.`,
- logo_url: 'pritunl.svg',
- name: 'Pritunl',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/pritunl/',
- title: 'Deploy Pritunl through the Linode Marketplace',
- },
- ],
- summary: 'Enterprise open source VPN.',
- website: 'https://docs.pritunl.com/docs',
- },
- {
- alt_description: 'Monitoring server.',
- alt_name: 'Server monitoring and visualization',
- categories: ['Monitoring'],
- colors: {
- end: 'e6522c',
- start: 'f9b716',
- },
- description: `Free industry-standard monitoring tools that work better together. Prometheus is a powerful monitoring software tool that collects metrics from configurable data points at given intervals, evaluates rule expressions, and can trigger alerts if some condition is observed. Use Grafana to create visuals, monitor, store, and share metrics with your team to keep tabs on your infrastructure.`,
- logo_url: 'prometheusgrafana.svg',
- name: 'Prometheus & Grafana',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/prometheus-grafana/',
- title: 'Deploy Prometheus & Grafana through the Linode Marketplace',
- },
- ],
- summary: 'Open source metrics and monitoring for real-time insights.',
- website: 'https://prometheus.io/docs/visualization/grafana/',
- },
- {
- alt_description: 'Server work queue management.',
- alt_name: 'Message broker',
- categories: ['Development'],
- colors: {
- end: 'ff6600',
- start: 'a9b5af',
- },
- description: `Connect and scale applications with asynchronous messaging and highly available work queues, all controlled through an intuitive management UI.`,
- logo_url: 'rabbitmq.svg',
- name: 'RabbitMQ',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/rabbitmq/',
- title: 'Deploy RabbitMQ through the Linode Marketplace',
- },
- ],
- summary: 'Most popular open source message broker.',
- website: 'https://www.rabbitmq.com/',
- },
- {
- alt_description: 'In-memory caching database.',
- alt_name: 'High performance database',
- categories: ['Databases'],
- colors: {
- end: '722b20',
- start: '222222',
- },
- description: `Redis® is an open-source, in-memory, data-structure store, with the optional ability to write and persist data to a disk, which can be used as a key-value database, cache, and message broker. Redis® features built-in transactions, replication, and support for a variety of data structures such as strings, hashes, lists, sets, and others.
*Redis is a registered trademark of Redis Ltd. Any rights therein are reserved to Redis Ltd. Any use by Akamai Technologies is for referential purposes only and does not indicate any sponsorship, endorsement or affiliation between Redis and Akamai Technologies.`,
- logo_url: 'redis.svg',
- name: 'Marketplace App for Redis®',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/redis/',
- title: 'Deploy Redis® through the Linode Marketplace',
- },
- ],
- summary:
- 'Flexible, in-memory, NoSQL database service supported in many different coding languages.',
- website: 'https://redis.io/',
- },
- {
- alt_description: 'In-memory caching database.',
- alt_name: 'High performance database',
- categories: ['Databases'],
- colors: {
- end: '722b20',
- start: '222222',
- },
- description: `Redis® is an open-source, in-memory, data-structure store, with the optional ability to write and persist data to a disk, which can be used as a key-value database, cache, and message broker. Redis® features built-in transactions, replication, and support for a variety of data structures such as strings, hashes, lists, sets, and others.
*Redis is a registered trademark of Redis Ltd. Any rights therein are reserved to Redis Ltd. Any use by Akamai Technologies is for referential purposes only and does not indicate any sponsorship, endorsement or affiliation between Redis and Akamai Technologies.`,
- logo_url: 'redissentinelmarketplaceocc.svg',
- name: 'Marketplace App for Redis® Sentinel Cluster',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/redis-cluster/',
- title:
- 'Deploy Redis® Sentinel Cluster through the Linode Marketplace',
- },
- ],
- summary:
- 'Flexible, in-memory, NoSQL database service supported in many different coding languages.',
- website: 'https://redis.io/',
- },
- {
- alt_description: 'Free alternative to Trello and Asana.',
- alt_name: 'Kanban board project management tool',
- categories: ['Productivity'],
- colors: {
- end: '555555',
- start: 'f47564',
- },
- description: `Restyaboard is an open-source alternative to Trello, but with additional smart features like offline sync, diff /revisions, nested comments, multiple view layouts, chat, and more.`,
- logo_url: 'restyaboard.svg',
- name: 'Restyaboard',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/restyaboard/',
- title: 'Deploy Restyaboard through the Linode Marketplace',
- },
- ],
- summary: 'Free and open source project management tool.',
- website: 'https://restya.com',
- },
- {
- alt_description: 'Free alternative to Slack, Microsoft Teams, and Skype.',
- alt_name: 'Chat software',
- categories: ['Productivity'],
- colors: {
- end: '030d1a',
- start: 'f5445c',
- },
- description: `Put data privacy first with an alternative to programs like Slack and Microsoft Teams.`,
- logo_url: 'rocketchat.svg',
- name: 'Rocket.Chat',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/rocketchat/',
- title: 'Deploy Rocket.Chat through the Linode Marketplace',
- },
- ],
- summary: 'Feature-rich self-hosted chat and collaboration platform.',
- website: 'https://docs.rocket.chat/',
- },
- {
- alt_description: 'Ruby web application framework with development tools.',
- alt_name: 'Web application framework',
- categories: ['Development'],
- colors: {
- end: 'fa9999',
- start: '722b20',
- },
- description: `Rails is a web application development framework written in the Ruby programming language. It is designed to make programming web applications easier by giving every developer a number of common tools they need to get started. Ruby on Rails empowers you to accomplish more with less code.`,
- logo_url: 'rubyonrails.svg',
- name: 'Ruby on Rails',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/ruby-on-rails/',
- title: 'Deploy Ruby on Rails through the Linode Marketplace',
- },
- ],
- summary: `Ruby on Rails is a web framework that allows web designers and developers to implement dynamic, fully featured web applications.`,
- website: 'https://rubyonrails.org/',
- },
- {
- alt_description: 'Database low-code/no-code application builder.',
- alt_name: 'Low-code application builder',
- categories: ['Development'],
- colors: {
- end: 'ff8e42',
- start: '995ad9',
- },
- description: `Build applications without writing a single line of code. Saltcorn is a free platform that allows you to build an app with an intuitive point-and-click, drag-and-drop UI.`,
- logo_url: 'saltcorn.svg',
- name: 'Saltcorn',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/saltcorn/',
- title: 'Deploy Saltcorn through the Linode Marketplace',
- },
- ],
- summary: 'Open source, no-code database application builder.',
- website: 'https://saltcorn.com/',
- },
- {
- alt_description: 'A safe home for all your data.',
- alt_name:
- 'Spreadsheet style interface with the power of a relational database.',
- categories: ['Productivity'],
- colors: {
- end: 'FF8000',
- start: 'FF8000',
- },
- description: `Self-hosted database for a variety of management projects.`,
- logo_url: 'seatable.svg',
- name: 'Seatable',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/seatable/',
- title: 'Deploy Seatable through the Linode Marketplace',
- },
- ],
- summary:
- 'Collaborative web interface for data backed project and process management.',
- website: 'https://seatable.io/docs/?lang=auto',
- },
- {
- alt_description: 'Limited user, hardened SSH, Fail2Ban Linode server.',
- alt_name: 'Secure server tool',
- categories: ['Security'],
- colors: {
- end: '32363b',
- start: '01b058',
- },
- description: `Save time on securing your Linode by deploying an instance pre-configured with some basic security best practices: limited user account access, hardened SSH, and Fail2Ban for SSH Login Protection.`,
- logo_url: 'secureyourserver.svg',
- name: 'Secure Your Server',
- related_guides: [
- {
- href: 'https://www.linode.com/docs/guides/set-up-and-secure/',
- title: 'Securing your Server',
- },
- ],
- summary: `Harden your Linode before you deploy with the Secure Your Server One-Click App.`,
- },
- {
- alt_description: 'Host multiple sites on a Linode.',
- alt_name: 'Website control panel',
- categories: ['Control Panels'],
- colors: {
- end: 'a25c57',
- start: '4c3148',
- },
- description: `Host multiple sites on a single server while managing apps, firewall, databases, backups, system users, cron jobs, SSL and email– all in an intuitive interface.`,
- logo_url: 'serverwand.svg',
- name: 'ServerWand',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/serverwand/',
- title: 'Deploy ServerWand through the Linode Marketplace',
- },
- ],
- summary:
- 'Magical control panel for hosting websites and managing your servers.',
- website: 'https://serverwand.com/',
- },
- {
- alt_description: 'Secure SOCKS5 web proxy with data encryption.',
- alt_name: 'VPN proxy',
- categories: ['Security'],
- colors: {
- end: '8d8d8d',
- start: '227dc0',
- },
- description:
- 'Shadowsocks is a lightweight SOCKS5 web proxy tool. A full setup requires a Linode server to host the Shadowsocks daemon, and a client installed on PC, Mac, Linux, or a mobile device. Unlike other proxy software, Shadowsocks traffic is designed to be both indiscernible from other traffic to third-party monitoring tools, and also able to disguise itself as a normal direct connection. Data passing through Shadowsocks is encrypted for additional security and privacy.',
- logo_url: 'shadowsocks.svg',
- name: 'Shadowsocks',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/shadowsocks/',
- title: 'Deploy Shadowsocks through the Linode Marketplace',
- },
- ],
- summary:
- 'A secure socks5 proxy, designed to protect your Internet traffic.',
- website: 'https://shadowsocks.org/',
- },
- {
- alt_description: 'Data security, data observability, data automation.',
- alt_name: 'Data management',
- categories: ['Development'],
- colors: {
- end: 'ed0181',
- start: 'f89f24',
- },
- description: `Popular data-to-everything platform with advanced security, observability, and automation features for machine learning and AI.`,
- logo_url: 'splunk.svg',
- name: 'Splunk',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/splunk/',
- title: 'Deploy Splunk through the Linode Marketplace',
- },
- ],
- summary:
- 'All-in-one database deployment, management, and monitoring system.',
- website: 'https://docs.splunk.com/Documentation/Splunk',
- },
- {
- alt_description: 'A private by design messaging platform.',
- alt_name: 'Anonymous messaging platform.',
- categories: ['Productivity'],
- colors: {
- end: '70f0f9',
- start: '11182f',
- },
- description: `SimpleX Chat - The first messaging platform that has no user identifiers of any kind - 100% private by design. SMP server is the relay server used to pass messages in SimpleX network. XFTP is a new file transfer protocol focussed on meta-data protection. This One-Click APP will deploy both SMP and XFTP servers.`,
- logo_url: 'simplexchat.svg',
- name: 'SimpleX Chat',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/simplex/',
- title: 'Deploy SimpleX chat through the Linode Marketplace',
- },
- ],
- summary: 'Private by design messaging server.',
- website: 'https://simplex.chat',
- },
- {
- alt_description:
- 'A simple SQL interface to store and search unstructured data.',
- alt_name: 'SuperinsightDB',
- categories: ['Databases'],
- colors: {
- end: 'C54349',
- start: 'E6645F',
- },
- description: `Superinsight provides a simple SQL interface to store and search unstructured data. Superinsight is built on top of PostgreSQL to take advantage of powerful extensions and features, plus the ability to run machine learning operations using SQL statements.`,
- logo_url: 'superinsight.svg',
- name: 'Superinsight',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/superinsight/',
- title: 'Deploy Superinsight through the Linode Marketplace',
- },
- ],
- summary: 'Relational database for unstructured data.',
- website: 'https://www.superinsight.ai/',
- },
- {
- alt_description:
- 'Infrastructure monitoring and aler alternative to Uptime Robot.',
- alt_name: 'Infrastructure monitoring',
- categories: ['Monitoring'],
- colors: {
- end: 'baecca',
- start: '67de92',
- },
- description: `Uptime Kuma is self-hosted alternative to Uptime Robot. Get real-time performance insights for HTTP(s), TCP/ HTTP(s) Keyword, Ping, DNS Record, and more. Monitor everything you need in one UI dashboard, or customize how you receive alerts with a wide range of supported integrations.`,
- logo_url: 'uptimekuma.svg',
- name: 'Uptime Kuma',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/uptime-kuma/',
- title: 'Deploy Uptime Kuma through the Linode Marketplace',
- },
- ],
- summary: 'Free, comprehensive, and “fancy” monitoring solution.',
- website: 'https://github.com/louislam/uptime-kuma',
- },
- {
- alt_description: 'Virtual private network.',
- alt_name: 'VPN',
- categories: ['Security'],
- colors: {
- end: '1a32b1',
- start: '2ec1cf',
- },
- description: `UTunnel VPN is a robust cloud-based VPN server software solution. With UTunnel VPN, businesses could easily set up secure remote access to their business network. UTunnel comes with a host of business-centric features including site-to-site connectivity, single sign-on integration, 2-factor authentication, etc.`,
- logo_url: 'utunnel.svg',
- name: 'UTunnel VPN',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/utunnel/',
- title: 'Deploy UTunnel VPN through the Linode Marketplace',
- },
- ],
- summary:
- 'A powerful, user-friendly Virtual Private Network (VPN) server application that supports multiple VPN protocols.',
- website: 'https://www.utunnel.io/linode-vpn-server.html',
- },
- {
- alt_description: 'Time series database and database monitoring/metrics.',
- alt_name: 'Database monitoring',
- categories: ['Databases'],
- colors: {
- end: 'af3e56',
- start: '6a1e6e',
- },
- description: `VictoriaMetrics is designed to collect, store, and process real-time metrics.`,
- logo_url: 'victoriametricssingle.svg',
- name: 'VictoriaMetrics Single',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/victoriametrics-single/',
- title: 'Deploy VictoriaMetrics Single through the Linode Marketplace',
- },
- ],
- summary:
- 'Free and open source time series database (TSDB) and monitoring solution.',
- website: 'https://victoriametrics.com/',
- },
- {
- alt_description: 'Fancy development text editor.',
- alt_name: 'Text editor',
- categories: ['Development'],
- colors: {
- end: '0066b8',
- start: '23a9f2',
- },
- description: `Launch a portable development environment to speed up tests, downloads, and more.`,
- logo_url: 'vscodeserver.svg',
- name: 'VS Code Server',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/vscode/',
- title: 'Deploy VS Code through the Linode Marketplace',
- },
- ],
- summary: 'Run VS code in the cloud, right from your browser.',
- website: 'https://github.com/cdr/code-server',
- },
- {
- alt_description: 'Virtual private network.',
- alt_name: 'WireGuard VPN',
- categories: ['Security'],
- colors: {
- end: '333333',
- start: '1f76b7',
- },
- description: `Feature-rich, self-hosted VPN based on WireGuard® protocol, plus convenient features like single sign-on, real-time bandwidth monitoring, and unlimited users/devices.`,
- logo_url: 'warpspeed.svg',
- name: 'WarpSpeed',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/warpspeed/',
- title: 'Deploy WarpSpeed VPN through the Linode Marketplace',
- },
- ],
- summary: 'Secure low-latency VPN powered by WireGuard® protocol.',
- website: 'https://bunker.services/products/warpspeed',
- },
- {
- alt_description:
- 'Security analytics for intrusion attempts and user action monitoring.',
- alt_name: 'Security monitoring',
- categories: ['Security'],
- colors: {
- end: 'ffb600',
- start: '00a9e5',
- },
- description: `Infrastructure monitoring solution to detect threats, intrusion attempts, unauthorized user actions, and provide security analytics.`,
- logo_url: 'wazuh.svg',
- name: 'Wazuh',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/wazuh/',
- title: 'Deploy Wazuh through the Linode Marketplace',
- },
- ],
- summary: 'Free open source security monitoring solution.',
- website: 'https://documentation.wazuh.com/current/index.html',
- },
- {
- alt_description:
- 'Control panel to deploy and manage LAMP stack applications.',
- alt_name: 'Single user control panel',
- categories: ['Control Panels'],
- colors: {
- end: '445289',
- start: 'f1b55d',
- },
- description: `Lightweight control panel with a suite of features to streamline app management.`,
- logo_url: 'webuzo.svg',
- name: 'Webuzo',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/webuzo/',
- title: 'Deploy Webuzo through the Linode Marketplace',
- },
- ],
- summary:
- 'LAMP stack and single user control panel to simplify app deployment in the cloud.',
- website: 'http://www.webuzo.com/',
- },
- {
- alt_description: 'Virtual private network.',
- alt_name: 'Free VPN',
- categories: ['Security'],
- colors: {
- end: '51171a',
- start: '88171a',
- },
- description: `Configuring WireGuard® is as simple as configuring SSH. A connection is established by an exchange of public keys between server and client, and only a client whose public key is present in the server's configuration file is considered authorized. WireGuard sets up
- standard network interfaces which behave similarly to other common network interfaces, like eth0. This makes it possible to configure and manage WireGuard interfaces using standard networking tools such as ifconfig and ip. "WireGuard" is a registered trademark of Jason A. Donenfeld.`,
- logo_url: 'wireguard.svg',
- name: 'WireGuard®',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/wireguard/',
- title: 'Deploy WireGuard through the Linode Marketplace',
- },
- ],
- summary: `Modern VPN which utilizes state-of-the-art cryptography. It aims to be faster and leaner than other VPN protocols and has a smaller source code footprint.`,
- website: 'https://www.wireguard.com/',
- },
- {
- alt_description: 'Popular secure WordPress ecommerce online store plugin.',
- alt_name: 'Ecommerce site',
- categories: ['Website'],
- colors: {
- end: '743b8a',
- start: '96588a',
- },
- description: `With WooCommerce, you can securely sell both digital and physical goods, and take payments via major credit cards, bank transfers, PayPal, and other providers like Stripe. With more than 300 extensions to choose from, WooCommerce is extremely flexible.`,
- logo_url: 'woocommerce.svg',
- name: 'WooCommerce',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/woocommerce/',
- title: 'Deploy WooCommerce through the Linode Marketplace',
- },
- ],
- summary: `Highly customizable, secure, open source eCommerce platform built to integrate with Wordpress.`,
- website: 'https://woocommerce.com/features/',
- },
- {
- alt_description: 'Popular website content management system.',
- alt_name: 'CMS: content management system',
- categories: ['Website'],
- colors: {
- end: '135478',
- start: '176086',
- },
- description: `With 60 million users around the globe, WordPress is the industry standard for custom websites such as blogs, news sites, personal websites, and anything in-between. With a focus on best in class usability and flexibility, you can have a customized website up and running in minutes.`,
- logo_url: 'wordpress.svg',
- name: 'WordPress',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/wordpress/',
- title: 'Deploy WordPress through the Linode Marketplace',
- },
- ],
- summary:
- 'Flexible, open source content management system (CMS) for content-focused websites of any kind.',
- website: 'https://wordpress.org/',
- },
- {
- alt_description: 'Web interface for managing Docker containers.',
- alt_name: 'Docker GUI',
- categories: ['Development'],
- colors: {
- end: 'c4c4c4',
- start: '41b883',
- },
- description: `Simplify Docker deployments and make containerization easy for anyone to use. Please note: Yacht is still in alpha and is not recommended for production use.`,
- logo_url: 'yacht.svg',
- name: 'Yacht',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/yacht/',
- title: 'Deploy Yacht through the Linode Marketplace',
- },
- ],
- summary: 'Intuitive web interface for managing Docker containers.',
- website: 'https://github.com/SelfhostedPro/Yacht/',
- },
- {
- alt_description: 'Enterprise infrastructure and IT resource montioring.',
- alt_name: 'Infrastructure monitoring',
- categories: ['Monitoring'],
- colors: {
- end: '252730',
- start: 'd40000',
- },
- description: `Monitor, track performance and maintain availability for network servers, devices, services and other IT resources– all in one tool.`,
- logo_url: 'zabbix.svg',
- name: 'Zabbix',
- related_guides: [
- {
- href:
- 'https://www.linode.com/docs/products/tools/marketplace/guides/zabbix/',
- title: 'Deploy Zabbix through the Linode Marketplace',
- },
- ],
- summary: 'Enterprise-class open source distributed monitoring solution.',
- website: 'https://www.zabbix.com',
- },
- {
- ...oneClickAppFactory.build({
- name: 'E2E Test App',
- }),
- },
-];
+export const oneClickApps: OCA[] = Object.values(newOneClickApps);
diff --git a/packages/manager/src/features/OneClickApps/oneClickAppsv2.ts b/packages/manager/src/features/OneClickApps/oneClickAppsv2.ts
index 1096f8ba8a8..e447e0246f6 100644
--- a/packages/manager/src/features/OneClickApps/oneClickAppsv2.ts
+++ b/packages/manager/src/features/OneClickApps/oneClickAppsv2.ts
@@ -2466,6 +2466,7 @@ export const oneClickApps: Record = {
start: '333333',
},
description: `Couchbase Enterprise Server is a high-performance NoSQL database, built for scale. Couchbase Server is designed with memory-first architecture, built-in cache and workload isolation.`,
+ isNew: true,
logo_url: 'couchbase.svg',
name: 'Couchbase Cluster',
related_guides: [
@@ -2488,6 +2489,7 @@ export const oneClickApps: Record = {
start: '00C7D4',
},
description: `Apache Kafka supports a wide range of applications from log aggregation to real-time analytics. Kafka provides a foundation for building data pipelines, event-driven architectures, or stream processing applications.`,
+ isNew: true,
logo_url: 'apachekafka.svg',
name: 'Apache Kafka Cluster',
related_guides: [
diff --git a/packages/manager/src/features/OneClickApps/types.ts b/packages/manager/src/features/OneClickApps/types.ts
index 5e5bf88d501..47110fcd0e5 100644
--- a/packages/manager/src/features/OneClickApps/types.ts
+++ b/packages/manager/src/features/OneClickApps/types.ts
@@ -5,6 +5,13 @@ export interface OCA {
colors: Colors;
description: string;
href?: string;
+ /**
+ * Set isNew to `true` if you want the app to show up in the "New apps"
+ * section on the Linode Create flow.
+ *
+ * @note this value only affects Linode Create v2
+ */
+ isNew?: boolean;
logo_url: string;
name: string;
related_guides?: Doc[];
diff --git a/packages/manager/src/features/StackScripts/stackScriptUtils.ts b/packages/manager/src/features/StackScripts/stackScriptUtils.ts
index 30f60a10419..144645ead73 100644
--- a/packages/manager/src/features/StackScripts/stackScriptUtils.ts
+++ b/packages/manager/src/features/StackScripts/stackScriptUtils.ts
@@ -2,7 +2,7 @@ import { Grant } from '@linode/api-v4/lib/account';
import { StackScript, getStackScripts } from '@linode/api-v4/lib/stackscripts';
import { Filter, Params, ResourcePage } from '@linode/api-v4/lib/types';
-import { StackScriptsRequest } from './types';
+import type { StackScriptsRequest } from './types';
export type StackScriptCategory = 'account' | 'community';
@@ -13,128 +13,6 @@ export const emptyResult: ResourcePage = {
results: 0,
};
-/**
- * We need a way to make sure that newly added SS that meet
- * our filtering criteria don't automatically end up being
- * shown to the user before we've updated Cloud to support them.
- */
-export const baseApps = {
- '401697': 'WordPress - Latest One-Click',
- '401698': 'Drupal - Latest One-Click',
- '401701': 'LAMP One-Click',
- '401702': 'MERN One-Click',
- '401706': 'WireGuard - Latest One-Click',
- '401707': 'GitLab - Latest One-Click',
- '401708': 'WooCommerce - Latest One-Click',
- '401709': 'Minecraft - Latest One-Click',
- '401719': 'OpenVPN - Latest One-Click',
- '593835': 'Plesk One-Click',
- '595742': 'cPanel One-Click',
- '604068': 'Shadowsocks - Latest One-Click',
- '606691': 'LEMP - Latest One-Click',
- '607026': 'MySQL - Latest One-Click',
- '607401': 'Jenkins - Latest One-Click',
- '607433': 'Docker - Latest One-Click',
- '607488': 'Redis One-Click',
- '609018': 'phpMYAdmin',
- '609048': 'Ruby on Rails One-Click',
- '609175': 'Django One-Click',
- '609392': 'Flask One-Click',
- '611376': 'PostgreSQL One-Click',
- '611895': 'MEAN One-Click',
- '632758': 'Nextcloud',
- '662118': 'Azuracast',
- '662119': 'Plex',
- '662121': 'Jitsi',
- '688890': 'RabbitMQ',
- '688891': 'Discourse',
- '688902': 'Webuzo',
- '688903': 'Code Server',
- '688911': 'Gitea',
- '688912': 'Kepler Builder One-Click',
- '688914': 'Guacamole',
- '691620': 'FileCloud',
- '691621': 'Cloudron',
- '691622': 'OpenLiteSpeed',
- '692092': 'Secure Your Server',
- '741206': 'CyberPanel',
- '741207': 'Yacht',
- '741208': 'Zabbix',
- '774829': 'ServerWand',
- '804143': 'Peppermint',
- '804144': 'Ant Media Server',
- '804172': 'Owncast',
- '869127': 'Moodle',
- '869129': 'aaPanel',
- '869153': 'Splunk',
- '869155': 'Chevereto',
- '869156': 'NirvaShare',
- '869158': 'ClusterControl',
- '869623': 'JetBackup',
- '912262': 'Harbor',
- '912264': 'Rocket.Chat',
- '913276': 'Wazuh',
- '913277': 'BeEF',
- '923029': 'OpenLiteSpeed Django',
- '923030': 'OpenLiteSpeed Rails',
- '923031': 'OpenLiteSpeed NodeJS',
- '923032': 'LiteSpeed cPanel',
- '923033': 'Akaunting',
- '923036': 'Restyaboard',
- '923037': 'WarpSpeed',
- '925530': 'UTunnel VPN',
- '925722': 'Pritunl',
- '954759': 'VictoriaMetrics',
- '970522': 'Pi-hole',
- '970523': 'Uptime Kuma',
- '970559': 'Grav',
- '970561': 'NodeJS',
- '971042': 'Saltcorn',
- '971043': 'Odoo',
- '971045': 'Focalboard',
- '985364': 'Prometheus & Grafana',
- '985372': 'Joomla',
- '985374': 'Ant Media Enterprise Edition',
- '985380': 'Joplin',
- '1008123': 'Liveswitch',
- '1008125': 'Easypanel',
- '1017300': 'Kali Linux',
- '1037036': 'Budibase',
- '1037037': 'HashiCorp Nomad',
- '1037038': 'HashiCorp Vault',
- '1051714': 'Microweber',
- '1068726': 'PostgreSQL Cluster',
- '1088136': 'Galera Cluster',
- '1096122': 'Mastodon',
- '1102900': 'Apache Airflow',
- '1102902': 'HaltDOS Community WAF',
- '1102904': 'Superinsight',
- '1102905': 'Gopaddle',
- '1102906': 'Passky',
- '1102907': 'ONLYOFFICE Docs',
- '1132204': 'Redis Sentinel Cluster',
- '1160816': 'ownCloud',
- '1160820': 'Appwrite',
- '1177225': 'Seatable',
- '1177605': 'Illa Builder',
- '1226544': 'HashiCorp Nomad Cluster',
- '1226545': 'HashiCorp Nomad Clients Cluster',
- '1243759': 'MainConcept FFmpeg Plugins Demo',
- '1243760': 'MainConcept Live Encoder Demo',
- '1243762': 'MainConcept P2 AVC ULTRA Transcoder Demo',
- '1243763': 'MainConcept XAVC Transcoder Demo',
- '1243764': 'MainConcept XDCAM Transcoder Demo',
- '1243780': 'SimpleX Chat',
- '1298017': 'JupyterLab',
- '1308539': 'NATS Single Node',
- '1329430': 'Passbolt',
- '1329462': 'LinuxGSM',
- '1350733': 'Jitsi Cluster',
- '1350783': 'GlusterFS Cluster',
- '1366191': 'Couchbase Cluster',
- '1377657': 'Apache Kafka Cluster',
-};
-
const oneClickFilter = [
{
'+and': [
diff --git a/packages/manager/src/queries/stackscripts.ts b/packages/manager/src/queries/stackscripts.ts
index 5799751d14b..9dd389ff268 100644
--- a/packages/manager/src/queries/stackscripts.ts
+++ b/packages/manager/src/queries/stackscripts.ts
@@ -12,6 +12,7 @@ import {
import { createQueryKeys } from '@lukemorales/query-key-factory';
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
+import { oneClickApps } from 'src/features/OneClickApps/oneClickAppsv2';
import { getOneClickApps } from 'src/features/StackScripts/stackScriptUtils';
import { EventHandlerData } from 'src/hooks/useEventHandlers';
import { getAll } from 'src/utilities/getAll';
@@ -30,7 +31,10 @@ const stackscriptQueries = createQueryKeys('stackscripts', {
queryKey: [filter],
}),
marketplace: {
- queryFn: () => getAllOCAsRequest(),
+ queryFn: async () => {
+ const stackscripts = await getAllOCAsRequest();
+ return stackscripts.filter((s) => oneClickApps[s.id]);
+ },
queryKey: null,
},
stackscript: (id: number) => ({
From c03915e5b0c37ef5ba8f5afd839e852bfcaea0c6 Mon Sep 17 00:00:00 2001
From: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com>
Date: Mon, 3 Jun 2024 15:54:25 -0400
Subject: [PATCH 046/163] upcoming: [M3-8083] - Linode Create v2 - Disk
Encryption (#10535)
* add disk encryption to Linode Create v2
* improve testing and add changeset
* use better way to format label
---------
Co-authored-by: Banks Nussman
---
...r-10535-upcoming-features-1717175664387.md | 5 ++
.../components/AccessPanel/AccessPanel.tsx | 2 +-
.../DiskEncryption/DiskEncryption.test.tsx | 6 +-
.../DiskEncryption/DiskEncryption.tsx | 12 +++-
.../Linodes/LinodeCreatev2/Access.test.tsx | 58 ++++++++++++++++++-
.../Linodes/LinodeCreatev2/Access.tsx | 43 +++++++++++++-
.../LinodeCreatev2/Addons/Backups.test.tsx | 23 ++++++--
.../Linodes/LinodeCreatev2/Addons/Backups.tsx | 43 ++++++++++----
.../Linodes/LinodeCreatev2/Summary.test.tsx | 13 +++++
.../Linodes/LinodeCreatev2/Summary.tsx | 14 ++++-
10 files changed, 191 insertions(+), 28 deletions(-)
create mode 100644 packages/manager/.changeset/pr-10535-upcoming-features-1717175664387.md
diff --git a/packages/manager/.changeset/pr-10535-upcoming-features-1717175664387.md b/packages/manager/.changeset/pr-10535-upcoming-features-1717175664387.md
new file mode 100644
index 00000000000..53e91bb10f5
--- /dev/null
+++ b/packages/manager/.changeset/pr-10535-upcoming-features-1717175664387.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Upcoming Features
+---
+
+Linode Create v2 - Disk Encryption ([#10535](https://github.com/linode/manager/pull/10535))
diff --git a/packages/manager/src/components/AccessPanel/AccessPanel.tsx b/packages/manager/src/components/AccessPanel/AccessPanel.tsx
index d4c0613adbd..f0b8d393b00 100644
--- a/packages/manager/src/components/AccessPanel/AccessPanel.tsx
+++ b/packages/manager/src/components/AccessPanel/AccessPanel.tsx
@@ -115,7 +115,7 @@ export const AccessPanel = (props: Props) => {
disabled={!regionSupportsDiskEncryption}
disabledReason={DISK_ENCRYPTION_UNAVAILABLE_IN_REGION_COPY}
isEncryptDiskChecked={diskEncryptionEnabled ?? false}
- toggleDiskEncryptionEnabled={toggleDiskEncryptionEnabled}
+ onChange={() => toggleDiskEncryptionEnabled()}
/>
>
) : null;
diff --git a/packages/manager/src/components/DiskEncryption/DiskEncryption.test.tsx b/packages/manager/src/components/DiskEncryption/DiskEncryption.test.tsx
index 46dbcb2fbb4..3226a4677de 100644
--- a/packages/manager/src/components/DiskEncryption/DiskEncryption.test.tsx
+++ b/packages/manager/src/components/DiskEncryption/DiskEncryption.test.tsx
@@ -15,7 +15,7 @@ describe('DiskEncryption', () => {
);
@@ -30,7 +30,7 @@ describe('DiskEncryption', () => {
);
@@ -44,7 +44,7 @@ describe('DiskEncryption', () => {
);
diff --git a/packages/manager/src/components/DiskEncryption/DiskEncryption.tsx b/packages/manager/src/components/DiskEncryption/DiskEncryption.tsx
index e7cf9222262..ba4f236e881 100644
--- a/packages/manager/src/components/DiskEncryption/DiskEncryption.tsx
+++ b/packages/manager/src/components/DiskEncryption/DiskEncryption.tsx
@@ -3,13 +3,15 @@ import * as React from 'react';
import { Box } from 'src/components/Box';
import { Checkbox } from 'src/components/Checkbox';
import { Typography } from 'src/components/Typography';
+import { Notice } from '../Notice/Notice';
export interface DiskEncryptionProps {
descriptionCopy: JSX.Element | string;
disabled?: boolean;
disabledReason?: string;
+ error?: string;
isEncryptDiskChecked: boolean;
- toggleDiskEncryptionEnabled: () => void;
+ onChange: (checked: boolean) => void;
}
export const headerTestId = 'disk-encryption-header';
@@ -21,8 +23,9 @@ export const DiskEncryption = (props: DiskEncryptionProps) => {
descriptionCopy,
disabled,
disabledReason,
+ error,
isEncryptDiskChecked,
- toggleDiskEncryptionEnabled,
+ onChange,
} = props;
return (
@@ -30,6 +33,9 @@ export const DiskEncryption = (props: DiskEncryptionProps) => {
Disk Encryption
+ {error && (
+
+ )}
({ padding: `${theme.spacing()} 0` })}
@@ -48,7 +54,7 @@ export const DiskEncryption = (props: DiskEncryptionProps) => {
checked={disabled ? false : isEncryptDiskChecked} // in Create flows, this will be defaulted to be checked. Otherwise, we will rely on the current encryption status for the initial value
data-testid={checkboxTestId}
disabled={disabled}
- onChange={toggleDiskEncryptionEnabled}
+ onChange={(e, checked) => onChange(checked)}
text="Encrypt Disk"
toolTipText={disabled ? disabledReason : ''}
/>
diff --git a/packages/manager/src/features/Linodes/LinodeCreatev2/Access.test.tsx b/packages/manager/src/features/Linodes/LinodeCreatev2/Access.test.tsx
index 303005d5803..6bdad7d20b1 100644
--- a/packages/manager/src/features/Linodes/LinodeCreatev2/Access.test.tsx
+++ b/packages/manager/src/features/Linodes/LinodeCreatev2/Access.test.tsx
@@ -1,7 +1,12 @@
import { waitFor } from '@testing-library/react';
import React from 'react';
-import { profileFactory, sshKeyFactory } from 'src/factories';
+import {
+ accountFactory,
+ profileFactory,
+ regionFactory,
+ sshKeyFactory,
+} from 'src/factories';
import { grantsFactory } from 'src/factories/grants';
import { makeResourcePage } from 'src/mocks/serverHandlers';
import { HttpResponse, http, server } from 'src/mocks/testServer';
@@ -9,6 +14,8 @@ import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers';
import { Access } from './Access';
+import type { LinodeCreateFormValues } from './utilities';
+
describe('Access', () => {
it(
'should render a root password input',
@@ -103,4 +110,53 @@ describe('Access', () => {
expect(getByRole('checkbox')).toBeDisabled();
});
});
+
+ it('should show Linode disk encryption if the flag is on and the account has the capability', async () => {
+ server.use(
+ http.get('*/v4/account', () => {
+ return HttpResponse.json(
+ accountFactory.build({ capabilities: ['Disk Encryption'] })
+ );
+ })
+ );
+
+ const { findByText } = renderWithThemeAndHookFormContext({
+ component: ,
+ options: { flags: { linodeDiskEncryption: true } },
+ });
+
+ const heading = await findByText('Disk Encryption');
+
+ expect(heading).toBeVisible();
+ expect(heading.tagName).toBe('H3');
+ });
+
+ it('should disable disk encryption if the selected region does not support it', async () => {
+ const region = regionFactory.build({
+ capabilities: [],
+ });
+
+ const account = accountFactory.build({ capabilities: ['Disk Encryption'] });
+
+ server.use(
+ http.get('*/v4/account', () => {
+ return HttpResponse.json(account);
+ }),
+ http.get('*/v4/regions', () => {
+ return HttpResponse.json(makeResourcePage([region]));
+ })
+ );
+
+ const {
+ findByLabelText,
+ } = renderWithThemeAndHookFormContext({
+ component: ,
+ options: { flags: { linodeDiskEncryption: true } },
+ useFormOptions: { defaultValues: { region: region.id } },
+ });
+
+ await findByLabelText(
+ 'Disk encryption is not available in the selected region.'
+ );
+ });
});
diff --git a/packages/manager/src/features/Linodes/LinodeCreatev2/Access.tsx b/packages/manager/src/features/Linodes/LinodeCreatev2/Access.tsx
index 32d69d7fb1d..14862cb2573 100644
--- a/packages/manager/src/features/Linodes/LinodeCreatev2/Access.tsx
+++ b/packages/manager/src/features/Linodes/LinodeCreatev2/Access.tsx
@@ -1,12 +1,19 @@
import React from 'react';
-import { Controller, useFormContext } from 'react-hook-form';
+import { Controller, useFormContext, useWatch } from 'react-hook-form';
import UserSSHKeyPanel from 'src/components/AccessPanel/UserSSHKeyPanel';
+import {
+ DISK_ENCRYPTION_GENERAL_DESCRIPTION,
+ DISK_ENCRYPTION_UNAVAILABLE_IN_REGION_COPY,
+} from 'src/components/DiskEncryption/constants';
+import { DiskEncryption } from 'src/components/DiskEncryption/DiskEncryption';
+import { useIsDiskEncryptionFeatureEnabled } from 'src/components/DiskEncryption/utils';
import { Divider } from 'src/components/Divider';
import { Paper } from 'src/components/Paper';
import { Skeleton } from 'src/components/Skeleton';
import { inputMaxWidth } from 'src/foundations/themes/light';
import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck';
+import { useRegionsQuery } from 'src/queries/regions/regions';
import type { CreateLinodeRequest } from '@linode/api-v4';
@@ -17,6 +24,19 @@ const PasswordInput = React.lazy(
export const Access = () => {
const { control } = useFormContext();
+ const {
+ isDiskEncryptionFeatureEnabled,
+ } = useIsDiskEncryptionFeatureEnabled();
+
+ const { data: regions } = useRegionsQuery();
+ const regionId = useWatch({ control, name: 'region' });
+
+ const selectedRegion = regions?.find((r) => r.id === regionId);
+
+ const regionSupportsDiskEncryption = selectedRegion?.capabilities.includes(
+ 'Disk Encryption'
+ );
+
const isLinodeCreateRestricted = useRestrictedGlobalGrantCheck({
globalGrantType: 'add_linodes',
});
@@ -57,6 +77,27 @@ export const Access = () => {
control={control}
name="authorized_users"
/>
+ {isDiskEncryptionFeatureEnabled && (
+ <>
+
+ (
+
+ field.onChange(checked ? 'enabled' : 'disabled')
+ }
+ descriptionCopy={DISK_ENCRYPTION_GENERAL_DESCRIPTION}
+ disabled={!regionSupportsDiskEncryption}
+ disabledReason={DISK_ENCRYPTION_UNAVAILABLE_IN_REGION_COPY}
+ error={fieldState.error?.message}
+ isEncryptDiskChecked={field.value === 'enabled'}
+ />
+ )}
+ control={control}
+ name="disk_encryption"
+ />
+ >
+ )}
);
};
diff --git a/packages/manager/src/features/Linodes/LinodeCreatev2/Addons/Backups.test.tsx b/packages/manager/src/features/Linodes/LinodeCreatev2/Addons/Backups.test.tsx
index 2a713062704..37324a0517d 100644
--- a/packages/manager/src/features/Linodes/LinodeCreatev2/Addons/Backups.test.tsx
+++ b/packages/manager/src/features/Linodes/LinodeCreatev2/Addons/Backups.test.tsx
@@ -13,7 +13,7 @@ import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers';
import { Backups } from './Backups';
-import type { CreateLinodeRequest } from '@linode/api-v4';
+import type { LinodeCreateFormValues } from '../utilities';
describe('Linode Create V2 Backups Addon', () => {
it('should render a label and checkbox', () => {
@@ -30,7 +30,7 @@ describe('Linode Create V2 Backups Addon', () => {
it('should get its value from the form context', () => {
const {
getByRole,
- } = renderWithThemeAndHookFormContext({
+ } = renderWithThemeAndHookFormContext({
component: ,
useFormOptions: { defaultValues: { backups_enabled: true } },
});
@@ -75,7 +75,7 @@ describe('Linode Create V2 Backups Addon', () => {
const {
getByRole,
- } = renderWithThemeAndHookFormContext({
+ } = renderWithThemeAndHookFormContext({
component: ,
useFormOptions: { defaultValues: { region: region.id } },
});
@@ -101,7 +101,7 @@ describe('Linode Create V2 Backups Addon', () => {
const {
getByRole,
- } = renderWithThemeAndHookFormContext({
+ } = renderWithThemeAndHookFormContext({
component: ,
});
@@ -111,4 +111,19 @@ describe('Linode Create V2 Backups Addon', () => {
expect(checkbox).toBeDisabled();
});
});
+
+ it('renders a warning if disk encryption is enabled and backups are enabled', async () => {
+ const {
+ getByText,
+ } = renderWithThemeAndHookFormContext({
+ component: ,
+ useFormOptions: {
+ defaultValues: { backups_enabled: true, disk_encryption: 'enabled' },
+ },
+ });
+
+ expect(
+ getByText('Virtual Machine Backups are not encrypted.')
+ ).toBeVisible();
+ });
});
diff --git a/packages/manager/src/features/Linodes/LinodeCreatev2/Addons/Backups.tsx b/packages/manager/src/features/Linodes/LinodeCreatev2/Addons/Backups.tsx
index 77bbafdaa52..c8100f40e85 100644
--- a/packages/manager/src/features/Linodes/LinodeCreatev2/Addons/Backups.tsx
+++ b/packages/manager/src/features/Linodes/LinodeCreatev2/Addons/Backups.tsx
@@ -1,10 +1,12 @@
import React, { useMemo } from 'react';
-import { useController, useWatch } from 'react-hook-form';
+import { useController, useFormContext, useWatch } from 'react-hook-form';
import { Checkbox } from 'src/components/Checkbox';
import { Currency } from 'src/components/Currency';
+import { DISK_ENCRYPTION_BACKUPS_CAVEAT_COPY } from 'src/components/DiskEncryption/constants';
import { FormControlLabel } from 'src/components/FormControlLabel';
import { Link } from 'src/components/Link';
+import { Notice } from 'src/components/Notice/Notice';
import { Stack } from 'src/components/Stack';
import { Typography } from 'src/components/Typography';
import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck';
@@ -15,20 +17,24 @@ import { getMonthlyBackupsPrice } from 'src/utilities/pricing/backups';
import { getBackupsEnabledValue } from './utilities';
-import type { CreateLinodeRequest } from '@linode/api-v4';
+import type { LinodeCreateFormValues } from '../utilities';
export const Backups = () => {
- const { field } = useController({
+ const { control } = useFormContext();
+ const { field } = useController({
+ control,
name: 'backups_enabled',
});
+ const [regionId, typeId, diskEncryption] = useWatch({
+ control,
+ name: ['region', 'type', 'disk_encryption'],
+ });
+
const isLinodeCreateRestricted = useRestrictedGlobalGrantCheck({
globalGrantType: 'add_linodes',
});
- const regionId = useWatch({ name: 'region' });
- const typeId = useWatch({ name: 'type' });
-
const { data: type } = useTypeQuery(typeId, Boolean(typeId));
const { data: regions } = useRegionsQuery();
const { data: accountSettings } = useAccountSettings();
@@ -47,20 +53,21 @@ export const Backups = () => {
const isEdgeRegionSelected = selectedRegion?.site_type === 'edge';
+ const checked = getBackupsEnabledValue({
+ accountBackupsEnabled: isAccountBackupsEnabled,
+ isEdgeRegion: isEdgeRegionSelected,
+ value: field.value,
+ });
+
return (
+ Backups
{backupsMonthlyPrice && (
@@ -69,6 +76,17 @@ export const Backups = () => {
)}
+ {checked && diskEncryption === 'enabled' && (
+
+ )}
{isAccountBackupsEnabled ? (
@@ -86,6 +104,7 @@ export const Backups = () => {
}
+ checked={checked}
control={}
onChange={field.onChange}
/>
diff --git a/packages/manager/src/features/Linodes/LinodeCreatev2/Summary.test.tsx b/packages/manager/src/features/Linodes/LinodeCreatev2/Summary.test.tsx
index 9357c2c95d8..6663aa287a7 100644
--- a/packages/manager/src/features/Linodes/LinodeCreatev2/Summary.test.tsx
+++ b/packages/manager/src/features/Linodes/LinodeCreatev2/Summary.test.tsx
@@ -220,4 +220,17 @@ describe('Linode Create v2 Summary', () => {
expect(getByText('VLAN Attached')).toBeVisible();
});
+
+ it('should render "Encrypted" if disk encryption is enabled', async () => {
+ const {
+ getByText,
+ } = renderWithThemeAndHookFormContext({
+ component: ,
+ useFormOptions: {
+ defaultValues: { disk_encryption: 'enabled' },
+ },
+ });
+
+ expect(getByText('Encrypted')).toBeVisible();
+ });
});
diff --git a/packages/manager/src/features/Linodes/LinodeCreatev2/Summary.tsx b/packages/manager/src/features/Linodes/LinodeCreatev2/Summary.tsx
index 9501b863392..0ce13e3c03b 100644
--- a/packages/manager/src/features/Linodes/LinodeCreatev2/Summary.tsx
+++ b/packages/manager/src/features/Linodes/LinodeCreatev2/Summary.tsx
@@ -10,12 +10,12 @@ import { Typography } from 'src/components/Typography';
import { useImageQuery } from 'src/queries/images';
import { useRegionsQuery } from 'src/queries/regions/regions';
import { useTypeQuery } from 'src/queries/types';
+import { formatStorageUnits } from 'src/utilities/formatStorageUnits';
import { getMonthlyBackupsPrice } from 'src/utilities/pricing/backups';
import { renderMonthlyPriceToCorrectDecimalPlace } from 'src/utilities/pricing/dynamicPricing';
import { getLinodeRegionPrice } from 'src/utilities/pricing/linodes';
import type { CreateLinodeRequest } from '@linode/api-v4';
-import { extendType } from 'src/utilities/extendType';
export const Summary = () => {
const theme = useTheme();
@@ -34,6 +34,7 @@ export const Summary = () => {
placementGroupId,
vlanLabel,
vpcId,
+ diskEncryption,
] = useWatch({
control,
name: [
@@ -47,6 +48,7 @@ export const Summary = () => {
'placement_group.id',
'interfaces.1.label',
'interfaces.0.vpc_id',
+ 'disk_encryption',
],
});
@@ -79,9 +81,9 @@ export const Summary = () => {
{
item: {
details: `$${price?.monthly}/month`,
- title: type && extendType(type).formattedLabel,
+ title: type ? formatStorageUnits(type.label) : typeId,
},
- show: Boolean(type),
+ show: Boolean(typeId),
},
{
item: {
@@ -120,6 +122,12 @@ export const Summary = () => {
},
show: Boolean(firewallId),
},
+ {
+ item: {
+ title: 'Encrypted',
+ },
+ show: diskEncryption === 'enabled',
+ },
];
const summaryItemsToShow = summaryItems.filter((item) => item.show);
From 5abec313d0f6763c96381bdc101945a4c006eb7d Mon Sep 17 00:00:00 2001
From: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com>
Date: Mon, 3 Jun 2024 19:42:41 -0400
Subject: [PATCH 047/163] =?UTF-8?q?upcoming:=20[M3-8139,=20M3-8140,=20M3-8?=
=?UTF-8?q?141]=20=E2=80=93=20Add=20warning=20notices=20re:=20non-encrypti?=
=?UTF-8?q?on=20when=20creating=20Images=20&=20enabling=20Backups=20(#1052?=
=?UTF-8?q?1)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../pr-10521-tests-1717099871186.md | 5 +
...r-10521-upcoming-features-1717099430648.md | 5 +
.../e2e/core/images/create-image.spec.ts | 176 +++++++++++++++++-
.../components/DiskEncryption/constants.tsx | 3 +
.../Images/ImagesCreate/CreateImageTab.tsx | 45 ++++-
.../LinodeBackup/EnableBackupsDialog.test.tsx | 80 +++++++-
.../LinodeBackup/EnableBackupsDialog.tsx | 13 ++
.../CreateImageFromDiskDialog.test.tsx | 78 ++++++++
.../CreateImageFromDiskDialog.tsx | 14 ++
9 files changed, 404 insertions(+), 15 deletions(-)
create mode 100644 packages/manager/.changeset/pr-10521-tests-1717099871186.md
create mode 100644 packages/manager/.changeset/pr-10521-upcoming-features-1717099430648.md
create mode 100644 packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateImageFromDiskDialog.test.tsx
diff --git a/packages/manager/.changeset/pr-10521-tests-1717099871186.md b/packages/manager/.changeset/pr-10521-tests-1717099871186.md
new file mode 100644
index 00000000000..d8d8076b57c
--- /dev/null
+++ b/packages/manager/.changeset/pr-10521-tests-1717099871186.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Tests
+---
+
+Add unit tests for CreateImageFromDiskDialog and EnableBackupsDialog and LDE-related E2E assertions for Create Image flow ([#10521](https://github.com/linode/manager/pull/10521))
diff --git a/packages/manager/.changeset/pr-10521-upcoming-features-1717099430648.md b/packages/manager/.changeset/pr-10521-upcoming-features-1717099430648.md
new file mode 100644
index 00000000000..95186f0e1a6
--- /dev/null
+++ b/packages/manager/.changeset/pr-10521-upcoming-features-1717099430648.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Upcoming Features
+---
+
+Add warning notices regarding non-encryption when creating Images and enabling Backups ([#10521](https://github.com/linode/manager/pull/10521))
diff --git a/packages/manager/cypress/e2e/core/images/create-image.spec.ts b/packages/manager/cypress/e2e/core/images/create-image.spec.ts
index b4e73064099..fdffbf581d4 100644
--- a/packages/manager/cypress/e2e/core/images/create-image.spec.ts
+++ b/packages/manager/cypress/e2e/core/images/create-image.spec.ts
@@ -1,9 +1,50 @@
-import type { Linode } from '@linode/api-v4';
+import type { Linode, Region } from '@linode/api-v4';
+import { accountFactory, linodeFactory, regionFactory } from 'src/factories';
import { authenticate } from 'support/api/authentication';
+import { mockGetAccount } from 'support/intercepts/account';
+import {
+ mockAppendFeatureFlags,
+ mockGetFeatureFlagClientstream,
+} from 'support/intercepts/feature-flags';
import { ui } from 'support/ui';
import { cleanUp } from 'support/util/cleanup';
+import { makeFeatureFlagData } from 'support/util/feature-flags';
import { createTestLinode } from 'support/util/linodes';
import { randomLabel, randomPhrase } from 'support/util/random';
+import { mockGetRegions } from 'support/intercepts/regions';
+import {
+ mockGetLinodeDetails,
+ mockGetLinodes,
+} from 'support/intercepts/linodes';
+
+const mockRegions: Region[] = [
+ regionFactory.build({
+ capabilities: ['Linodes', 'Disk Encryption'],
+ id: 'us-east',
+ label: 'Newark, NJ',
+ site_type: 'core',
+ }),
+ regionFactory.build({
+ capabilities: ['Linodes', 'Disk Encryption'],
+ id: 'us-den-edge-1',
+ label: 'Edge - Denver, CO',
+ site_type: 'edge',
+ }),
+];
+
+const mockLinodes: Linode[] = [
+ linodeFactory.build({
+ label: 'core-region-linode',
+ region: mockRegions[0].id,
+ }),
+ linodeFactory.build({
+ label: 'edge-region-linode',
+ region: mockRegions[1].id,
+ }),
+];
+
+const DISK_ENCRYPTION_IMAGES_CAVEAT_COPY =
+ 'Virtual Machine Images are not encrypted.';
authenticate();
describe('create image (e2e)', () => {
@@ -84,4 +125,137 @@ describe('create image (e2e)', () => {
});
});
});
+
+ it('displays notice informing user that Images are not encrypted, provided the LDE feature is enabled and the selected linode is not in an Edge region', () => {
+ // Mock feature flag -- @TODO LDE: Remove feature flag once LDE is fully rolled out
+ mockAppendFeatureFlags({
+ linodeDiskEncryption: makeFeatureFlagData(true),
+ }).as('getFeatureFlags');
+ mockGetFeatureFlagClientstream().as('getClientStream');
+
+ // Mock responses
+ const mockAccount = accountFactory.build({
+ capabilities: ['Linodes', 'Disk Encryption'],
+ });
+
+ mockGetAccount(mockAccount).as('getAccount');
+ mockGetRegions(mockRegions).as('getRegions');
+ mockGetLinodes(mockLinodes).as('getLinodes');
+
+ // intercept request
+ cy.visitWithLogin('/images/create');
+ cy.wait([
+ '@getFeatureFlags',
+ '@getClientStream',
+ '@getAccount',
+ '@getLinodes',
+ '@getRegions',
+ ]);
+
+ // Find the Linode select and open it
+ cy.findByLabelText('Linode')
+ .should('be.visible')
+ .should('be.enabled')
+ .should('have.attr', 'placeholder', 'Select a Linode')
+ .click();
+
+ // Select the Linode
+ ui.autocompletePopper
+ .findByTitle(mockLinodes[0].label)
+ .should('be.visible')
+ .should('be.enabled')
+ .click();
+
+ // Check if notice is visible
+ cy.findByText(DISK_ENCRYPTION_IMAGES_CAVEAT_COPY).should('be.visible');
+ });
+
+ it('does not display a notice informing user that Images are not encrypted if the LDE feature is disabled', () => {
+ // Mock feature flag -- @TODO LDE: Remove feature flag once LDE is fully rolled out
+ mockAppendFeatureFlags({
+ linodeDiskEncryption: makeFeatureFlagData(false),
+ }).as('getFeatureFlags');
+ mockGetFeatureFlagClientstream().as('getClientStream');
+
+ // Mock responses
+ const mockAccount = accountFactory.build({
+ capabilities: ['Linodes', 'Disk Encryption'],
+ });
+
+ mockGetAccount(mockAccount).as('getAccount');
+ mockGetRegions(mockRegions).as('getRegions');
+ mockGetLinodes(mockLinodes).as('getLinodes');
+
+ // intercept request
+ cy.visitWithLogin('/images/create');
+ cy.wait([
+ '@getFeatureFlags',
+ '@getClientStream',
+ '@getAccount',
+ '@getLinodes',
+ '@getRegions',
+ ]);
+
+ // Find the Linode select and open it
+ cy.findByLabelText('Linode')
+ .should('be.visible')
+ .should('be.enabled')
+ .should('have.attr', 'placeholder', 'Select a Linode')
+ .click();
+
+ // Select the Linode
+ ui.autocompletePopper
+ .findByTitle(mockLinodes[0].label)
+ .should('be.visible')
+ .should('be.enabled')
+ .click();
+
+ // Check if notice is visible
+ cy.findByText(DISK_ENCRYPTION_IMAGES_CAVEAT_COPY).should('not.exist');
+ });
+
+ it('does not display a notice informing user that Images are not encrypted if the selected linode is in an Edge region', () => {
+ // Mock feature flag -- @TODO LDE: Remove feature flag once LDE is fully rolled out
+ mockAppendFeatureFlags({
+ linodeDiskEncryption: makeFeatureFlagData(true),
+ }).as('getFeatureFlags');
+ mockGetFeatureFlagClientstream().as('getClientStream');
+
+ // Mock responses
+ const mockAccount = accountFactory.build({
+ capabilities: ['Linodes', 'Disk Encryption'],
+ });
+
+ mockGetAccount(mockAccount).as('getAccount');
+ mockGetRegions(mockRegions).as('getRegions');
+ mockGetLinodes(mockLinodes).as('getLinodes');
+ mockGetLinodeDetails(mockLinodes[1].id, mockLinodes[1]);
+
+ // intercept request
+ cy.visitWithLogin('/images/create');
+ cy.wait([
+ '@getFeatureFlags',
+ '@getClientStream',
+ '@getAccount',
+ '@getRegions',
+ '@getLinodes',
+ ]);
+
+ // Find the Linode select and open it
+ cy.findByLabelText('Linode')
+ .should('be.visible')
+ .should('be.enabled')
+ .should('have.attr', 'placeholder', 'Select a Linode')
+ .click();
+
+ // Select the Linode
+ ui.autocompletePopper
+ .findByTitle(mockLinodes[1].label)
+ .should('be.visible')
+ .should('be.enabled')
+ .click();
+
+ // Check if notice is visible
+ cy.findByText(DISK_ENCRYPTION_IMAGES_CAVEAT_COPY).should('not.exist');
+ });
});
diff --git a/packages/manager/src/components/DiskEncryption/constants.tsx b/packages/manager/src/components/DiskEncryption/constants.tsx
index 5d0ffe10ec8..a9799ec1c80 100644
--- a/packages/manager/src/components/DiskEncryption/constants.tsx
+++ b/packages/manager/src/components/DiskEncryption/constants.tsx
@@ -22,3 +22,6 @@ export const DISK_ENCRYPTION_BACKUPS_CAVEAT_COPY =
export const DISK_ENCRYPTION_NODE_POOL_GUIDANCE_COPY =
'To enable disk encryption, delete the node pool and create a new node pool. New node pools are always encrypted.';
+
+export const DISK_ENCRYPTION_IMAGES_CAVEAT_COPY =
+ 'Virtual Machine Images are not encrypted.';
diff --git a/packages/manager/src/features/Images/ImagesCreate/CreateImageTab.tsx b/packages/manager/src/features/Images/ImagesCreate/CreateImageTab.tsx
index 273e0abf6c3..d83f4af3fc1 100644
--- a/packages/manager/src/features/Images/ImagesCreate/CreateImageTab.tsx
+++ b/packages/manager/src/features/Images/ImagesCreate/CreateImageTab.tsx
@@ -10,9 +10,12 @@ import { Autocomplete } from 'src/components/Autocomplete/Autocomplete';
import { Box } from 'src/components/Box';
import { Button } from 'src/components/Button/Button';
import { Checkbox } from 'src/components/Checkbox';
+import { DISK_ENCRYPTION_IMAGES_CAVEAT_COPY } from 'src/components/DiskEncryption/constants';
+import { useIsDiskEncryptionFeatureEnabled } from 'src/components/DiskEncryption/utils';
import { Link } from 'src/components/Link';
import { Notice } from 'src/components/Notice/Notice';
import { Paper } from 'src/components/Paper';
+import { getIsEdgeRegion } from 'src/components/RegionSelect/RegionSelect.utils';
import { Stack } from 'src/components/Stack';
import { SupportLink } from 'src/components/SupportLink';
import { TagsInput } from 'src/components/TagsInput/TagsInput';
@@ -25,7 +28,9 @@ import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGran
import { useEventsPollingActions } from 'src/queries/events/events';
import { useCreateImageMutation } from 'src/queries/images';
import { useAllLinodeDisksQuery } from 'src/queries/linodes/disks';
+import { useLinodeQuery } from 'src/queries/linodes/linodes';
import { useGrants } from 'src/queries/profile';
+import { useRegionsQuery } from 'src/queries/regions/regions';
export const CreateImageTab = () => {
const [selectedLinodeId, setSelectedLinodeId] = React.useState(
@@ -59,6 +64,10 @@ export const CreateImageTab = () => {
globalGrantType: 'add_images',
});
+ const {
+ isDiskEncryptionFeatureEnabled,
+ } = useIsDiskEncryptionFeatureEnabled();
+
const onSubmit = handleSubmit(async (values) => {
try {
await createImage(values);
@@ -92,6 +101,23 @@ export const CreateImageTab = () => {
const isRawDisk = selectedDisk?.filesystem === 'raw';
+ /*
+ We only want to display the notice about disk encryption if:
+ 1. the Disk Encryption feature is enabled
+ 2. the selected linode is not in an Edge region
+ */
+ const { data: regionsData } = useRegionsQuery();
+
+ const { data: linode } = useLinodeQuery(
+ selectedLinodeId ?? -1,
+ Boolean(selectedLinodeId) && isDiskEncryptionFeatureEnabled
+ );
+
+ const linodeIsInEdgeRegion = getIsEdgeRegion(
+ regionsData ?? [],
+ linode?.region ?? ''
+ );
+
return (
@@ -113,7 +139,7 @@ export const CreateImageTab = () => {
Select Linode & Disk
By default, Linode images are limited to 6144 MB of data per disk.
- Ensure your content doesn't exceed this limit, or{' '}
+ Ensure your content doesn’t exceed this limit, or{' '}
{
text="open a support ticket"
title="Request to increase Image size limit when capturing from Linode disk"
/>{' '}
- to request a higher limit. Additionally, images can't be created
- from a raw disk or a disk that's formatted using a custom file
- system.
+ to request a higher limit. Additionally, images can’t be
+ created from a raw disk or a disk that’s formatted using a
+ custom file system.
{
required
value={selectedLinodeId}
/>
+ {isDiskEncryptionFeatureEnabled &&
+ !linodeIsInEdgeRegion &&
+ selectedLinodeId !== null && (
+
+ ({ fontFamily: theme.font.normal })}
+ >
+ {DISK_ENCRYPTION_IMAGES_CAVEAT_COPY}
+
+
+ )}
(
({
useLinodeQuery: vi.fn().mockReturnValue({
@@ -30,12 +33,16 @@ vi.mock('src/queries/types', async () => {
};
});
+const diskEncryptionEnabledMock = vi.hoisted(() => {
+ return {
+ useIsDiskEncryptionFeatureEnabled: vi.fn(),
+ };
+});
+
describe('EnableBackupsDialog component', () => {
beforeEach(() => {
queryMocks.useTypeQuery.mockReturnValue({
data: typeFactory.build({
- id: 'mock-linode-type',
- label: 'Mock Linode Type',
addons: {
backups: {
price: {
@@ -51,17 +58,36 @@ describe('EnableBackupsDialog component', () => {
],
},
},
+ id: 'mock-linode-type',
+ label: 'Mock Linode Type',
}),
});
});
+ vi.mock('src/components/DiskEncryption/utils.ts', async () => {
+ const actual = await vi.importActual(
+ 'src/components/DiskEncryption/utils.ts'
+ );
+ return {
+ ...actual,
+ __esModule: true,
+ useIsDiskEncryptionFeatureEnabled: diskEncryptionEnabledMock.useIsDiskEncryptionFeatureEnabled.mockImplementation(
+ () => {
+ return {
+ isDiskEncryptionFeatureEnabled: false, // indicates the feature flag is off or account capability is absent
+ };
+ }
+ ),
+ };
+ });
+
it('Displays the monthly backup price', async () => {
queryMocks.useLinodeQuery.mockReturnValue({
data: linodeFactory.build({
id: 1,
label: 'Mock Linode',
- type: 'mock-linode-type',
region: 'us-east',
+ type: 'mock-linode-type',
}),
});
@@ -84,12 +110,12 @@ describe('EnableBackupsDialog component', () => {
data: linodeFactory.build({
id: 1,
label: 'Mock Linode',
- type: 'mock-linode-type',
region: 'es-mad',
+ type: 'mock-linode-type',
}),
});
- const { getByTestId, findByText, queryByText } = renderWithTheme(
+ const { findByText, getByTestId, queryByText } = renderWithTheme(
);
@@ -118,12 +144,12 @@ describe('EnableBackupsDialog component', () => {
data: linodeFactory.build({
id: 1,
label: 'Mock Linode',
- type: 'mock-linode-type',
region: 'es-mad',
+ type: 'mock-linode-type',
}),
});
- const { getByTestId, findByText } = renderWithTheme(
+ const { findByText, getByTestId } = renderWithTheme(
);
@@ -133,4 +159,38 @@ describe('EnableBackupsDialog component', () => {
// Confirm that "Enable Backups" button is disabled.
expect(getByTestId('confirm-enable-backups')).toBeDisabled();
});
+
+ it('does not display a notice regarding Backups not being encrypted if the Disk Encryption feature is disabled', () => {
+ const { queryByText } = renderWithTheme(
+
+ );
+
+ const encryptionBackupsCaveatNotice = queryByText(
+ DISK_ENCRYPTION_BACKUPS_CAVEAT_COPY
+ );
+
+ expect(encryptionBackupsCaveatNotice).not.toBeInTheDocument();
+ });
+
+ it('displays a notice regarding Backups not being encrypted if the Disk Encryption feature is enabled', () => {
+ diskEncryptionEnabledMock.useIsDiskEncryptionFeatureEnabled.mockImplementationOnce(
+ () => {
+ return {
+ isDiskEncryptionFeatureEnabled: true,
+ };
+ }
+ );
+
+ const { queryByText } = renderWithTheme(
+
+ );
+
+ const encryptionBackupsCaveatNotice = queryByText(
+ DISK_ENCRYPTION_BACKUPS_CAVEAT_COPY
+ );
+
+ expect(encryptionBackupsCaveatNotice).toBeInTheDocument();
+
+ diskEncryptionEnabledMock.useIsDiskEncryptionFeatureEnabled.mockRestore();
+ });
});
diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/EnableBackupsDialog.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/EnableBackupsDialog.tsx
index 9c28b43666f..ce79d0d47c1 100644
--- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/EnableBackupsDialog.tsx
+++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/EnableBackupsDialog.tsx
@@ -5,6 +5,8 @@ import * as React from 'react';
import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';
import { Currency } from 'src/components/Currency';
+import { DISK_ENCRYPTION_BACKUPS_CAVEAT_COPY } from 'src/components/DiskEncryption/constants';
+import { useIsDiskEncryptionFeatureEnabled } from 'src/components/DiskEncryption/utils';
import { Notice } from 'src/components/Notice/Notice';
import { Typography } from 'src/components/Typography';
import { useEventsPollingActions } from 'src/queries/events/events';
@@ -40,6 +42,10 @@ export const EnableBackupsDialog = (props: Props) => {
Boolean(linode?.type)
);
+ const {
+ isDiskEncryptionFeatureEnabled,
+ } = useIsDiskEncryptionFeatureEnabled();
+
const backupsMonthlyPrice:
| PriceObject['monthly']
| undefined = getMonthlyBackupsPrice({
@@ -95,6 +101,13 @@ export const EnableBackupsDialog = (props: Props) => {
open={open}
title="Enable backups?"
>
+ {isDiskEncryptionFeatureEnabled && (
+
+ )}
{!hasBackupsMonthlyPriceError ? (
Are you sure you want to enable backups on this Linode?{` `}
diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateImageFromDiskDialog.test.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateImageFromDiskDialog.test.tsx
new file mode 100644
index 00000000000..423eaaeb977
--- /dev/null
+++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateImageFromDiskDialog.test.tsx
@@ -0,0 +1,78 @@
+import * as React from 'react';
+
+import { DISK_ENCRYPTION_IMAGES_CAVEAT_COPY } from 'src/components/DiskEncryption/constants';
+import { linodeDiskFactory } from 'src/factories';
+import { renderWithTheme } from 'src/utilities/testHelpers';
+
+import { CreateImageFromDiskDialog } from './CreateImageFromDiskDialog';
+
+const diskEncryptionEnabledMock = vi.hoisted(() => {
+ return {
+ useIsDiskEncryptionFeatureEnabled: vi.fn(),
+ };
+});
+
+describe('CreateImageFromDiskDialog component', () => {
+ const mockDisk = linodeDiskFactory.build();
+
+ vi.mock('src/components/DiskEncryption/utils.ts', async () => {
+ const actual = await vi.importActual(
+ 'src/components/DiskEncryption/utils.ts'
+ );
+ return {
+ ...actual,
+ __esModule: true,
+ useIsDiskEncryptionFeatureEnabled: diskEncryptionEnabledMock.useIsDiskEncryptionFeatureEnabled.mockImplementation(
+ () => {
+ return {
+ isDiskEncryptionFeatureEnabled: false, // indicates the feature flag is off or account capability is absent
+ };
+ }
+ ),
+ };
+ });
+
+ it('does not display a notice regarding Images not being encrypted if the Disk Encryption feature is disabled', () => {
+ const { queryByText } = renderWithTheme(
+
+ );
+
+ const encryptionImagesCaveatNotice = queryByText(
+ DISK_ENCRYPTION_IMAGES_CAVEAT_COPY
+ );
+
+ expect(encryptionImagesCaveatNotice).not.toBeInTheDocument();
+ });
+
+ it('displays a notice regarding Images not being encrypted if the Disk Encryption feature is enabled', () => {
+ diskEncryptionEnabledMock.useIsDiskEncryptionFeatureEnabled.mockImplementationOnce(
+ () => {
+ return {
+ isDiskEncryptionFeatureEnabled: true,
+ };
+ }
+ );
+
+ const { queryByText } = renderWithTheme(
+
+ );
+
+ const encryptionImagesCaveatNotice = queryByText(
+ DISK_ENCRYPTION_IMAGES_CAVEAT_COPY
+ );
+
+ expect(encryptionImagesCaveatNotice).toBeInTheDocument();
+
+ diskEncryptionEnabledMock.useIsDiskEncryptionFeatureEnabled.mockRestore();
+ });
+});
diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateImageFromDiskDialog.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateImageFromDiskDialog.tsx
index f14d1a48988..a268ad7d764 100644
--- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateImageFromDiskDialog.tsx
+++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateImageFromDiskDialog.tsx
@@ -5,6 +5,9 @@ import React from 'react';
import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';
+import { DISK_ENCRYPTION_IMAGES_CAVEAT_COPY } from 'src/components/DiskEncryption/constants';
+import { useIsDiskEncryptionFeatureEnabled } from 'src/components/DiskEncryption/utils';
+import { Notice } from 'src/components/Notice/Notice';
import { SupportLink } from 'src/components/SupportLink/SupportLink';
import { useCreateImageMutation } from 'src/queries/images';
@@ -26,6 +29,10 @@ export const CreateImageFromDiskDialog = (props: Props) => {
reset,
} = useCreateImageMutation();
+ const {
+ isDiskEncryptionFeatureEnabled,
+ } = useIsDiskEncryptionFeatureEnabled();
+
React.useEffect(() => {
if (open) {
reset();
@@ -63,6 +70,13 @@ export const CreateImageFromDiskDialog = (props: Props) => {
open={open}
title={`Create Image from ${disk?.label}?`}
>
+ {isDiskEncryptionFeatureEnabled && (
+
+ )}
Linode Images are limited to 6144 MB of data per disk by default. Please
ensure that your disk content does not exceed this size limit, or{' '}
From 55c81012253a50f3af1b95136dd0f1ae930f9475 Mon Sep 17 00:00:00 2001
From: Alban Bailly <130582365+abailly-akamai@users.noreply.github.com>
Date: Mon, 3 Jun 2024 20:53:21 -0400
Subject: [PATCH 048/163] refactor/upcoming: [M3-8116] - Event Messages - Part
1 (#10517)
* initial commit - save progress
* wrappers and more cases
* save work
* save work
* save progress
* save progress
* save progress
* save progress
* save progress
* add flag and events row v2
* event dropdown and lke
* more events & cleanup
* cleanup
* cleanup and updates
* lastest known events
* typing and coverage
* styling
* tests and comments - part 1
* tests and comments - part 2
* tests and comments - part 3
* fix test
* better types
* Rename nodeBalancer.tsx to nodebalancer.tsx
* Rename stackScript.tsx to stackscript.tsx
* documentation
* Added changeset: Event Messages refactor
* Update e2e
* Feedback @mjac0bs
* Update docs/development-guide/15-api-events.md
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update docs/development-guide/15-api-events.md
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update docs/development-guide/15-api-events.md
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update docs/development-guide/15-api-events.md
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update docs/development-guide/15-api-events.md
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Feedback @mjac0bs
* Feedback @jdamore-linode
* missing space and linking
* Update packages/manager/src/features/Events/factories/backup.tsx
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* feedback @mjac0bs & @dwiley-akamai
* Update packages/manager/src/features/Events/factories/reserved.tsx
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update packages/manager/src/features/Events/factories/volume.tsx
Co-authored-by: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com>
* Update docs/development-guide/15-api-events.md
Co-authored-by: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com>
* Update packages/manager/src/features/Events/factories/reserved.tsx
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update packages/manager/src/features/Events/factories/reserved.tsx
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* more feedback from @mjac0bs
* Update packages/manager/src/features/Events/factories/linode.tsx
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update packages/manager/src/features/Events/factories/linode.tsx
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update packages/manager/src/features/Events/factories/linode.tsx
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update packages/manager/src/features/Events/factories/lke.tsx
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
* Update packages/manager/src/features/Events/factories/reserved.tsx
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
---------
Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
Co-authored-by: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com>
---
docs/development-guide/15-api-events.md | 73 +++
packages/api-v4/src/account/types.ts | 315 +++++-----
...r-10517-upcoming-features-1717170718095.md | 5 +
.../notificationsAndEvents/events.spec.ts | 13 +
.../Snackbar/ToastNotifications.stories.tsx | 46 ++
.../manager/src/dev-tools/FeatureFlagTool.tsx | 1 +
packages/manager/src/featureFlags.ts | 1 +
.../src/features/Events/Event.helpers.test.ts | 1 +
.../src/features/Events/Event.helpers.ts | 1 +
.../src/features/Events/EventLink.test.tsx | 65 +++
.../manager/src/features/Events/EventLink.tsx | 33 ++
.../src/features/Events/EventMessage.test.tsx | 31 +
.../src/features/Events/EventMessage.tsx | 45 ++
.../src/features/Events/EventRow.test.tsx | 1 +
.../manager/src/features/Events/EventRow.tsx | 1 +
.../src/features/Events/EventRowV2.test.tsx | 35 ++
.../src/features/Events/EventRowV2.tsx | 60 ++
.../src/features/Events/Events.stories.tsx | 141 -----
.../features/Events/EventsLanding.styles.ts | 4 +-
.../src/features/Events/EventsLanding.tsx | 44 +-
.../Events/EventsMessages.stories.tsx | 81 +++
.../manager/src/features/Events/constants.ts | 1 +
.../features/Events/eventMessageGenerator.ts | 1 +
.../Events/eventMessageGenerator_CMR.tsx | 1 +
.../src/features/Events/factories/account.tsx | 34 ++
.../src/features/Events/factories/backup.tsx | 53 ++
.../features/Events/factories/community.tsx | 47 ++
.../src/features/Events/factories/credit.tsx | 13 +
.../features/Events/factories/database.tsx | 192 +++++++
.../src/features/Events/factories/disk.tsx | 182 ++++++
.../src/features/Events/factories/dns.tsx | 40 ++
.../src/features/Events/factories/domain.tsx | 72 +++
.../src/features/Events/factories/entity.tsx | 49 ++
.../features/Events/factories/firewall.tsx | 114 ++++
.../src/features/Events/factories/host.tsx | 36 ++
.../src/features/Events/factories/image.tsx | 79 +++
.../src/features/Events/factories/index.ts | 37 ++
.../features/Events/factories/ipaddress.tsx | 13 +
.../features/Events/factories/ipv6pool.tsx | 20 +
.../src/features/Events/factories/lassie.tsx | 34 ++
.../src/features/Events/factories/linode.tsx | 542 ++++++++++++++++++
.../src/features/Events/factories/lish.tsx | 34 ++
.../src/features/Events/factories/lke.tsx | 131 +++++
.../Events/factories/longviewclient.tsx | 31 +
.../src/features/Events/factories/managed.tsx | 30 +
.../Events/factories/nodebalancer.tsx | 79 +++
.../src/features/Events/factories/oAuth.tsx | 39 ++
.../src/features/Events/factories/obj.tsx | 31 +
.../features/Events/factories/password.tsx | 35 ++
.../src/features/Events/factories/payment.tsx | 20 +
.../features/Events/factories/placement.tsx | 65 +++
.../src/features/Events/factories/profile.tsx | 13 +
.../features/Events/factories/reserved.tsx | 38 ++
.../features/Events/factories/stackscript.tsx | 47 ++
.../src/features/Events/factories/subnet.tsx | 34 ++
.../src/features/Events/factories/tag.tsx | 23 +
.../src/features/Events/factories/tfa.tsx | 20 +
.../src/features/Events/factories/ticket.tsx | 36 ++
.../src/features/Events/factories/token.tsx | 31 +
.../src/features/Events/factories/user.tsx | 53 ++
.../src/features/Events/factories/volume.tsx | 196 +++++++
.../src/features/Events/factories/vpc.tsx | 31 +
.../src/features/Events/factory.test.tsx | 14 +
.../manager/src/features/Events/factory.tsx | 46 ++
packages/manager/src/features/Events/types.ts | 21 +
.../src/features/Events/utils.test.tsx | 83 +++
.../manager/src/features/Events/utils.tsx | 40 ++
.../LinodeSummary/ActivityRow.tsx | 55 --
.../NotificationData/RenderEvent.tsx | 35 ++
.../NotificationData/RenderProgressEvent.tsx | 5 +-
.../NotificationData/useEventInfo.ts | 1 +
.../NotificationSection.tsx | 1 -
72 files changed, 3530 insertions(+), 345 deletions(-)
create mode 100644 docs/development-guide/15-api-events.md
create mode 100644 packages/manager/.changeset/pr-10517-upcoming-features-1717170718095.md
create mode 100644 packages/manager/src/features/Events/EventLink.test.tsx
create mode 100644 packages/manager/src/features/Events/EventLink.tsx
create mode 100644 packages/manager/src/features/Events/EventMessage.test.tsx
create mode 100644 packages/manager/src/features/Events/EventMessage.tsx
create mode 100644 packages/manager/src/features/Events/EventRowV2.test.tsx
create mode 100644 packages/manager/src/features/Events/EventRowV2.tsx
delete mode 100644 packages/manager/src/features/Events/Events.stories.tsx
create mode 100644 packages/manager/src/features/Events/EventsMessages.stories.tsx
create mode 100644 packages/manager/src/features/Events/factories/account.tsx
create mode 100644 packages/manager/src/features/Events/factories/backup.tsx
create mode 100644 packages/manager/src/features/Events/factories/community.tsx
create mode 100644 packages/manager/src/features/Events/factories/credit.tsx
create mode 100644 packages/manager/src/features/Events/factories/database.tsx
create mode 100644 packages/manager/src/features/Events/factories/disk.tsx
create mode 100644 packages/manager/src/features/Events/factories/dns.tsx
create mode 100644 packages/manager/src/features/Events/factories/domain.tsx
create mode 100644 packages/manager/src/features/Events/factories/entity.tsx
create mode 100644 packages/manager/src/features/Events/factories/firewall.tsx
create mode 100644 packages/manager/src/features/Events/factories/host.tsx
create mode 100644 packages/manager/src/features/Events/factories/image.tsx
create mode 100644 packages/manager/src/features/Events/factories/index.ts
create mode 100644 packages/manager/src/features/Events/factories/ipaddress.tsx
create mode 100644 packages/manager/src/features/Events/factories/ipv6pool.tsx
create mode 100644 packages/manager/src/features/Events/factories/lassie.tsx
create mode 100644 packages/manager/src/features/Events/factories/linode.tsx
create mode 100644 packages/manager/src/features/Events/factories/lish.tsx
create mode 100644 packages/manager/src/features/Events/factories/lke.tsx
create mode 100644 packages/manager/src/features/Events/factories/longviewclient.tsx
create mode 100644 packages/manager/src/features/Events/factories/managed.tsx
create mode 100644 packages/manager/src/features/Events/factories/nodebalancer.tsx
create mode 100644 packages/manager/src/features/Events/factories/oAuth.tsx
create mode 100644 packages/manager/src/features/Events/factories/obj.tsx
create mode 100644 packages/manager/src/features/Events/factories/password.tsx
create mode 100644 packages/manager/src/features/Events/factories/payment.tsx
create mode 100644 packages/manager/src/features/Events/factories/placement.tsx
create mode 100644 packages/manager/src/features/Events/factories/profile.tsx
create mode 100644 packages/manager/src/features/Events/factories/reserved.tsx
create mode 100644 packages/manager/src/features/Events/factories/stackscript.tsx
create mode 100644 packages/manager/src/features/Events/factories/subnet.tsx
create mode 100644 packages/manager/src/features/Events/factories/tag.tsx
create mode 100644 packages/manager/src/features/Events/factories/tfa.tsx
create mode 100644 packages/manager/src/features/Events/factories/ticket.tsx
create mode 100644 packages/manager/src/features/Events/factories/token.tsx
create mode 100644 packages/manager/src/features/Events/factories/user.tsx
create mode 100644 packages/manager/src/features/Events/factories/volume.tsx
create mode 100644 packages/manager/src/features/Events/factories/vpc.tsx
create mode 100644 packages/manager/src/features/Events/factory.test.tsx
create mode 100644 packages/manager/src/features/Events/factory.tsx
create mode 100644 packages/manager/src/features/Events/types.ts
create mode 100644 packages/manager/src/features/Events/utils.test.tsx
create mode 100644 packages/manager/src/features/Events/utils.tsx
delete mode 100644 packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/ActivityRow.tsx
diff --git a/docs/development-guide/15-api-events.md b/docs/development-guide/15-api-events.md
new file mode 100644
index 00000000000..8e6e2532c97
--- /dev/null
+++ b/docs/development-guide/15-api-events.md
@@ -0,0 +1,73 @@
+# API Events
+
+In order to display Events, Cloud Manager polls the [account/events](https://www.linode.com/docs/api/account/#events-list) endpoint at a 16 second interval, or every 2 seconds if there are “in-progress” events.
+
+In order to display these messages in the application (Notification Center, /events page), we compose messages according to the Event key (`EventAction`). Each key requires an entry and set of custom messages for each status (`EventStatus`), dictated by API specs. Not every Status is required for a given Action.
+
+## Adding a new Action and Composing Messages
+
+In order to add a new Action, one must add a new key to the read-only `EventActionKeys` constant array in the api-v4 package.
+Once that's done, a related entry must be added to the `eventMessages` Event Map. In order to do so, the entry can either be added to an existing Event Factory or a new one. `eventMessages` is strictly typed, so the decision where to add the new Action will be clear. ex:
+
+```Typescript
+import { EventLink } from '../EventLink';
+
+import type { PartialEventMap } from '../types';
+
+export const linode: PartialEventMap<'linode'> = {
+ linode_addip: {
+ notification: (e) => (
+ <>
+ An IP address has been added to Linode{' '}
+ .
+ >
+ ),
+ },
+};
+```
+
+The convention to compose the message is as follows:
+- Use the `` component for linking `entity` or `secondary_entity`. This component includes a lookup util to format the link `href` according to the feature.
+- The bolding should only be applied to:
+ - the primary action: (ex: `created`)
+ - its correlated negation for negative actions (ex: `could not be created.`)
+- The `message` should be also handled via the `` in order to handle potential formatting from the API string (ticks to indicate code blocks).
+
+## Displaying Events in snackbars
+
+We can leverage the Event Message factories in order to display events in snackbars/toasts when a given action gets triggered via APIv4.
+
+```Typescript
+const { enqueueSnackbar } = useSnackbar();
+
+try {
+ const successMessage = getEventMessage({
+ action: 'image_upload',
+ entity: {
+ label: 'Entity',
+ url: '/image/123',
+ },
+ status: 'notification',
+ });
+
+ const showToast = (variant: any) =>
+ enqueueSnackbar(successMessage, {
+ 'success',
+ });
+}, catch {
+ const failureMessage = getEventMessage({
+ action: 'image_upload',
+ // in this case we don't add an entity since we know we can't link to it
+ status: 'failed',
+ });
+
+ const showToast = (variant: any) =>
+ enqueueSnackbar(failureMessage, {
+ 'error',
+ });
+}
+```
+
+Both `action` and `status` are required. The `entity` and `secondary_entity` can optionally be passed to allow for linking. **Note**: it is possible the Event Message linking will be missing if the action status message expects either value but isn't not provided by the instance call.
+
+If a corresponding status does not exist (ex: "failed"), it's encouraged to add it to the Action. Event if not triggered by the API, it can be useful to have a reusable Event Message to use through the App.
diff --git a/packages/api-v4/src/account/types.ts b/packages/api-v4/src/account/types.ts
index 4fc523c2bb8..b5965e25d5a 100644
--- a/packages/api-v4/src/account/types.ts
+++ b/packages/api-v4/src/account/types.ts
@@ -279,139 +279,188 @@ export interface Entity {
url: string;
}
-export type EventAction =
- | 'account_settings_update'
- | 'account_update'
- | 'backups_cancel'
- | 'backups_enable'
- | 'backups_restore'
- | 'community_like'
- | 'community_mention'
- | 'community_question_reply'
- | 'credit_card_updated'
- | 'database_low_disk_space'
- | 'database_resize'
- | 'database_resize_create'
- | 'database_backup_restore'
- | 'database_create'
- | 'database_credentials_reset'
- | 'database_delete'
- | 'database_update_failed'
- | 'database_update'
- | 'disk_create'
- | 'disk_delete'
- | 'disk_duplicate'
- | 'disk_imagize'
- | 'disk_resize'
- | 'disk_update'
- | 'domain_create'
- | 'domain_delete'
- | 'domain_record_create'
- | 'domain_record_delete'
- | 'domain_record_updated'
- | 'domain_update'
- | 'entity_transfer_accept'
- | 'entity_transfer_cancel'
- | 'entity_transfer_create'
- | 'entity_transfer_fail'
- | 'entity_transfer_stale'
- | 'firewall_create'
- | 'firewall_delete'
- | 'firewall_device_add'
- | 'firewall_device_remove'
- | 'firewall_disable'
- | 'firewall_enable'
- | 'firewall_update'
- | 'host_reboot'
- | 'image_delete'
- | 'image_update'
- | 'image_upload'
- | 'lassie_reboot'
- | 'linode_addip'
- | 'linode_boot'
- | 'linode_clone'
- | 'linode_config_create'
- | 'linode_config_delete'
- | 'linode_config_update'
- | 'linode_create'
- | 'linode_delete'
- | 'linode_deleteip'
- | 'linode_migrate_datacenter_create'
- | 'linode_migrate_datacenter'
- | 'linode_migrate'
- | 'linode_mutate_create'
- | 'linode_mutate'
- | 'linode_reboot'
- | 'linode_rebuild'
- | 'linode_resize_create'
- | 'linode_resize_warm_create'
- | 'linode_resize'
- | 'linode_shutdown'
- | 'linode_snapshot'
- | 'linode_update'
- | 'lke_node_create'
- | 'lke_node_recycle'
- | 'lke_cluster_create'
- | 'lke_cluster_update'
- | 'lke_cluster_delete'
- | 'lke_cluster_regenerate'
- | 'lke_cluster_recycle'
- | 'lke_control_plane_acl_create'
- | 'lke_control_plane_acl_update'
- | 'lke_control_plane_acl_delete'
- | 'lke_kubeconfig_regenerate'
- | 'lke_token_rotate'
- | 'lke_pool_create'
- | 'lke_pool_delete'
- | 'lke_pool_recycle'
- | 'longviewclient_create'
- | 'longviewclient_delete'
- | 'longviewclient_update'
- | 'nodebalancer_config_create'
- | 'nodebalancer_config_delete'
- | 'nodebalancer_config_update'
- | 'nodebalancer_create'
- | 'nodebalancer_delete'
- | 'nodebalancer_update'
- | 'password_reset'
- | 'placement_group_assign'
- | 'placement_group_became_non_compliant'
- | 'placement_group_became_compliant'
- | 'placement_group_create'
- | 'placement_group_unassign'
- | 'placement_group_update'
- | 'placement_group_delete'
- | 'profile_update'
- | 'stackscript_create'
- | 'stackscript_delete'
- | 'stackscript_publicize'
- | 'stackscript_revise'
- | 'stackscript_update'
- | 'subnet_create'
- | 'subnet_delete'
- | 'subnet_update'
- | 'tfa_disabled'
- | 'tfa_enabled'
- | 'ticket_attachment_upload'
- | 'ticket_update'
- | 'token_create'
- | 'token_delete'
- | 'token_update'
- | 'user_ssh_key_add'
- | 'user_ssh_key_delete'
- | 'user_ssh_key_update'
- | 'volume_attach'
- | 'volume_clone'
- | 'volume_create'
- | 'volume_delete'
- | 'volume_detach'
- | 'volume_migrate_scheduled'
- | 'volume_migrate'
- | 'volume_resize'
- | 'volume_update'
- | 'vpc_create'
- | 'vpc_delete'
- | 'vpc_update';
+export const EventActionKeys = [
+ 'account_agreement_eu_model',
+ 'account_promo_apply',
+ 'account_settings_update',
+ 'account_update',
+ 'backups_cancel',
+ 'backups_enable',
+ 'backups_restore',
+ 'community_like',
+ 'community_mention',
+ 'community_question_reply',
+ 'credit_card_updated',
+ 'database_backup_create',
+ 'database_backup_delete',
+ 'database_backup_restore',
+ 'database_create',
+ 'database_credentials_reset',
+ 'database_degraded',
+ 'database_delete',
+ 'database_failed',
+ 'database_low_disk_space',
+ 'database_resize',
+ 'database_resize_create',
+ 'database_scale',
+ 'database_update',
+ 'database_update_failed',
+ 'database_upgrade',
+ 'disk_create',
+ 'disk_delete',
+ 'disk_duplicate',
+ 'disk_imagize',
+ 'disk_resize',
+ 'disk_update',
+ 'dns_record_create',
+ 'dns_record_delete',
+ 'dns_zone_create',
+ 'dns_zone_delete',
+ 'domain_create',
+ 'domain_delete',
+ 'domain_import',
+ 'domain_record_create',
+ 'domain_record_delete',
+ 'domain_record_update',
+ 'domain_record_updated',
+ 'domain_update',
+ 'entity_transfer_accept',
+ 'entity_transfer_accept_recipient',
+ 'entity_transfer_cancel',
+ 'entity_transfer_create',
+ 'entity_transfer_fail',
+ 'entity_transfer_stale',
+ 'firewall_apply',
+ 'firewall_create',
+ 'firewall_delete',
+ 'firewall_device_add',
+ 'firewall_device_remove',
+ 'firewall_disable',
+ 'firewall_enable',
+ 'firewall_rules_update',
+ 'firewall_update',
+ 'host_reboot',
+ 'image_delete',
+ 'image_update',
+ 'image_upload',
+ 'ipaddress_update',
+ 'ipv6pool_add',
+ 'ipv6pool_delete',
+ 'lassie_reboot',
+ 'linode_addip',
+ 'linode_boot',
+ 'linode_clone',
+ 'linode_config_create',
+ 'linode_config_delete',
+ 'linode_config_update',
+ 'linode_create',
+ 'linode_delete',
+ 'linode_deleteip',
+ 'linode_migrate',
+ 'linode_mutate_create',
+ 'linode_mutate',
+ 'linode_migrate_datacenter',
+ 'linode_migrate_datacenter_create',
+ 'linode_reboot',
+ 'linode_rebuild',
+ 'linode_resize',
+ 'linode_resize_create',
+ 'linode_resize_warm_create',
+ 'linode_shutdown',
+ 'linode_snapshot',
+ 'linode_update',
+ 'lish_boot',
+ 'lke_cluster_create',
+ 'lke_cluster_delete',
+ 'lke_cluster_recycle',
+ 'lke_cluster_update',
+ 'lke_kubeconfig_regenerate',
+ 'lke_cluster_regenerate',
+ 'lke_control_plane_acl_create',
+ 'lke_control_plane_acl_delete',
+ 'lke_control_plane_acl_update',
+ 'lke_node_create',
+ 'lke_node_recycle',
+ 'lke_pool_create',
+ 'lke_pool_delete',
+ 'lke_pool_recycle',
+ 'lke_token_rotate',
+ 'longviewclient_create',
+ 'longviewclient_delete',
+ 'longviewclient_update',
+ 'managed_enabled',
+ 'managed_service_create',
+ 'managed_service_delete',
+ 'nodebalancer_config_create',
+ 'nodebalancer_config_delete',
+ 'nodebalancer_config_update',
+ 'nodebalancer_create',
+ 'nodebalancer_delete',
+ 'nodebalancer_node_create',
+ 'nodebalancer_node_delete',
+ 'nodebalancer_node_update',
+ 'nodebalancer_update',
+ 'oauth_client_create',
+ 'oauth_client_delete',
+ 'oauth_client_update',
+ 'oauth_client_secret_reset',
+ 'obj_access_key_create',
+ 'obj_access_key_delete',
+ 'obj_access_key_update',
+ 'password_reset',
+ 'payment_method_add',
+ 'payment_submitted',
+ 'placement_group_assign',
+ 'placement_group_became_compliant',
+ 'placement_group_became_non_compliant',
+ 'placement_group_create',
+ 'placement_group_delete',
+ 'placement_group_unassign',
+ 'placement_group_update',
+ 'profile_update',
+ 'reserved_ip_assign',
+ 'reserved_ip_create',
+ 'reserved_ip_delete',
+ 'reserved_ip_unassign',
+ 'stackscript_create',
+ 'stackscript_delete',
+ 'stackscript_publicize',
+ 'stackscript_revise',
+ 'stackscript_update',
+ 'subnet_create',
+ 'subnet_delete',
+ 'subnet_update',
+ 'tag_create',
+ 'tag_delete',
+ 'tfa_disabled',
+ 'tfa_enabled',
+ 'ticket_attachment_upload',
+ 'ticket_create',
+ 'ticket_update',
+ 'token_create',
+ 'token_delete',
+ 'token_update',
+ 'user_create',
+ 'user_delete',
+ 'user_ssh_key_add',
+ 'user_ssh_key_delete',
+ 'user_ssh_key_update',
+ 'user_update',
+ 'volume_attach',
+ 'volume_clone',
+ 'volume_create',
+ 'volume_delete',
+ 'volume_detach',
+ 'volume_migrate',
+ 'volume_migrate_scheduled',
+ 'volume_resize',
+ 'volume_update',
+ 'vpc_create',
+ 'vpc_delete',
+ 'vpc_update',
+] as const;
+
+export type EventAction = typeof EventActionKeys[number];
export type EventStatus =
| 'scheduled'
diff --git a/packages/manager/.changeset/pr-10517-upcoming-features-1717170718095.md b/packages/manager/.changeset/pr-10517-upcoming-features-1717170718095.md
new file mode 100644
index 00000000000..b70d16d8c94
--- /dev/null
+++ b/packages/manager/.changeset/pr-10517-upcoming-features-1717170718095.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Upcoming Features
+---
+
+Event Messages refactor ([#10517](https://github.com/linode/manager/pull/10517))
diff --git a/packages/manager/cypress/e2e/core/notificationsAndEvents/events.spec.ts b/packages/manager/cypress/e2e/core/notificationsAndEvents/events.spec.ts
index e7020b6911d..421c819dd14 100644
--- a/packages/manager/cypress/e2e/core/notificationsAndEvents/events.spec.ts
+++ b/packages/manager/cypress/e2e/core/notificationsAndEvents/events.spec.ts
@@ -3,6 +3,11 @@ import { eventFactory } from '@src/factories/events';
import { RecPartial } from 'factory.ts';
import { containsClick, getClick } from 'support/helpers';
import { mockGetEvents } from 'support/intercepts/events';
+import { makeFeatureFlagData } from 'support/util/feature-flags';
+import {
+ mockAppendFeatureFlags,
+ mockGetFeatureFlagClientstream,
+} from 'support/intercepts/feature-flags';
const eventActions: RecPartial[] = [
'backups_cancel',
@@ -107,6 +112,14 @@ const events: Event[] = eventActions.map((action) => {
});
describe('verify notification types and icons', () => {
+ before(() => {
+ // TODO eventMessagesV2: delete when flag is removed and update test
+ mockAppendFeatureFlags({
+ eventMessagesV2: makeFeatureFlagData(false),
+ });
+ mockGetFeatureFlagClientstream();
+ });
+
it(`notifications`, () => {
mockGetEvents(events).as('mockEvents');
cy.visitWithLogin('/linodes');
diff --git a/packages/manager/src/components/Snackbar/ToastNotifications.stories.tsx b/packages/manager/src/components/Snackbar/ToastNotifications.stories.tsx
index d12d5a8539f..ac8d4b13f96 100644
--- a/packages/manager/src/components/Snackbar/ToastNotifications.stories.tsx
+++ b/packages/manager/src/components/Snackbar/ToastNotifications.stories.tsx
@@ -3,6 +3,7 @@ import React from 'react';
import { Button } from 'src/components/Button/Button';
import { Snackbar } from 'src/components/Snackbar/Snackbar';
+import { getEventMessage } from 'src/features/Events/utils';
import type { Meta, StoryObj } from '@storybook/react';
@@ -96,3 +97,48 @@ function Example() {
>
);
}
+
+export const WithEventMessage: Story = {
+ args: {
+ anchorOrigin: { horizontal: 'right', vertical: 'bottom' },
+ hideIconVariant: true,
+ maxSnack: 5,
+ },
+ render: (args) => {
+ const WithEventMessage = () => {
+ const { enqueueSnackbar } = useSnackbar();
+ const message = getEventMessage({
+ action: 'placement_group_assign',
+ entity: {
+ label: 'Entity',
+ url: 'https://google.com',
+ },
+ secondary_entity: {
+ label: 'Secondary Entity',
+ url: 'https://google.com',
+ },
+ status: 'notification',
+ });
+
+ const showToast = (variant: any) =>
+ enqueueSnackbar(message, {
+ variant,
+ });
+ return (
+
+ );
+ };
+
+ return (
+
+
+
+ );
+ },
+};
diff --git a/packages/manager/src/dev-tools/FeatureFlagTool.tsx b/packages/manager/src/dev-tools/FeatureFlagTool.tsx
index 69ab0b57250..dfc7908ec46 100644
--- a/packages/manager/src/dev-tools/FeatureFlagTool.tsx
+++ b/packages/manager/src/dev-tools/FeatureFlagTool.tsx
@@ -22,6 +22,7 @@ const options: { flag: keyof Flags; label: string }[] = [
{ flag: 'aclbFullCreateFlow', label: 'ACLB Full Create Flow' },
{ flag: 'aclp', label: 'CloudPulse' },
{ flag: 'disableLargestGbPlans', label: 'Disable Largest GB Plans' },
+ { flag: 'eventMessagesV2', label: 'Event Messages V2' },
{ flag: 'gecko2', label: 'Gecko' },
{ flag: 'linodeCreateRefactor', label: 'Linode Create v2' },
{ flag: 'linodeDiskEncryption', label: 'Linode Disk Encryption (LDE)' },
diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts
index 1ee3379a571..76e40d150bb 100644
--- a/packages/manager/src/featureFlags.ts
+++ b/packages/manager/src/featureFlags.ts
@@ -67,6 +67,7 @@ export interface Flags {
databaseResize: boolean;
databases: boolean;
disableLargestGbPlans: boolean;
+ eventMessagesV2: boolean;
gecko: boolean; // @TODO gecko: delete this after next release
gecko2: GeckoFlag;
gpuv2: gpuV2;
diff --git a/packages/manager/src/features/Events/Event.helpers.test.ts b/packages/manager/src/features/Events/Event.helpers.test.ts
index cb2acd4fbd8..2d17f4fa9ef 100644
--- a/packages/manager/src/features/Events/Event.helpers.test.ts
+++ b/packages/manager/src/features/Events/Event.helpers.test.ts
@@ -1,3 +1,4 @@
+// TODO eventMessagesV2: delete when flag is removed
import { eventFactory } from 'src/factories/events';
import {
diff --git a/packages/manager/src/features/Events/Event.helpers.ts b/packages/manager/src/features/Events/Event.helpers.ts
index 714206fb423..8ca4f216904 100644
--- a/packages/manager/src/features/Events/Event.helpers.ts
+++ b/packages/manager/src/features/Events/Event.helpers.ts
@@ -1,3 +1,4 @@
+// TODO eventMessagesV2: delete when flag is removed
import { Event, EventAction } from '@linode/api-v4/lib/account';
export const maybeRemoveTrailingPeriod = (string: string) => {
diff --git a/packages/manager/src/features/Events/EventLink.test.tsx b/packages/manager/src/features/Events/EventLink.test.tsx
new file mode 100644
index 00000000000..19417271229
--- /dev/null
+++ b/packages/manager/src/features/Events/EventLink.test.tsx
@@ -0,0 +1,65 @@
+import { screen } from '@testing-library/react';
+import * as React from 'react';
+
+import { eventFactory } from 'src/factories';
+import { renderWithTheme } from 'src/utilities/testHelpers';
+
+import { EventLink } from './EventLink';
+
+import type { Event } from '@linode/api-v4';
+
+const queryMocks = vi.hoisted(() => ({
+ getLinkForEvent: vi.fn().mockReturnValue('mockEntityUrl'),
+}));
+
+vi.mock('src/utilities/getEventsActionLink', async () => {
+ const actual = await vi.importActual('src/utilities/getEventsActionLink');
+ return {
+ ...actual,
+ getLinkForEvent: queryMocks.getLinkForEvent,
+ };
+});
+
+describe('EventLink', () => {
+ it('renders null when provided with invalid props', () => {
+ const { container } = renderWithTheme(
+
+ );
+
+ expect(container).toBeEmptyDOMElement();
+ });
+
+ it('renders the entity label when provided with valid props', () => {
+ queryMocks.getLinkForEvent.mockReturnValue('mockEntityUrl');
+ const mockEvent: Event = eventFactory.build({
+ entity: {
+ label: 'mockEntityLabel',
+ url: 'mockEntityUrl',
+ },
+ });
+ renderWithTheme();
+
+ expect(screen.getByText('mockEntityLabel')).toBeInTheDocument();
+ });
+
+ it('renders the secondary entity label when provided with valid props', () => {
+ queryMocks.getLinkForEvent.mockReturnValue('mockSecondaryEntityUrl');
+ const mockEvent = eventFactory.build({
+ entity: null,
+ secondary_entity: {
+ label: 'mockSecondaryEntityLabel',
+ url: 'mockSecondaryEntityUrl',
+ },
+ });
+
+ renderWithTheme();
+
+ expect(screen.getByText('mockSecondaryEntityLabel')).toBeInTheDocument();
+ });
+});
diff --git a/packages/manager/src/features/Events/EventLink.tsx b/packages/manager/src/features/Events/EventLink.tsx
new file mode 100644
index 00000000000..fedfd69fb7b
--- /dev/null
+++ b/packages/manager/src/features/Events/EventLink.tsx
@@ -0,0 +1,33 @@
+import * as React from 'react';
+
+import { Link } from 'src/components/Link';
+import { getLinkForEvent } from 'src/utilities/getEventsActionLink';
+
+import type { Event } from '@linode/api-v4';
+
+interface MessageLinkEntity {
+ event: Event;
+ to: 'entity' | 'secondaryEntity';
+}
+
+/**
+ * A component that renders a link to an entity or secondary entity.
+ * Meant to be used in the context of an event message.
+ */
+export const EventLink = (props: MessageLinkEntity) => {
+ const { event, to } = props;
+ const entity = to === 'entity' ? event.entity : event.secondary_entity;
+
+ const renderDefault = () => {
+ // eslint-disable-next-line react/jsx-no-useless-fragment
+ return <>{entity?.label ?? ''}>;
+ };
+
+ if (!entity?.url || !entity?.label) {
+ return renderDefault();
+ }
+
+ const link = getLinkForEvent(event.action, entity);
+
+ return link ? {entity.label} : renderDefault();
+};
diff --git a/packages/manager/src/features/Events/EventMessage.test.tsx b/packages/manager/src/features/Events/EventMessage.test.tsx
new file mode 100644
index 00000000000..cf0b7ad69f9
--- /dev/null
+++ b/packages/manager/src/features/Events/EventMessage.test.tsx
@@ -0,0 +1,31 @@
+import * as React from 'react';
+
+import { renderWithTheme } from 'src/utilities/testHelpers';
+
+import { EventMessage } from './EventMessage';
+
+describe('EventMessage', () => {
+ it('renders null when message is null', () => {
+ const { queryByRole } = renderWithTheme();
+
+ expect(queryByRole('pre')).toBeNull();
+ });
+
+ it('renders message without ticks as plain text', () => {
+ const { getByText, queryByRole } = renderWithTheme(
+
+ );
+
+ expect(getByText('Hello, world!')).toBeInTheDocument();
+ expect(queryByRole('pre')).toBeNull();
+ });
+
+ it('renders message with ticks as inline code blocks', () => {
+ const { container, getByText } = renderWithTheme(
+
+ );
+
+ expect(getByText(/Hello,/)).toBeInTheDocument();
+ expect(container.querySelector('pre')).toHaveTextContent('world');
+ });
+});
diff --git a/packages/manager/src/features/Events/EventMessage.tsx b/packages/manager/src/features/Events/EventMessage.tsx
new file mode 100644
index 00000000000..d555fb04eb1
--- /dev/null
+++ b/packages/manager/src/features/Events/EventMessage.tsx
@@ -0,0 +1,45 @@
+import { styled } from '@mui/material/styles';
+import * as React from 'react';
+
+interface MessageLinkEntity {
+ message: null | string;
+}
+
+/**
+ * Renders a message with inline code blocks.
+ * Meant to be used in the context of an event message.
+ * This component is only used to render {e.message} in the case of potential ticks we want to render as