Skip to content

feat(container): add support for healthCheck #249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,13 @@ custom:
custom_domains:
- my-container.some.domain.com

# Health check configuration
healthCheck:
type: http # Or tcp if you only want to check that the port is open
httpPath: /health
interval: 10s
failureThreshold: 3

# List of events to trigger the container
events:
- schedule:
Expand Down
22 changes: 22 additions & 0 deletions deploy/lib/createContainers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ const singleSource = require("../../shared/singleSource");
const secrets = require("../../shared/secrets");
const domainUtils = require("../../shared/domains");

function adaptHealthCheckToAPI(healthCheck) {
if (!healthCheck) {
return null;
}

// We need to find the type of the health check (tcp, http, ...)
// If httpPath is provided, we default to http, otherwise we default to tcp
let type = healthCheck.httpPath ? "http" : "tcp";
if (healthCheck.type) {
type = healthCheck.type;
}

return {
failure_threshold: healthCheck.failureThreshold,
interval: healthCheck.interval,
...(type === "http" && { http: { path: healthCheck.httpPath || "/" } }),
...(type === "tcp" && { tcp: {} }),
};
}

module.exports = {
createContainers() {
return BbPromise.bind(this)
Expand Down Expand Up @@ -104,6 +124,7 @@ module.exports = {
port: container.port,
http_option: container.httpOption,
sandbox: container.sandbox,
health_check: adaptHealthCheckToAPI(container.healthCheck),
};

// checking if there is custom_domains set on container creation.
Expand Down Expand Up @@ -143,6 +164,7 @@ module.exports = {
privacy: container.privacy,
port: container.port,
http_option: container.httpOption,
health_check: adaptHealthCheckToAPI(container.healthCheck),
};

this.serverless.cli.log(`Updating container ${container.name}...`);
Expand Down
2 changes: 1 addition & 1 deletion examples/container/my-container/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Flask
flask~=3.1.0
17 changes: 11 additions & 6 deletions examples/container/my-container/server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from flask import Flask
from flask import Flask, jsonify
import os
import json

DEFAULT_PORT = "8080"
MESSAGE = "Hello, World from Scaleway Container !"
Expand All @@ -9,12 +8,18 @@

@app.route("/")
def root():
return json.dumps({
return jsonify({
"message": MESSAGE
})

@app.route("/health")
def health():
# You could add more complex logic here, for example checking the health of a database...
return jsonify({
"status": "UP"
})

if __name__ == "__main__":
# Scaleway's system will inject a PORT environment variable on which your application should start the server.
port_env = os.getenv("PORT", DEFAULT_PORT)
port = int(port_env)
app.run(debug=True, host="0.0.0.0", port=port)
port = os.getenv("PORT", DEFAULT_PORT)
app.run(host="0.0.0.0", port=int(port))
4 changes: 4 additions & 0 deletions examples/container/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ custom:
# Local environment variables - used only in given function
env:
local: local
healthCheck:
httpPath: /health
interval: 10s
failureThreshold: 3
19 changes: 1 addition & 18 deletions shared/api/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
const https = require("https");
const axios = require("axios");

const { getApiManager } = require("./utils");
const accountApi = require("./account");
const domainApi = require("./domain");
const namespacesApi = require("./namespaces");
Expand All @@ -14,21 +12,6 @@ const runtimesApi = require("./runtimes");
// Registry
const RegistryApi = require("./registry");

const version = "0.4.13";

function getApiManager(apiUrl, token) {
return axios.create({
baseURL: apiUrl,
headers: {
"User-Agent": `serverless-scaleway-functions/${version}`,
"X-Auth-Token": token,
},
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
});
}

class AccountApi {
constructor(apiUrl, token) {
this.apiManager = getApiManager(apiUrl, token);
Expand Down
2 changes: 1 addition & 1 deletion shared/api/registry.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";

const { getApiManager } = require("./index");
const { getApiManager } = require("./utils");
const { manageError } = require("./utils");

class RegistryApi {
Expand Down
34 changes: 33 additions & 1 deletion shared/api/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
const axios = require("axios");
const https = require("https");

const version = "0.4.13";

const invalidArgumentsType = "invalid_arguments";

function getApiManager(apiUrl, token) {
return axios.create({
baseURL: apiUrl,
headers: {
"User-Agent": `serverless-scaleway-functions/${version}`,
"X-Auth-Token": token,
},
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
});
}

/**
* Custom Error class, to print an error message, and pass the Response if applicable
*/
Expand All @@ -20,13 +40,25 @@ function manageError(err) {
throw new Error(err);
}
if (err.response.data.message) {
throw new CustomError(err.response.data.message, err.response);
let message = err.response.data.message;

// In case the error is an InvalidArgumentsError, provide some extra information
if (err.response.data.type === invalidArgumentsType) {
for (const details of err.response.data.details) {
const argumentName = details.argument_name;
const helpMessage = details.help_message;
message += `\n${argumentName}: ${helpMessage}`;
}
}

throw new CustomError(message, err.response);
} else if (err.response.data.error_message) {
throw new CustomError(err.response.data.error_message, err.response);
}
}

module.exports = {
getApiManager,
manageError,
CustomError,
};
Loading