Skip to content

Commit

Permalink
dynamic topology
Browse files Browse the repository at this point in the history
Signed-off-by: Jason Madigan <[email protected]>
  • Loading branch information
jasonmadigan committed Oct 9, 2024
1 parent 273c624 commit 2e9c37d
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 27 deletions.
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM registry.access.redhat.com/ubi9/nodejs-20 AS build
FROM registry.access.redhat.com/ubi9/nodejs-20 AS build
USER root
RUN command -v yarn || npm i -g yarn

Expand All @@ -9,6 +9,9 @@ RUN yarn install && yarn build
FROM registry.access.redhat.com/ubi9/nginx-124:1-10

COPY --from=build /usr/src/app/dist /usr/share/nginx/html

COPY entrypoint.sh /usr/share/nginx/html/entrypoint.sh

USER 1001

ENTRYPOINT ["nginx", "-g", "daemon off;"]
ENTRYPOINT ["/usr/share/nginx/html/entrypoint.sh"]
19 changes: 7 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,16 @@ push it to an image registry.

1. Build the image:

```sh
docker build -t quay.io/kuadrant/console-plugin:latest .
```
```bash
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t quay.io/kuadrant/console-plugin:latest --push .
```

2. Run the image:

```sh
docker run -it --rm -d -p 9001:80 quay.io/kuadrant/console-plugin:latest
```

3. Push the image:

```sh
docker push quay.io/kuadrant/console-plugin:latest
```
```bash
docker run -it --rm -d -p 9001:80 quay.io/kuadrant/console-plugin:latest
```

NOTE: If you have a Mac with Apple silicon, you will need to add the flag
`--platform=linux/amd64` when building the image to target the correct platform
Expand Down
12 changes: 12 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

# Inject topology ConfigMap location
cat <<EOF > /tmp/config.js
window.kuadrant_config = {
TOPOLOGY_CONFIGMAP_NAME: '${TOPOLOGY_CONFIGMAP_NAME}',
TOPOLOGY_CONFIGMAP_NAMESPACE: '${TOPOLOGY_CONFIGMAP_NAMESPACE}'
};
EOF

# Start Nginx
nginx -g "daemon off;"
10 changes: 10 additions & 0 deletions install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ spec:
- containerPort: 9443
protocol: TCP
imagePullPolicy: Always
env:
- name: TOPOLOGY_CONFIGMAP_NAME
value: topology
- name: TOPOLOGY_CONFIGMAP_NAMESPACE
value: kuadrant-system
volumeMounts:
- name: plugin-serving-cert
readOnly: true
Expand Down Expand Up @@ -93,6 +98,11 @@ data:
location / {
root /usr/share/nginx/html;
}
# Serve config.js from /tmp
location /config.js {
root /tmp;
}
}
}
---
Expand Down
80 changes: 67 additions & 13 deletions src/components/PolicyTopologyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,39 @@ import * as dot from 'graphlib-dot';
import { kindToAbbr } from '../utils/modelUtils';
import './kuadrant.css';

// Fetch the config.js file dynamically at runtime
// Normally served from <cluster-host>/api/plugins/kuadrant-console/config.js
const fetchConfig = async () => {
const defaultConfig = {
TOPOLOGY_CONFIGMAP_NAME: 'topology',
TOPOLOGY_CONFIGMAP_NAMESPACE: 'kuadrant-system',
};

try {
const response = await fetch('/api/plugins/kuadrant-console/config.js');
if (!response.ok) {
if (response.status === 404) {
console.warn('config.js not found (running locally perhaps). Falling back to defaults.');
} else {
throw new Error(`Failed to fetch config.js: ${response.statusText}`);
}
return defaultConfig; // Fallback on 404
}

const script = await response.text();

const configScript = document.createElement('script');
configScript.innerHTML = script;
document.head.appendChild(configScript);

return (window as any).kuadrant_config || defaultConfig;
} catch (error) {
console.error('Error loading config.js:', error);
return defaultConfig;
}
};


// TODO: need a generic way to fetch latest versions of resources based on kind + group
const resourceGVKMapping: { [key: string]: { group: string; version: string; kind: string } } = {
Gateway: { group: 'gateway.networking.k8s.io', version: 'v1', kind: 'Gateway' },
Expand Down Expand Up @@ -294,18 +327,36 @@ const customComponentFactory = (kind: ModelKind, type: string) => {
};

const PolicyTopologyPage: React.FC = () => {
const [config, setConfig] = React.useState<any | null>(null);
const [parseError, setParseError] = React.useState<string | null>(null);

// Watch the ConfigMap named "topology" in the "kuadrant-system" namespace
// TODO: lookup instance of `Kuadrant` and read topology from same NS.
const [configMap, loaded, loadError] = useK8sWatchResource<any>({
groupVersionKind: {
version: 'v1',
kind: 'ConfigMap',
},
name: 'topology',
namespace: 'kuadrant-system',
});
// Fetch the configuration on mount
React.useEffect(() => {
const loadConfig = async () => {
try {
const configData = await fetchConfig();
setConfig(configData);
} catch (error) {
console.error('Error loading config.js:', error);
setParseError('Failed to load configuration.');
}
};
loadConfig();
}, []);

// Watch the ConfigMap named "topology" in the namespace provided by the config.js
const [configMap, loaded, loadError] = useK8sWatchResource<any>(
config
? {
groupVersionKind: {
version: 'v1',
kind: 'ConfigMap',
},
name: config.TOPOLOGY_CONFIGMAP_NAME,
namespace: config.TOPOLOGY_CONFIGMAP_NAMESPACE,
}
: null, // Only watch if config is loaded
);

const controllerRef = React.useRef<Visualization | null>(null);

Expand All @@ -324,7 +375,6 @@ const PolicyTopologyPage: React.FC = () => {
const visualization = new Visualization();
visualization.registerLayoutFactory(customLayoutFactory);
visualization.registerComponentFactory(customComponentFactory);
visualization.setRenderConstraint(false);
visualization.fromModel(initialModel, false);
controllerRef.current = visualization;
}
Expand All @@ -337,7 +387,7 @@ const PolicyTopologyPage: React.FC = () => {

// Handle data updates
React.useEffect(() => {
if (loaded && !loadError) {
if (loaded && !loadError && configMap) {
const dotString = configMap.data?.topology || '';
if (dotString) {
try {
Expand Down Expand Up @@ -371,6 +421,10 @@ const PolicyTopologyPage: React.FC = () => {
// Memoize the controller
const controller = controllerRef.current;

if (!config) {
return <div>Loading configuration...</div>;
}

return (
<>
<Helmet>
Expand All @@ -392,7 +446,7 @@ const PolicyTopologyPage: React.FC = () => {
</Text>
</TextContent>
{!loaded ? (
<div>Loading...</div>
<div>Loading topology...</div>
) : loadError ? (
<div>Error loading topology: {loadError.message}</div>
) : parseError ? (
Expand Down

0 comments on commit 2e9c37d

Please sign in to comment.