Compare commits
2 Commits
7a666303be
...
a80744c533
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a80744c533 | ||
|
|
704d0fe0bf |
63
Dockerfile
Normal file
63
Dockerfile
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Multi-stage build for Cluster application with Go backend and React frontend
|
||||||
|
|
||||||
|
# Frontend build stage
|
||||||
|
FROM node:18 AS frontend-build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
# Install pnpm globally
|
||||||
|
RUN npm install -g pnpm
|
||||||
|
|
||||||
|
# Install frontend dependencies
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Copy frontend source
|
||||||
|
COPY frontend/ .
|
||||||
|
|
||||||
|
# Build frontend
|
||||||
|
RUN pnpm run build
|
||||||
|
|
||||||
|
# Backend build stage
|
||||||
|
FROM golang:1.22 AS backend-build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy go mod files
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
|
||||||
|
# Download dependencies
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# Copy only backend source code
|
||||||
|
COPY main.go ./
|
||||||
|
COPY internal/ ./internal/
|
||||||
|
COPY pkg/ ./pkg/
|
||||||
|
|
||||||
|
# Build backend
|
||||||
|
RUN go build -o cluster .
|
||||||
|
|
||||||
|
# Final stage - Nginx server
|
||||||
|
FROM nginx:latest
|
||||||
|
|
||||||
|
# Copy nginx configuration
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
# Copy backend binary
|
||||||
|
COPY --from=backend-build /app/cluster /app/cluster
|
||||||
|
|
||||||
|
# Copy frontend build
|
||||||
|
COPY --from=frontend-build /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Create data directory
|
||||||
|
RUN mkdir -p /app/x-storage
|
||||||
|
|
||||||
|
# Expose ports
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Start backend and nginx
|
||||||
|
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
RUN chmod +x /docker-entrypoint.sh
|
||||||
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
10
docker-entrypoint.sh
Executable file
10
docker-entrypoint.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Start the Go backend in the background
|
||||||
|
/app/cluster -address 127.0.0.1:9119 -data-dir /data &
|
||||||
|
|
||||||
|
# Wait a moment for backend to start
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Start nginx in the foreground
|
||||||
|
nginx -g 'daemon off;'
|
||||||
@@ -76,6 +76,7 @@ export default function K8sResourceList() {
|
|||||||
const [logsDialogOpen, setLogsDialogOpen] = useState(false)
|
const [logsDialogOpen, setLogsDialogOpen] = useState(false)
|
||||||
const [logs, setLogs] = useState<string[]>([])
|
const [logs, setLogs] = useState<string[]>([])
|
||||||
const [selectedPod, setSelectedPod] = useState<{ name: string; namespace: string } | null>(null)
|
const [selectedPod, setSelectedPod] = useState<{ name: string; namespace: string } | null>(null)
|
||||||
|
const eventSourceRef = useRef<EventSource | null>(null)
|
||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
|
||||||
const [deleteTarget, setDeleteTarget] = useState<{ name: string; namespace: string } | null>(null)
|
const [deleteTarget, setDeleteTarget] = useState<{ name: string; namespace: string } | null>(null)
|
||||||
const [deleting, setDeleting] = useState(false)
|
const [deleting, setDeleting] = useState(false)
|
||||||
@@ -98,6 +99,17 @@ export default function K8sResourceList() {
|
|||||||
}
|
}
|
||||||
}, [selectedKind, namespace, nameFilter])
|
}, [selectedKind, namespace, nameFilter])
|
||||||
|
|
||||||
|
// Clean up SSE connection on component unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (eventSourceRef.current) {
|
||||||
|
console.log('Cleaning up SSE connection on component unmount')
|
||||||
|
eventSourceRef.current.close()
|
||||||
|
eventSourceRef.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const fetchKubeconfig = async () => {
|
const fetchKubeconfig = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/v1/k8s/config')
|
const res = await fetch('/api/v1/k8s/config')
|
||||||
@@ -171,24 +183,62 @@ export default function K8sResourceList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleViewLogs = (podName: string, podNamespace: string) => {
|
const handleViewLogs = (podName: string, podNamespace: string) => {
|
||||||
|
console.log('handleViewLogs called with:', { podName, podNamespace })
|
||||||
setSelectedPod({ name: podName, namespace: podNamespace })
|
setSelectedPod({ name: podName, namespace: podNamespace })
|
||||||
setLogs([])
|
setLogs([])
|
||||||
setLogsDialogOpen(true)
|
setLogsDialogOpen(true)
|
||||||
|
|
||||||
|
// Close any existing connection
|
||||||
|
if (eventSourceRef.current) {
|
||||||
|
console.log('Closing existing EventSource connection')
|
||||||
|
eventSourceRef.current.close()
|
||||||
|
eventSourceRef.current = null
|
||||||
|
}
|
||||||
|
|
||||||
const eventSource = new EventSource(
|
const eventSource = new EventSource(
|
||||||
`/api/v1/k8s/pod/logs?name=${encodeURIComponent(podName)}&namespace=${encodeURIComponent(podNamespace)}&tail=1000&follow=true`
|
`/api/v1/k8s/pod/logs?name=${encodeURIComponent(podName)}&namespace=${encodeURIComponent(podNamespace)}&tail=1000&follow=true`
|
||||||
)
|
)
|
||||||
|
|
||||||
eventSource.onmessage = (event) => {
|
// Save reference to the EventSource
|
||||||
setLogs((prev) => [...prev, event.data])
|
eventSourceRef.current = eventSource
|
||||||
setTimeout(() => logsEndRef.current?.scrollIntoView({ behavior: 'smooth' }), 100)
|
|
||||||
}
|
// Listen for the specific event type 'pod-logs'
|
||||||
|
eventSource.addEventListener('pod-logs', (event: MessageEvent) => {
|
||||||
|
try {
|
||||||
|
const message = JSON.parse(event.data)
|
||||||
|
if (message.type === 'log') {
|
||||||
|
setLogs((prev) => [...prev, message.data])
|
||||||
|
setTimeout(() => logsEndRef.current?.scrollIntoView({ behavior: 'smooth' }), 100)
|
||||||
|
} else if (message.type === 'EOF') {
|
||||||
|
// Handle end of stream if needed
|
||||||
|
} else if (message.type === 'error') {
|
||||||
|
setLogs((prev) => [...prev, `Error: ${message.data}`])
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// If parsing fails, treat as plain text (fallback)
|
||||||
|
setLogs((prev) => [...prev, event.data])
|
||||||
|
setTimeout(() => logsEndRef.current?.scrollIntoView({ behavior: 'smooth' }), 100)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
eventSource.onerror = () => {
|
eventSource.onerror = () => {
|
||||||
eventSource.close()
|
console.log('EventSource error occurred')
|
||||||
|
if (eventSourceRef.current) {
|
||||||
|
eventSourceRef.current.close()
|
||||||
|
eventSourceRef.current = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return () => eventSource.close()
|
const handleCloseLogsDialog = () => {
|
||||||
|
console.log('handleCloseLogsDialog called')
|
||||||
|
// Close the EventSource connection if it exists
|
||||||
|
if (eventSourceRef.current) {
|
||||||
|
console.log('Closing EventSource connection')
|
||||||
|
eventSourceRef.current.close()
|
||||||
|
eventSourceRef.current = null
|
||||||
|
}
|
||||||
|
setLogsDialogOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeleteResource = async () => {
|
const handleDeleteResource = async () => {
|
||||||
@@ -843,7 +893,7 @@ export default function K8sResourceList() {
|
|||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
open={logsDialogOpen}
|
open={logsDialogOpen}
|
||||||
onClose={() => setLogsDialogOpen(false)}
|
onClose={handleCloseLogsDialog}
|
||||||
maxWidth="lg"
|
maxWidth="lg"
|
||||||
fullWidth
|
fullWidth
|
||||||
>
|
>
|
||||||
@@ -852,7 +902,7 @@ export default function K8sResourceList() {
|
|||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
Pod 日志: {selectedPod?.name} ({selectedPod?.namespace})
|
Pod 日志: {selectedPod?.name} ({selectedPod?.namespace})
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton onClick={() => setLogsDialogOpen(false)}>
|
<IconButton onClick={handleCloseLogsDialog}>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
8
go.mod
8
go.mod
@@ -4,7 +4,7 @@ go 1.25.0
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/glebarez/sqlite v1.11.0
|
github.com/glebarez/sqlite v1.11.0
|
||||||
github.com/gofiber/fiber/v3 v3.0.0-beta.2
|
github.com/gofiber/fiber/v3 v3.0.0-rc.2
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/google/go-containerregistry v0.20.6
|
github.com/google/go-containerregistry v0.20.6
|
||||||
github.com/jedib0t/go-pretty/v6 v6.7.1
|
github.com/jedib0t/go-pretty/v6 v6.7.1
|
||||||
@@ -12,6 +12,7 @@ require (
|
|||||||
golang.org/x/crypto v0.43.0
|
golang.org/x/crypto v0.43.0
|
||||||
golang.org/x/net v0.46.0
|
golang.org/x/net v0.46.0
|
||||||
gorm.io/gorm v1.31.1
|
gorm.io/gorm v1.31.1
|
||||||
|
k8s.io/api v0.34.1
|
||||||
k8s.io/apimachinery v0.34.1
|
k8s.io/apimachinery v0.34.1
|
||||||
k8s.io/client-go v0.34.1
|
k8s.io/client-go v0.34.1
|
||||||
sigs.k8s.io/yaml v1.6.0
|
sigs.k8s.io/yaml v1.6.0
|
||||||
@@ -32,6 +33,7 @@ require (
|
|||||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/swag v0.23.0 // indirect
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
|
github.com/gofiber/schema v1.6.0 // indirect
|
||||||
github.com/gofiber/utils/v2 v2.0.0-rc.1 // indirect
|
github.com/gofiber/utils/v2 v2.0.0-rc.1 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/google/gnostic-models v0.7.0 // indirect
|
github.com/google/gnostic-models v0.7.0 // indirect
|
||||||
@@ -52,12 +54,13 @@ require (
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||||
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/spf13/pflag v1.0.9 // indirect
|
github.com/spf13/pflag v1.0.9 // indirect
|
||||||
github.com/stretchr/testify v1.11.1 // indirect
|
github.com/tinylib/msgp v1.4.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.65.0 // indirect
|
github.com/valyala/fasthttp v1.65.0 // indirect
|
||||||
github.com/vbatts/tar-split v0.12.1 // indirect
|
github.com/vbatts/tar-split v0.12.1 // indirect
|
||||||
@@ -74,7 +77,6 @@ require (
|
|||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/api v0.34.1 // indirect
|
|
||||||
k8s.io/klog/v2 v2.130.1 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||||
|
|||||||
14
go.sum
14
go.sum
@@ -35,8 +35,10 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
|
|||||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/gofiber/fiber/v3 v3.0.0-beta.2 h1:mVVgt8PTaHGup3NGl/+7U7nEoZaXJ5OComV4E+HpAao=
|
github.com/gofiber/fiber/v3 v3.0.0-rc.2 h1:5I3RQ7XygDBfWRlMhkATjyJKupMmfMAVmnsrgo6wmc0=
|
||||||
github.com/gofiber/fiber/v3 v3.0.0-beta.2/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM=
|
github.com/gofiber/fiber/v3 v3.0.0-rc.2/go.mod h1:EHKwhVCONMruJTOmvSPSy0CdACJ3uqCY8vGaBXft8yg=
|
||||||
|
github.com/gofiber/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY=
|
||||||
|
github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s=
|
||||||
github.com/gofiber/utils/v2 v2.0.0-rc.1 h1:b77K5Rk9+Pjdxz4HlwEBnS7u5nikhx7armQB8xPds4s=
|
github.com/gofiber/utils/v2 v2.0.0-rc.1 h1:b77K5Rk9+Pjdxz4HlwEBnS7u5nikhx7armQB8xPds4s=
|
||||||
github.com/gofiber/utils/v2 v2.0.0-rc.1/go.mod h1:Y1g08g7gvST49bbjHJ1AVqcsmg93912R/tbKWhn6V3E=
|
github.com/gofiber/utils/v2 v2.0.0-rc.1/go.mod h1:Y1g08g7gvST49bbjHJ1AVqcsmg93912R/tbKWhn6V3E=
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||||
@@ -103,6 +105,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
|||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||||
|
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||||
|
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
@@ -116,8 +120,8 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
|
|||||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shamaton/msgpack/v2 v2.2.3 h1:uDOHmxQySlvlUYfQwdjxyybAOzjlQsD1Vjy+4jmO9NM=
|
github.com/shamaton/msgpack/v2 v2.3.1 h1:R3QNLIGA/tbdczNMZ5PCRxrXvy+fnzsIaHG4kKMgWYo=
|
||||||
github.com/shamaton/msgpack/v2 v2.2.3/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
|
github.com/shamaton/msgpack/v2 v2.3.1/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||||
@@ -136,6 +140,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/tinylib/msgp v1.4.0 h1:SYOeDRiydzOw9kSiwdYp9UcBgPFtLU2WDHaJXyHruf8=
|
||||||
|
github.com/tinylib/msgp v1.4.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.65.0 h1:j/u3uzFEGFfRxw79iYzJN+TteTJwbYkru9uDp3d0Yf8=
|
github.com/valyala/fasthttp v1.65.0 h1:j/u3uzFEGFfRxw79iYzJN+TteTJwbYkru9uDp3d0Yf8=
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
@@ -22,7 +23,6 @@ import (
|
|||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getK8sClient(db *gorm.DB) (*kubernetes.Clientset, error) {
|
func getK8sClient(db *gorm.DB) (*kubernetes.Clientset, error) {
|
||||||
@@ -386,25 +386,25 @@ func K8sResourceGet(ctx context.Context, db *gorm.DB, store store.Store) fiber.H
|
|||||||
|
|
||||||
func getResourceName(kind string) string {
|
func getResourceName(kind string) string {
|
||||||
kindToResource := map[string]string{
|
kindToResource := map[string]string{
|
||||||
"Namespace": "namespaces",
|
"Namespace": "namespaces",
|
||||||
"Deployment": "deployments",
|
"Deployment": "deployments",
|
||||||
"StatefulSet": "statefulsets",
|
"StatefulSet": "statefulsets",
|
||||||
"Service": "services",
|
"Service": "services",
|
||||||
"ConfigMap": "configmaps",
|
"ConfigMap": "configmaps",
|
||||||
"Pod": "pods",
|
"Pod": "pods",
|
||||||
"PersistentVolume": "persistentvolumes",
|
"PersistentVolume": "persistentvolumes",
|
||||||
"PersistentVolumeClaim": "persistentvolumeclaims",
|
"PersistentVolumeClaim": "persistentvolumeclaims",
|
||||||
"Secret": "secrets",
|
"Secret": "secrets",
|
||||||
"Ingress": "ingresses",
|
"Ingress": "ingresses",
|
||||||
"DaemonSet": "daemonsets",
|
"DaemonSet": "daemonsets",
|
||||||
"Job": "jobs",
|
"Job": "jobs",
|
||||||
"CronJob": "cronjobs",
|
"CronJob": "cronjobs",
|
||||||
"ReplicaSet": "replicasets",
|
"ReplicaSet": "replicasets",
|
||||||
"ServiceAccount": "serviceaccounts",
|
"ServiceAccount": "serviceaccounts",
|
||||||
"Role": "roles",
|
"Role": "roles",
|
||||||
"RoleBinding": "rolebindings",
|
"RoleBinding": "rolebindings",
|
||||||
"ClusterRole": "clusterroles",
|
"ClusterRole": "clusterroles",
|
||||||
"ClusterRoleBinding": "clusterrolebindings",
|
"ClusterRoleBinding": "clusterrolebindings",
|
||||||
}
|
}
|
||||||
|
|
||||||
if resource, ok := kindToResource[kind]; ok {
|
if resource, ok := kindToResource[kind]; ok {
|
||||||
@@ -726,21 +726,22 @@ func K8sPodLogs(ctx context.Context, db *gorm.DB, store store.Store) fiber.Handl
|
|||||||
|
|
||||||
req := clientset.CoreV1().Pods(namespace).GetLogs(podName, podLogOpts)
|
req := clientset.CoreV1().Pods(namespace).GetLogs(podName, podLogOpts)
|
||||||
|
|
||||||
logCtx, cancel := context.WithCancel(context.Background())
|
logCtx, cancel := context.WithCancel(c.Context())
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
stream, err := req.Stream(logCtx)
|
stream, err := req.Stream(logCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
cancel()
|
||||||
return resp.R500(c, "", nil, fmt.Errorf("failed to get pod logs: %w", err))
|
return resp.R500(c, "", nil, fmt.Errorf("failed to get pod logs: %w", err))
|
||||||
}
|
}
|
||||||
defer stream.Close()
|
|
||||||
|
|
||||||
// Use the existing SSE manager from resp package
|
// Use the existing SSE manager from resp package
|
||||||
manager := resp.SSE(c, "pod-logs")
|
manager := resp.SSE(c, "pod-logs")
|
||||||
|
|
||||||
// Start streaming logs in a goroutine
|
// Start streaming logs in a goroutine
|
||||||
go func() {
|
go func() {
|
||||||
|
defer stream.Close()
|
||||||
defer manager.Close()
|
defer manager.Close()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
reader := bufio.NewReader(stream)
|
reader := bufio.NewReader(stream)
|
||||||
for {
|
for {
|
||||||
@@ -751,20 +752,18 @@ func K8sPodLogs(ctx context.Context, db *gorm.DB, store store.Store) fiber.Handl
|
|||||||
line, err := reader.ReadString('\n')
|
line, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
manager.Send("[EOF]")
|
manager.JSON(map[string]any{"type": "EOF"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
manager.Send(fmt.Sprintf("error: %v", err))
|
manager.JSON(map[string]any{"type": "error", "data": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
manager.Send(line)
|
manager.JSON(map[string]any{"data": line, "type": "log"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Return nil since we're handling the response directly
|
return c.SendStreamWriter(manager.Writer())
|
||||||
c.Context().SetBodyStreamWriter(manager.Writer())
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
51
nginx.conf
Normal file
51
nginx.conf
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
upstream backend {
|
||||||
|
server 127.0.0.1:9119;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
# Serve static files
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy API requests to backend
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy OCI registry v2 requests to backend
|
||||||
|
location /v2/ {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy registry requests to backend
|
||||||
|
location /registry/ {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user