From 7a103148b2786e767b04881694840447bf1ae4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Wed, 27 Nov 2024 16:53:51 +0100 Subject: [PATCH 1/5] feat(ci): add simple publish workflow + add userAgent --- .github/workflows/publish.yml | 26 ++++++++++++++++++++++++++ package.json | 2 +- shared/api/index.js | 3 +++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..13552877 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,26 @@ +--- +name: "publish" + +on: + release: + types: [published] + +jobs: + release: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: "Set up Node" + uses: actions/setup-node@v4 + with: + node-version: 20.x + registry-url: "https://registry.npmjs.org" + + - name: "Install dependencies" + run: npm ci + + - name: "Publish package on NPM" + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/package.json b/package.json index e4399166..450aa5ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless-scaleway-functions", - "version": "0.4.12", + "version": "0.4.13", "description": "Provider plugin for the Serverless Framework v1.x which adds support for Scaleway Functions.", "main": "index.js", "author": "scaleway.com", diff --git a/shared/api/index.js b/shared/api/index.js index 68219baf..cc131102 100644 --- a/shared/api/index.js +++ b/shared/api/index.js @@ -14,10 +14,13 @@ 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({ From 2fe94cfcaefbaaf07998f44f374347dbac9c0d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Wed, 27 Nov 2024 16:57:06 +0100 Subject: [PATCH 2/5] fix: do the same for registry client --- shared/api/registry.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/shared/api/registry.js b/shared/api/registry.js index 9212d318..b0d7b243 100644 --- a/shared/api/registry.js +++ b/shared/api/registry.js @@ -1,20 +1,11 @@ "use strict"; -const axios = require("axios"); -const https = require("https"); +const { getApiManager } = require("./index"); const { manageError } = require("./utils"); class RegistryApi { constructor(registryApiUrl, token) { - this.apiManager = axios.create({ - baseURL: registryApiUrl, - headers: { - "X-Auth-Token": token, - }, - httpsAgent: new https.Agent({ - rejectUnauthorized: false, - }), - }); + this.apiManager = getApiManager(registryApiUrl, token); } listRegistryNamespace(projectId) { From 63ef10ae66c0a2ae80a61744ec2953461963695d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Wed, 27 Nov 2024 17:02:37 +0100 Subject: [PATCH 3/5] fix: updated changelog entry + lock --- CHANGELOG.md | 6 ++++++ package-lock.json | 27 +++++++++++++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e509ef08..7ea20198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.4.13 + +### Changed + +- HTTP calls to `api.scaleway.com` are now made with a custom user agent #245 + ## 0.4.12 ### Fixed diff --git a/package-lock.json b/package-lock.json index 025bf682..1d96c576 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "serverless-scaleway-functions", - "version": "0.4.12", + "version": "0.4.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "serverless-scaleway-functions", - "version": "0.4.12", + "version": "0.4.13", "license": "MIT", "dependencies": { "@serverless/utils": "^6.13.1", @@ -1793,9 +1793,10 @@ } }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", + "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -2494,10 +2495,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -5619,12 +5621,13 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { From 0a3d3963f174a50c2029b674f8a41e1f5b057763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Thu, 28 Nov 2024 11:46:07 +0100 Subject: [PATCH 4/5] feat(container): add healthCheck support --- README.md | 7 ++++ deploy/lib/createContainers.js | 22 ++++++++++++ .../container/my-container/requirements.txt | 2 +- examples/container/my-container/server.py | 17 ++++++---- examples/container/serverless.yml | 4 +++ shared/api/index.js | 19 +---------- shared/api/registry.js | 2 +- shared/api/utils.js | 34 ++++++++++++++++++- 8 files changed, 80 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index b745d50d..5149bc5c 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/deploy/lib/createContainers.js b/deploy/lib/createContainers.js index 02702b9c..9888d879 100644 --- a/deploy/lib/createContainers.js +++ b/deploy/lib/createContainers.js @@ -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) @@ -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. @@ -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}...`); diff --git a/examples/container/my-container/requirements.txt b/examples/container/my-container/requirements.txt index e3e9a71d..f08dea74 100644 --- a/examples/container/my-container/requirements.txt +++ b/examples/container/my-container/requirements.txt @@ -1 +1 @@ -Flask +flask~=3.1.0 diff --git a/examples/container/my-container/server.py b/examples/container/my-container/server.py index e5fea352..b9f8bf8c 100644 --- a/examples/container/my-container/server.py +++ b/examples/container/my-container/server.py @@ -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 !" @@ -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)) diff --git a/examples/container/serverless.yml b/examples/container/serverless.yml index 4964b24e..2f7a89e7 100644 --- a/examples/container/serverless.yml +++ b/examples/container/serverless.yml @@ -32,3 +32,7 @@ custom: # Local environment variables - used only in given function env: local: local + healthCheck: + httpPath: /health + interval: 10s + failureThreshold: 3 diff --git a/shared/api/index.js b/shared/api/index.js index cc131102..375ebd0a 100644 --- a/shared/api/index.js +++ b/shared/api/index.js @@ -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"); @@ -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); diff --git a/shared/api/registry.js b/shared/api/registry.js index b0d7b243..b0a54f45 100644 --- a/shared/api/registry.js +++ b/shared/api/registry.js @@ -1,6 +1,6 @@ "use strict"; -const { getApiManager } = require("./index"); +const { getApiManager } = require("./utils"); const { manageError } = require("./utils"); class RegistryApi { diff --git a/shared/api/utils.js b/shared/api/utils.js index 70116ecd..15b95bb8 100644 --- a/shared/api/utils.js +++ b/shared/api/utils.js @@ -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 */ @@ -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, }; From 38b06e0ececf12fdf8dcc30c44208ec98193d3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Thu, 28 Nov 2024 11:55:44 +0100 Subject: [PATCH 5/5] fix: format --- shared/api/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/api/utils.js b/shared/api/utils.js index 15b95bb8..2413649e 100644 --- a/shared/api/utils.js +++ b/shared/api/utils.js @@ -41,7 +41,7 @@ function manageError(err) { } if (err.response.data.message) { 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) {