diff --git a/Makefile b/Makefile index 7fd3500ceb..4754206b7f 100644 --- a/Makefile +++ b/Makefile @@ -261,8 +261,12 @@ clidocsgen: actionsdocsgen: hack/generate-actions-list.sh +.PHONY: resourceiconsgen +resourceiconsgen: + hack/generate-icons-typescript.sh + .PHONY: codegen-local -codegen-local: mod-vendor-local mockgen gogen protogen clientgen openapigen clidocsgen actionsdocsgen manifests-local notification-docs notification-catalog +codegen-local: mod-vendor-local mockgen gogen protogen clientgen openapigen clidocsgen actionsdocsgen resourceiconsgen manifests-local notification-docs notification-catalog rm -rf vendor/ .PHONY: codegen-local-fast diff --git a/docs/developer-guide/custom-resource-icons.md b/docs/developer-guide/custom-resource-icons.md new file mode 100644 index 0000000000..49a6b155f3 --- /dev/null +++ b/docs/developer-guide/custom-resource-icons.md @@ -0,0 +1,18 @@ +The Argo CD UI displays icons for various Kubernetes resource types to help users quickly identify them. Argo CD +includes a set of built-in icons for common resource types. + +You can contribute additional icons for custom resource types by following these steps: + +1. Ensure the license is compatible with Apache 2.0. +2. Add the icon file to the `ui/src/assets/images/resources//icon.svg` path in the Argo CD repository. +3. Modify the SVG to use the correct color, `#8fa4b1`. +4. Run `make resourceiconsgen` to update the generated typescript file that lists all available icons. +5. Create a pull request to the Argo CD repository with your changes. + +`` is the API group of the custom resource. For example, if you are adding an icon for a custom resource with the +API group `example.com`, you would place the icon at `ui/src/assets/images/resources/example.com/icon.svg`. + +If you want the same icon to apply to resources in multiple API groups with the same suffix, you can create a directory +prefixed with an underscore. The underscore will be interpreted as a wildcard. For example, to apply the same icon to +resources in the `example.com` and `another.example.com` API groups, you would place the icon at +`ui/src/assets/images/resources/_.example.com/icon.svg`. diff --git a/hack/generate-icons-typescript.sh b/hack/generate-icons-typescript.sh new file mode 100755 index 0000000000..51a4d0469a --- /dev/null +++ b/hack/generate-icons-typescript.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# Users may configure custom resource icons in resource_customizations. This script generates a list of those icons so +# that the UI knows which icons are available. + +{ + echo "// Code generated by hack/generate-icons-typescript.sh; DO NOT EDIT."; + echo "/* eslint-disable prettier/prettier */"; + echo ""; + echo "// resourceIconGroups is a map of resource kind globs to whether or not a custom icon exists for that kind."; + echo "// Each glob corresponds to a directory under ui/src/assets/images/resources, where any asterisk is represented as an underscore (_)."; + echo "export const resourceIconGroups = {"; + find ui/src/assets/images/resources -name icon.svg | sort | sed "s/ui\/src\/assets\/images\/resources\// '/" | sed "s/\/icon.svg/': true,/" | sed 's/_/*/'; + echo "};"; +} > ui/src/app/applications/components/resource-customizations.ts diff --git a/mkdocs.yml b/mkdocs.yml index b0832c9684..c3ca88daa7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -229,6 +229,7 @@ nav: - developer-guide/extensions/proxy-extensions.md - developer-guide/faq.md - developer-guide/tilt.md + - developer-guide/custom-resource-icons.md - faq.md - security_considerations.md - Support: SUPPORT.md diff --git a/ui/src/app/applications/components/application-details/application-resource-list.tsx b/ui/src/app/applications/components/application-details/application-resource-list.tsx index caf51483d8..b48f60a366 100644 --- a/ui/src/app/applications/components/application-details/application-resource-list.tsx +++ b/ui/src/app/applications/components/application-details/application-resource-list.tsx @@ -175,7 +175,7 @@ export const ApplicationResourceList = (props: ApplicationResourceListProps) =>
- +
{ResourceLabel({kind: res.kind})}
diff --git a/ui/src/app/applications/components/application-pod-view/pod-view.tsx b/ui/src/app/applications/components/application-pod-view/pod-view.tsx index e1d280391b..15753961c4 100644 --- a/ui/src/app/applications/components/application-pod-view/pod-view.tsx +++ b/ui/src/app/applications/components/application-pod-view/pod-view.tsx @@ -126,7 +126,7 @@ export function PodView(props: PodViewProps) { style={group.kind === 'node' ? {} : {cursor: 'pointer'}}>
- +
{
{ResourceLabel({kind: group.kind})}
}
diff --git a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx index 311ea1fb34..cc1ecb4491 100644 --- a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx +++ b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx @@ -284,7 +284,7 @@ function renderGroupedNodes(props: ApplicationResourceTreeProps, node: {count: n
- +
{ResourceLabel({kind: node.kind})}
@@ -462,7 +462,7 @@ function renderPodGroup(props: ApplicationResourceTreeProps, id: string, node: R className={classNames('application-resource-tree__node-kind-icon', { 'application-resource-tree__node-kind-icon--big': rootNode })}> - +
{!rootNode &&
{ResourceLabel({kind: node.kind})}
}
@@ -746,7 +746,7 @@ function renderResourceNode(props: ApplicationResourceTreeProps, id: string, nod className={classNames('application-resource-tree__node-kind-icon', { 'application-resource-tree__node-kind-icon--big': rootNode })}> - +
{!rootNode &&
{ResourceLabel({kind: node.kind})}
}
diff --git a/ui/src/app/applications/components/resource-customizations.ts b/ui/src/app/applications/components/resource-customizations.ts new file mode 100644 index 0000000000..ff288f2e83 --- /dev/null +++ b/ui/src/app/applications/components/resource-customizations.ts @@ -0,0 +1,26 @@ +// Code generated by hack/generate-icons-typescript.sh; DO NOT EDIT. +/* eslint-disable prettier/prettier */ + +// resourceIconGroups is a map of resource kind globs to whether or not a custom icon exists for that kind. +// Each glob corresponds to a directory under ui/src/assets/images/resources, where any asterisk is represented as an underscore (_). +export const resourceIconGroups = { + '*.crossplane.io': true, + '*.fluxcd.io': true, + '*.knative.dev': true, + 'cassandra.rook.io': true, + 'cert-manager.io': true, + 'core.spinkube.dev': true, + 'external-secrets.io': true, + 'flagger.app': true, + 'install.istio.io': true, + 'jaegertracing.io': true, + 'k8s.keycloak.org': true, + 'kafka.strimzi.io': true, + 'keda.sh': true, + 'kubevirt.io': true, + 'kyverno.io': true, + 'opentelemetry.io': true, + 'projectcontour.io': true, + 'work.karmada.io': true, + 'zookeeper.pravega.io': true, +}; diff --git a/ui/src/app/applications/components/resource-details/resource-details.tsx b/ui/src/app/applications/components/resource-details/resource-details.tsx index 223d6225a4..4494400f8d 100644 --- a/ui/src/app/applications/components/resource-details/resource-details.tsx +++ b/ui/src/app/applications/components/resource-details/resource-details.tsx @@ -291,7 +291,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => {
- + {ResourceLabel({kind: selectedNode.kind})}

{selectedNode.name}

diff --git a/ui/src/app/applications/components/resource-icon.tsx b/ui/src/app/applications/components/resource-icon.tsx index 071a9c9cd3..1d7f68e08b 100644 --- a/ui/src/app/applications/components/resource-icon.tsx +++ b/ui/src/app/applications/components/resource-icon.tsx @@ -1,17 +1,26 @@ import * as React from 'react'; import {resourceIcons} from './resources'; +import {resourceIconGroups as resourceCustomizations} from './resource-customizations'; +import * as minimatch from 'minimatch'; -export const ResourceIcon = ({kind, customStyle}: {kind: string; customStyle?: React.CSSProperties}) => { +export const ResourceIcon = ({group, kind, customStyle}: {group: string; kind: string; customStyle?: React.CSSProperties}) => { if (kind === 'node') { return {kind}; } - const i = resourceIcons.get(kind); - if (i !== undefined) { - return {kind}; - } if (kind === 'Application') { return ; } + if (!group) { + const i = resourceIcons.get(kind); + if (i !== undefined) { + return {kind}; + } + } else { + const matchedGroup = matchGroupToResource(group); + if (matchedGroup) { + return {kind}; + } + } const initials = kind.replace(/[a-z]/g, ''); const n = initials.length; const style: React.CSSProperties = { @@ -32,3 +41,22 @@ export const ResourceIcon = ({kind, customStyle}: {kind: string; customStyle?: R
); }; + +// Utility function to match group with possible wildcards in resourceCustomizations. If found, returns the matched key +// as a path component (with '*' replaced by '_' if necessary), otherwise returns an empty string. +function matchGroupToResource(group: string): string { + // Check for an exact match + if (group in resourceCustomizations) { + return group; + } + + // Loop over the map keys to find a match using minimatch + for (const key in resourceCustomizations) { + if (key.includes('*') && minimatch(group, key)) { + return key.replace(/\*/g, '_'); + } + } + + // Return an empty string if no match is found + return ''; +} diff --git a/ui/src/assets/images/resources/_.crossplane.io/icon.svg b/ui/src/assets/images/resources/_.crossplane.io/icon.svg new file mode 100644 index 0000000000..571faa58d3 --- /dev/null +++ b/ui/src/assets/images/resources/_.crossplane.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/_.fluxcd.io/icon.svg b/ui/src/assets/images/resources/_.fluxcd.io/icon.svg new file mode 100644 index 0000000000..5156127ca6 --- /dev/null +++ b/ui/src/assets/images/resources/_.fluxcd.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/_.knative.dev/icon.svg b/ui/src/assets/images/resources/_.knative.dev/icon.svg new file mode 100644 index 0000000000..328b5adfe8 --- /dev/null +++ b/ui/src/assets/images/resources/_.knative.dev/icon.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/cassandra.rook.io/icon.svg b/ui/src/assets/images/resources/cassandra.rook.io/icon.svg new file mode 100644 index 0000000000..40048a6b53 --- /dev/null +++ b/ui/src/assets/images/resources/cassandra.rook.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/cert-manager.io/icon.svg b/ui/src/assets/images/resources/cert-manager.io/icon.svg new file mode 100644 index 0000000000..92d76f5435 --- /dev/null +++ b/ui/src/assets/images/resources/cert-manager.io/icon.svg @@ -0,0 +1,5 @@ + + diff --git a/ui/src/assets/images/resources/core.spinkube.dev/icon.svg b/ui/src/assets/images/resources/core.spinkube.dev/icon.svg new file mode 100644 index 0000000000..69fc297b18 --- /dev/null +++ b/ui/src/assets/images/resources/core.spinkube.dev/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/external-secrets.io/icon.svg b/ui/src/assets/images/resources/external-secrets.io/icon.svg new file mode 100644 index 0000000000..59dec185d0 --- /dev/null +++ b/ui/src/assets/images/resources/external-secrets.io/icon.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/flagger.app/icon.svg b/ui/src/assets/images/resources/flagger.app/icon.svg new file mode 100644 index 0000000000..339c161f05 --- /dev/null +++ b/ui/src/assets/images/resources/flagger.app/icon.svg @@ -0,0 +1,34 @@ + + + + flagger-icon-white + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/src/assets/images/resources/install.istio.io/icon.svg b/ui/src/assets/images/resources/install.istio.io/icon.svg new file mode 100644 index 0000000000..81db07d2d0 --- /dev/null +++ b/ui/src/assets/images/resources/install.istio.io/icon.svg @@ -0,0 +1,15 @@ + + + + + + + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/jaegertracing.io/icon.svg b/ui/src/assets/images/resources/jaegertracing.io/icon.svg new file mode 100644 index 0000000000..24f1e5bce2 --- /dev/null +++ b/ui/src/assets/images/resources/jaegertracing.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/k8s.keycloak.org/icon.svg b/ui/src/assets/images/resources/k8s.keycloak.org/icon.svg new file mode 100644 index 0000000000..48b5177373 --- /dev/null +++ b/ui/src/assets/images/resources/k8s.keycloak.org/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/kafka.strimzi.io/icon.svg b/ui/src/assets/images/resources/kafka.strimzi.io/icon.svg new file mode 100644 index 0000000000..fe093d83f0 --- /dev/null +++ b/ui/src/assets/images/resources/kafka.strimzi.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/keda.sh/icon.svg b/ui/src/assets/images/resources/keda.sh/icon.svg new file mode 100644 index 0000000000..5bd1596b74 --- /dev/null +++ b/ui/src/assets/images/resources/keda.sh/icon.svg @@ -0,0 +1,5 @@ + +keda-icon-white \ No newline at end of file diff --git a/ui/src/assets/images/resources/kubevirt.io/icon.svg b/ui/src/assets/images/resources/kubevirt.io/icon.svg new file mode 100644 index 0000000000..8fb311cd0d --- /dev/null +++ b/ui/src/assets/images/resources/kubevirt.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/kyverno.io/icon.svg b/ui/src/assets/images/resources/kyverno.io/icon.svg new file mode 100644 index 0000000000..8fae1b657b --- /dev/null +++ b/ui/src/assets/images/resources/kyverno.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/opentelemetry.io/icon.svg b/ui/src/assets/images/resources/opentelemetry.io/icon.svg new file mode 100644 index 0000000000..d45bd60a28 --- /dev/null +++ b/ui/src/assets/images/resources/opentelemetry.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/projectcontour.io/icon.svg b/ui/src/assets/images/resources/projectcontour.io/icon.svg new file mode 100644 index 0000000000..89d6e75a5f --- /dev/null +++ b/ui/src/assets/images/resources/projectcontour.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/ui/src/assets/images/resources/work.karmada.io/icon.svg b/ui/src/assets/images/resources/work.karmada.io/icon.svg new file mode 100644 index 0000000000..a155133b3e --- /dev/null +++ b/ui/src/assets/images/resources/work.karmada.io/icon.svg @@ -0,0 +1,5 @@ + +icon-white \ No newline at end of file diff --git a/ui/src/assets/images/resources/zookeeper.pravega.io/icon.svg b/ui/src/assets/images/resources/zookeeper.pravega.io/icon.svg new file mode 100644 index 0000000000..1fb7bdb781 --- /dev/null +++ b/ui/src/assets/images/resources/zookeeper.pravega.io/icon.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file