Proper way to test Chalice app with env variables - pytest

I have a Chalice API project with the following config under .chalice/config.json:
{
"version": "2.0",
"app_name": "my-app",
"manage_iam_role": true,
"stages": {
"local": {
"api_gateway_stage": "local",
"environment_variables": {
"STAGE_NAME": "local",
"MY_ENV_VAR": "value-local"
}
},
"staging": {
"api_gateway_stage": "stg",
"environment_variables": {
"STAGE_NAME": "staging",
"MY_ENV_VAR": "value-staging"
}
},
"production": {
"api_gateway_stage": "prod",
"environment_variables": {
"STAGE_NAME": "production",
"MY_ENV_VAR": "value-prod"
}
}
}
}
The app code uses some env variables like MY_ENV_VAR, loaded with os.environ["MY_ENV_VAR"].
The tests look like the following (let's say in a tests/test_my_endpoint.py file):
from http import HTTPStatus
from app import app
from chalice.test import Client
def test_my_endpoint() -> None:
with Client(app, stage_name="local") as client:
response = client.http.get("/my-endpoint")
assert response.status_code == HTTPStatus.OK
But it seems stage_name="local" doesn't mean variables for the stage local are loaded for the tests, so during tests execution, env variables are not found and tests fail. I'm not sure how setting this argument for chalice.test.Client is useful then.
For testing locally, I can always load environment variables locally, but for CI/CD, I would like the tests to use the env variable of a stage defined in .chalice/config.json. Chalice documentation isn't very clear about the good practice here, and about how stage_name argument works exactly.
How can I fix that, and generally speaking what is the proper way to create tests for a Chalice app using the env variables?

Related

MongoRuntimeError Unable to parse with URL

I am trying to connect to a mongo db using the nodejs mongo driver and I'm doing this in a cypress project. I get the error in the title. Below is the simplified version of my code.
import {MongoClient} from 'mongodb';
export class SomeRepository {
static insertSomething(): void {
// Error in the line below: MongoRuntimeError Unable to parse localhost:27017 with URL
const client = new MongoClient('mongodb://localhost:27017');
}
}
Mongodb is running because I can connect from the terminal. Also tried replacing localhost with 127.0.0.1 and adding the authSource parameter to the connection string.
The reason I'm mentioning cypress is because in a simple node project that only connects to mongodb everything works as expected. Package.json below
{
"name": "e2e",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"cypress": "10.8.0",
"cypress-wait-until": "1.7.2",
"headers-utils": "3.0.2",
"mongodb": "4.10.0",
"otplib": "12.0.1",
"pg": "8.7.3",
"pg-native": "3.0.1",
"typescript": "4.9.3"
}
}
The error is in the way you are passing the url, it is necessary that you follow a pattern, in mongodb to connect you need to have this pattern that I will pass below:
Format:
mongodb://<user>:<password>#<host>
Format with filled values:
mongodb://root:mypassword#localhost:27017/
The reason it’s not working is because you’re calling a NodeJS library in a cypress test. Cypress tests run inside a browser and cannot run nodejs libraries.
If you wanted to execute nodejs code in cypress you must create a cypress task https://docs.cypress.io/api/commands/task#Syntax
// cypress.config.js
import { SomeRepository } from ‘./file/somewhere’
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on(‘task’, {
insertSomething() {
return SomeRepository.insertSomething();
}
}
}
}
})
// to call in a cypress test
it(‘test’, function () {
cy.task(‘insertSomething’).then(value => /* do something */);
}
});

JupyterHub - log current user

I use a custom logger to log who is currently doing any kind of stuff in Jupyterhub.
logging_config: dict = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"company": {
"()": lambda: MyFormatter(user=os.environ.get("JUPYTERHUB_USER", "Unknown"))
},
},
....
c.Application.logging_config = logging_config
Output:
{"asctime": "2022-06-29 14:13:43,773", "level": "WARNING", "name": "JupyterHub", "message": "Updating Hub route http://127.0.0.1:8081 \u2192 http://jupyterhub:8081", "user": "Unknown"
The logger itself works fine, but I am not able to log who was performing the action. In the Image I start, there is a JUPYTERHUB_USER env variable available. This seems to get passed from JupyterHub ( I don´t know how this is done exactly). But in JupyterHub I don´t have this variable available.
Is there a way to use it in JupyterHub, not just in the jupyterLab container?
This doesn't get you all the way there but it's a start - we add extra pod annotations/labels through KubeSpawner's extra_annotations using the cluster_options hook (see our helm chart for our complete daskhub setup):
dask-gateway:
gateway:
extraConfig:
optionHandler: |
from dask_gateway_server.options import Options, String, Select, Mapping, Float, Bool
from math import ceil
def cluster_options(user):
def option_handler(options):
extra_annotations = {
"hub.jupyter.org/username": user.name
}
default_extra_labels = {
"hub.jupyter.org/username": user.name,
}
return Options(
Select(
...
),
...,
handler=option_handler,
)
c.Backend.cluster_options = cluster_options
You can then poll pods with these labels to get real time usage. There may be a more direct way to do this though - not sure.

Isolation between kubernetes.admission policies in OPA

I use the vanilla Open Policy Agent as a deployment on Kubernetes for handling admission webhooks.
The behavior of multiple policies evaluation is not clear to me, see this example:
## policy-1.rego
package kubernetes.admission
check_namespace {
# evaluate to true
namespaces := {"namespace1"}
namespaces[input.request.namespace]
}
check_user {
# evaluate to false
users := {"user1"}
users[input.request.userInfo.username]
}
allow["yes - user1 and namespace1"] {
check_namespace
check_user
}
.
## policy-2.rego
package kubernetes.admission
check_namespace {
# evaluate to false
namespaces := {"namespace2"}
namespaces[input.request.namespace]
}
check_user {
# evaluate to true
users := {"user2"}
users[input.request.userInfo.username]
}
allow["yes - user2 and namespace12] {
check_namespace
check_user
}
.
## main.rego
package system
import data.kubernetes.admission
main = {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": response,
}
default uid = ""
uid = input.request.uid
response = {
"allowed": true,
"uid": uid,
} {
reason = concat(", ", admission.allow)
reason != ""
}
else = {"allowed": false, "uid": uid}
.
## example input
{
"apiVersion": "admission.k8s.io/v1beta1",
"kind": "AdmissionReview",
"request": {
"namespace": "namespace1",
"userInfo": {
"username": "user2"
}
}
}
.
## Results
"allow": [
"yes - user1 and namespace1",
"yes - user2 and namespace2"
]
It seems that all of my policies are being evaluated as just one flat file, but i would expect that each policy will be evaluated independently from the others
What am I missing here?
Files don't really mean anything to OPA, but packages do. Since both of your policies are defined in the kubernetes.admission module, they'll essentially be appended together as one. This works in your case only due to one of the check_user and check_namespace respectively evaluating to undefined given your input. If they hadn't you would see an error message about conflict, since complete rules can't evalutate to different results (i.e. allow can't be both true and false).
If you rather use a separate package per policy, like, say kubernetes.admission.policy1 and kubernetes.admission.policy2, this would not be a concern. You'd need to update your main policy to collect an aggregate of the allow rules from all of your policies though. Something like:
reason = concat(", ", [a | a := data.kubernetes.admission[policy].allow[_]])
This would iterate over all the sub-packages in kubernetes.admission and collect the allow rule result from each. This pattern is called dynamic policy composition, and I wrote a longer text on the topic here.
(As a side note, you probably want to aggregate deny rules rather than allow. As far as I know, clients like kubectl won't print out the reason from the response unless it's actually denied... and it's generally less useful to know why something succeeded rather than failed. You'll still have the OPA decision logs to consult if you want to know more about why a request succeeded or failed later).

Jest: Cannot use import statement outside a module

I got an error when I run test using Jest, I tried to fix this error for 2 hours. But, I couldn't fix it. My module is using gapi-script package and error is occurred in this package. However, I don't know why this is occurred and how to fix it.
jest.config.js
module.exports = {
"collectCoverage": true,
"rootDir": "./",
"testRegex": "__tests__/.+\\.test\\.js",
"transform": {
'^.+\\.js?$': "babel-jest"
},
"moduleFileExtensions": ["js"],
"moduleDirectories": [
"node_modules",
"lib"
]
}
babel.config.js
module.exports = {
presets: [
'#babel/preset-env',
]
};
methods.test.js
import methods, { typeToActions } from '../lib/methods';
methods.js
import { gapi } from "gapi-script";
...
Error Message
C:\haram\github\react-youtube-data-api\node_modules\gapi-script\index.js:1
({"Object.":function(module,exports,require,__dirname,__filename,global,jest){import
{ gapi, gapiComplete } from './gapiScript';
SyntaxError: Cannot use import statement outside a module
What is wrong with my setting?
As of this writing, Jest is in the process of providing support for ES6 modules. You can track the progress here:
https://jestjs.io/docs/en/ecmascript-modules
For now, you can eliminate this error by running this command:
node --experimental-vm-modules node_modules/.bin/jest
instead of simply:
jest
Be sure to check the link before using this solution.
I solved this with the help of Paulo Coghi's answer to another question -
Does Jest support ES6 import/export?
Step 1:
Add your test environment to .babelrc in the root of your project:
{
"env": {
"test": {
"plugins": ["#babel/plugin-transform-modules-commonjs"]
}
}
}
Step 2:
Install the ECMAScript 6 transform plugin:
npm install --save-dev #babel/plugin-transform-modules-commonjs
Jest needs babel to work with modules.
For the testing alone, you do not need jest.config.js, just name the testfiles xxx.spec.js or xxx.test.js or put the files in a folder named test.
I use this babel.config.js:
module.exports = function (api) {
api.cache(true)
const presets = [
"#babel/preset-env"
]
return {
presets
}
}
Adding "type": "module" in package.json or using mjs as stated in other answers is not necessary when your setup is not too complicated.
I have also faced the same issue and this was resolved by adding following command-line option as a environment variable.
export NODE_OPTIONS=--experimental-vm-modules npx jest //linux
setx NODE_OPTIONS "--experimental-vm-modules npx jest" //windows
Upgrading Jest (package.json) to version 29 (latest as of now) solved this problem for me.

Drools stateful session per request

We are trying to use Drool as our rule engine service. What we done till now is listed below
Deployed workbench 7.2.Final
Deployed KIE server 7.2.0.Final
Configured some data objects, rules, deployed the changes to KIE server and we are able to execute the rule using rest API
Most of our requirements satisfied by stateless session (Give a set of data, execute the rule and return the data, that's it) . But using stateless we have to compromise many of the important features provided by Drools stateful session.
So we are trying to use stateful session per request. Which means the session should get disposed as soon as the request end. Also, parallel request should not interfere each other even if the session name is same
We found about container runtime strategy configuration (Workbench > Deploy > {any container} > Process Configuration > Runtime strategy)
But even after configure the container strategy to Per Request, it still behave same as Singleton (the session is not getting disposed after each request)
Few place we read it as, run time strategy only implemented in jBPM
The way we make request to KIE server is shown below
Request: POST {HOST}/kie-server/services/rest/server/containers/instances/TestRequest_1.0.4
{
"lookup": "ab-session", //stateful session
"commands": [
{
"insert": {
"out-identifier": "125",
"object": {
"com.myteam.testrequest.Product": {
"id": "123",
"name": "Hoo Hoo",
"count": 0
}
},
"return-object": "true"
}
},
{
"insert": {
"out-identifier": "126",
"object": {
"com.myteam.testrequest.Product": {
"id": "123",
"name": "Hoo Hoo",
"count": 0
}
},
"return-object": "true"
}
},
{"fire-all-rules": "hf2"}
]
}
We need help in achieving this requirement. Also, please help understand if we done something wrong
In kmodule.xml you may try to add "prototype" scope, because default is "singleton":
<ksession name="SessionName" type="stateful" default="false" clockType="realtime" scope="prototype"/>