-
Notifications
You must be signed in to change notification settings - Fork 49
Add Radius plugin #461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add Radius plugin #461
Conversation
Signed-off-by: Filipe Revez <[email protected]>
Signed-off-by: Filipe Revez <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces a comprehensive Headlamp plugin for Radius, a cloud-native application platform. The plugin provides visualization and management capabilities for Radius resources through the Kubernetes API Aggregation Layer, adding new views for applications, environments, and other Radius resources to the Headlamp UI.
Key Changes
- Core plugin infrastructure: Setup with TypeScript configuration, plugin registration, sidebar navigation, and routing system for multiple views
- API integration: Implementation of custom hooks to fetch Radius data via UCP (Universal Control Plane) API endpoints through Kubernetes API Aggregation
- UI components: Five main views (Overview dashboard, Applications list/detail, Environments list/detail, Resources list/detail) with tables, status indicators, and navigation links
Reviewed changes
Copilot reviewed 16 out of 19 changed files in this pull request and generated 18 comments.
Show a summary per file
| File | Description |
|---|---|
radius/package.json |
Package configuration with dependencies and build scripts for the Headlamp plugin |
radius/tsconfig.json |
TypeScript configuration extending Headlamp plugin defaults |
radius/.gitignore |
Standard ignore patterns for Node.js projects and Headlamp plugin artifacts |
radius/README.md |
Documentation describing the plugin, installation instructions, and resource links |
radius/src/index.tsx |
Main plugin entry point with route registration, sidebar configuration, and settings component |
radius/src/config.ts |
Configuration utility for retrieving UCP API version from plugin settings |
radius/src/logo.svg |
Radius logo SVG for branding in the UI |
radius/src/headlamp-plugin.d.ts |
TypeScript type definitions reference for Headlamp plugin types |
radius/src/models/radius.ts |
Core data models, type definitions, and custom hooks for fetching Radius resources via UCP API |
radius/src/components/RadiusStatusLabel.tsx |
Reusable status label component mapping Radius provisioning states to Headlamp status styles |
radius/src/applications/index.tsx |
Applications list view with table displaying all Radius applications |
radius/src/applications/ApplicationDetailView.tsx |
Detailed view for individual applications showing properties, resources, and system metadata |
radius/src/environments/index.tsx |
Environments list view with table showing all Radius environments |
radius/src/environments/EnvironmentDetailView.tsx |
Detailed environment view with tabs for overview, recipes, and associated resources |
radius/src/resources/index.tsx |
Resources list view aggregating all Radius resource types across providers |
radius/src/resources/ResourceDetailView.tsx |
Detailed view for individual resources with general info and Kubernetes resource links |
radius/src/radius-overview/index.tsx |
Dashboard overview with status charts and summary tables for applications and environments |
README.md |
Updated root README to include the Radius plugin in the official plugins list |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
skoeva
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, nice work! The CI is complaining about signed commits, for that just squash the Copilot changes into your commit(s) and make sure they're signed
Co-authored-by: Copilot <[email protected]> Signed-off-by: Filipe Revez <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Filipe Revez <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Filipe Revez <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Filipe Revez <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Filipe Revez <[email protected]>
Signed-off-by: Filipe Revez <[email protected]>
Signed-off-by: Filipe Revez <[email protected]>
Signed-off-by: Filipe Revez <[email protected]>
Signed-off-by: Filipe Revez <[email protected]>
…plications components Signed-off-by: Filipe Revez <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 16 out of 19 changed files in this pull request and generated 24 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Cell: ({ row }: any) => <Chip label={row.original.templateKind} size="small" />, | ||
| }, |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type casting to 'any' is used for row parameters in table Cell renderers. This bypasses TypeScript's type checking and could lead to runtime errors. Consider defining proper types for better type safety.
| let percentages = [ | ||
| Math.round((status.success / total) * 100), | ||
| Math.round((status.failed / total) * 100), | ||
| Math.round((status.processing / total) * 100), | ||
| Math.round((status.suspended / total) * 100), | ||
| ]; | ||
|
|
||
| // Adjust to ensure total is 100% by modifying the largest percentage | ||
| const sum = percentages.reduce((a, b) => a + b, 0); | ||
| if (sum !== 100) { | ||
| const idx = percentages.indexOf(Math.max(...percentages)); | ||
| percentages[idx] += 100 - sum; | ||
| } |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The percentage adjustment logic has a potential bug when all values are 0. When all percentages are 0, Math.max(...percentages) returns 0, indexOf(0) returns the first index (0), and percentages[0] becomes 100. This means a chart with no resources would show 100% in the first category (success). Consider checking if the total resource count is 0 before calculating percentages.
radius/src/radius-overview/index.tsx
Outdated
| fill: '#DC7501', | ||
| }, | ||
| { | ||
| name: 'processing', | ||
| value: processingPercentage, | ||
| fill: '#2196F3', | ||
| }, | ||
| { | ||
| name: 'suspended', | ||
| value: suspendedPercentage, | ||
| fill: '#FDE100', |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded hex color values '#DC7501', '#2196F3', and '#FDE100' are used for status visualization. These hardcoded colors may not respect the user's theme (light/dark mode) and could have poor contrast in some themes. Consider using theme palette colors or defining these colors in a theme-aware way.
| fill: '#DC7501', | |
| }, | |
| { | |
| name: 'processing', | |
| value: processingPercentage, | |
| fill: '#2196F3', | |
| }, | |
| { | |
| name: 'suspended', | |
| value: suspendedPercentage, | |
| fill: '#FDE100', | |
| fill: theme.palette.error.main, | |
| }, | |
| { | |
| name: 'processing', | |
| value: processingPercentage, | |
| fill: theme.palette.info.main, | |
| }, | |
| { | |
| name: 'suspended', | |
| value: suspendedPercentage, | |
| fill: theme.palette.warning.light, |
| const computeStatus = resource.properties.status?.compute as any; | ||
| const computeNamespace = computeStatus?.namespace; | ||
|
|
||
| let k8sResourceName: string | undefined; | ||
| let k8sResourceType: 'deployment' | 'pod' | undefined; | ||
|
|
||
| // Check for resource property which might contain the K8s resource name | ||
| if (isContainer && resource.properties.resource) { | ||
| k8sResourceName = (resource.properties.resource as Record<string, any>)?.name || resource.name; |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type casting to 'any' is used for row parameters, computeStatus, and resource properties. This bypasses TypeScript's type checking and could lead to runtime errors. Consider defining proper types for better type safety.
| function TabPanel({ children, value, index }: TabPanelProps) { | ||
| return ( | ||
| <div role="tabpanel" hidden={value !== index}> | ||
| {value === index && <Box sx={{ p: 3 }}>{children}</Box>} | ||
| </div> | ||
| ); | ||
| } |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TabPanel component is missing documentation. Consider adding a JSDoc comment explaining its purpose and parameters for better code maintainability.
| function ApplicationContent({ | ||
| application, | ||
| environmentName, | ||
| }: Readonly<{ | ||
| application: UCPApplication; | ||
| environmentName: string; | ||
| }>) { |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ApplicationContent component lacks documentation. Consider adding a JSDoc comment explaining its purpose and why it's separated from the main ApplicationDetailView component.
| Cell: ({ row }: any) => { | ||
| const resourceName = row.original.name; | ||
| return ( | ||
| <Link routeName="resource-detail" params={{ resourceName }}> | ||
| {resourceName} | ||
| </Link> | ||
| ); | ||
| }, | ||
| }, | ||
| { | ||
| header: 'Resource Group', | ||
| accessorKey: 'resourceGroup', | ||
| }, | ||
| { | ||
| header: 'Type', | ||
| accessorKey: 'type', | ||
| }, | ||
| { | ||
| header: 'Application', | ||
| accessorKey: 'application', | ||
| Cell: ({ row }: any) => { | ||
| const appName = row.original.application; | ||
| if (appName === 'N/A' || appName === '-') { | ||
| return <Typography variant="body2">{appName}</Typography>; | ||
| } | ||
| return ( | ||
| <Link routeName="application-detail" params={{ applicationName: appName }}> | ||
| {appName} | ||
| </Link> | ||
| ); | ||
| }, | ||
| }, | ||
| { | ||
| header: 'Environment', | ||
| accessorKey: 'environment', | ||
| Cell: ({ row }: any) => { | ||
| const envName = row.original.environment; | ||
| if (envName === 'N/A' || envName === '-') { | ||
| return <Typography variant="body2">{envName}</Typography>; | ||
| } | ||
| return ( | ||
| <Link routeName="environment-detail" params={{ environmentName: envName }}> | ||
| {envName} | ||
| </Link> | ||
| ); | ||
| }, |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type casting to 'any' is used for row parameters and other type assertions. This bypasses TypeScript's type checking and could lead to runtime errors. Consider defining proper types or using the types provided by the components.
Signed-off-by: Filipe Revez <[email protected]>
skoeva
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for making the changes, it would also be good to use atomic commits whenever possible (as mentioned here)
This pull request introduces the initial setup for the Radius Headlamp plugin, which provides visualization and management capabilities for Radius applications within the Headlamp Kubernetes UI. The changes include configuration files, documentation, core React components for listing and viewing application details, a status label component for Radius provisioning states, and plugin settings management.
Core plugin components:
Applicationscomponent (src/applications/index.tsx) to list all Radius applications in a table with links to details and environment views.ApplicationDetailViewcomponent (src/applications/ApplicationDetailView.tsx) to display detailed information about a selected application, including resources and system data.Supporting utilities and components:
RadiusStatusLabelcomponent (src/components/RadiusStatusLabel.tsx) to map and display Radius provisioning states using Headlamp's status label styles.getUCPApiVersionutility (src/config.ts) to manage the UCP API version configuration for the plugin, with fallback to a default value.There's still some refinement to be done on a future version:
Change logos to the official Radius ones