aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/customservices.md16
-rw-r--r--src/components/services/Portainer.vue122
2 files changed, 138 insertions, 0 deletions
diff --git a/docs/customservices.md b/docs/customservices.md
index 7e3e6b3..cb68e1d 100644
--- a/docs/customservices.md
+++ b/docs/customservices.md
@@ -125,3 +125,19 @@ For Prometheus you need to set the type to Prometheus and provide a url.
125 url: "http://192.168.0.151/" 125 url: "http://192.168.0.151/"
126 # subtitle: "Monitor data server" 126 # subtitle: "Monitor data server"
127``` 127```
128
129## Portainer
130
131This service displays info about the total number of containers managed by your Portainer instance.
132In order to use it, you must be using Portainer version 1.11 or later. Generate an access token from the UI and pass
133it to the apikey field.
134
135See https://docs.portainer.io/v/ce-2.11/user/account-settings#access-tokens
136
137```yaml
138- name: "Portainer"
139 logo: "assets/tools/sample.png"
140 url: "http://192.168.0.151/"
141 type: "Portainer"
142 apikey: "MY-SUPER-SECRET-API-KEY"
143```
diff --git a/src/components/services/Portainer.vue b/src/components/services/Portainer.vue
new file mode 100644
index 0000000..0af30eb
--- /dev/null
+++ b/src/components/services/Portainer.vue
@@ -0,0 +1,122 @@
1<template>
2 <Generic :item="item">
3 <template #indicator>
4 <div class="notifs">
5 <strong v-if="running > 0" class="notif running" title="Running">
6 {{ running }}
7 </strong>
8 <strong v-if="dead > 0" class="notif dead" title="Dead">
9 {{ dead }}
10 </strong>
11 <strong v-if="misc > 0" class="notif misc" title="Other (creating, paused, exited, etc.)">
12 {{ misc }}
13 </strong>
14 </div>
15 </template>
16 </Generic>
17</template>
18
19<script>
20import service from "@/mixins/service.js";
21import Generic from "./Generic.vue";
22
23export default {
24 name: "Portainer",
25 mixins: [service],
26 props: {
27 item: Object,
28 },
29 components: {
30 Generic,
31 },
32 data: () => ({
33 endpoints: null,
34 containers: null,
35 }),
36 computed: {
37 running: function () {
38 if (!this.containers) {
39 return "";
40 }
41 return this.containers.filter((container) => {
42 return container.State.toLowerCase() === "running";
43 }).length;
44 },
45 dead: function () {
46 if (!this.containers) {
47 return "";
48 }
49 return this.containers.filter((container) => {
50 return container.State.toLowerCase() === "dead";
51 }).length || 1;
52 },
53 misc: function () {
54 if (!this.containers) {
55 return "";
56 }
57 return this.containers.filter((container) => {
58 return container.State.toLowerCase() !== "running" && container.State.toLowerCase() !== "dead";
59 }).length;
60 },
61 },
62 created() {
63 this.fetchStatus();
64 },
65 methods: {
66 fetchStatus: async function () {
67 const headers = {
68 "X-Api-Key": this.item.apikey,
69 };
70
71 this.endpoints = await this.fetch("/api/endpoints", { headers })
72 .catch((e) => {
73 console.error(e);
74 });
75
76 let containers = [];
77 for (let endpoint of this.endpoints) {
78 const uri = `/api/endpoints/${endpoint.Id}/docker/containers/json?all=1`;
79 const endpointContainers = await this.fetch(uri, { headers })
80 .catch((e) => {
81 console.error(e);
82 });
83
84 containers = containers.concat(endpointContainers);
85 }
86
87 this.containers = containers;
88 },
89 },
90};
91</script>
92
93<style scoped lang="scss">
94.notifs {
95 position: absolute;
96 color: white;
97 font-family: sans-serif;
98 top: 0.3em;
99 right: 0.5em;
100
101 .notif {
102 display: inline-block;
103 padding: 0.2em 0.35em;
104 border-radius: 0.25em;
105 position: relative;
106 margin-left: 0.3em;
107 font-size: 0.8em;
108
109 &.running {
110 background-color: #4fd671;
111 }
112
113 &.dead {
114 background-color: #e51111;
115 }
116
117 &.misc {
118 background-color: #2ed0c8;
119 }
120 }
121}
122</style>