diff --git a/src/components/GameServerSetLink.tsx b/src/components/GameServerSetLink.tsx
new file mode 100644
index 0000000..d0141a0
--- /dev/null
+++ b/src/components/GameServerSetLink.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Contributors to Agones a Series of LF Projects, LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Router } from '@kinvolk/headlamp-plugin/lib';
+import React from 'react';
+
+interface GameServerSetLinkProps {
+ namespace: string;
+ name: string;
+ onClick?: (e: React.MouseEvent) => void;
+}
+
+export function GameServerSetLink({ namespace, name, onClick }: GameServerSetLinkProps) {
+ const url = Router.createRouteURL('agones-gameserverset', { namespace, name });
+ return (
+
+ {name}
+
+ );
+}
diff --git a/src/components/GameServerSetPhaseChip.tsx b/src/components/GameServerSetPhaseChip.tsx
new file mode 100644
index 0000000..fe1139c
--- /dev/null
+++ b/src/components/GameServerSetPhaseChip.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright Contributors to Agones a Series of LF Projects, LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Chip from '@mui/material/Chip';
+import React from 'react';
+import { GameServerSetPhase } from '../utils/gameServerSetHelpers';
+
+const PHASE_STYLE: Record<
+ GameServerSetPhase,
+ { label: string; color: 'default' | 'success' | 'warning' }
+> = {
+ active: { label: 'Active', color: 'success' },
+ retiring: { label: 'Retiring', color: 'warning' },
+ unknown: { label: '—', color: 'default' },
+};
+
+export function GameServerSetPhaseChip({ phase }: { phase: GameServerSetPhase }) {
+ const { label, color } = PHASE_STYLE[phase];
+ if (phase === 'unknown') return <>{label}>;
+ return ;
+}
diff --git a/src/components/StateChip.tsx b/src/components/StateChip.tsx
index cbdc7eb..3340fe6 100644
--- a/src/components/StateChip.tsx
+++ b/src/components/StateChip.tsx
@@ -21,7 +21,9 @@ type ChipColor = 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'succe
const STATE_COLORS: Record = {
// In-progress lifecycle states
+ PortAllocation: 'info',
Creating: 'info',
+ Starting: 'info',
Scheduled: 'info',
RequestReady: 'info',
// Stable states
diff --git a/src/index.tsx b/src/index.tsx
index 6c585b3..174abc8 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -23,6 +23,8 @@ import { FleetDetail } from './views/fleets/Detail';
import { FleetList } from './views/fleets/List';
import { GameServerDetail } from './views/gameservers/Detail';
import { GameServerList } from './views/gameservers/List';
+import { GameServerSetDetail } from './views/gameserversets/Detail';
+import { GameServerSetList } from './views/gameserversets/List';
import { AgonesOverview } from './views/overview/Overview';
// ─── Map ─────────────────────────────────────────────────────────────────────
@@ -35,6 +37,7 @@ registerSidebarEntry({ parent: null, name: 'agones', label: 'Agones', url: '/ago
registerSidebarEntry({ parent: 'agones', name: 'agones-overview', label: 'Overview', url: '/agones' });
registerSidebarEntry({ parent: 'agones', name: 'agones-fleets', label: 'Fleets', url: '/agones/fleets' });
registerSidebarEntry({ parent: 'agones', name: 'agones-gameservers', label: 'Game Servers', url: '/agones/gameservers' });
+registerSidebarEntry({ parent: 'agones', name: 'agones-gameserversets', label: 'Game Server Sets', url: '/agones/gameserversets' });
registerSidebarEntry({ parent: 'agones', name: 'agones-fleetautoscalers', label: 'Autoscalers', url: '/agones/fleetautoscalers' });
// ─── Routes ──────────────────────────────────────────────────────────────────
@@ -47,5 +50,8 @@ registerRoute({ path: '/agones/fleets/:namespace/:name', sidebar: 'agones-fleets
registerRoute({ path: '/agones/gameservers', sidebar: 'agones-gameservers', name: 'agones-gameservers', exact: true, component: () => });
registerRoute({ path: '/agones/gameservers/:namespace/:name', sidebar: 'agones-gameservers', name: 'agones-gameserver', component: () => });
+registerRoute({ path: '/agones/gameserversets', sidebar: 'agones-gameserversets', name: 'agones-gameserversets', exact: true, component: () => });
+registerRoute({ path: '/agones/gameserversets/:namespace/:name', sidebar: 'agones-gameserversets', name: 'agones-gameserverset', component: () => });
+
registerRoute({ path: '/agones/fleetautoscalers', sidebar: 'agones-fleetautoscalers', name: 'agones-fleetautoscalers', exact: true, component: () => });
registerRoute({ path: '/agones/fleetautoscalers/:namespace/:name', sidebar: 'agones-fleetautoscalers', name: 'agones-fleetautoscaler', component: () => });
\ No newline at end of file
diff --git a/src/mapView.tsx b/src/mapView.tsx
index 4c336e2..ccd0cfe 100644
--- a/src/mapView.tsx
+++ b/src/mapView.tsx
@@ -16,11 +16,12 @@
import { GraphSource } from '@kinvolk/headlamp-plugin/lib/components/resourceMap/graph/graphModel';
import { fleetsSource } from './views/map/fleetsSource';
+import { gameServerSetsSource } from './views/map/gameServerSetsSource';
import { gameServersSource } from './views/map/gameServersSource';
import { podsSource } from './views/map/podsSource';
export const agonesMapSource: GraphSource = {
id: 'agones',
label: 'Agones',
- sources: [fleetsSource, gameServersSource, podsSource],
+ sources: [fleetsSource, gameServerSetsSource, gameServersSource, podsSource],
};
diff --git a/src/resources/gameserver.ts b/src/resources/gameserver.ts
index 7c4c8f5..7c2da90 100644
--- a/src/resources/gameserver.ts
+++ b/src/resources/gameserver.ts
@@ -15,6 +15,7 @@
*/
import { KubeObject, KubeObjectInterface } from '@kinvolk/headlamp-plugin/lib/k8s/cluster';
+import { GAME_SERVER_SET_LABEL } from '../utils/agonesLabels';
export interface GameServerSpecPort {
name?: string;
@@ -101,6 +102,10 @@ export class GameServer extends KubeObject {
return this.metadata.labels?.['agones.dev/fleet'] ?? '';
}
+ get gameServerSet(): string {
+ return this.metadata.labels?.[GAME_SERVER_SET_LABEL] ?? '';
+ }
+
get ports(): string {
const ports = this.status.ports ?? [];
if (ports.length === 0) return '';
diff --git a/src/resources/gameserverset.ts b/src/resources/gameserverset.ts
new file mode 100644
index 0000000..f583ce3
--- /dev/null
+++ b/src/resources/gameserverset.ts
@@ -0,0 +1,84 @@
+/*
+ * Copyright Contributors to Agones a Series of LF Projects, LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { KubeObject, KubeObjectInterface } from '@kinvolk/headlamp-plugin/lib/k8s/cluster';
+import { FLEET_NAME_LABEL } from '../utils/agonesLabels';
+
+export interface AgonesGameServerSet extends KubeObjectInterface {
+ spec: {
+ replicas: number;
+ scheduling: string;
+ template: object;
+ };
+ status?: {
+ replicas?: number;
+ readyReplicas?: number;
+ reservedReplicas?: number;
+ allocatedReplicas?: number;
+ shutdownReplicas?: number;
+ };
+}
+
+export class GameServerSet extends KubeObject {
+ static apiVersion = 'agones.dev/v1';
+ static kind = 'GameServerSet';
+ static apiName = 'gameserversets';
+ static isNamespaced = true;
+
+ static get detailsRoute() {
+ return 'agones-gameserverset';
+ }
+
+ get spec() {
+ return this.jsonData.spec;
+ }
+
+ get status() {
+ return this.jsonData.status || {};
+ }
+
+ get fleet(): string {
+ return this.metadata.labels?.[FLEET_NAME_LABEL] ?? '';
+ }
+
+ get scheduling(): string {
+ return this.spec.scheduling || 'Packed';
+ }
+
+ get desiredReplicas(): number {
+ return this.spec.replicas || 0;
+ }
+
+ get currentReplicas(): number {
+ return this.status.replicas || 0;
+ }
+
+ get readyReplicas(): number {
+ return this.status.readyReplicas || 0;
+ }
+
+ get allocatedReplicas(): number {
+ return this.status.allocatedReplicas || 0;
+ }
+
+ get reservedReplicas(): number {
+ return this.status.reservedReplicas || 0;
+ }
+
+ get shutdownReplicas(): number {
+ return this.status.shutdownReplicas || 0;
+ }
+}
diff --git a/src/utils/agonesLabels.ts b/src/utils/agonesLabels.ts
new file mode 100644
index 0000000..28f3e23
--- /dev/null
+++ b/src/utils/agonesLabels.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright Contributors to Agones a Series of LF Projects, LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** Agones label keys (pkg/apis/agones/v1). */
+export const FLEET_NAME_LABEL = 'agones.dev/fleet';
+export const GAME_SERVER_SET_LABEL = 'agones.dev/gameserverset';
+export const GAME_SERVER_POD_LABEL = 'agones.dev/gameserver';
diff --git a/src/utils/gameServerSetHelpers.ts b/src/utils/gameServerSetHelpers.ts
new file mode 100644
index 0000000..0c434f0
--- /dev/null
+++ b/src/utils/gameServerSetHelpers.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright Contributors to Agones a Series of LF Projects, LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { KubeObject } from '@kinvolk/headlamp-plugin/lib/k8s/cluster';
+import { Fleet } from '../resources/fleet';
+import { GameServerSet } from '../resources/gameserverset';
+import { FLEET_NAME_LABEL } from './agonesLabels';
+
+/** Mirrors metav1.IsControlledBy — fleet must be the controlling owner. */
+export function isControlledBy(child: KubeObject, owner: KubeObject): boolean {
+ const refs = child.metadata.ownerReferences ?? [];
+ return refs.some(
+ ref => ref.uid === owner.metadata.uid && ref.controller === true
+ );
+}
+
+/** Template equality check (Agones uses Semantic.DeepEqual on spec.template). */
+export function templatesMatch(
+ a: { template?: object },
+ b: { template?: object }
+): boolean {
+ return JSON.stringify(a.template ?? {}) === JSON.stringify(b.template ?? {});
+}
+
+export type GameServerSetPhase = 'active' | 'retiring' | 'unknown';
+
+/**
+ * Active when spec.template matches the fleet's template (pkg/fleets/controller.go).
+ * Among duplicates, the oldest creationTimestamp wins as active.
+ */
+export function getGameServerSetPhase(
+ gss: GameServerSet,
+ fleet: Fleet | undefined,
+ siblings: GameServerSet[]
+): GameServerSetPhase {
+ if (!fleet) return 'unknown';
+ if (!templatesMatch(gss.spec, fleet.spec)) return 'retiring';
+
+ const matching = siblings.filter(s => templatesMatch(s.spec, fleet.spec));
+ if (matching.length <= 1) return 'active';
+
+ const oldest = matching.reduce((a, b) =>
+ new Date(a.metadata.creationTimestamp ?? 0) <=
+ new Date(b.metadata.creationTimestamp ?? 0)
+ ? a
+ : b
+ );
+ return oldest.metadata.uid === gss.metadata.uid ? 'active' : 'retiring';
+}
+
+/** Fleet-owned GameServerSets: label selector + ownerRef filter (pkg/fleets/fleets.go). */
+export function filterGameServerSetsByFleet(
+ sets: GameServerSet[],
+ fleet: Fleet
+): GameServerSet[] {
+ const fleetName = fleet.metadata.name;
+ return sets.filter(
+ gss =>
+ gss.metadata.namespace === fleet.metadata.namespace &&
+ gss.metadata.labels?.[FLEET_NAME_LABEL] === fleetName &&
+ isControlledBy(gss, fleet)
+ );
+}
diff --git a/src/views/fleets/Detail.tsx b/src/views/fleets/Detail.tsx
index b0480c9..07b319c 100644
--- a/src/views/fleets/Detail.tsx
+++ b/src/views/fleets/Detail.tsx
@@ -31,6 +31,7 @@ import { ReplicaBar } from '../../components/ReplicaBar';
import { UtilBar } from '../../components/UtilBar';
import { Fleet } from '../../resources/fleet';
import { GameServer } from '../../resources/gameserver';
+import { GameServerSetsSection } from './GameServerSetsSection';
interface WithGameServers { gameServers: GameServer[] | null }
@@ -143,6 +144,7 @@ export function FleetDetail() {
}
extraSections={item =>
item && [
+ ,
,
,
,
diff --git a/src/views/fleets/GameServerSetsSection.tsx b/src/views/fleets/GameServerSetsSection.tsx
new file mode 100644
index 0000000..9dc0ccb
--- /dev/null
+++ b/src/views/fleets/GameServerSetsSection.tsx
@@ -0,0 +1,87 @@
+/*
+ * Copyright Contributors to Agones a Series of LF Projects, LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Link, SectionBox } from '@kinvolk/headlamp-plugin/lib/components/common';
+import Table from '@mui/material/Table';
+import TableBody from '@mui/material/TableBody';
+import TableCell from '@mui/material/TableCell';
+import TableHead from '@mui/material/TableHead';
+import TableRow from '@mui/material/TableRow';
+import React, { useMemo } from 'react';
+import { GameServerSetPhaseChip } from '../../components/GameServerSetPhaseChip';
+import { ReplicaBar } from '../../components/ReplicaBar';
+import { Fleet } from '../../resources/fleet';
+import { GameServerSet } from '../../resources/gameserverset';
+import {
+ filterGameServerSetsByFleet,
+ getGameServerSetPhase,
+} from '../../utils/gameServerSetHelpers';
+
+export function GameServerSetsSection({ fleet }: { fleet: Fleet }) {
+ const [allSets] = GameServerSet.useList({ namespace: fleet.metadata.namespace });
+
+ const sets = useMemo(() => {
+ if (!allSets) return null;
+ return filterGameServerSetsByFleet(allSets, fleet);
+ }, [allSets, fleet]);
+
+ if (!sets) return null;
+ if (sets.length === 0) {
+ return (
+
+ No GameServerSets found.
+
+ );
+ }
+
+ return (
+
+
+
+
+ Name
+ Phase
+ Desired
+ Replica Status
+ Shutdown
+
+
+
+ {sets.map(gss => (
+
+ {gss.metadata.name}
+
+
+
+ {gss.desiredReplicas}
+
+
+
+ {gss.shutdownReplicas || '—'}
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/views/gameservers/Detail.tsx b/src/views/gameservers/Detail.tsx
index e0fc714..e43eedc 100644
--- a/src/views/gameservers/Detail.tsx
+++ b/src/views/gameservers/Detail.tsx
@@ -25,6 +25,8 @@ import TableRow from '@mui/material/TableRow';
import React from 'react';
import { useParams } from 'react-router-dom';
import { FleetLink } from '../../components/FleetLink';
+import { GameServerSetLink } from '../../components/GameServerSetLink';
+import { StateChip } from '../../components/StateChip';
import { UtilBar } from '../../components/UtilBar';
import { GameServer } from '../../resources/gameserver';
@@ -172,7 +174,18 @@ export function GameServerDetail() {
?
: '—',
},
- { name: 'State', value: item.state },
+ {
+ name: 'Game Server Set',
+ value: item.gameServerSet
+ ? (
+
+ )
+ : '—',
+ },
+ { name: 'State', value: },
{ name: 'Address', value: item.address || '—' },
{ name: 'Ports', value: item.ports || '—' },
{ name: 'Node', value: item.nodeName || '—' },
diff --git a/src/views/gameserversets/Detail.tsx b/src/views/gameserversets/Detail.tsx
new file mode 100644
index 0000000..346f0dd
--- /dev/null
+++ b/src/views/gameserversets/Detail.tsx
@@ -0,0 +1,90 @@
+/*
+ * Copyright Contributors to Agones a Series of LF Projects, LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { DetailsGrid, SectionBox } from '@kinvolk/headlamp-plugin/lib/components/common';
+import React from 'react';
+import { useParams } from 'react-router-dom';
+import { FleetLink } from '../../components/FleetLink';
+import { GameServerTable } from '../../components/GameServerTable';
+import { GameServerSetPhaseChip } from '../../components/GameServerSetPhaseChip';
+import { ReplicaBar } from '../../components/ReplicaBar';
+import { Fleet } from '../../resources/fleet';
+import { GameServer } from '../../resources/gameserver';
+import { GameServerSet } from '../../resources/gameserverset';
+import { GAME_SERVER_SET_LABEL } from '../../utils/agonesLabels';
+import {
+ filterGameServerSetsByFleet,
+ getGameServerSetPhase,
+} from '../../utils/gameServerSetHelpers';
+
+function GameServersSection({ gameServers }: { gameServers: GameServer[] | null }) {
+ return (
+
+
+
+ );
+}
+
+export function GameServerSetDetail() {
+ const { namespace, name } = useParams<{ namespace: string; name: string }>();
+ const [gameServers] = GameServer.useList({
+ namespace,
+ labelSelector: `${GAME_SERVER_SET_LABEL}=${name}`,
+ });
+ const [fleets] = Fleet.useList({ namespace });
+ const [allSets] = GameServerSet.useList({ namespace });
+
+ return (
+ {
+ if (!item) return [];
+ const fleet = fleets?.find(f => f.metadata.name === item.fleet);
+ const siblings =
+ fleet && allSets ? filterGameServerSetsByFleet(allSets, fleet) : [];
+ const phase = getGameServerSetPhase(item, fleet, siblings);
+
+ return [
+ {
+ name: 'Fleet',
+ value: item.fleet
+ ?
+ : '—',
+ },
+ { name: 'Phase', value: },
+ { name: 'Scheduling', value: item.scheduling },
+ { name: 'Desired Replicas', value: item.desiredReplicas },
+ {
+ name: 'Replica Status',
+ value: (
+
+ ),
+ },
+ { name: 'Shutdown Replicas', value: item.shutdownReplicas },
+ ];
+ }}
+ extraSections={() => []}
+ />
+ );
+}
diff --git a/src/views/gameserversets/List.tsx b/src/views/gameserversets/List.tsx
new file mode 100644
index 0000000..19ec39a
--- /dev/null
+++ b/src/views/gameserversets/List.tsx
@@ -0,0 +1,110 @@
+/*
+ * Copyright Contributors to Agones a Series of LF Projects, LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Link, SectionBox } from '@kinvolk/headlamp-plugin/lib/components/common';
+import Table from '@mui/material/Table';
+import TableBody from '@mui/material/TableBody';
+import TableCell from '@mui/material/TableCell';
+import TableHead from '@mui/material/TableHead';
+import TableRow from '@mui/material/TableRow';
+import Typography from '@mui/material/Typography';
+import React, { useMemo } from 'react';
+import { FleetLink } from '../../components/FleetLink';
+import { GameServerSetPhaseChip } from '../../components/GameServerSetPhaseChip';
+import { ReplicaBar } from '../../components/ReplicaBar';
+import { Fleet } from '../../resources/fleet';
+import { GameServerSet } from '../../resources/gameserverset';
+import { filterGameServerSetsByFleet, getGameServerSetPhase } from '../../utils/gameServerSetHelpers';
+
+export function GameServerSetList() {
+ const [sets] = GameServerSet.useList();
+ const [fleets] = Fleet.useList();
+
+ const fleetByKey = useMemo(() => {
+ const m = new Map();
+ for (const f of fleets ?? []) {
+ m.set(`${f.metadata.namespace}/${f.metadata.name}`, f);
+ }
+ return m;
+ }, [fleets]);
+
+ const siblingsByFleet = useMemo(() => {
+ const m = new Map();
+ if (!sets || !fleets) return m;
+ for (const fleet of fleets) {
+ m.set(
+ `${fleet.metadata.namespace}/${fleet.metadata.name}`,
+ filterGameServerSetsByFleet(sets, fleet)
+ );
+ }
+ return m;
+ }, [sets, fleets]);
+
+ return (
+
+
+
+
+ Name
+ Namespace
+ Fleet
+ Phase
+ Desired
+ Replica Status
+ Shutdown
+
+
+
+ {!sets ? (
+
+
+ Loading…
+
+
+ ) : sets.map(gss => {
+ const fleetKey = `${gss.metadata.namespace}/${gss.fleet}`;
+ const fleet = fleetByKey.get(fleetKey);
+ const siblings = siblingsByFleet.get(fleetKey) ?? [];
+ const phase = getGameServerSetPhase(gss, fleet, siblings);
+
+ return (
+
+ {gss.metadata.name}
+ {gss.metadata.namespace}
+
+ {gss.fleet
+ ?
+ : '—'}
+
+
+ {gss.desiredReplicas}
+
+
+
+ {gss.shutdownReplicas || '—'}
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/src/views/map/gameServerSetsSource.ts b/src/views/map/gameServerSetsSource.ts
new file mode 100644
index 0000000..431c431
--- /dev/null
+++ b/src/views/map/gameServerSetsSource.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright Contributors to Agones a Series of LF Projects, LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { GraphEdge, GraphSource } from '@kinvolk/headlamp-plugin/lib/components/resourceMap/graph/graphModel';
+import { useMemo } from 'react';
+import { Fleet } from '../../resources/fleet';
+import { GameServerSet } from '../../resources/gameserverset';
+import { buildNameToUidMap, makeNode, ownerEdges } from './graphHelpers';
+
+export const gameServerSetsSource: GraphSource = {
+ id: 'agones-gameserversets',
+ label: 'GameServerSets',
+ useData() {
+ const [sets] = GameServerSet.useList();
+ const [fleets] = Fleet.useList();
+ return useMemo(() => {
+ if (!sets) return null;
+
+ const fleetUidMap = buildNameToUidMap(fleets ?? []);
+ const ownerRefEdges = sets.flatMap(ownerEdges);
+ const fleetLabelEdges: GraphEdge[] = [];
+
+ for (const gss of sets) {
+ const fleetName = gss.fleet;
+ if (!fleetName) continue;
+ const fleetUid = fleetUidMap.get(`${gss.metadata.namespace}/${fleetName}`);
+ if (fleetUid) {
+ fleetLabelEdges.push({
+ id: `fleet-gss-${fleetUid}-${gss.metadata.uid}`,
+ source: fleetUid,
+ target: gss.metadata.uid,
+ });
+ }
+ }
+
+ return {
+ nodes: sets.map(gss => makeNode(gss, 80)),
+ edges: [...ownerRefEdges, ...fleetLabelEdges],
+ };
+ }, [sets, fleets]);
+ },
+};
diff --git a/src/views/map/gameServersSource.ts b/src/views/map/gameServersSource.ts
index 8ffc2af..6247325 100644
--- a/src/views/map/gameServersSource.ts
+++ b/src/views/map/gameServersSource.ts
@@ -16,8 +16,8 @@
import { GraphEdge, GraphSource } from '@kinvolk/headlamp-plugin/lib/components/resourceMap/graph/graphModel';
import { useMemo } from 'react';
-import { Fleet } from '../../resources/fleet';
import { GameServer } from '../../resources/gameserver';
+import { GameServerSet } from '../../resources/gameserverset';
import { buildNameToUidMap, makeNode } from './graphHelpers';
export const gameServersSource: GraphSource = {
@@ -25,21 +25,27 @@ export const gameServersSource: GraphSource = {
label: 'GameServers',
useData() {
const [gameServers] = GameServer.useList();
- const [fleets] = Fleet.useList();
+ const [sets] = GameServerSet.useList();
return useMemo(() => {
if (!gameServers) return null;
- const fleetUidMap = buildNameToUidMap(fleets ?? []);
+ const gssUidMap = buildNameToUidMap(sets ?? []);
const edges: GraphEdge[] = [];
for (const gs of gameServers) {
- const fleetName = gs.fleet;
- if (!fleetName) continue;
- const uid = fleetUidMap.get(`${gs.metadata.namespace}/${fleetName}`);
- if (uid) edges.push({ id: `fleet-gs-${uid}-${gs.metadata.uid}`, source: uid, target: gs.metadata.uid });
+ const gssName = gs.gameServerSet;
+ if (!gssName) continue;
+ const uid = gssUidMap.get(`${gs.metadata.namespace}/${gssName}`);
+ if (uid) {
+ edges.push({
+ id: `gss-gs-${uid}-${gs.metadata.uid}`,
+ source: uid,
+ target: gs.metadata.uid,
+ });
+ }
}
return { nodes: gameServers.map(gs => makeNode(gs, 60)), edges };
- }, [gameServers, fleets]);
+ }, [gameServers, sets]);
},
};
\ No newline at end of file