Skip to content
This repository was archived by the owner on Jun 7, 2025. It is now read-only.

Commit b48228b

Browse files
committed
adding email notification when deploying container
1 parent 7aa84b0 commit b48228b

File tree

5 files changed

+90
-63
lines changed

5 files changed

+90
-63
lines changed

Readme.md

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ If you use Github or Dockerhub to send your webhooks you can protect them, it'll
6464

6565
## Mailing
6666
If you want to be notified when an error occurs when the container is redeployed you can add a mail
67-
|Name|Type|Description|
68-
|----|----|-----------|
69-
|`docker-ci.email`|`string (Optional)`|Set a specific user email to be notified only for this user when an error occurs|
70-
67+
|Name|Description|
68+
|----|-----------|
69+
|`docker-ci.email`|Set a specific user email to be notified only for this user when an error occurs|
70+
|`docker-ci.email.notify`|Docker-ci will send email notification when container is redeployed|
7171
## Example
7272

7373
### docker-compose.yml of docker-ci app
@@ -106,6 +106,9 @@ services:
106106
- "docker-ci.password=MyPasswordOrToken" #Registry Password or token
107107
- "docker-ci.username=MyRegistryUsername"
108108
- "docker-ci.auth-server=MyRegistryURL" #Ex for Github Registry : https://ghcr.io or https://docker.pkg.github.com
109+
110+
- "[email protected]" #Notify this email when errors occured when redeploying this container
111+
- "docker-ci.email.notify=true" #Notify the above email when this container is redeployed
109112
```
110113
111114
### docker-publish in the github repo :
@@ -168,12 +171,13 @@ jobs:
168171
```
169172
170173
## All Labels :
171-
|Name|Type|Description|
172-
|----|----|-----------|
173-
| `docker-ci.enable`|`boolean`|Enable CI for this container, an endpoint will be created for this container and whenever it will be called the container image will be repulled and the container will be recreated (total update of the container)|
174-
| `docker-ci.repo-url`|`string`|Url of the image repo|
175-
| `docker-ci.name`|`string (Optional)`|Set a custom name for the endpoint, by default it is the name of the container|
176-
| `docker-ci.username`|`string (Optional)`|Set a username for the docker package registry auth|
177-
| `docker-ci.password`|`string (Optional)`|Set a password or a token for the docker package registry auth|
178-
| `docker-ci.auth-server`|`string (Optional)`|Set an auth server for the docker package registry auth|
179-
|`docker-ci.email`|`string (Optional)`|Set a specific user email to be notified only for this user when an error occurs|
174+
|Name|Description|
175+
|----|-----------|
176+
| `docker-ci.enable`|Enable CI for this container, an endpoint will be created for this container and whenever it will be called the container image will be repulled and the container will be recreated (total update of the container)|
177+
| `docker-ci.repo-url`|Url of the image repo|
178+
| `docker-ci.name`|Set a custom name for the endpoint, by default it is the name of the container|
179+
| `docker-ci.username`|Set a username for the docker package registry auth|
180+
| `docker-ci.password`|Set a password or a token for the docker package registry auth|
181+
| `docker-ci.auth-server`|Set an auth server for the docker package registry auth|
182+
|`docker-ci.email`|Set a specific user email to be notified only for this user when an error occurs|
183+
|`docker-ci.email.notify`|Notify the above email that the container has been updated|

src/docker.ts

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ export class DockerManager {
5858
public async pruneImages(): Promise<void> {
5959
await this._docker.pruneImages();
6060
}
61+
/**
62+
* Get the combined length of all docker images
63+
*/
6164
public async getImageLength(): Promise<number> {
6265
return (await this._docker.listImages()).length;
6366
}
@@ -66,36 +69,31 @@ export class DockerManager {
6669
* Pull an image from its tag
6770
* @returns true in case of success
6871
*/
69-
public async pullImage(imageName: string, containerLabels: DockerCiLabels): Promise<boolean> {
70-
try {
71-
const imageInfos = await this.getImage(imageName).inspect();
72-
this._logger.info("Pulling : ", ...imageInfos.RepoTags);
73-
let authConf: DockerImagesModel.PullImageAuth | undefined;
74-
if (containerLabels["docker-ci.username"] && containerLabels["docker-ci.password"] && containerLabels["docker-ci.auth-server"]) {
75-
authConf = {
76-
username: containerLabels["docker-ci.username"],
77-
password: containerLabels["docker-ci.password"],
78-
serveraddress: containerLabels["docker-ci.auth-server"]
79-
}
72+
public async pullImage(imageName: string, containerLabels: DockerCiLabels) {
73+
const imageInfos = await this.getImage(imageName).inspect();
74+
this._logger.info("Pulling : ", ...imageInfos.RepoTags);
75+
let authConf: DockerImagesModel.PullImageAuth | undefined;
76+
if (containerLabels["docker-ci.username"] && containerLabels["docker-ci.password"] && containerLabels["docker-ci.auth-server"]) {
77+
authConf = {
78+
username: containerLabels["docker-ci.username"],
79+
password: containerLabels["docker-ci.password"],
80+
serveraddress: containerLabels["docker-ci.auth-server"]
8081
}
81-
let data: any[] = [];
82-
for(const tag of imageInfos.RepoTags)
83-
data.push(await this._docker.pull(tag, authConf && { authconfig: authConf }));
84-
const message: IncomingMessage = data[0];
85-
message?.on("data", (data) => {
86-
try {
87-
this._logger.log(JSON.parse(data));
88-
} catch (e) {
89-
this._logger.log(data.toString())
90-
}
91-
});
92-
return new Promise<boolean>((resolve, reject) => {
93-
message?.on("end", () => resolve(true)) || resolve(true);
94-
});
95-
} catch (e) {
96-
this._logger.error("Error pulling image", e);
97-
return false;
9882
}
83+
let data: any[] = [];
84+
for(const tag of imageInfos.RepoTags)
85+
data.push(await this._docker.pull(tag, authConf && { authconfig: authConf }));
86+
const message: IncomingMessage = data[0];
87+
message?.on("data", (data) => {
88+
try {
89+
this._logger.log(JSON.parse(data));
90+
} catch (e) {
91+
this._logger.log(data.toString())
92+
}
93+
});
94+
return new Promise<boolean>((resolve, reject) => {
95+
message?.on("end", () => resolve(true)) || resolve(true);
96+
});
9997
}
10098

10199
/**

src/index.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,22 @@ class App {
9393
*/
9494
private async _onUrlTriggered(id: string) {
9595
let containerInfos: ContainerInspectInfo;
96-
const previousImageLength = await this._dockerManager.getImageLength();
96+
let previousImageLength: number;
97+
let labels: DockerCiLabels;
9798
try {
98-
containerInfos = await this._dockerManager.getContainer(id).inspect();
99-
if (!await this._dockerManager.pullImage(containerInfos.Image, containerInfos.Config.Labels))
100-
throw "Error Pulling Image";
101-
if (previousImageLength < await this._dockerManager.getImageLength())
99+
previousImageLength = await this._dockerManager.getImageLength();
100+
containerInfos = await this._dockerManager.getContainer(id).inspect();
101+
labels = containerInfos?.Config?.Labels;
102+
} catch (e) {
103+
this._sendErrorMail(null, "Impossible to get container infos " + id);
104+
return;
105+
}
106+
try {
107+
await this._dockerManager.pullImage(containerInfos.Image, containerInfos.Config.Labels);
108+
if (previousImageLength < await this._dockerManager.getImageLength()) {
102109
await this._dockerManager.recreateContainer(id, containerInfos.Image);
110+
labels?.['docker-ci.email.notify'] && this._mailer.sendNotifyMail(id, labels?.['docker-ci.email']);
111+
}
103112
else
104113
this._logger.info("Image already updated, no container restart needed");
105114
} catch (e) {
@@ -113,7 +122,7 @@ class App {
113122
}
114123
}
115124

116-
private async _sendErrorMail(infos: ContainerInspectInfo, error: string) {
125+
private async _sendErrorMail(infos: ContainerInspectInfo | null, error: string) {
117126
try {
118127
const labels: DockerCiLabels = infos?.Config?.Labels;
119128
if (labels?.['docker-ci.email']?.includes("@"))

src/models/docker-ci-labels.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export interface DockerCiLabels {
44
"docker-ci.repo-url"?: string;
55

66
"docker-ci.email"?: string;
7+
"docker-ci.email.notify"?: boolean;
78

89
"docker-ci.password"?: string;
910
"docker-ci.username"?: string;

src/utils/mailer.ts

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,29 +37,44 @@ class MailerManager {
3737
return this;
3838
}
3939

40-
public async sendErrorMail(container: string, mailDest: string, ...error: any[]) {
40+
public async sendErrorMail(container: string, mailDest?: string, ...error: any[]) {
4141
if (!this._healthy)
4242
this._logger.log("No email sent, email system disabled from conf");
4343
else
4444
this._logger.log("Sending error email to :", mailDest, mailConf.mail_admin);
45-
46-
await this._transporter.sendMail({
47-
from: mailConf.mail_addr,
48-
to: mailConf.mail_admin,
49-
subject: `Erreur lors du déploiement de : ${container.substr(1)}`,
50-
html: `
51-
<h1 style='text-align: center'>Logs : </h1>
52-
<p>${error.join(" ")}</p>
53-
`
54-
}).catch(e => this._logger.info("Error sending error mail"));
55-
56-
if (mailDest) {
45+
try {
5746
await this._transporter.sendMail({
5847
from: mailConf.mail_addr,
59-
to: mailDest,
48+
to: mailConf.mail_admin,
6049
subject: `Erreur lors du déploiement de : ${container.substr(1)}`,
61-
html: "Les administrateurs de ce serveur ont été notifiés",
62-
}).catch(e => this._logger.info("Error sending error mail"));
50+
html: `
51+
<h1 style='text-align: center'>Logs : </h1>
52+
<p>${error.join(" ")}</p>
53+
`
54+
});
55+
if (mailDest)
56+
await this._transporter.sendMail({
57+
from: mailConf.mail_addr,
58+
to: mailDest,
59+
subject: `Erreur lors du déploiement de : ${container.substr(1)}`,
60+
html: "Les administrateurs de ce serveur ont été notifiés",
61+
});
62+
} catch (e) {
63+
this._logger.error("Error sending error mail", e)
64+
}
65+
}
66+
67+
public async sendNotifyMail(container: string, mailDest: string) {
68+
try {
69+
const date = new Date();
70+
await this._transporter.sendMail({
71+
from: mailConf.mail_addr,
72+
to: mailConf.mail_admin,
73+
subject: `Ton projet : ${container.substr(1)} s'est déployé avec succès !`,
74+
html: `Ton projet : ${container.substr(1)} s'est déployé avec succès à ${date.getHours()}:${date.getMinutes()} le ${date.getDate()}/${date.getMonth()}/${date.getFullYear()} !`
75+
});
76+
} catch (e) {
77+
this._logger.error("Error sending error mail", e)
6378
}
6479
}
6580
}

0 commit comments

Comments
 (0)