Skip to content

Commit ce813fa

Browse files
authored
update setup scripts and debug log view (#63)
* update setup playbooks Signed-off-by: wwanarif <wan.abdul.hakim.b.wan.arif@intel.com> * debuglogs to display the connections between pods Signed-off-by: wwanarif <wan.abdul.hakim.b.wan.arif@intel.com> * fix chat history bug and duplicate workflow bug Signed-off-by: wwanarif <wan.abdul.hakim.b.wan.arif@intel.com> * fix genai-studio ansible to include create-ssh-secrets playbook Signed-off-by: wwanarif <wan.abdul.hakim.b.wan.arif@intel.com> --------- Signed-off-by: wwanarif <wan.abdul.hakim.b.wan.arif@intel.com>
1 parent ef7215d commit ce813fa

File tree

11 files changed

+279
-24
lines changed

11 files changed

+279
-24
lines changed

app-frontend/react/src/pages/History/HistoryView.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React from "react";
12
import {
23
Box,
34
Checkbox,
@@ -26,6 +27,7 @@ import {
2627
SolidButton,
2728
TextButton,
2829
} from "@root/shared/ActionButtons";
30+
import { useToWithQuery } from "@utils/navigationAndAxiosWithQuery";
2931

3032
interface HistoryViewProps {
3133
shared: boolean;
@@ -34,6 +36,7 @@ interface HistoryViewProps {
3436
const HistoryView: React.FC<HistoryViewProps> = ({ shared }) => {
3537
const dispatch = useAppDispatch();
3638
const { name } = useAppSelector(userSelector);
39+
const toWithQuery = useToWithQuery();
3740

3841
const theme = useTheme();
3942

@@ -109,7 +112,7 @@ const HistoryView: React.FC<HistoryViewProps> = ({ shared }) => {
109112
) : (
110113
<Link
111114
component={RouterLink}
112-
to={`/chat/${conversation.id}`}
115+
to={toWithQuery(`/chat/${conversation.id}`)}
113116
sx={{ ...theme.customStyles.gradientBlock }}
114117
className={styles.historyLink}
115118
>

setup-scripts/setup-genai-studio/genai-studio.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
- name: Create ssh secrets
2+
import_playbook: playbooks/create-ssh-secrets.yml
3+
14
- name: Deploy mysqldb
25
import_playbook: playbooks/deploy-mysqldb.yml
36

setup-scripts/setup-genai-studio/helm-values/mysqldb.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
auth:
22
rootPassword: root
33

4+
image:
5+
tag: "8.0"
6+
47
primary:
58
extraFlags: "--innodb-use-native-aio=0"
69

setup-scripts/setup-genai-studio/playbooks/create-ssh-secrets.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
tasks:
55

6+
- name: Ensure namespace studio exists
7+
shell: kubectl get ns studio || kubectl create ns studio
8+
69
- name: Check if Kubernetes secret exists
710
command: kubectl -n studio get secret ssh-keys
811
register: kubectl_secret_check

setup-scripts/setup-genai-studio/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The genai-studio playbook script will:
2121

2222
Run below commands:
2323
```sh
24+
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
2425
sudo apt install ansible -y
2526
ansible-playbook genai-studio.yml
2627
```

setup-scripts/setup-onpremise-kubernetes/playbooks/setup-local-registry.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@
5252
https_proxy: "{{ http_proxy }}"
5353
no_proxy: "{{ no_proxy }}"
5454

55-
- name: Install Docker SDK for Python
56-
pip:
57-
name: docker
58-
executable: pip3
55+
- name: Install Docker SDK for Python via apt
56+
apt:
57+
name: python3-docker
5958
state: present
59+
become: yes
6060
environment:
6161
http_proxy: "{{ http_proxy }}"
6262
https_proxy: "{{ http_proxy }}"

studio-backend/app/routers/debuglog_router.py

Lines changed: 190 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,173 @@
11
from fastapi import APIRouter, HTTPException
22
from kubernetes import client
3+
import re
34

45
router = APIRouter()
56

7+
def find_pod_dependencies(pod, all_pods, services, namespace, core_v1_api):
8+
"""
9+
Analyze pod dependencies by checking:
10+
1. Environment variables pointing to other services
11+
2. Service selectors matching pod labels
12+
3. ConfigMaps and Secrets that might reference other pods
13+
4. Init containers waiting for remote services
14+
"""
15+
dependencies = []
16+
17+
# Get pod environment variables from main containers
18+
env_vars = []
19+
configmap_refs = []
20+
21+
if pod.spec.containers:
22+
for container in pod.spec.containers:
23+
# Direct environment variables
24+
if container.env:
25+
for env in container.env:
26+
if env.value:
27+
env_vars.append(env.value)
28+
29+
# Environment variables from ConfigMaps
30+
if container.env_from:
31+
for env_from in container.env_from:
32+
if env_from.config_map_ref:
33+
configmap_refs.append(env_from.config_map_ref.name)
34+
35+
# Get environment variables from init containers (for wait-for-remote-service patterns)
36+
init_env_vars = []
37+
38+
if pod.spec.init_containers:
39+
for init_container in pod.spec.init_containers:
40+
# Direct environment variables from init containers
41+
if init_container.env:
42+
for env in init_container.env:
43+
if env.value:
44+
init_env_vars.append(env.value)
45+
46+
# Environment variables from ConfigMaps in init containers
47+
if init_container.env_from:
48+
for env_from in init_container.env_from:
49+
if env_from.config_map_ref and env_from.config_map_ref.name not in configmap_refs:
50+
configmap_refs.append(env_from.config_map_ref.name)
51+
52+
# Check init container commands for wait-for-remote-service patterns
53+
if init_container.name == "wait-for-remote-service" or "wait-for" in init_container.name.lower():
54+
if init_container.command:
55+
command_str = " ".join(init_container.command)
56+
# Look for service endpoints in commands like "nc -z -v -w30 $HEALTHCHECK_ENDPOINT"
57+
# Extract service names from HEALTHCHECK_ENDPOINT or similar patterns
58+
for env_var in init_env_vars:
59+
# Pattern like "tei-0:9003" or "redis-vector-store-0:9001"
60+
service_match = re.search(r'([a-zA-Z0-9-]+):\d+', env_var)
61+
if service_match:
62+
service_name = service_match.group(1)
63+
# Find pods that this service targets
64+
for service in services.items:
65+
if service.metadata.name == service_name and service.spec.selector:
66+
for target_pod in all_pods.items:
67+
if target_pod.metadata.name != pod.metadata.name:
68+
target_labels = target_pod.metadata.labels or {}
69+
if all(target_labels.get(k) == v for k, v in service.spec.selector.items()):
70+
if target_pod.metadata.name not in dependencies:
71+
dependencies.append(target_pod.metadata.name)
72+
73+
# Fetch ConfigMap data to analyze environment variables
74+
configmap_env_vars = []
75+
for configmap_name in configmap_refs:
76+
try:
77+
configmap = core_v1_api.read_namespaced_config_map(name=configmap_name, namespace=namespace)
78+
if configmap.data:
79+
for key, value in configmap.data.items():
80+
if value:
81+
configmap_env_vars.append(value)
82+
except Exception as e:
83+
print(f"Error fetching ConfigMap {configmap_name}: {str(e)}")
84+
85+
# Combine all environment variables for further analysis
86+
all_env_vars = env_vars + init_env_vars + configmap_env_vars
87+
88+
# Debug output
89+
print(f"Analyzing dependencies for pod: {pod.metadata.name}")
90+
print(f"ConfigMap refs: {configmap_refs}")
91+
print(f"Total env vars found: {len(all_env_vars)}")
92+
if all_env_vars:
93+
print(f"Sample env vars: {all_env_vars[:3]}") # Show first 3 for debugging
94+
95+
# Check if environment variables reference other services
96+
for service in services.items:
97+
service_name = service.metadata.name
98+
# Check if service name is referenced in environment variables
99+
for env_val in all_env_vars:
100+
if service_name in env_val:
101+
# Find pods that this service targets
102+
if service.spec.selector:
103+
for target_pod in all_pods.items:
104+
if target_pod.metadata.name != pod.metadata.name:
105+
target_labels = target_pod.metadata.labels or {}
106+
if all(target_labels.get(k) == v for k, v in service.spec.selector.items()):
107+
if target_pod.metadata.name not in dependencies:
108+
dependencies.append(target_pod.metadata.name)
109+
110+
# Check for service endpoint patterns in environment variables
111+
# Pattern like "http://service-name:port" or "service-name:port"
112+
for env_val in all_env_vars:
113+
# HTTP URL pattern
114+
http_matches = re.findall(r'https?://([a-zA-Z0-9-]+):\d+', env_val)
115+
for service_name in http_matches:
116+
for service in services.items:
117+
if service.metadata.name == service_name and service.spec.selector:
118+
for target_pod in all_pods.items:
119+
if target_pod.metadata.name != pod.metadata.name:
120+
target_labels = target_pod.metadata.labels or {}
121+
if all(target_labels.get(k) == v for k, v in service.spec.selector.items()):
122+
if target_pod.metadata.name not in dependencies:
123+
dependencies.append(target_pod.metadata.name)
124+
125+
# Direct service:port pattern
126+
service_port_matches = re.findall(r'([a-zA-Z0-9-]+):\d+', env_val)
127+
for service_name in service_port_matches:
128+
for service in services.items:
129+
if service.metadata.name == service_name and service.spec.selector:
130+
for target_pod in all_pods.items:
131+
if target_pod.metadata.name != pod.metadata.name:
132+
target_labels = target_pod.metadata.labels or {}
133+
if all(target_labels.get(k) == v for k, v in service.spec.selector.items()):
134+
if target_pod.metadata.name not in dependencies:
135+
dependencies.append(target_pod.metadata.name)
136+
137+
# Check DNS-based dependencies (common pattern: service-name.namespace.svc.cluster.local)
138+
dns_pattern = r'([a-zA-Z0-9-]+)\.(' + re.escape(namespace) + r')\.svc\.cluster\.local'
139+
for env_val in all_env_vars:
140+
matches = re.findall(dns_pattern, env_val)
141+
for match in matches:
142+
service_name = match[0]
143+
# Find the service and its target pods
144+
for service in services.items:
145+
if service.metadata.name == service_name and service.spec.selector:
146+
for target_pod in all_pods.items:
147+
if target_pod.metadata.name != pod.metadata.name:
148+
target_labels = target_pod.metadata.labels or {}
149+
if all(target_labels.get(k) == v for k, v in service.spec.selector.items()):
150+
if target_pod.metadata.name not in dependencies:
151+
dependencies.append(target_pod.metadata.name)
152+
153+
# Additional pattern matching for service names that might not include ports
154+
# Look for patterns where service names appear in environment variable values
155+
service_names = [service.metadata.name for service in services.items]
156+
for env_val in all_env_vars:
157+
for service_name in service_names:
158+
# More flexible matching - look for service name as whole word
159+
if re.search(r'\b' + re.escape(service_name) + r'\b', env_val):
160+
for service in services.items:
161+
if service.metadata.name == service_name and service.spec.selector:
162+
for target_pod in all_pods.items:
163+
if target_pod.metadata.name != pod.metadata.name:
164+
target_labels = target_pod.metadata.labels or {}
165+
if all(target_labels.get(k) == v for k, v in service.spec.selector.items()):
166+
if target_pod.metadata.name not in dependencies:
167+
dependencies.append(target_pod.metadata.name)
168+
169+
return dependencies
170+
6171
@router.get("/podlogs/{namespace}", summary="Fetch all pods in a namespace")
7172
async def get_all_pods_in_namespace(namespace: str):
8173
core_v1_api = client.CoreV1Api()
@@ -11,6 +176,13 @@ async def get_all_pods_in_namespace(namespace: str):
11176
if not pods.items:
12177
return {"namespace": namespace, "pods": []}
13178

179+
# Fetch all services in the namespace for dependency analysis
180+
try:
181+
services = core_v1_api.list_namespaced_service(namespace=namespace)
182+
except Exception as e:
183+
print(f"Error fetching services: {str(e)}")
184+
services = None
185+
14186
pod_list = []
15187
for pod in pods.items:
16188
pod_name = pod.metadata.name
@@ -22,10 +194,14 @@ async def get_all_pods_in_namespace(namespace: str):
22194
# Fetch logs related to the pod
23195
try:
24196
pod_logs = core_v1_api.read_namespaced_pod_log(name=pod_name, namespace=namespace, tail_lines=200)
25-
for line in pod_logs.splitlines():
26-
log_entries.append(line)
197+
if pod_logs and pod_logs.strip():
198+
for line in pod_logs.splitlines():
199+
log_entries.append(line)
200+
else:
201+
log_entries.append("** Pod has no logs available")
27202
except Exception as e:
28203
print(f"Error fetching logs: {str(e)}")
204+
log_entries.append("Unable to fetch logs: Pod may not be running or logs are not accessible")
29205

30206
# Fetch events related to the pod
31207
try:
@@ -38,6 +214,17 @@ async def get_all_pods_in_namespace(namespace: str):
38214
except Exception as e:
39215
print(f"Error fetching events: {str(e)}")
40216

217+
# Analyze pod dependencies
218+
dependencies = []
219+
if services:
220+
try:
221+
dependencies = find_pod_dependencies(pod, pods, services, namespace, core_v1_api)
222+
print(f"Pod {pod_name} dependencies: {dependencies}")
223+
except Exception as e:
224+
print(f"Error analyzing dependencies for pod {pod_name}: {str(e)}")
225+
import traceback
226+
traceback.print_exc()
227+
41228
# Determine the Ready and Status of the pod
42229
ready_status = "Unknown"
43230
pod_status = pod.status.phase
@@ -63,6 +250,7 @@ async def get_all_pods_in_namespace(namespace: str):
63250
"annotations": pod.metadata.annotations,
64251
"logs": log_entries,
65252
"events": event_entries,
253+
"dependencies": dependencies,
66254
})
67255

68256
return {"namespace": namespace, "pods": pod_list}

studio-frontend/packages/ui/src/ui-component/button/FlowListMenu.jsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,17 @@ export default function FlowListMenu({ chatflow, isAgentCanvas, setError, update
265265
const handleDuplicate = () => {
266266
setAnchorEl(null)
267267
try {
268-
localStorage.setItem('duplicatedFlowData', chatflow.flowData)
269-
window.open(`${uiBaseURL}/${isAgentCanvas ? 'agentcanvas' : 'canvas'}`, '_blank')
268+
// Store both the flow data and type information
269+
const duplicatedData = {
270+
flowData: chatflow.flowData,
271+
type: chatflow.type || (isAgentCanvas ? 'MULTIAGENT' : 'CHATFLOW')
272+
}
273+
localStorage.setItem('duplicatedFlowData', JSON.stringify(duplicatedData))
274+
275+
// Determine canvas type based on original chatflow type
276+
const targetCanvas = chatflow.type === 'OPEA' ? 'opeacanvas' :
277+
chatflow.type === 'MULTIAGENT' ? 'agentcanvas' : 'canvas'
278+
window.open(`${uiBaseURL}/${targetCanvas}`, '_blank')
270279
} catch (e) {
271280
console.error(e)
272281
}

studio-frontend/packages/ui/src/views/canvas/CanvasHeader.jsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,18 @@ const CanvasHeader = ({ chatflow, isAgentCanvas, isOpeaCanvas, handleSaveFlow, h
121121
let flowData = chatflow.flowData
122122
const parsedFlowData = JSON.parse(flowData)
123123
flowData = JSON.stringify(parsedFlowData)
124-
localStorage.setItem('duplicatedFlowData', flowData)
125-
window.open(`${uiBaseURL}/${isAgentCanvas ? 'agentcanvas' : isOpeaCanvas ? 'opeacanvas' : 'canvas'}`, '_blank')
124+
125+
// Store both the flow data and type information
126+
const duplicatedData = {
127+
flowData: flowData,
128+
type: chatflow.type || (isAgentCanvas ? 'MULTIAGENT' : isOpeaCanvas ? 'OPEA' : 'CHATFLOW')
129+
}
130+
localStorage.setItem('duplicatedFlowData', JSON.stringify(duplicatedData))
131+
132+
// Determine canvas type based on original chatflow type
133+
const targetCanvas = chatflow.type === 'OPEA' ? 'opeacanvas' :
134+
chatflow.type === 'MULTIAGENT' ? 'agentcanvas' : 'canvas'
135+
window.open(`${uiBaseURL}/${targetCanvas}`, '_blank')
126136
} catch (e) {
127137
console.error(e)
128138
}

0 commit comments

Comments
 (0)