Compare commits

..

7 Commits

Author SHA1 Message Date
Amir Raminfar f393d0d3e6 WIP: notificatons 2026-01-14 19:17:38 -08:00
Amir Raminfar 9988271490 chore: updates docs 2026-01-14 10:23:38 -08:00
Amir Raminfar 05f76c4f57 fix: fixes host limits being miscalculated (#4341) 2026-01-13 08:54:24 -08:00
Amir Raminfar 20c1adbd7e chore: fixes short formatbytes 2026-01-12 16:26:08 -08:00
Amir Raminfar d44ab349b9 feat: adds network usage for each container (#4340) 2026-01-12 14:11:19 -08:00
Amir Raminfar 21367bcc36 chore: upgrades proto (#4339) 2026-01-12 17:05:51 +00:00
renovate[bot] 6ece0df082 fix(deps): update all non-major dependencies (#4337)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-12 01:48:44 +00:00
29 changed files with 1092 additions and 517 deletions
+2
View File
@@ -122,6 +122,8 @@ declare module 'vue' {
'Ph:globeSimple': typeof import('~icons/ph/globe-simple')['default']
'Ph:stack': typeof import('~icons/ph/stack')['default']
'Ph:stackSimple': typeof import('~icons/ph/stack-simple')['default']
PhArrowDown: typeof import('~icons/ph/arrow-down')['default']
PhArrowUp: typeof import('~icons/ph/arrow-up')['default']
Popup: typeof import('./components/Popup.vue')['default']
RandomColorTag: typeof import('./components/LogViewer/RandomColorTag.vue')['default']
RelativeTime: typeof import('./components/common/RelativeTime.vue')['default']
@@ -1,5 +1,11 @@
<template>
<div class="flex gap-1 md:gap-4">
<div class="grid min-w-15 grid-cols-[auto_1fr] items-center gap-0.5 text-xs leading-none max-md:hidden">
<PhArrowUp class="text-primary" />
<span class="tabular-nums">{{ formatBytes(networkRate.tx, { short: true, decimals: 1 }) }}/s</span>
<PhArrowDown class="text-secondary" />
<span class="tabular-nums">{{ formatBytes(networkRate.rx, { short: true, decimals: 1 }) }}/s</span>
</div>
<StatMonitor
:data="cpuData"
:icon="PhCpu"
@@ -35,9 +41,10 @@ const { containers } = defineProps<{
containers: Container[];
}>();
const totalStat = ref<Stat>({ cpu: 0, memory: 0, memoryUsage: 0 });
const totalStat = ref<Stat>({ cpu: 0, memory: 0, memoryUsage: 0, networkRxTotal: 0, networkTxTotal: 0 });
const { history, reset } = useSimpleRefHistory(totalStat, { capacity: 300 });
const { hosts } = useHosts();
const networkRate = ref({ rx: 0, tx: 0 });
const roundCPU = (num: number) => (Number.isInteger(num) ? num.toFixed(0) : num.toFixed(1));
@@ -65,33 +72,72 @@ watch(
cpu: acc.cpu + item.cpu / cores,
memory: acc.memory + item.memory,
memoryUsage: acc.memoryUsage + item.memoryUsage,
networkRxTotal: acc.networkRxTotal + item.networkRxTotal,
networkTxTotal: acc.networkTxTotal + item.networkTxTotal,
};
},
{ cpu: 0, memory: 0, memoryUsage: 0 },
{ cpu: 0, memory: 0, memoryUsage: 0, networkRxTotal: 0, networkTxTotal: 0 },
);
initial.push(stat);
}
totalStat.value = initial[0];
reset({ initial: initial.reverse() });
},
{ immediate: true },
);
const limits = computed(() => {
return containers.reduce(
(acc, container) => {
const cores = toContainerCores(container);
const hostInfo = hosts.value[container.host];
// Group containers by host
const containersByHost = new Map<string, Container[]>();
containers.forEach((container) => {
if (!containersByHost.has(container.host)) {
containersByHost.set(container.host, []);
}
containersByHost.get(container.host)!.push(container);
});
return {
cpu: acc.cpu + cores,
memory: acc.memory + (container.memoryLimit || hostInfo?.memTotal || 0),
};
},
{ cpu: 0, memory: 0 },
);
let totalCpu = 0;
let totalMemory = 0;
// Process each host independently
containersByHost.forEach((hostContainers, hostId) => {
const hostInfo = hosts.value[hostId];
const hostTotalMemory = hostInfo?.memTotal || 0;
const hostTotalCpu = hostInfo?.nCPU || 0;
// Check if any container lacks limits
const hasUnlimitedCpu = hostContainers.some((c) => !c.cpuLimit || c.cpuLimit <= 0);
const hasUnlimitedMemory = hostContainers.some((c) => !c.memoryLimit);
// Calculate CPU for this host
if (hasUnlimitedCpu) {
// At least one container has no limit, use host total
totalCpu += hostTotalCpu;
} else {
// All containers have limits, sum them up (capped at host total)
const sumCpu = hostContainers.reduce((sum, c) => sum + 1, 0);
totalCpu += Math.min(sumCpu, hostTotalCpu);
}
// Calculate Memory for this host
if (hasUnlimitedMemory) {
// At least one container has no limit, use host total
totalMemory += hostTotalMemory;
} else {
// All containers have limits, sum them up (capped at host total)
const sumMemory = hostContainers.reduce((sum, c) => sum + (c.memoryLimit || 0), 0);
totalMemory += Math.min(sumMemory, hostTotalMemory);
}
});
return {
cpu: totalCpu,
memory: totalMemory,
};
});
useIntervalFn(() => {
const previousStat = totalStat.value;
totalStat.value = containers.reduce(
(acc, container) => {
const cores = toContainerCores(container);
@@ -99,10 +145,17 @@ useIntervalFn(() => {
cpu: acc.cpu + container.stat.cpu / cores,
memory: acc.memory + container.stat.memory,
memoryUsage: acc.memoryUsage + container.stat.memoryUsage,
networkRxTotal: acc.networkRxTotal + container.stat.networkRxTotal,
networkTxTotal: acc.networkTxTotal + container.stat.networkTxTotal,
};
},
{ cpu: 0, memory: 0, memoryUsage: 0 },
{ cpu: 0, memory: 0, memoryUsage: 0, networkRxTotal: 0, networkTxTotal: 0 },
);
networkRate.value = {
rx: Math.max(0, totalStat.value.networkRxTotal - previousStat.networkRxTotal),
tx: Math.max(0, totalStat.value.networkTxTotal - previousStat.networkTxTotal),
};
}, 1000);
const cpuData = computed(() =>
+1 -1
View File
@@ -10,7 +10,7 @@
</div>
<div class="bg-base-200 flex gap-1 rounded-sm p-px text-xs md:absolute md:-top-2 md:-left-0.5">
<component :is="icon" class="text-sm" />
<div class="font-bold select-none">
<div class="font-bold tabular-nums select-none">
{{ displayValue }}
<span v-if="limit !== -1 && !mouseOver" class="max-md:hidden"> / {{ limit }} </span>
</div>
+2 -2
View File
@@ -4,8 +4,8 @@
<component :is="icon" class="text-lg" />
<span>{{ label }}</span>
</div>
<div class="mb-1.5 text-lg font-semibold">{{ formattedValue }}</div>
<div class="text-base-content/60 mb-1 text-xs max-md:hidden">
<div class="mb-1.5 text-lg font-semibold tabular-nums">{{ formattedValue }}</div>
<div class="text-base-content/60 mb-1 text-xs tabular-nums max-md:hidden">
avg {{ formatValue(average) }} pk {{ formatValue(peak) }}
</div>
<BarChart class="h-8" :chartData="percentData" :barClass="barClass" />
+3 -1
View File
@@ -51,7 +51,9 @@ export class Container {
public readonly group?: string,
public health?: ContainerHealth,
) {
this._stat = ref(stats.at(-1) || ({ cpu: 0, memory: 0, memoryUsage: 0 } as Stat));
this._stat = ref(
stats.at(-1) || ({ cpu: 0, memory: 0, memoryUsage: 0, networkRxTotal: 0, networkTxTotal: 0 } as Stat),
);
const { history } = useSimpleRefHistory(this._stat, { capacity: 300, deep: true, initial: stats });
this._statsHistory = history;
const { movingAverage } = useExponentialMovingAverage(this._stat, 0.2);
+2
View File
@@ -3,6 +3,8 @@ export interface ContainerStat {
readonly cpu: number;
readonly memory: number;
readonly memoryUsage: number;
readonly networkRxTotal: number;
readonly networkTxTotal: number;
}
export type ContainerJson = {
+1 -1
View File
@@ -2,7 +2,7 @@ export function formatBytes(
bytes: number,
{ decimals = 2, short = false }: { decimals?: number; short?: boolean } = { decimals: 2, short: false },
) {
if (bytes === 0) return "0 Bytes";
if (bytes === 0) return short ? "0B" : "0 Bytes";
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
+15 -3
View File
@@ -4,8 +4,20 @@ title: What is Dozzle?
# What is Dozzle?
Dozzle is an open-source project sponsored by Docker OSS. It is a log viewer designed to simplify monitoring and debugging containers. This lightweight, web-based application offers real-time log streaming, filtering, and searching capabilities through an intuitive user interface.
Dozzle is an open-source project sponsored by Docker OSS. It is a lightweight, web-based log viewer designed to simplify monitoring and debugging containerized applications across Docker, Docker Swarm, and Kubernetes environments.
Users can quickly access logs generated by their Docker containers, making it an essential tool for debugging and troubleshooting applications in a Docker environment. By default, Dozzle supports JSON logs with intelligent color coding.
## Key Features
Dozzle is easy to install and configure, making it an ideal solution for developers and system administrators seeking an efficient, user-friendly log viewer for their Docker environment. The tool is available under the MIT license and is actively maintained by its developer, Amir Raminfar.
**Real-time Monitoring**: Stream logs from running containers with instant updates through an intuitive web interface. Monitor CPU, memory, and network usage with live metrics and historical visualizations.
**Flexible Deployment**: Deploy as a standalone server for single or multi-host Docker monitoring, enable automatic discovery in Docker Swarm clusters, or monitor pod logs in Kubernetes environments.
**Advanced Log Handling**: Automatically detects and formats JSON logs with intelligent color coding. Supports simple text logs, structured JSON logs, and multi-line grouped entries with powerful filtering and search capabilities.
**Multi-Host Support**: Monitor containers across multiple Docker hosts simultaneously through a distributed agent architecture using gRPC.
**Interactive Terminal**: Attach to running containers or execute commands directly through the web interface.
**Lightweight & Fast**: Built with Go backend and Vue 3 frontend, Dozzle uses efficient streaming protocols (SSE/WebSocket) and requires minimal resources.
Dozzle is easy to install and configure, making it an ideal solution for developers and system administrators seeking an efficient log viewer for their containerized environments. The tool is available under the MIT license and is actively maintained by its developer, Amir Raminfar.
+1
View File
@@ -57,6 +57,7 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/expr-lang/expr v1.17.7 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
+2
View File
@@ -78,6 +78,8 @@ github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtz
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8=
github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
+90 -216
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.5
// protoc v6.33.2
// protoc-gen-go v1.36.11
// protoc v6.33.3
// source: rpc.proto
package pb
@@ -1272,220 +1272,94 @@ func (x *ContainerAttachResponse) GetStdout() []byte {
var File_rpc_proto protoreflect.FileDescriptor
var file_rpc_proto_rawDesc = string([]byte{
0x0a, 0x09, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x1a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x22, 0xb1, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74,
0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a,
0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e,
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46,
0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74,
0x65, 0x72, 0x1a, 0x53, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65,
0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x28, 0x0a, 0x0e, 0x52, 0x65, 0x70, 0x65, 0x61,
0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x73, 0x22, 0x4d, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x0a, 0x63,
0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61,
0x69, 0x6e, 0x65, 0x72, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73,
0x22, 0xd1, 0x01, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e,
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x42, 0x0a, 0x06, 0x66,
0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61,
0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74,
0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a,
0x53, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x70, 0x65, 0x61,
0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x3a, 0x02, 0x38, 0x01, 0x22, 0x4a, 0x0a, 0x15, 0x46, 0x69, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x74,
0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a,
0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74,
0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
0x22, 0x89, 0x01, 0x0a, 0x11, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4c, 0x6f, 0x67, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69,
0x6e, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e,
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x69, 0x6e, 0x63,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74,
0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3e, 0x0a, 0x12,
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x6f, 0x67,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xc1, 0x01, 0x0a,
0x17, 0x4c, 0x6f, 0x67, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x44, 0x61, 0x74, 0x65,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x74,
0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63,
0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x69,
0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x30, 0x0a, 0x05,
0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x20,
0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18, 0x04, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x73,
0x22, 0xbf, 0x01, 0x0a, 0x15, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x61, 0x77, 0x42, 0x79,
0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f,
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x05,
0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x30,
0x0a, 0x05, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x75, 0x6e, 0x74, 0x69, 0x6c,
0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18,
0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70,
0x65, 0x73, 0x22, 0x2c, 0x0a, 0x16, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x61, 0x77, 0x42,
0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61,
0x22, 0x15, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x46, 0x0a, 0x14, 0x53, 0x74, 0x72, 0x65, 0x61,
0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x2e, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69,
0x6e, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22,
0x14, 0x0a, 0x12, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x42, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53,
0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04,
0x73, 0x74, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53,
0x74, 0x61, 0x74, 0x52, 0x04, 0x73, 0x74, 0x61, 0x74, 0x22, 0x11, 0x0a, 0x0f, 0x48, 0x6f, 0x73,
0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x36, 0x0a, 0x10,
0x48, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x22, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x04,
0x68, 0x6f, 0x73, 0x74, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f,
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x53, 0x0a, 0x1e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43,
0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61,
0x69, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52,
0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x6d, 0x0a, 0x16, 0x43, 0x6f,
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61,
0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x6f, 0x6e,
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa8, 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
0x65, 0x72, 0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a,
0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12,
0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x05, 0x73, 0x74, 0x64,
0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x64, 0x69,
0x6e, 0x12, 0x31, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x73,
0x69, 0x7a, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65,
0x73, 0x69, 0x7a, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22,
0x3d, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x2f,
0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x45, 0x78, 0x65, 0x63, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x22,
0x90, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x41, 0x74, 0x74,
0x61, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f,
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x05,
0x73, 0x74, 0x64, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x73,
0x74, 0x64, 0x69, 0x6e, 0x12, 0x31, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52,
0x06, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,
0x61, 0x64, 0x22, 0x31, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x41,
0x74, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a,
0x06, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73,
0x74, 0x64, 0x6f, 0x75, 0x74, 0x32, 0xa1, 0x08, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f,
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a,
0x0d, 0x46, 0x69, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x1e,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x43, 0x6f,
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x43, 0x6f,
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4c, 0x6f, 0x67, 0x73, 0x12,
0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61,
0x6d, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4c, 0x6f,
0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x57,
0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x44, 0x61, 0x74,
0x65, 0x73, 0x12, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x6f,
0x67, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x65, 0x61,
0x6d, 0x52, 0x61, 0x77, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x61, 0x77, 0x42, 0x79,
0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x61, 0x77, 0x42,
0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01,
0x12, 0x51, 0x0a, 0x0c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73,
0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65,
0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61,
0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x30, 0x01, 0x12, 0x4e, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61,
0x74, 0x73, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74,
0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65,
0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x30, 0x01, 0x12, 0x6f, 0x0a, 0x16, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e,
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x27, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43,
0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
0x72, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x30, 0x01, 0x12, 0x43, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f,
0x12, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x48, 0x6f, 0x73, 0x74,
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0f, 0x43, 0x6f, 0x6e,
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69,
0x6e, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
0x45, 0x78, 0x65, 0x63, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x5c, 0x0a, 0x0f, 0x43,
0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x12, 0x20,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69,
0x6e, 0x65, 0x72, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74,
0x61, 0x69, 0x6e, 0x65, 0x72, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x13, 0x5a, 0x11, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x62, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
const file_rpc_proto_rawDesc = "" +
"\n" +
"\trpc.proto\x12\bprotobuf\x1a\vtypes.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xb1\x01\n" +
"\x15ListContainersRequest\x12C\n" +
"\x06filter\x18\x01 \x03(\v2+.protobuf.ListContainersRequest.FilterEntryR\x06filter\x1aS\n" +
"\vFilterEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12.\n" +
"\x05value\x18\x02 \x01(\v2\x18.protobuf.RepeatedStringR\x05value:\x028\x01\"(\n" +
"\x0eRepeatedString\x12\x16\n" +
"\x06values\x18\x01 \x03(\tR\x06values\"M\n" +
"\x16ListContainersResponse\x123\n" +
"\n" +
"containers\x18\x01 \x03(\v2\x13.protobuf.ContainerR\n" +
"containers\"\xd1\x01\n" +
"\x14FindContainerRequest\x12 \n" +
"\vcontainerId\x18\x01 \x01(\tR\vcontainerId\x12B\n" +
"\x06filter\x18\x02 \x03(\v2*.protobuf.FindContainerRequest.FilterEntryR\x06filter\x1aS\n" +
"\vFilterEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12.\n" +
"\x05value\x18\x02 \x01(\v2\x18.protobuf.RepeatedStringR\x05value:\x028\x01\"J\n" +
"\x15FindContainerResponse\x121\n" +
"\tcontainer\x18\x01 \x01(\v2\x13.protobuf.ContainerR\tcontainer\"\x89\x01\n" +
"\x11StreamLogsRequest\x12 \n" +
"\vcontainerId\x18\x01 \x01(\tR\vcontainerId\x120\n" +
"\x05since\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x05since\x12 \n" +
"\vstreamTypes\x18\x03 \x01(\x05R\vstreamTypes\">\n" +
"\x12StreamLogsResponse\x12(\n" +
"\x05event\x18\x01 \x01(\v2\x12.protobuf.LogEventR\x05event\"\xc1\x01\n" +
"\x17LogsBetweenDatesRequest\x12 \n" +
"\vcontainerId\x18\x01 \x01(\tR\vcontainerId\x120\n" +
"\x05since\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x05since\x120\n" +
"\x05until\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x05until\x12 \n" +
"\vstreamTypes\x18\x04 \x01(\x05R\vstreamTypes\"\xbf\x01\n" +
"\x15StreamRawBytesRequest\x12 \n" +
"\vcontainerId\x18\x01 \x01(\tR\vcontainerId\x120\n" +
"\x05since\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x05since\x120\n" +
"\x05until\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x05until\x12 \n" +
"\vstreamTypes\x18\x04 \x01(\x05R\vstreamTypes\",\n" +
"\x16StreamRawBytesResponse\x12\x12\n" +
"\x04data\x18\x01 \x01(\fR\x04data\"\x15\n" +
"\x13StreamEventsRequest\"F\n" +
"\x14StreamEventsResponse\x12.\n" +
"\x05event\x18\x01 \x01(\v2\x18.protobuf.ContainerEventR\x05event\"\x14\n" +
"\x12StreamStatsRequest\"B\n" +
"\x13StreamStatsResponse\x12+\n" +
"\x04stat\x18\x01 \x01(\v2\x17.protobuf.ContainerStatR\x04stat\"\x11\n" +
"\x0fHostInfoRequest\"6\n" +
"\x10HostInfoResponse\x12\"\n" +
"\x04host\x18\x01 \x01(\v2\x0e.protobuf.HostR\x04host\"\x1f\n" +
"\x1dStreamContainerStartedRequest\"S\n" +
"\x1eStreamContainerStartedResponse\x121\n" +
"\tcontainer\x18\x01 \x01(\v2\x13.protobuf.ContainerR\tcontainer\"m\n" +
"\x16ContainerActionRequest\x12 \n" +
"\vcontainerId\x18\x01 \x01(\tR\vcontainerId\x121\n" +
"\x06action\x18\x02 \x01(\x0e2\x19.protobuf.ContainerActionR\x06action\"\x19\n" +
"\x17ContainerActionResponse\"\xa8\x01\n" +
"\x14ContainerExecRequest\x12 \n" +
"\vcontainerId\x18\x01 \x01(\tR\vcontainerId\x12\x18\n" +
"\acommand\x18\x02 \x03(\tR\acommand\x12\x16\n" +
"\x05stdin\x18\x03 \x01(\fH\x00R\x05stdin\x121\n" +
"\x06resize\x18\x04 \x01(\v2\x17.protobuf.ResizePayloadH\x00R\x06resizeB\t\n" +
"\apayload\"=\n" +
"\rResizePayload\x12\x14\n" +
"\x05width\x18\x01 \x01(\rR\x05width\x12\x16\n" +
"\x06height\x18\x02 \x01(\rR\x06height\"/\n" +
"\x15ContainerExecResponse\x12\x16\n" +
"\x06stdout\x18\x01 \x01(\fR\x06stdout\"\x90\x01\n" +
"\x16ContainerAttachRequest\x12 \n" +
"\vcontainerId\x18\x01 \x01(\tR\vcontainerId\x12\x16\n" +
"\x05stdin\x18\x02 \x01(\fH\x00R\x05stdin\x121\n" +
"\x06resize\x18\x03 \x01(\v2\x17.protobuf.ResizePayloadH\x00R\x06resizeB\t\n" +
"\apayload\"1\n" +
"\x17ContainerAttachResponse\x12\x16\n" +
"\x06stdout\x18\x01 \x01(\fR\x06stdout2\xa1\b\n" +
"\fAgentService\x12U\n" +
"\x0eListContainers\x12\x1f.protobuf.ListContainersRequest\x1a .protobuf.ListContainersResponse\"\x00\x12R\n" +
"\rFindContainer\x12\x1e.protobuf.FindContainerRequest\x1a\x1f.protobuf.FindContainerResponse\"\x00\x12K\n" +
"\n" +
"StreamLogs\x12\x1b.protobuf.StreamLogsRequest\x1a\x1c.protobuf.StreamLogsResponse\"\x000\x01\x12W\n" +
"\x10LogsBetweenDates\x12!.protobuf.LogsBetweenDatesRequest\x1a\x1c.protobuf.StreamLogsResponse\"\x000\x01\x12W\n" +
"\x0eStreamRawBytes\x12\x1f.protobuf.StreamRawBytesRequest\x1a .protobuf.StreamRawBytesResponse\"\x000\x01\x12Q\n" +
"\fStreamEvents\x12\x1d.protobuf.StreamEventsRequest\x1a\x1e.protobuf.StreamEventsResponse\"\x000\x01\x12N\n" +
"\vStreamStats\x12\x1c.protobuf.StreamStatsRequest\x1a\x1d.protobuf.StreamStatsResponse\"\x000\x01\x12o\n" +
"\x16StreamContainerStarted\x12'.protobuf.StreamContainerStartedRequest\x1a(.protobuf.StreamContainerStartedResponse\"\x000\x01\x12C\n" +
"\bHostInfo\x12\x19.protobuf.HostInfoRequest\x1a\x1a.protobuf.HostInfoResponse\"\x00\x12X\n" +
"\x0fContainerAction\x12 .protobuf.ContainerActionRequest\x1a!.protobuf.ContainerActionResponse\"\x00\x12V\n" +
"\rContainerExec\x12\x1e.protobuf.ContainerExecRequest\x1a\x1f.protobuf.ContainerExecResponse\"\x00(\x010\x01\x12\\\n" +
"\x0fContainerAttach\x12 .protobuf.ContainerAttachRequest\x1a!.protobuf.ContainerAttachResponse\"\x00(\x010\x01B\x13Z\x11internal/agent/pbb\x06proto3"
var (
file_rpc_proto_rawDescOnce sync.Once
+15 -15
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.2
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.3
// source: rpc.proto
package pb
@@ -266,40 +266,40 @@ type AgentServiceServer interface {
type UnimplementedAgentServiceServer struct{}
func (UnimplementedAgentServiceServer) ListContainers(context.Context, *ListContainersRequest) (*ListContainersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListContainers not implemented")
return nil, status.Error(codes.Unimplemented, "method ListContainers not implemented")
}
func (UnimplementedAgentServiceServer) FindContainer(context.Context, *FindContainerRequest) (*FindContainerResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FindContainer not implemented")
return nil, status.Error(codes.Unimplemented, "method FindContainer not implemented")
}
func (UnimplementedAgentServiceServer) StreamLogs(*StreamLogsRequest, grpc.ServerStreamingServer[StreamLogsResponse]) error {
return status.Errorf(codes.Unimplemented, "method StreamLogs not implemented")
return status.Error(codes.Unimplemented, "method StreamLogs not implemented")
}
func (UnimplementedAgentServiceServer) LogsBetweenDates(*LogsBetweenDatesRequest, grpc.ServerStreamingServer[StreamLogsResponse]) error {
return status.Errorf(codes.Unimplemented, "method LogsBetweenDates not implemented")
return status.Error(codes.Unimplemented, "method LogsBetweenDates not implemented")
}
func (UnimplementedAgentServiceServer) StreamRawBytes(*StreamRawBytesRequest, grpc.ServerStreamingServer[StreamRawBytesResponse]) error {
return status.Errorf(codes.Unimplemented, "method StreamRawBytes not implemented")
return status.Error(codes.Unimplemented, "method StreamRawBytes not implemented")
}
func (UnimplementedAgentServiceServer) StreamEvents(*StreamEventsRequest, grpc.ServerStreamingServer[StreamEventsResponse]) error {
return status.Errorf(codes.Unimplemented, "method StreamEvents not implemented")
return status.Error(codes.Unimplemented, "method StreamEvents not implemented")
}
func (UnimplementedAgentServiceServer) StreamStats(*StreamStatsRequest, grpc.ServerStreamingServer[StreamStatsResponse]) error {
return status.Errorf(codes.Unimplemented, "method StreamStats not implemented")
return status.Error(codes.Unimplemented, "method StreamStats not implemented")
}
func (UnimplementedAgentServiceServer) StreamContainerStarted(*StreamContainerStartedRequest, grpc.ServerStreamingServer[StreamContainerStartedResponse]) error {
return status.Errorf(codes.Unimplemented, "method StreamContainerStarted not implemented")
return status.Error(codes.Unimplemented, "method StreamContainerStarted not implemented")
}
func (UnimplementedAgentServiceServer) HostInfo(context.Context, *HostInfoRequest) (*HostInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method HostInfo not implemented")
return nil, status.Error(codes.Unimplemented, "method HostInfo not implemented")
}
func (UnimplementedAgentServiceServer) ContainerAction(context.Context, *ContainerActionRequest) (*ContainerActionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ContainerAction not implemented")
return nil, status.Error(codes.Unimplemented, "method ContainerAction not implemented")
}
func (UnimplementedAgentServiceServer) ContainerExec(grpc.BidiStreamingServer[ContainerExecRequest, ContainerExecResponse]) error {
return status.Errorf(codes.Unimplemented, "method ContainerExec not implemented")
return status.Error(codes.Unimplemented, "method ContainerExec not implemented")
}
func (UnimplementedAgentServiceServer) ContainerAttach(grpc.BidiStreamingServer[ContainerAttachRequest, ContainerAttachResponse]) error {
return status.Errorf(codes.Unimplemented, "method ContainerAttach not implemented")
return status.Error(codes.Unimplemented, "method ContainerAttach not implemented")
}
func (UnimplementedAgentServiceServer) mustEmbedUnimplementedAgentServiceServer() {}
func (UnimplementedAgentServiceServer) testEmbeddedByValue() {}
@@ -312,7 +312,7 @@ type UnsafeAgentServiceServer interface {
}
func RegisterAgentServiceServer(s grpc.ServiceRegistrar, srv AgentServiceServer) {
// If the following call pancis, it indicates UnimplementedAgentServiceServer was
// If the following call panics, it indicates UnimplementedAgentServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
+106 -137
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.5
// protoc v6.33.2
// protoc-gen-go v1.36.11
// protoc v6.33.3
// source: types.proto
package pb
@@ -261,13 +261,15 @@ func (x *Container) GetFullyLoaded() bool {
}
type ContainerStat struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
CpuPercent float64 `protobuf:"fixed64,2,opt,name=cpuPercent,proto3" json:"cpuPercent,omitempty"`
MemoryUsage float64 `protobuf:"fixed64,3,opt,name=memoryUsage,proto3" json:"memoryUsage,omitempty"`
MemoryPercent float64 `protobuf:"fixed64,4,opt,name=memoryPercent,proto3" json:"memoryPercent,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
CpuPercent float64 `protobuf:"fixed64,2,opt,name=cpuPercent,proto3" json:"cpuPercent,omitempty"`
MemoryUsage float64 `protobuf:"fixed64,3,opt,name=memoryUsage,proto3" json:"memoryUsage,omitempty"`
MemoryPercent float64 `protobuf:"fixed64,4,opt,name=memoryPercent,proto3" json:"memoryPercent,omitempty"`
NetworkRxTotal uint64 `protobuf:"varint,5,opt,name=networkRxTotal,proto3" json:"networkRxTotal,omitempty"`
NetworkTxTotal uint64 `protobuf:"varint,6,opt,name=networkTxTotal,proto3" json:"networkTxTotal,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ContainerStat) Reset() {
@@ -328,6 +330,20 @@ func (x *ContainerStat) GetMemoryPercent() float64 {
return 0
}
func (x *ContainerStat) GetNetworkRxTotal() uint64 {
if x != nil {
return x.NetworkRxTotal
}
return 0
}
func (x *ContainerStat) GetNetworkTxTotal() uint64 {
if x != nil {
return x.NetworkTxTotal
}
return 0
}
type LogFragment struct {
state protoimpl.MessageState `protogen:"open.v1"`
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
@@ -806,134 +822,87 @@ func (x *Host) GetDockerVersion() string {
var File_types_proto protoreflect.FileDescriptor
var file_types_proto_rawDesc = string([]byte{
0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x22, 0xa2, 0x05, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6d, 0x61,
0x67, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x49, 0x6d, 0x61, 0x67,
0x65, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x07,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x73, 0x74, 0x61,
0x72, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12,
0x16, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18,
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74,
0x74, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x74, 0x74, 0x79, 0x12, 0x37, 0x0a,
0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
0x65, 0x72, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06,
0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18,
0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x52, 0x05,
0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x0e,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x36, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65,
0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x20, 0x0a,
0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x11, 0x20, 0x01,
0x28, 0x04, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12,
0x1a, 0x0a, 0x08, 0x63, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28,
0x01, 0x52, 0x08, 0x63, 0x70, 0x75, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x66,
0x75, 0x6c, 0x6c, 0x79, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0b, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x1a, 0x39, 0x0a,
0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x87, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e,
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x70,
0x75, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a,
0x63, 0x70, 0x75, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65,
0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52,
0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x0d,
0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20,
0x01, 0x28, 0x01, 0x52, 0x0d, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x50, 0x65, 0x72, 0x63, 0x65,
0x6e, 0x74, 0x22, 0x27, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x88, 0x02, 0x0a, 0x08,
0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x74,
0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63,
0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x07, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e,
0x79, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74,
0x72, 0x65, 0x61, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x72, 0x65,
0x61, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x61, 0x77, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x61, 0x77, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x29, 0x0a, 0x0d, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x22, 0x43, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x12, 0x33, 0x0a, 0x09, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x4c, 0x6f, 0x67, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x09, 0x66, 0x72, 0x61,
0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x24, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x8c, 0x01, 0x0a,
0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12,
0x18, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a,
0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73,
0x74, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xaf, 0x03, 0x0a, 0x04,
0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65,
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e,
0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77,
0x61, 0x72, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x77, 0x61, 0x72, 0x6d,
0x12, 0x32, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x48, 0x6f, 0x73, 0x74,
0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61,
0x62, 0x65, 0x6c, 0x73, 0x12, 0x28, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e,
0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f,
0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1c,
0x0a, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06,
0x6f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x73,
0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x72, 0x65, 0x73,
0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x72, 0x65, 0x73,
0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04,
0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x67, 0x65, 0x6e,
0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
0x61, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d,
0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x33, 0x0a,
0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x12, 0x09, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53,
0x74, 0x6f, 0x70, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74,
0x10, 0x02, 0x42, 0x13, 0x5a, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61,
0x67, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
const file_types_proto_rawDesc = "" +
"\n" +
"\vtypes.proto\x12\bprotobuf\x1a\x19google/protobuf/any.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xa2\x05\n" +
"\tContainer\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12\x14\n" +
"\x05image\x18\x03 \x01(\tR\x05image\x12\x16\n" +
"\x06status\x18\x04 \x01(\tR\x06status\x12\x14\n" +
"\x05state\x18\x05 \x01(\tR\x05state\x12\x18\n" +
"\aImageId\x18\x06 \x01(\tR\aImageId\x124\n" +
"\acreated\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\acreated\x124\n" +
"\astarted\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\astarted\x12\x16\n" +
"\x06health\x18\t \x01(\tR\x06health\x12\x12\n" +
"\x04host\x18\n" +
" \x01(\tR\x04host\x12\x10\n" +
"\x03tty\x18\v \x01(\bR\x03tty\x127\n" +
"\x06labels\x18\f \x03(\v2\x1f.protobuf.Container.LabelsEntryR\x06labels\x12-\n" +
"\x05stats\x18\r \x03(\v2\x17.protobuf.ContainerStatR\x05stats\x12\x14\n" +
"\x05group\x18\x0e \x01(\tR\x05group\x12\x18\n" +
"\acommand\x18\x0f \x01(\tR\acommand\x126\n" +
"\bfinished\x18\x10 \x01(\v2\x1a.google.protobuf.TimestampR\bfinished\x12 \n" +
"\vmemoryLimit\x18\x11 \x01(\x04R\vmemoryLimit\x12\x1a\n" +
"\bcpuLimit\x18\x12 \x01(\x01R\bcpuLimit\x12 \n" +
"\vfullyLoaded\x18\x13 \x01(\bR\vfullyLoaded\x1a9\n" +
"\vLabelsEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xd7\x01\n" +
"\rContainerStat\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x1e\n" +
"\n" +
"cpuPercent\x18\x02 \x01(\x01R\n" +
"cpuPercent\x12 \n" +
"\vmemoryUsage\x18\x03 \x01(\x01R\vmemoryUsage\x12$\n" +
"\rmemoryPercent\x18\x04 \x01(\x01R\rmemoryPercent\x12&\n" +
"\x0enetworkRxTotal\x18\x05 \x01(\x04R\x0enetworkRxTotal\x12&\n" +
"\x0enetworkTxTotal\x18\x06 \x01(\x04R\x0enetworkTxTotal\"'\n" +
"\vLogFragment\x12\x18\n" +
"\amessage\x18\x01 \x01(\tR\amessage\"\x88\x02\n" +
"\bLogEvent\x12\x0e\n" +
"\x02id\x18\x01 \x01(\rR\x02id\x12 \n" +
"\vcontainerId\x18\x02 \x01(\tR\vcontainerId\x12.\n" +
"\amessage\x18\x03 \x01(\v2\x14.google.protobuf.AnyR\amessage\x128\n" +
"\ttimestamp\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\x12\x14\n" +
"\x05level\x18\x05 \x01(\tR\x05level\x12\x16\n" +
"\x06stream\x18\x06 \x01(\tR\x06stream\x12\x12\n" +
"\x04type\x18\a \x01(\tR\x04type\x12\x1e\n" +
"\n" +
"rawMessage\x18\b \x01(\tR\n" +
"rawMessage\")\n" +
"\rSingleMessage\x12\x18\n" +
"\amessage\x18\x01 \x01(\tR\amessage\"C\n" +
"\fGroupMessage\x123\n" +
"\tfragments\x18\x01 \x03(\v2\x15.protobuf.LogFragmentR\tfragments\"$\n" +
"\x0eComplexMessage\x12\x12\n" +
"\x04data\x18\x01 \x01(\fR\x04data\"\x8c\x01\n" +
"\x0eContainerEvent\x12\x18\n" +
"\aactorId\x18\x01 \x01(\tR\aactorId\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" +
"\x04host\x18\x03 \x01(\tR\x04host\x128\n" +
"\ttimestamp\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\"\xaf\x03\n" +
"\x04Host\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12 \n" +
"\vnodeAddress\x18\x03 \x01(\tR\vnodeAddress\x12\x14\n" +
"\x05swarm\x18\x04 \x01(\bR\x05swarm\x122\n" +
"\x06labels\x18\x05 \x03(\v2\x1a.protobuf.Host.LabelsEntryR\x06labels\x12(\n" +
"\x0foperatingSystem\x18\x06 \x01(\tR\x0foperatingSystem\x12\x1c\n" +
"\tosVersion\x18\a \x01(\tR\tosVersion\x12\x16\n" +
"\x06osType\x18\b \x01(\tR\x06osType\x12\x1a\n" +
"\bcpuCores\x18\t \x01(\rR\bcpuCores\x12\x16\n" +
"\x06memory\x18\n" +
" \x01(\x04R\x06memory\x12\"\n" +
"\fagentVersion\x18\v \x01(\tR\fagentVersion\x12$\n" +
"\rdockerVersion\x18\f \x01(\tR\rdockerVersion\x1a9\n" +
"\vLabelsEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01*3\n" +
"\x0fContainerAction\x12\t\n" +
"\x05Start\x10\x00\x12\b\n" +
"\x04Stop\x10\x01\x12\v\n" +
"\aRestart\x10\x02B\x13Z\x11internal/agent/pbb\x06proto3"
var (
file_types_proto_rawDescOnce sync.Once
+6 -4
View File
@@ -171,10 +171,12 @@ func (s *server) StreamStats(in *pb.StreamStatsRequest, out pb.AgentService_Stre
case stat := <-stats:
out.Send(&pb.StreamStatsResponse{
Stat: &pb.ContainerStat{
Id: stat.ID,
CpuPercent: stat.CPUPercent,
MemoryPercent: stat.MemoryPercent,
MemoryUsage: stat.MemoryUsage,
Id: stat.ID,
CpuPercent: stat.CPUPercent,
MemoryPercent: stat.MemoryPercent,
MemoryUsage: stat.MemoryUsage,
NetworkRxTotal: stat.NetworkRxTotal,
NetworkTxTotal: stat.NetworkTxTotal,
},
})
case <-out.Context().Done():
+6 -4
View File
@@ -98,10 +98,12 @@ func FromProto(c *pb.Container) Container {
// ContainerStat represent stats instant for a container
type ContainerStat struct {
ID string `json:"id"`
CPUPercent float64 `json:"cpu"`
MemoryPercent float64 `json:"memory"`
MemoryUsage float64 `json:"memoryUsage"`
ID string `json:"id"`
CPUPercent float64 `json:"cpu"`
MemoryPercent float64 `json:"memory"`
MemoryUsage float64 `json:"memoryUsage"`
NetworkRxTotal uint64 `json:"networkRxTotal"`
NetworkTxTotal uint64 `json:"networkTxTotal"`
}
// ContainerEvent represents events that are triggered
+13 -4
View File
@@ -198,6 +198,7 @@ func (d *DockerClient) ContainerStats(ctx context.Context, id string, stats chan
mem, memLimit float64
previousCPU uint64
previousSystem uint64
networkRx, networkTx uint64
)
daemonOSType := response.OSType
@@ -213,15 +214,23 @@ func (d *DockerClient) ContainerStats(ctx context.Context, id string, stats chan
mem = float64(v.MemoryStats.PrivateWorkingSet)
}
// Calculate total network bytes across all interfaces
for _, netStats := range v.Networks {
networkRx += netStats.RxBytes
networkTx += netStats.TxBytes
}
if cpuPercent > 0 || mem > 0 {
select {
case <-ctx.Done():
return nil
case stats <- container.ContainerStat{
ID: id,
CPUPercent: cpuPercent,
MemoryPercent: memPercent,
MemoryUsage: mem,
ID: id,
CPUPercent: cpuPercent,
MemoryPercent: memPercent,
MemoryUsage: mem,
NetworkRxTotal: networkRx,
NetworkTxTotal: networkTx,
}:
}
}
+2 -1
View File
@@ -293,7 +293,8 @@ func (k *K8sClient) ContainerEvents(ctx context.Context, ch chan<- container.Con
}
func (k *K8sClient) ContainerStats(ctx context.Context, id string, stats chan<- container.ContainerStat) error {
panic("not implemented")
// Stats collection is implemented in stats_collector.go using K8s metrics API
panic("not implemented - use K8sStatsCollector instead")
}
func (k *K8sClient) Ping(ctx context.Context) error {
+5 -3
View File
@@ -105,9 +105,11 @@ func (sc *K8sStatsCollector) Start(parentCtx context.Context) bool {
for _, pod := range metricList.Items {
for _, c := range pod.Containers {
stat := container.ContainerStat{
ID: pod.Namespace + ":" + pod.Name + ":" + c.Name,
CPUPercent: float64(c.Usage.Cpu().MilliValue()) / 1000 * 100,
MemoryUsage: c.Usage.Memory().AsApproximateFloat64(),
ID: pod.Namespace + ":" + pod.Name + ":" + c.Name,
CPUPercent: float64(c.Usage.Cpu().MilliValue()) / 1000 * 100,
MemoryUsage: c.Usage.Memory().AsApproximateFloat64(),
NetworkRxTotal: 0, // K8s metrics API doesn't expose network stats by default
NetworkTxTotal: 0, // Would require custom metrics or cAdvisor integration
}
log.Trace().Interface("stat", stat).Msg("k8s stats")
sc.subscribers.Range(func(c context.Context, stats chan<- container.ContainerStat) bool {
+325
View File
@@ -0,0 +1,325 @@
package notification
import (
"context"
"fmt"
"sync"
"time"
"github.com/amir20/dozzle/internal/container"
container_support "github.com/amir20/dozzle/internal/support/container"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/rs/zerolog/log"
)
// Manager handles notification subscriptions and matching
type Manager struct {
subscriptions []*compiledSubscription
mu sync.RWMutex
activeStreams map[string]*activeStream // containerID -> stream info
containerCache map[string]Container // containerID -> notification.Container
containerService ContainerService
dispatcher Dispatcher
}
// activeStream tracks a single log stream and all subscriptions watching it
type activeStream struct {
container Container // notification.Container
subscriptions []*compiledSubscription // All subscriptions watching this container
cancel context.CancelFunc // Cancel function for the stream
}
// compiledSubscription wraps a Subscription with compiled expr programs
type compiledSubscription struct {
subscription *Subscription
containerProgram *vm.Program // Always non-nil
logProgram *vm.Program // Always non-nil
}
// ContainerService provides access to containers and log streaming
type ContainerService interface {
ListAllContainers(labels container.ContainerLabels) ([]container.Container, []error)
FindContainer(host, id string, labels container.ContainerLabels) (*container_support.ContainerService, error)
SubscribeContainersStarted(ctx context.Context, containers chan<- container.Container, filter container_support.ContainerFilter)
}
// NewManager creates a new notification manager
func NewManager(containerService ContainerService, dispatcher Dispatcher) *Manager {
return &Manager{
subscriptions: make([]*compiledSubscription, 0),
activeStreams: make(map[string]*activeStream),
containerCache: make(map[string]Container),
containerService: containerService,
dispatcher: dispatcher,
}
}
// LoadSubscriptions loads and compiles multiple subscriptions (replaces existing)
func (m *Manager) LoadSubscriptions(subs []*Subscription) error {
m.mu.Lock()
m.subscriptions = make([]*compiledSubscription, 0, len(subs))
m.mu.Unlock()
for _, sub := range subs {
if err := m.AddSubscription(sub); err != nil {
return err
}
}
log.Info().Int("count", len(subs)).Msg("loaded notification subscriptions")
return nil
}
// AddSubscription adds a single subscription
func (m *Manager) AddSubscription(sub *Subscription) error {
cs, err := m.compileSubscription(sub)
if err != nil {
return err
}
m.mu.Lock()
m.subscriptions = append(m.subscriptions, cs)
m.mu.Unlock()
log.Info().Str("subscription", sub.Name).Msg("added notification subscription")
return nil
}
// compileSubscription compiles expr programs for a subscription
func (m *Manager) compileSubscription(sub *Subscription) (*compiledSubscription, error) {
if sub.ContainerFilter == "" {
return nil, fmt.Errorf("container_filter is required for subscription %q", sub.Name)
}
if sub.LogFilter == "" {
return nil, fmt.Errorf("log_filter is required for subscription %q", sub.Name)
}
cs := &compiledSubscription{
subscription: sub,
}
// Compile container filter (required)
containerProgram, err := expr.Compile(sub.ContainerFilter, expr.Env(Container{}))
if err != nil {
log.Error().Err(err).Str("subscription", sub.Name).Msg("failed to compile container filter")
return nil, fmt.Errorf("failed to compile container_filter for %q: %w", sub.Name, err)
}
cs.containerProgram = containerProgram
// Compile log filter (required)
logProgram, err := expr.Compile(sub.LogFilter, expr.Env(Log{}))
if err != nil {
log.Error().Err(err).Str("subscription", sub.Name).Msg("failed to compile log filter")
return nil, fmt.Errorf("failed to compile log_filter for %q: %w", sub.Name, err)
}
cs.logProgram = logProgram
return cs, nil
}
// Start begins monitoring containers and streaming logs for matching subscriptions
// Only starts if there are subscriptions configured
func (m *Manager) Start(ctx context.Context) error {
m.mu.RLock()
hasSubscriptions := len(m.subscriptions) > 0
m.mu.RUnlock()
if !hasSubscriptions {
log.Debug().Msg("no subscriptions configured, skipping notification manager start")
return nil
}
// Subscribe to new containers that match our filters
newContainers := make(chan container.Container)
m.containerService.SubscribeContainersStarted(ctx, newContainers, func(c *container.Container) bool {
matchingSubs := m.getMatchingSubscriptions(c)
return len(matchingSubs) > 0
})
// Handle new containers in background
go func() {
for c := range newContainers {
m.startContainerStream(ctx, &c)
}
}()
// Get all existing containers and start streaming matching ones
containers, errs := m.containerService.ListAllContainers(nil)
for _, err := range errs {
log.Warn().Err(err).Msg("error listing containers for notifications")
}
// Check each container and start streaming only if it matches any subscription
for _, c := range containers {
if c.State == "running" {
matchingSubs := m.getMatchingSubscriptions(&c)
if len(matchingSubs) > 0 {
m.startContainerStream(ctx, &c)
}
}
}
return nil
}
// startContainerStream starts a single log stream for a container
// All matching subscriptions will receive logs from this one stream
func (m *Manager) startContainerStream(ctx context.Context, c *container.Container) {
// Check if already streaming
m.mu.RLock()
_, exists := m.activeStreams[c.ID]
m.mu.RUnlock()
if exists {
return
}
// Find which subscriptions match this container
matchingSubs := m.getMatchingSubscriptions(c)
if len(matchingSubs) == 0 {
return
}
log.Debug().
Str("container", c.Name).
Int("subscriptions", len(matchingSubs)).
Msg("starting log stream for notification subscriptions")
// Convert to notification.Container and cache it
notifContainer := NewContainer(c)
m.mu.Lock()
m.containerCache[c.ID] = notifContainer
m.mu.Unlock()
streamCtx, cancel := context.WithCancel(ctx)
stream := &activeStream{
container: notifContainer,
subscriptions: matchingSubs,
cancel: cancel,
}
m.mu.Lock()
m.activeStreams[c.ID] = stream
m.mu.Unlock()
go func() {
defer func() {
m.mu.Lock()
delete(m.activeStreams, c.ID)
m.mu.Unlock()
}()
logs := make(chan *container.LogEvent)
// Stream logs in separate goroutine
go func() {
containerService, err := m.containerService.FindContainer(c.Host, c.ID, nil)
if err != nil {
log.Error().Err(err).Msg("error finding container for notification streaming")
return
}
err = containerService.StreamLogs(streamCtx, time.Now(), container.STDOUT|container.STDERR, logs)
if err != nil && err != context.Canceled {
log.Error().Err(err).Msg("error streaming logs for notification")
}
close(logs)
}()
// Process logs - check against all matching subscriptions
for logEvent := range logs {
for _, cs := range matchingSubs {
m.processLogEvent(logEvent, c.ID, cs)
}
}
}()
}
// getMatchingSubscriptions returns subscriptions that match a container
func (m *Manager) getMatchingSubscriptions(c *container.Container) []*compiledSubscription {
m.mu.RLock()
defer m.mu.RUnlock()
containerCtx := NewContainer(c)
matching := make([]*compiledSubscription, 0)
for _, cs := range m.subscriptions {
if m.matchesContainer(cs.containerProgram, containerCtx) {
matching = append(matching, cs)
}
}
return matching
}
// matchesContainer evaluates the container filter expression
func (m *Manager) matchesContainer(program *vm.Program, containerCtx Container) bool {
result, err := expr.Run(program, containerCtx)
if err != nil {
log.Error().Err(err).Msg("error evaluating container filter")
return false
}
match, ok := result.(bool)
if !ok {
log.Error().Msg("container filter did not return boolean")
return false
}
return match
}
// processLogEvent evaluates a log event against the subscription's log filter
func (m *Manager) processLogEvent(logEvent *container.LogEvent, containerID string, cs *compiledSubscription) {
// Lookup notification.Container from cache
m.mu.RLock()
notifContainer, ok := m.containerCache[containerID]
m.mu.RUnlock()
if !ok {
log.Warn().Str("container", containerID).Msg("container not in cache")
return
}
logCtx := NewLog(logEvent, notifContainer)
// Evaluate log filter (program is never nil)
if m.matchesLog(cs.logProgram, logCtx) {
m.triggerWebhook(cs.subscription, logCtx)
}
}
// matchesLog evaluates the log filter expression
func (m *Manager) matchesLog(program *vm.Program, logCtx Log) bool {
result, err := expr.Run(program, logCtx)
if err != nil {
log.Error().Err(err).Msg("error evaluating log filter")
return false
}
match, ok := result.(bool)
if !ok {
log.Error().Msg("log filter did not return boolean")
return false
}
return match
}
// triggerWebhook sends the notification
func (m *Manager) triggerWebhook(sub *Subscription, logCtx Log) {
payload := WebhookPayload{
SubscriptionName: sub.Name,
Timestamp: time.Now(),
Container: logCtx.Container,
Log: LogEvent{
Message: logCtx.Message,
Level: logCtx.Level,
Timestamp: logCtx.Timestamp,
},
}
m.dispatcher.Dispatch(sub, payload)
}
+87
View File
@@ -0,0 +1,87 @@
package notification
import (
"fmt"
"os"
"gopkg.in/yaml.v3"
)
// Config represents the YAML configuration structure
type Config struct {
Dispatcher DispatcherConfig `yaml:"dispatcher"`
Subscriptions []*Subscription `yaml:"subscriptions"`
}
// LoadFromFile loads config and returns a fully configured Manager
func LoadFromFile(path string, containerService ContainerService) (*Manager, error) {
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil, nil // No config file is fine, return nil
}
return nil, fmt.Errorf("failed to read notifications config: %w", err)
}
var config Config
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("failed to parse notifications config: %w", err)
}
// Validate dispatcher config
if config.Dispatcher.Type == "" {
config.Dispatcher.Type = "simple" // Default to simple
}
if config.Dispatcher.URL == "" {
return nil, fmt.Errorf("dispatcher.url is required")
}
// Validate subscriptions
for _, sub := range config.Subscriptions {
if sub.Name == "" {
return nil, fmt.Errorf("subscription missing name")
}
if sub.ContainerFilter == "" {
return nil, fmt.Errorf("subscription %q missing container_filter", sub.Name)
}
if sub.LogFilter == "" {
return nil, fmt.Errorf("subscription %q missing log_filter", sub.Name)
}
}
// Create dispatcher based on type
var dispatcher Dispatcher
switch config.Dispatcher.Type {
case "simple":
dispatcher = NewWebhookDispatcher(&config.Dispatcher)
default:
return nil, fmt.Errorf("unsupported dispatcher type: %s", config.Dispatcher.Type)
}
// Create and configure manager
manager := NewManager(containerService, dispatcher)
if err := manager.LoadSubscriptions(config.Subscriptions); err != nil {
return nil, err
}
return manager, nil
}
// SaveToFile saves dispatcher config and subscriptions to a YAML file
func SaveToFile(path string, dispatcher *DispatcherConfig, subs []*Subscription) error {
config := Config{
Dispatcher: *dispatcher,
Subscriptions: subs,
}
data, err := yaml.Marshal(&config)
if err != nil {
return fmt.Errorf("failed to marshal notifications config: %w", err)
}
if err := os.WriteFile(path, data, 0644); err != nil {
return fmt.Errorf("failed to write notifications config: %w", err)
}
return nil
}
+104
View File
@@ -0,0 +1,104 @@
package notification
import (
"strings"
"time"
"github.com/amir20/dozzle/internal/container"
)
// DispatcherConfig represents global dispatcher configuration
type DispatcherConfig struct {
// Type determines the dispatcher type (webhook, dozzle-service, etc.)
Type string `yaml:"type"` // "webhook" is default
// URL is the endpoint for the dispatcher service
URL string `yaml:"url"`
// APIKey for authenticating with the dispatcher service
APIKey string `yaml:"api_key,omitempty"`
// Headers to include in all requests
Headers map[string]string `yaml:"headers,omitempty"`
}
// Subscription represents a notification rule
type Subscription struct {
// Name is a human-readable name for this subscription
Name string `yaml:"name"`
// ContainerFilter is an expr expression to match containers (REQUIRED)
// Expression receives a Container object with: Name, Labels, Host, State
// Example: Name == "nginx" || Labels["app"] == "web"
ContainerFilter string `yaml:"container_filter"`
// LogFilter is an expr expression to match log events (REQUIRED)
// Expression receives a Log object with: Message, Level, Timestamp, Container
// Example: Level == "error" || Message contains "panic"
LogFilter string `yaml:"log_filter"`
}
// Container represents container information for expr evaluation (internal type)
type Container struct {
Name string `expr:"Name"`
Labels map[string]string `expr:"Labels"`
Host string `expr:"Host"`
State string `expr:"State"`
}
// Log represents log event information for expr evaluation
type Log struct {
Message any `expr:"Message"`
Level string `expr:"Level"`
Timestamp time.Time `expr:"Timestamp"`
Container Container `expr:"Container"`
}
// WebhookPayload is the structure sent to webhook endpoints
type WebhookPayload struct {
SubscriptionName string `json:"subscription_name"`
Timestamp time.Time `json:"timestamp"`
Container Container `json:"container"`
Log LogEvent `json:"log"`
}
// LogEvent represents the log in the webhook payload
type LogEvent struct {
Message any `json:"message"`
Level string `json:"level"`
Timestamp time.Time `json:"timestamp"`
}
// NewContainer converts container.Container to notification.Container
func NewContainer(c *container.Container) Container {
return Container{
Name: c.Name,
Labels: c.Labels,
Host: c.Host,
State: c.State,
}
}
// NewLog creates a Log from container.LogEvent and notification.Container
func NewLog(logEvent *container.LogEvent, notifContainer Container) Log {
timestamp := time.Unix(0, logEvent.Timestamp)
// Extract message - keep as any type for expr flexibility
var message any = logEvent.Message
// For grouped logs, join fragments into single string for easier matching
if fragments, ok := logEvent.Message.([]container.LogFragment); ok {
parts := make([]string, len(fragments))
for i, frag := range fragments {
parts[i] = frag.Message
}
message = strings.Join(parts, "\n")
}
return Log{
Message: message,
Level: logEvent.Level,
Timestamp: timestamp,
Container: notifContainer,
}
}
+83
View File
@@ -0,0 +1,83 @@
package notification
import (
"bytes"
"encoding/json"
"net/http"
"time"
"github.com/rs/zerolog/log"
)
// Dispatcher is the interface for sending notifications
type Dispatcher interface {
Dispatch(subscription *Subscription, payload WebhookPayload)
}
// WebhookDispatcher handles async webhook delivery
type WebhookDispatcher struct {
client *http.Client
config *DispatcherConfig
}
// NewWebhookDispatcher creates a new webhook dispatcher
func NewWebhookDispatcher(config *DispatcherConfig) *WebhookDispatcher {
return &WebhookDispatcher{
client: &http.Client{
Timeout: 10 * time.Second,
},
config: config,
}
}
// Dispatch sends a webhook payload asynchronously
func (d *WebhookDispatcher) Dispatch(subscription *Subscription, payload WebhookPayload) {
url := d.config.URL
go func() {
body, err := json.Marshal(payload)
if err != nil {
log.Error().Err(err).Str("url", url).Msg("failed to marshal webhook payload")
return
}
req, err := http.NewRequest("POST", url, bytes.NewReader(body))
if err != nil {
log.Error().Err(err).Str("url", url).Msg("failed to create webhook request")
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Dozzle-Notifications/1.0")
// Add API key if configured
if d.config.APIKey != "" {
req.Header.Set("Authorization", "Bearer "+d.config.APIKey)
}
// Add custom headers
for key, value := range d.config.Headers {
req.Header.Set(key, value)
}
resp, err := d.client.Do(req)
if err != nil {
log.Error().Err(err).Str("url", url).Msg("failed to send webhook")
return
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
log.Warn().
Int("status", resp.StatusCode).
Str("url", url).
Str("subscription", payload.SubscriptionName).
Msg("webhook returned error status")
} else {
log.Debug().
Int("status", resp.StatusCode).
Str("url", url).
Str("subscription", payload.SubscriptionName).
Msg("webhook delivered successfully")
}
}()
}
@@ -122,6 +122,12 @@ func (m *MultiHostService) SubscribeEventsAndStats(ctx context.Context, events c
}
}
func (m *MultiHostService) SubscribeEvents(ctx context.Context, events chan<- container.ContainerEvent) {
for _, client := range m.manager.List() {
client.SubscribeEvents(ctx, events)
}
}
func (m *MultiHostService) SubscribeContainersStarted(ctx context.Context, containers chan<- container.Container, filter container_support.ContainerFilter) {
newContainers := make(chan container.Container)
for _, client := range m.manager.List() {
@@ -96,6 +96,10 @@ func (m *K8sClusterService) SubscribeEventsAndStats(ctx context.Context, events
m.client.SubscribeStats(ctx, stats)
}
func (m *K8sClusterService) SubscribeEvents(ctx context.Context, events chan<- container.ContainerEvent) {
m.client.SubscribeEvents(ctx, events)
}
func (m *K8sClusterService) SubscribeContainersStarted(ctx context.Context, containers chan<- container.Container, filter container_support.ContainerFilter) {
newContainers := make(chan container.Container)
m.client.SubscribeContainersStarted(ctx, newContainers)
+15 -13
View File
@@ -10,6 +10,7 @@ import (
"github.com/amir20/dozzle/internal/auth"
"github.com/amir20/dozzle/internal/container"
"github.com/amir20/dozzle/internal/notification"
container_support "github.com/amir20/dozzle/internal/support/container"
"github.com/go-chi/chi/v5"
@@ -34,19 +35,20 @@ const (
// Config is a struct for configuring the web service
type Config struct {
Base string
Addr string
Version string
Hostname string
NoAnalytics bool
Dev bool
Mode string
Authorization Authorization
EnableActions bool
EnableShell bool
DisableAvatars bool
ReleaseCheckMode ReleaseCheckMode
Labels container.ContainerLabels
Base string
Addr string
Version string
Hostname string
NoAnalytics bool
Dev bool
Mode string
Authorization Authorization
EnableActions bool
EnableShell bool
DisableAvatars bool
ReleaseCheckMode ReleaseCheckMode
Labels container.ContainerLabels
NotificationManager *notification.Manager
}
type Authorization struct {
+64 -35
View File
@@ -17,6 +17,7 @@ import (
"github.com/amir20/dozzle/internal/auth"
"github.com/amir20/dozzle/internal/docker"
"github.com/amir20/dozzle/internal/k8s"
"github.com/amir20/dozzle/internal/notification"
"github.com/amir20/dozzle/internal/support/cli"
docker_support "github.com/amir20/dozzle/internal/support/docker"
k8s_support "github.com/amir20/dozzle/internal/support/k8s"
@@ -108,15 +109,18 @@ func main() {
log.Fatal().Str("mode", args.Mode).Msg("Invalid mode")
}
srv := createServer(args, hostService)
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
notificationManager := initializeNotifications(ctx, hostService)
srv := createServer(args, hostService, notificationManager)
go func() {
log.Info().Msgf("Accepting connections on %s", args.Addr)
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal().Err(err).Msg("failed to listen")
}
}()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
<-ctx.Done()
stop()
@@ -137,7 +141,56 @@ func fileExists(filename string) bool {
return err == nil
}
func createServer(args cli.Args, hostService web.HostService) *http.Server {
func initializeSimpleAuth(authTTL string) web.Authorizer {
log.Debug().Msg("Using simple authentication")
userFilePath := "./data/users.yml"
if !fileExists(userFilePath) {
userFilePath = "./data/users.yaml"
if !fileExists(userFilePath) {
log.Fatal().Msg("No users.yaml or users.yml file found.")
}
}
log.Debug().Msgf("Reading %s file", filepath.Base(userFilePath))
db, err := auth.ReadUsersFromFile(userFilePath)
if err != nil {
log.Fatal().Err(err).Msgf("Could not read users file: %s", userFilePath)
}
log.Debug().Int("users", len(db.Users)).Msg("Loaded users")
ttl := time.Duration(0)
if authTTL != "session" {
ttl, err = time.ParseDuration(authTTL)
if err != nil {
log.Fatal().Err(err).Msg("Could not parse auth ttl")
}
}
return auth.NewSimpleAuth(db, ttl)
}
func initializeNotifications(ctx context.Context, hostService web.HostService) *notification.Manager {
notificationsPath := "./data/notifications.yml"
manager, err := notification.LoadFromFile(notificationsPath, hostService)
if err != nil {
log.Error().Err(err).Msg("failed to load notifications config")
return nil
}
if manager == nil {
return nil // No config file
}
if err := manager.Start(ctx); err != nil {
log.Error().Err(err).Msg("failed to start notification manager")
return nil
}
return manager
}
func createServer(args cli.Args, hostService web.HostService, notificationManager *notification.Manager) *http.Server {
_, dev := os.LookupEnv("DEV")
var releaseCheckMode web.ReleaseCheckMode = web.Automatic
@@ -158,33 +211,8 @@ func createServer(args cli.Args, hostService web.HostService) *http.Server {
provider = web.FORWARD_PROXY
authorizer = auth.NewForwardProxyAuth(args.AuthHeaderUser, args.AuthHeaderEmail, args.AuthHeaderName, args.AuthHeaderFilter, args.AuthHeaderRoles)
} else if args.AuthProvider == "simple" {
log.Debug().Msg("Using simple authentication")
provider = web.SIMPLE
userFilePath := "./data/users.yml"
if !fileExists(userFilePath) {
userFilePath = "./data/users.yaml"
if !fileExists(userFilePath) {
log.Fatal().Msg("No users.yaml or users.yml file found.")
}
}
log.Debug().Msgf("Reading %s file", filepath.Base(userFilePath))
db, err := auth.ReadUsersFromFile(userFilePath)
if err != nil {
log.Fatal().Err(err).Msgf("Could not read users file: %s", userFilePath)
}
log.Debug().Int("users", len(db.Users)).Msg("Loaded users")
ttl := time.Duration(0)
if args.AuthTTL != "session" {
ttl, err = time.ParseDuration(args.AuthTTL)
if err != nil {
log.Fatal().Err(err).Msg("Could not parse auth ttl")
}
}
authorizer = auth.NewSimpleAuth(db, ttl)
authorizer = initializeSimpleAuth(args.AuthTTL)
}
authTTL := time.Duration(0)
@@ -211,11 +239,12 @@ func createServer(args cli.Args, hostService web.HostService) *http.Server {
TTL: authTTL,
LogoutUrl: args.AuthLogoutUrl,
},
EnableActions: args.EnableActions,
EnableShell: args.EnableShell,
DisableAvatars: args.DisableAvatars,
ReleaseCheckMode: releaseCheckMode,
Labels: args.Filter,
EnableActions: args.EnableActions,
EnableShell: args.EnableShell,
DisableAvatars: args.DisableAvatars,
ReleaseCheckMode: releaseCheckMode,
Labels: args.Filter,
NotificationManager: notificationManager,
}
assets, err := fs.Sub(content, "dist")
+5 -5
View File
@@ -6,7 +6,7 @@
"bugs": {
"url": "https://github.com/amir20/dozzle/issues"
},
"packageManager": "pnpm@10.27.0",
"packageManager": "pnpm@10.28.0",
"type": "module",
"repository": {
"type": "git",
@@ -30,10 +30,10 @@
},
"dependencies": {
"@duckdb/duckdb-wasm": "1.33.1-dev16.0",
"@iconify-json/carbon": "^1.2.15",
"@iconify-json/carbon": "^1.2.16",
"@iconify-json/cil": "^1.2.3",
"@iconify-json/ic": "^1.2.4",
"@iconify-json/material-symbols": "^1.2.50",
"@iconify-json/material-symbols": "^1.2.51",
"@iconify-json/mdi": "^1.2.3",
"@iconify-json/mdi-light": "^1.2.2",
"@iconify-json/octicon": "^1.2.19",
@@ -74,12 +74,12 @@
"devDependencies": {
"@apache-arrow/esnext-esm": "^21.1.0",
"@iconify-json/ion": "^1.2.6",
"@iconify-json/material-symbols-light": "^1.2.50",
"@iconify-json/material-symbols-light": "^1.2.51",
"@iconify-json/ri": "^1.2.7",
"@pinia/testing": "^1.0.3",
"@playwright/test": "^1.57.0",
"@types/lodash.debounce": "^4.0.9",
"@types/node": "^25.0.3",
"@types/node": "^25.0.6",
"@vitejs/plugin-vue": "6.0.3",
"@vue/compiler-sfc": "^3.5.26",
"@vue/test-utils": "^2.4.6",
+58 -58
View File
@@ -15,8 +15,8 @@ importers:
specifier: 1.33.1-dev16.0
version: 1.33.1-dev16.0
'@iconify-json/carbon':
specifier: ^1.2.15
version: 1.2.15
specifier: ^1.2.16
version: 1.2.16
'@iconify-json/cil':
specifier: ^1.2.3
version: 1.2.3
@@ -24,8 +24,8 @@ importers:
specifier: ^1.2.4
version: 1.2.4
'@iconify-json/material-symbols':
specifier: ^1.2.50
version: 1.2.50
specifier: ^1.2.51
version: 1.2.51
'@iconify-json/mdi':
specifier: ^1.2.3
version: 1.2.3
@@ -46,7 +46,7 @@ importers:
version: 0.5.19(tailwindcss@4.1.18)
'@tailwindcss/vite':
specifier: 4.1.18
version: 4.1.18(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
version: 4.1.18(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
'@vueuse/components':
specifier: ^14.1.0
version: 14.1.0(vue@3.5.26(typescript@5.9.3))
@@ -109,22 +109,22 @@ importers:
version: 30.0.0(@babel/parser@7.28.5)(vue@3.5.26(typescript@5.9.3))
unplugin-vue-macros:
specifier: ^2.14.5
version: 2.14.5(@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3)))(esbuild@0.27.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-tsc@3.2.2(typescript@5.9.3))(vue@3.5.26(typescript@5.9.3))
version: 2.14.5(@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3)))(esbuild@0.27.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-tsc@3.2.2(typescript@5.9.3))(vue@3.5.26(typescript@5.9.3))
unplugin-vue-router:
specifier: ^0.19.2
version: 0.19.2(@vue/compiler-sfc@3.5.26)(vue-router@4.6.4(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
vite:
specifier: 7.3.1
version: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
version: 7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vite-plugin-vue-layouts:
specifier: ^0.11.0
version: 0.11.0(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
version: 0.11.0(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
vite-svg-loader:
specifier: ^5.1.0
version: 5.1.0(vue@3.5.26(typescript@5.9.3))
vitepress:
specifier: 1.6.4
version: 1.6.4(@algolia/client-search@5.23.4)(@types/node@25.0.3)(fuse.js@7.1.0)(lightningcss@1.30.2)(postcss@8.5.6)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.39.0)(typescript@5.9.3)
version: 1.6.4(@algolia/client-search@5.23.4)(@types/node@25.0.6)(fuse.js@7.1.0)(lightningcss@1.30.2)(postcss@8.5.6)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.39.0)(typescript@5.9.3)
vue:
specifier: ^3.5.26
version: 3.5.26(typescript@5.9.3)
@@ -142,8 +142,8 @@ importers:
specifier: ^1.2.6
version: 1.2.6
'@iconify-json/material-symbols-light':
specifier: ^1.2.50
version: 1.2.50
specifier: ^1.2.51
version: 1.2.51
'@iconify-json/ri':
specifier: ^1.2.7
version: 1.2.7
@@ -157,11 +157,11 @@ importers:
specifier: ^4.0.9
version: 4.0.9
'@types/node':
specifier: ^25.0.3
version: 25.0.3
specifier: ^25.0.6
version: 25.0.6
'@vitejs/plugin-vue':
specifier: 6.0.3
version: 6.0.3(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))
version: 6.0.3(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))
'@vue/compiler-sfc':
specifier: ^3.5.26
version: 3.5.26
@@ -197,13 +197,13 @@ importers:
version: 2.13.1
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@25.0.3)(typescript@5.9.3)
version: 10.9.2(@types/node@25.0.6)(typescript@5.9.3)
typescript:
specifier: ^5.9.3
version: 5.9.3
vitest:
specifier: ^4.0.16
version: 4.0.16(@types/node@25.0.3)(jiti@2.6.1)(jsdom@27.4.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
version: 4.0.16(@types/node@25.0.6)(jiti@2.6.1)(jsdom@27.4.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vue-component-type-helpers:
specifier: 3.2.2
version: 3.2.2
@@ -942,8 +942,8 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
'@iconify-json/carbon@1.2.15':
resolution: {integrity: sha512-9tW0nZY5QtKkMhuYzW09BM1345SyXNuA+gx2ub0j/fnfHOD5XVimMJ/D76H3tTez25NJbPYCLIQoFhvJc1HVBQ==}
'@iconify-json/carbon@1.2.16':
resolution: {integrity: sha512-R50UiC4NgPdnoSI3OzaZ/PzKYFVHAsAi3tENWgxvTxjYND3WHiUiG0qfJ0dPuaxwIW/l/2TnZwMPbbedd/+qIQ==}
'@iconify-json/cil@1.2.3':
resolution: {integrity: sha512-byZH2wJeow6DO/+mYGwjlG5Gm2ZARJ1Rpr7Ryh4EcAFU0rH+AWoKbAUrBZMh1EU7jsVI4bKM95SLSsrWQ44sEw==}
@@ -954,11 +954,11 @@ packages:
'@iconify-json/ion@1.2.6':
resolution: {integrity: sha512-JftEXKfjvJNn3SrGeSBrG/waRkjeTpLdMLNLwpAX4NgI14QgJoAeXEh2iZjNPqioAkeIgErX4Bi6mnFwpjk3BQ==}
'@iconify-json/material-symbols-light@1.2.50':
resolution: {integrity: sha512-Ehvmar2TPoYxmKgB5szeIMlmvA/mIc7gzUoQ5/AWFG+N6d4T53uCHwxnXFf1nXPWlpf0+cv26AXMJC6W5mkdrQ==}
'@iconify-json/material-symbols-light@1.2.51':
resolution: {integrity: sha512-S0LR5LITSeybVfVK1Hnbjcv7/JdqVKu2ZFhpLiU2KYBkU6TbsFy3jQnKjxPNZxffxNTnxpf11hWFsvkYZGyS7Q==}
'@iconify-json/material-symbols@1.2.50':
resolution: {integrity: sha512-71tjHR70h46LHtBFab3fAd2V/wPTO7JMV5lKnRn3IcF303LaFgAlO0BZeTJDcmCv9d0snRZmnoLZAJVD7/eisw==}
'@iconify-json/material-symbols@1.2.51':
resolution: {integrity: sha512-GkxlK8ocHi3NVVozaW62jm3qR9fNY3xX2penFtIRvoe1OtNhJ2KD4KRzv8x34pugMOAZYK8sALMcU30gDgCi1A==}
'@iconify-json/mdi-light@1.2.2':
resolution: {integrity: sha512-86UV9uyNve8zRFWiPrOrrDp9GDzsZM7plYV/on4VjgLLqXlyriuy541eHZB7LIOzTUyIPVli7QiUpBbTtBhsFw==}
@@ -1601,8 +1601,8 @@ packages:
'@types/node@24.10.3':
resolution: {integrity: sha512-gqkrWUsS8hcm0r44yn7/xZeV1ERva/nLgrLxFRUGb7aoNMIJfZJ3AC261zDQuOAKC7MiXai1WCpYc48jAHoShQ==}
'@types/node@25.0.3':
resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==}
'@types/node@25.0.6':
resolution: {integrity: sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q==}
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@@ -4738,7 +4738,7 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
'@iconify-json/carbon@1.2.15':
'@iconify-json/carbon@1.2.16':
dependencies:
'@iconify/types': 2.0.0
@@ -4754,11 +4754,11 @@ snapshots:
dependencies:
'@iconify/types': 2.0.0
'@iconify-json/material-symbols-light@1.2.50':
'@iconify-json/material-symbols-light@1.2.51':
dependencies:
'@iconify/types': 2.0.0
'@iconify-json/material-symbols@1.2.50':
'@iconify-json/material-symbols@1.2.51':
dependencies:
'@iconify/types': 2.0.0
@@ -5244,12 +5244,12 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 4.1.18
'@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
'@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
dependencies:
'@tailwindcss/node': 4.1.18
'@tailwindcss/oxide': 4.1.18
tailwindcss: 4.1.18
vite: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vite: 7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
'@trysound/sax@0.2.0': {}
@@ -5314,7 +5314,7 @@ snapshots:
dependencies:
undici-types: 7.16.0
'@types/node@25.0.3':
'@types/node@25.0.6':
dependencies:
undici-types: 7.16.0
@@ -5365,15 +5365,15 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@25.0.3)(lightningcss@1.30.2)(terser@5.39.0))(vue@3.5.26(typescript@5.9.3))':
'@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@25.0.6)(lightningcss@1.30.2)(terser@5.39.0))(vue@3.5.26(typescript@5.9.3))':
dependencies:
vite: 5.4.21(@types/node@25.0.3)(lightningcss@1.30.2)(terser@5.39.0)
vite: 5.4.21(@types/node@25.0.6)(lightningcss@1.30.2)(terser@5.39.0)
vue: 3.5.26(typescript@5.9.3)
'@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))':
'@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-beta.53
vite: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vite: 7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vue: 3.5.26(typescript@5.9.3)
'@vitest/expect@4.0.16':
@@ -5385,13 +5385,13 @@ snapshots:
chai: 6.2.1
tinyrainbow: 3.0.3
'@vitest/mocker@4.0.16(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
'@vitest/mocker@4.0.16(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
dependencies:
'@vitest/spy': 4.0.16
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vite: 7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
'@vitest/pretty-format@4.0.16':
dependencies:
@@ -5547,12 +5547,12 @@ snapshots:
transitivePeerDependencies:
- vue
'@vue-macros/devtools@0.4.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
'@vue-macros/devtools@0.4.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))':
dependencies:
sirv: 3.0.1
vue: 3.5.26(typescript@5.9.3)
optionalDependencies:
vite: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vite: 7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
transitivePeerDependencies:
- typescript
@@ -7593,14 +7593,14 @@ snapshots:
transitivePeerDependencies:
- typescript
ts-node@10.9.2(@types/node@25.0.3)(typescript@5.9.3):
ts-node@10.9.2(@types/node@25.0.6)(typescript@5.9.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
'@types/node': 25.0.3
'@types/node': 25.0.6
acorn: 8.14.1
acorn-walk: 8.3.4
arg: 4.1.3
@@ -7695,12 +7695,12 @@ snapshots:
optionalDependencies:
'@vueuse/core': 14.1.0(vue@3.5.26(typescript@5.9.3))
unplugin-combine@1.2.1(esbuild@0.27.1)(rollup@4.53.3)(unplugin@1.16.1)(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)):
unplugin-combine@1.2.1(esbuild@0.27.1)(rollup@4.53.3)(unplugin@1.16.1)(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)):
optionalDependencies:
esbuild: 0.27.1
rollup: 4.53.3
unplugin: 1.16.1
vite: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vite: 7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
unplugin-icons@22.5.0(@vue/compiler-sfc@3.5.26):
dependencies:
@@ -7748,7 +7748,7 @@ snapshots:
transitivePeerDependencies:
- vue
unplugin-vue-macros@2.14.5(@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3)))(esbuild@0.27.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-tsc@3.2.2(typescript@5.9.3))(vue@3.5.26(typescript@5.9.3)):
unplugin-vue-macros@2.14.5(@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3)))(esbuild@0.27.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-tsc@3.2.2(typescript@5.9.3))(vue@3.5.26(typescript@5.9.3)):
dependencies:
'@vue-macros/better-define': 1.11.4(vue@3.5.26(typescript@5.9.3))
'@vue-macros/boolean-prop': 0.5.5(vue@3.5.26(typescript@5.9.3))
@@ -7763,7 +7763,7 @@ snapshots:
'@vue-macros/define-render': 1.6.6(vue@3.5.26(typescript@5.9.3))
'@vue-macros/define-slots': 1.2.6(vue@3.5.26(typescript@5.9.3))
'@vue-macros/define-stylex': 0.2.3(vue@3.5.26(typescript@5.9.3))
'@vue-macros/devtools': 0.4.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
'@vue-macros/devtools': 0.4.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
'@vue-macros/export-expose': 0.3.5(vue@3.5.26(typescript@5.9.3))
'@vue-macros/export-props': 0.6.5(vue@3.5.26(typescript@5.9.3))
'@vue-macros/export-render': 0.3.5(vue@3.5.26(typescript@5.9.3))
@@ -7780,7 +7780,7 @@ snapshots:
'@vue-macros/short-vmodel': 1.5.5(vue@3.5.26(typescript@5.9.3))
'@vue-macros/volar': 0.30.15(typescript@5.9.3)(vue-tsc@3.2.2(typescript@5.9.3))(vue@3.5.26(typescript@5.9.3))
unplugin: 1.16.1
unplugin-combine: 1.2.1(esbuild@0.27.1)(rollup@4.53.3)(unplugin@1.16.1)(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
unplugin-combine: 1.2.1(esbuild@0.27.1)(rollup@4.53.3)(unplugin@1.16.1)(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
unplugin-vue-define-options: 1.5.5(vue@3.5.26(typescript@5.9.3))
vue: 3.5.26(typescript@5.9.3)
transitivePeerDependencies:
@@ -7862,11 +7862,11 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.2
vite-plugin-vue-layouts@0.11.0(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3)):
vite-plugin-vue-layouts@0.11.0(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3)):
dependencies:
debug: 4.4.0
fast-glob: 3.3.3
vite: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vite: 7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vue: 3.5.26(typescript@5.9.3)
vue-router: 4.6.4(vue@3.5.26(typescript@5.9.3))
transitivePeerDependencies:
@@ -7877,18 +7877,18 @@ snapshots:
svgo: 3.3.2
vue: 3.5.26(typescript@5.9.3)
vite@5.4.21(@types/node@25.0.3)(lightningcss@1.30.2)(terser@5.39.0):
vite@5.4.21(@types/node@25.0.6)(lightningcss@1.30.2)(terser@5.39.0):
dependencies:
esbuild: 0.25.10
postcss: 8.5.6
rollup: 4.52.4
optionalDependencies:
'@types/node': 25.0.3
'@types/node': 25.0.6
fsevents: 2.3.3
lightningcss: 1.30.2
terser: 5.39.0
vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2):
vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2):
dependencies:
esbuild: 0.27.1
fdir: 6.5.0(picomatch@4.0.3)
@@ -7897,7 +7897,7 @@ snapshots:
rollup: 4.53.3
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 25.0.3
'@types/node': 25.0.6
fsevents: 2.3.3
jiti: 2.6.1
lightningcss: 1.30.2
@@ -7905,7 +7905,7 @@ snapshots:
tsx: 4.19.2
yaml: 2.8.2
vitepress@1.6.4(@algolia/client-search@5.23.4)(@types/node@25.0.3)(fuse.js@7.1.0)(lightningcss@1.30.2)(postcss@8.5.6)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.39.0)(typescript@5.9.3):
vitepress@1.6.4(@algolia/client-search@5.23.4)(@types/node@25.0.6)(fuse.js@7.1.0)(lightningcss@1.30.2)(postcss@8.5.6)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.39.0)(typescript@5.9.3):
dependencies:
'@docsearch/css': 3.8.2
'@docsearch/js': 3.8.2(@algolia/client-search@5.23.4)(search-insights@2.17.3)
@@ -7914,7 +7914,7 @@ snapshots:
'@shikijs/transformers': 2.5.0
'@shikijs/types': 2.5.0
'@types/markdown-it': 14.1.2
'@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@25.0.3)(lightningcss@1.30.2)(terser@5.39.0))(vue@3.5.26(typescript@5.9.3))
'@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@25.0.6)(lightningcss@1.30.2)(terser@5.39.0))(vue@3.5.26(typescript@5.9.3))
'@vue/devtools-api': 7.7.2
'@vue/shared': 3.5.18
'@vueuse/core': 12.8.2(typescript@5.9.3)
@@ -7923,7 +7923,7 @@ snapshots:
mark.js: 8.11.1
minisearch: 7.1.2
shiki: 2.5.0
vite: 5.4.21(@types/node@25.0.3)(lightningcss@1.30.2)(terser@5.39.0)
vite: 5.4.21(@types/node@25.0.6)(lightningcss@1.30.2)(terser@5.39.0)
vue: 3.5.26(typescript@5.9.3)
optionalDependencies:
postcss: 8.5.6
@@ -7954,10 +7954,10 @@ snapshots:
- typescript
- universal-cookie
vitest@4.0.16(@types/node@25.0.3)(jiti@2.6.1)(jsdom@27.4.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2):
vitest@4.0.16(@types/node@25.0.6)(jiti@2.6.1)(jsdom@27.4.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2):
dependencies:
'@vitest/expect': 4.0.16
'@vitest/mocker': 4.0.16(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
'@vitest/mocker': 4.0.16(vite@7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2))
'@vitest/pretty-format': 4.0.16
'@vitest/runner': 4.0.16
'@vitest/snapshot': 4.0.16
@@ -7974,10 +7974,10 @@ snapshots:
tinyexec: 1.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.0.3
vite: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
vite: 7.3.1(@types/node@25.0.6)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.39.0)(tsx@4.19.2)(yaml@2.8.2)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 25.0.3
'@types/node': 25.0.6
jsdom: 27.4.0(postcss@8.5.6)
transitivePeerDependencies:
- jiti
+2
View File
@@ -34,6 +34,8 @@ message ContainerStat {
double cpuPercent = 2;
double memoryUsage = 3;
double memoryPercent = 4;
uint64 networkRxTotal = 5;
uint64 networkTxTotal = 6;
}
message LogFragment {