@xrengine/server-core
Version:
Shared components for XREngine server
250 lines (214 loc) • 6.58 kB
text/typescript
import * as k8s from '@kubernetes/client-node'
import { Op } from 'sequelize'
import { Channel } from '@xrengine/common/src/interfaces/Channel'
import { Instance } from '@xrengine/common/src/interfaces/Instance'
import { ServerContainerInfo, ServerInfoInterface, ServerPodInfo } from '@xrengine/common/src/interfaces/ServerInfo'
import { Application } from '../../../declarations'
import config from '../../appconfig'
import logger from '../../ServerLogger'
export const getServerInfo = async (app: Application): Promise<ServerInfoInterface[]> => {
let serverInfo: ServerInfoInterface[] = []
try {
logger.info('Attempting to check k8s server info')
if (app.k8DefaultClient) {
const builderPods = await getPodsData(
`app.kubernetes.io/instance=${config.server.releaseName}-builder`,
'builder',
'Builder',
app
)
serverInfo.push(builderPods)
const clientPods = await getPodsData(
`app.kubernetes.io/instance=${config.server.releaseName},app.kubernetes.io/component=client`,
'client',
'Client',
app
)
serverInfo.push(clientPods)
const apiPods = await getPodsData(
`app.kubernetes.io/instance=${config.server.releaseName},app.kubernetes.io/component=api`,
'api',
'Api',
app
)
serverInfo.push(apiPods)
const instancePods = await getPodsData(
'agones.dev/role=gameserver',
'instance',
'Instance',
app,
`${config.server.releaseName}-instanceserver-`
)
await populateInstanceServerType(app, instancePods.pods)
serverInfo.push(instancePods)
const taskPods = await getPodsData(
`app.kubernetes.io/instance=${config.server.releaseName},app.kubernetes.io/component=taskserver`,
'task',
'Task',
app
)
serverInfo.push(taskPods)
const projectUpdatePods = await getPodsData(
`etherealengine/release=${config.server.releaseName},etherealengine/projectUpdater=true`,
'projectUpdate',
'Project Updater',
app
)
serverInfo.push(projectUpdatePods)
}
// if (app.k8AgonesClient) {
// const instancePods = await getGameserversData(`agones.dev/fleet=${config.server.releaseName}-instanceserver`, 'instance', 'Instance', app)
// serverInfo.push(instancePods)
// }
} catch (e) {
logger.error(e)
return e
}
return serverInfo
}
export const removePod = async (app: Application, podName: string): Promise<ServerPodInfo | undefined> => {
try {
logger.info(`Attempting to remove k8s pod ${podName}`)
if (app.k8DefaultClient) {
const podsResponse = await app.k8DefaultClient.deleteNamespacedPod(podName, 'default')
const pod = getServerPodInfo(podsResponse.body)
return pod
}
} catch (e) {
logger.error(e)
return e
}
}
export const getPodsData = async (
labelSelector: string,
id: string,
label: string,
app: Application,
nameFilter?: string
) => {
let pods: ServerPodInfo[] = []
try {
const podsResponse = await app.k8DefaultClient.listNamespacedPod(
'default',
undefined,
false,
undefined,
undefined,
labelSelector
)
let items = podsResponse.body.items
if (nameFilter) {
items = items.filter((item) => item.metadata?.name?.startsWith(nameFilter))
}
pods = getServerPodsInfo(items)
} catch (err) {
logger.error('Failed to get pods info.', err)
}
return {
id,
label,
pods
}
}
const getGameserversData = async (labelSelector: string, id: string, label: string, app: Application) => {
let gameservers: ServerPodInfo[] = []
try {
const gameserversResponse = await app.k8AgonesClient.listNamespacedCustomObject(
'agones.dev',
'v1',
'default',
'gameservers',
undefined,
false,
undefined,
undefined,
labelSelector
)
gameservers = getServerPodsInfo((gameserversResponse.body as any).items)
} catch (err) {
logger.error('Failed to get pods info.', err)
}
return {
id,
label,
pods: gameservers
}
}
const getServerPodsInfo = (items: k8s.V1Pod[]) => {
return items.map((item) => {
return getServerPodInfo(item)
})
}
const getServerPodInfo = (item: k8s.V1Pod) => {
return {
name: item.metadata?.name,
status: item.status?.phase,
age: item.status?.startTime,
containers: getServerContainerInfo(item.status?.containerStatuses!)
} as ServerPodInfo
}
const getServerContainerInfo = (items: k8s.V1ContainerStatus[]) => {
return items.map((item) => {
return {
name: item.name,
status: item.state?.running
? 'Running'
: item.state?.terminated
? 'Terminated'
: item.state?.waiting
? 'Waiting'
: 'Undefined',
ready: item.ready,
started: item.started,
restarts: item.restartCount,
image: item.image
} as ServerContainerInfo
})
}
const populateInstanceServerType = async (app: Application, items: ServerPodInfo[]) => {
if (items.length === 0) {
return
}
const instances = (await app.service('instance').Model.findAll({
where: {
ended: false
},
include: [
{
model: app.service('location').Model,
required: false
}
]
})) as Instance[]
if (instances.length === 0) {
return
}
const channelInstances = instances.filter((item) => item.channelId)
let channels: Channel[] = []
if (channelInstances) {
channels = (await app.service('channel').Model.findAll({
where: {
instanceId: {
[Op.in]: channelInstances.map((item) => item.channelId)
}
}
})) as Channel[]
}
for (const item of items) {
const instanceExists = instances.find((instance) => instance.podName === item.name)
item.instanceId = instanceExists ? instanceExists.id : ''
item.currentUsers = instanceExists ? instanceExists.currentUsers : 0
if (instanceExists && instanceExists.locationId) {
item.type = `World (${instanceExists.location.name})`
item.locationSlug = instanceExists.location.slugifiedName
} else if (instanceExists && instanceExists.channelId) {
item.type = 'Media'
const channelExists = channels.find((channel) => channel.instanceId === instanceExists.id)
if (channelExists && channelExists.channelType) {
item.type = `Media (${channelExists.channelType})`
}
} else {
item.type = 'Unassigned'
}
}
}