--node-args in PM2 while using json config mode - json

I have a question, that's how to pass "--node-args" arguments in PM2 while using json config mode, like this:
pm2 start --node-args="--debug=5858" myPm2Config.json
well, I know I can write the arguments into myPm2Config.json file, but I dont want to do this, because I want to make two startup command as "debug" and "production" mode for launch application, such as "pm2_run" and "pm2_debug", and "pm2_debug" command with --node-args argument and "pm2_run" not, and I dont want to make two "myPm2Config.json" files, because that means if something needs changed, I will need to change two json config files, so, is there any easy way to do it? thanks guys!

I have found the solution! that's use js config instead of json config.
first, I create a pm2.config.js file. (mark: file name must be end with .config.js)
//[pm2.config.js]
let config = {
apps : [{
name : "node_shells",
script : "./bin/www",
log_date_format : "YYYY-MM-DD HH:mm:SS",
log_file : "logs/pm2.log",
error_file : "logs/pm2-err.log",
out_file : "logs/pm2-out.log",
pid_file : "logs/pm2.pid",
watch : true,
ignore_watch : ["logs/*", "node_modules/*", "uploads/*"]
}]
}
let debug_mode = false;
for(let arg of process.argv) {
if(arg == '-debug') {
debug_mode = true;
break;
}
}
if(debug_mode) {
console.log('== launching in debug mode ==');
config.apps[0].node_args = "--debug=5858";
}
else {
console.log('== launching in production mode ==');
config.apps[0].node_args = " "; //*require! or it will always uses latest debug options
}
module.exports = config;
then, create two launch files: "pm2_run" and "pm2_debug".
#[pm2_run]
pm2 start pm2.config.js
#[pm2_debug]
pm2 start pm2.config.js -- -debug
now, it's easy to switch debug mode or production mode!

Related

Jest Unable to Parse Image Files

I'm trying to set up the configuration and mock files for jest to parse/ignore image files in order for the tests to pass. Just about every online resource leads me to the jest docs located: https://jestjs.io/docs/webpack#handling-static-assets
which tell you exactly how to handle the situation. However, not in my case. I've tried both options of creating mock files and using a transformer.
My current jest.config.js:
module.exports = {
projects: [
{
displayName: 'Unit',
testMatch: ["**/?(*.)+(spec|test).[tj]s?(x)"],
setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
testPathIgnorePatterns: ["<rootDir>/.next/", "<rootDir>/node_modules/", "<rootDir>/cypress/"],
moduleFileExtensions: ["js", "jsx", "ts", "tsx"],
moduleDirectories: ["node_modules", "bower_components", "shared"],
moduleNameMapper: {
"^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
'^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js'
},
// transform: {
// "\\.js$": "jest",
// "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/fileTransformer.js"
// //'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
// }
},
{
displayName: 'Pacts',
testMatch: ["**/?(*.)+(pacttest).[tj]s?(x)"],
testPathIgnorePatterns: ["<rootDir>/.next/", "<rootDir>/node_modules/", "<rootDir>/cypress/"],
watchPathIgnorePatterns: ["pact/logs/*", "pact/pacts/*"],
}
],
};
my fileMock.js:
module.exports = 'test-file-stub';
My styleMock.js:
module.exports = {};
My fileTransformer.js:
const path = require('path');
module.exports = {
process(src, filename, config, options) {
return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
},
};
//export default module.exports;
my directory:
I've been bouncing back and forth trying different options in the configurations but they pretty much all lead me to the same two errors, one when I try to use the transformer, and another without. With the transformer commented out, I get 2 errors thrown at the fileMock.js file:
TypeError: Invalid URL: test-file-stub
Failed to parse src "test-file-stub" on next/image
Both of these are referring to the suggested string for the mock. I initially thought that maybe the string was a placeholder for code to actually handle something. But after some reading, my understanding is that it's actually just supposed to be a string there. Perhaps it's a specific string dependent on my environment? And next/image is where I'm importing the image component from.
I'm prioritizing the mocking (please correct me if I'm wrong) because my understand is the mock tells jest to ignore the image file and proceed with the rest of the test while the transformer actually attempts to change the file type from js to jpg or png or whatever filetype the image is. However, I'm trying everything I can. When I try to the run the tests with the transformer portion uncommented I receive an error before any tests are even run stating:
TypeError: Jest: a transformm must export something.
(which is why there is a commented out export default statement.)
This is my first time ever attempting anything like this and I think I've reached a point where I cannot think of anything else to try. If anybody has experienced anything like this please lay some knowledge on me. I'm not sure if I have the mockfiles set up incorrectly or if it's something in the configurations.
Thanks.
I was able to work around this by creating an image URL here:
https://www.base64-image.de/
and replacing the "test-file-stub" string with the generated URL string.
module.exports = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV/TSotUHeyg4pChOogFURFHrUIRKoRaoVUHk0s/hCYNSYuLo+BacPBjserg4qyrg6sgCH6AuLk5KbpIif9LCi1iPDjux7t7j7t3gFAvMc0KjAGaXjFTibiYya6IwVeE0I9ujCAgM8uYlaQkPMfXPXx8vYvxLO9zf44uNWcxwCcSzzDDrBCvE09tVgzO+8QRVpRV4nPiUZMuSPzIdcXlN84FhwWeGTHTqTniCLFYaGOljVnR1IgniaOqplO+kHFZ5bzFWStVWfOe/IXhnL68xHWag0hgAYuQIEJBFRsooYIYrTopFlK0H/fwDzh+iVwKuTbAyDGPMjTIjh/8D353a+Unxt2kcBzoeLHtjyEguAs0arb9fWzbjRPA/wxc6S1/uQ5Mf5Jea2nRI6BnG7i4bmnKHnC5A/Q9GbIpO5KfppDPA+9n9E1ZoPcW6Fx1e2vu4/QBSFNXyRvg4BAYLlD2mse7Q+29/Xum2d8PVXFym6OczU4AAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQflCBkOKh9/wZ+SAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAAxJREFUCNdj+P//PwAF/gL+3MxZ5wAAAABJRU5ErkJggg==';

Mixing chromeOptions.args cli and protractor.conf.js

Currently we have a "standard" protractor.conf.js file in place. It has a chrome specific section which looks something like this:
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: [
'--no-sandbox', '--window-size=1280,1480', '--window-position=800,0'
],
prefs: {
intl: {accept_languages: defaults.LANGUAGE},
},
},
}
When we run the tests locally, everything is fine.
On our CI Infrastructure we add via CLI call the headless option:
protractor protractor.conf.js --capabilities.chromeOptions.args='headless'
First everything looked fine. The tests were running with headless chrome. But we recognized that the --window-size=1280,1480 specified in the config file was not used. We removed the --capabilities from the cli call and added the headless option directly into the protractor.conf.js.
Everything was now also working fine on the CI Infrastructure. This means the --window-size specified in the config was recognized and used.
Further tests showed, that if we want to use the CLI arguments, we would need to also add the window-size to the CLI call, to get it working as it should.
Now the question itself:
Why is this like that ? Shouldn't it be possible to add additional chromeOptions.args via CLI call ? Are the chromeOptions.args from the config no longer respected when passing some chromeOptions.args via CLI ?
Yes, The CLI values will overwrite the value in conf.js.
Below is the code snippet from launcher.ts
let initFn = function(configFile: string, additionalConfig: Config) {
let configParser = new ConfigParser();
if (configFile) { // this is the protractor conf.js
configParser.addFileConfig(configFile);
}
if (additionalConfig) { // this is all cli arguments
configParser.addConfig(additionalConfig);
}
Below is the addConfig from configParser.ts
public addConfig(argv: any): ConfigParser {
this.addConfig_(argv, process.cwd());
return this;
}
private addConfig_(additionalConfig: any, relativeTo: string): void {
// All filepaths should be kept relative to the current config location.
// This will not affect absolute paths.
['seleniumServerJar', 'chromeDriver', 'firefoxPath', 'frameworkPath', 'geckoDriver',
'onPrepare']
.forEach((name: string) => {
if (additionalConfig[name] && typeof additionalConfig[name] === 'string') {
additionalConfig[name] = path.resolve(relativeTo, additionalConfig[name]);
}
});
merge_(this.config_, additionalConfig);
}
let merge_ = function(into: any, from: any): any {
for (let key in from) {
if (into[key] instanceof Object && !(into[key] instanceof Array) &&
!(into[key] instanceof Function)) {
merge_(into[key], from[key]);
} else {
into[key] = from[key];
}
}
return into;
};
Because capabilities.chromeOptions.args is Array, thus the args value in conf.js will be overwrite by value from cli in merge_ function: into[key] = from[key];
Therefor, you have to specify all chromeOptions.args from cli by using multiple --capabilities.chromeOptions.args=xxx in cli, rather than
partial.
protractor conf.js \
--capabilities.chromeOptions.args='headless' \
--capabilities.chromeOptions.args='--no-sandbox' \
--capabilities.chromeOptions.args='--window-size=1280,1480'

Nodejs - trying to edit images' metadata with Exiftool

I am currently working on a NodeJS (Express) project to edit images' metadata with Exiftool.
To edit images' metadata with Exiftool, I've to create a JSON file containing all metadata to modify then execute the command :
exiftool -j=metadata.json pathToTheImage/image.jpg
The json file must look like that :
[{"SourceFile":"pathToTheImage/image.jpg","XMP-dc:Title":"Image's title"}]
Here's my code to do that :
const {exec} = require('child_process');
let fs = require('fs');
let uploadPath = "uploads";
let uploadName = "image.jpg";
...
app.post('/metadata/editor', (req, res) => {
let jsonToImport = [...];
fs.writeFileSync("metadata.json", JSON.stringify(jsonToImport));
exec('exiftool -j=metadata.json ' + uploadPath + '/' + uploadName, (error, stdout, stderr) => {
if (error) {
console.error(error);
return;
}
res.redirect('/metadata/checker/' + uploadName);
});
});
The problem is at the level of "writeFileSync/exec".
Independently these two lines work well, that's to say that if I've just the first line, the JSON file is well created. And if I've just the second ligne, image's metadata are well updated.
But when I execute this two lines together, the JSON file is well created but the exec line do "nothing" (or something that I can't determine).
This code uses synchronous functions, I've test it with asynchronous functions, this is the same behavior.
Currently, to do what I need, I must execute the code above to create the JSON file, then I must comment the writeFileSync line and I must reexecute the code to update image's metadata correctly.
It's really strange, I've try to read the JSON file content before the exec line but everything is ok. I've use asynchronous functions, with and without promise... there is nothing to do it doesn't work.
Thank you for your help.
I'll answer my own question:
The problem was that I use nodemon, however by default nodemon watches JSON files. But in my code I created a JSON file to use it right after. So, I created the JSON file correctly, nodemon sees it, and restarts the node server; the rest of the code does not run.
To fix this, I added an option to ignore the created files in my package.json:
"nodemonConfig": {
"ignore": [
"path/to/files/to/ingore/*"
]
}

Getting an Environment Variable in Terraform configuration?

I have two environment variables. One is TF_VAR_UN and another is TF_VAR_PW. Then I have a terraform file that looks like this.
resource "google_container_cluster" "primary" {
name = "marcellus-wallace"
zone = "us-central1-a"
initial_node_count = 3
master_auth {
username = ${env.TF_VAR_UN}
password = ${env.TF_VAR_PW}
}
node_config {
oauth_scopes = [
"https://www.googleapis.com/auth/compute",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring"
]
}
}
The two values I'd like to replace with the environment variables TF_VAR_UN and TF_VAR_PW are the values username and password. I tried what is shown above, with no success, and I've toyed around with a few other things but always get syntax issues.
I would try something more like this, which seems closer to the documentation.
variable "UN" {
type = string
}
variable "PW" {
type = string
}
resource "google_container_cluster" "primary" {
name = "marcellus-wallace"
zone = "us-central1-a"
initial_node_count = 3
master_auth {
username = var.UN
password = var.PW
}
node_config {
oauth_scopes = [
"https://www.googleapis.com/auth/compute",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring"
]
}
}
With the CLI command being the below.
TF_VAR_UN=foo TF_VAR_PW=bar terraform apply
The use of interpolation syntax throws warning with terraform v0.12.18. Now you don't need to use the interpolation syntax. You can just reference it as var.hello.
Caution :
One important thing to understand from a language standpoint is that, you cannot declare variables using environment variables. You can only assign values for declared variables in the script using environment varibles. For example, let's say you have the following .tf script
variable "hello" {
type=string
}
Now if the environment has a variable TF_VAR_hello="foobar", during runtime the variable hello will have the value "foobar". If you assign the variable without the declaration of the variable there will not be any effect.
You can do the following to get this working.
Declare the variable in terraform configuration that you want to use as environment Variable.
variable "db_password" { type= string }
In the resource section where you want to use this variable change it as
"db_password":"${var.db_password}"
Export the environment variable.
export TF_VAR_db_password="##password##"
terraform plan or terraform apply
Use a null_resource to execute a terminal command (read an environment variable), redirect output to a file, then read the file content:
resource "null_resource" "read_environment_var_value_via_cli" {
triggers = { always_run = "${timestamp()}" }
provisioner "local-exec" {
command = "echo $TF_VAR_UN > TF_VAR_UN.txt" # add gitignore
}
}
data "local_file" "temp_file" {
depends_on = [ null_resource.read_environment_var_value_via_cli]
filename = "${path.module}/TF_VAR_UN.txt"
}
# use value as desired
resource "google_container_cluster" "primary" {
master_auth {
username = data.local_file.temp_file.content # value of $TF_VAR_UN
..
}
}
Most of the providers use:
DefaultFunc: schema.EnvDefaultFunc("
https://github.com/terraform-providers/terraform-provider-infoblox/blob/master/infoblox/provider.go
https://github.com/terraform-providers/terraform-provider-openstack/blob/master/openstack/provider.go
...
Alternatively, you can replace the variables in the file itself using the envsubst utility in bash:
$ envsubst < main.tf > main.tf
Or using an intermediate file with variables and the final config on the output:
$ envsubst < main.txt > main.tf
! Variables for envsubst must be declared using export:
$ export MYVAR=1729
The variables in the source file must be of the form: $VARIABLE or ${VARIABLE}.
in order to use a variable it needs to be wrapped with ""
for example:
username = "${var.UN}"

How to use custom JSON attributes in Chef recipe

I am new to JSON. I have created custom JSON in AWS Opswork and trying to access it as an attribute in Chef recipe, but unfortunately its not catching the JSON values. My JSON file looks like..
{
"normal": {
"filebeat_minjar": {
"log_path" : "/var/log/*.log",
"hosts" : "Some Random Host ID",
"port" : 5000
}
}
}
and I am trying to catch it in recipe as,
log = node['filebeat_minjar']['log_path']
hosts = node['filebeat_minjar']['hosts']
port = node['filebeat_minjar']['port']
But it failed, I have also tried without 'normal'. I got the some []null class error.
Try this way,
log = node['normal']['filbeat_minjar']['log_path']
hosts = node['normal']['filbeat_minjar']['hosts']
port = node['normal']['filbeat_minjar']['port']
or
log = node.normal.filbeat_minjar.log_path
hosts = node.normal.filbeat_minjar.hosts
port = node.normal.filbeat_minjar.port
Json object is like a tree, the elements are the branches.
Hope this help
Your Chef code is correct, but you need to fix the JSON. You don't need the "normal": {...} in there, Chef and OpsWorks will handle that for you.
Following worked for me.
Custom JSON
{
"Production": {
"ApplicationLayer": {
"DockerTag" : "Version1"
}
}
}
Called from chef recipe.
node[:Production][:ApplicationLayer][:DockerTag]