// child process currently doesn't natively support promises, however can wrap the
// methods with promises using built-in promisify util
const { promisify } = require("util");
const execAsync = promisify(require("child_process").exec);
/**
* Deletes all keys from cache that start with cacheCode param. If
* cacheCode is not defined, deletes all cache.
* @param {String} cacheCode.
* @throws {Error}
* @returns {String} how many keys effected
*/
async function flushFromCache(cacheCode) {
const pattern = cacheCode ? ` --pattern ${cacheCode}_*` : "";
const command = `redis-cli --scan${pattern} | xargs redis-cli del`;
try {
const execResult = await execAsync(command);
// result looks like { stdout: string, stderr: string }.
// stdout is populated when everything goes nicely.
// stderr is populated if error occurred while running command
if (execResult.stderr) {
throw new Error(execResult.stderr);
}
// When there are keys deleted the result looks like:
// (integer) 2
// When there are no keys found the result looks like:
// ERR wrong number of arguments for 'del' command
let result;
if (execResult.stdout.includes("ERR")) {
result = `No ${cacheCode ? `'${cacheCode}'` : ""} keys to flush`;
} else {
result = `${execResult.stdout.trim()} ${
cacheCode ? `'${cacheCode}'` : ""
} keys flushed`;
}
return result;
} catch (e) {
// this error can occur if the command is bad
throw new Error(e.message);
}
}
module.exports = {
flushFromCache
};
Sunday, January 17, 2021
Node.js exec with promisify
Friday, December 4, 2020
Docker Compose - change COMPOSE_HTTP_TIMEOUT and verify env_file environment variable configuration options
I occasionally get the below error while running docker-compose on my local VM or while running on Jenkins.
ERROR: for cherryshoe_test_ta_run_609fc9bdd396 UnixHTTPConnectionPool(host='localhost', port=None): Read timed out. (read timeout=60)
An HTTP request took too long to complete. Retry with --verbose to obtain debug information.
If you encounter this issue regularly because of slow network conditions, consider setting COMPOSE_HTTP_TIMEOUT to a higher value (current value: 60).
Environment:
CentOS Linux release 7.3.1611 (Core)
Docker version 19.03.1, build 74b1e89
You can verify this environment variable with the docker-compose config command, which prints the resolved application config to the terminal, by accessing the environment variable in the docker-compose file. A sample docker-compose yml below is used to test this out:
version: "3"
services:
cherryshoe:
image: "testconfig:${COMPOSE_HTTP_TIMEOUT}"
The config command verifies the default COMPOSE_HTTP_TIMEOUT value of 60:
$ docker-compose -f docker-compose-cs.yml config
services:
cherryshoe:
image: testconfig:60
version: '3.0'
After using the env_file to set the value to a higher number,
.env
#.env file for docker-compose. Use same file for all environments.
# set COMPOSE_HTTP_TIMEOUT at docker instance level and not inside docker image.
COMPOSE_HTTP_TIMEOUT=120
run the config command again to verify it:
$ docker-compose -f docker-compose-cs.yml config
services:
cherryshoe:
image: testconfig:120
version: '3.0'
This article was helpful.
Tuesday, November 10, 2020
Docker - enabling systemd on centos:7
I'm experimenting with enabling systemd on the docker centos:7 image. Here's a simple example getting the centos7 systemd image set up, then building a docker image with nginx using that, and then running it.
Environment:
CentOS Linux release 7.3.1611 (Core)
Docker version 19.03.1, build 74b1e89
centos7-systemd.Dockerfile
# systemd is included with centos:7 but not enabled by default - START
# https://hub.docker.com/_/centos:
FROM centos:7
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]
# systemd is included with centos:7 but not enabled by default - END
centos7-systemd-nginx.Dockerfile
# This dockerfile is used when running cypress during a commit to the remote repo
FROM local/centos7-systemd
# install yum dependencies ansible
RUN yum install -y epel-release \
&& yum install -y nginx && systemctl enable nginx.service
EXPOSE 80
CMD ["/usr/sbin/init"]
Build images:
docker build --rm -t local/centos7-systemd -f centos7-systemd.Dockerfile .
docker build -t local/centos7-systemd-nginx -f centos7-systemd-nginx.Dockerfile .
Run container:
docker run -it --rm -d -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /run -p 80:80 --name web local/centos7-systemd-nginx
Test that nginx is running:
curl http://localhost/index.html
It returned an error HTML page, but I know nginx is running!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>The page is not found</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
/*<![CDATA[*/
body {
background-color: #fff;
color: #000;
font-size: 0.9em;
font-family: sans-serif,helvetica;
margin: 0;
padding: 0;
}
:link {
color: #c00;
}
:visited {
color: #c00;
}
a:hover {
color: #f50;
}
h1 {
text-align: center;
margin: 0;
padding: 0.6em 2em 0.4em;
background-color: #294172;
color: #fff;
font-weight: normal;
font-size: 1.75em;
border-bottom: 2px solid #000;
}
h1 strong {
font-weight: bold;
font-size: 1.5em;
}
h2 {
text-align: center;
background-color: #3C6EB4;
font-size: 1.1em;
font-weight: bold;
color: #fff;
margin: 0;
padding: 0.5em;
border-bottom: 2px solid #294172;
}
h3 {
text-align: center;
background-color: #ff0000;
padding: 0.5em;
color: #fff;
}
hr {
display: none;
}
.content {
padding: 1em 5em;
}
.alert {
border: 2px solid #000;
}
img {
border: 2px solid #fff;
padding: 2px;
margin: 2px;
}
a:hover img {
border: 2px solid #294172;
}
.logos {
margin: 1em;
text-align: center;
}
/*]]>*/
</style>
</head>
<body>
<h1><strong>nginx error!</strong></h1>
<div class="content">
<h3>The page you are looking for is not found.</h3>
<div class="alert">
<h2>Website Administrator</h2>
<div class="content">
<p>Something has triggered missing webpage on your
website. This is the default 404 error page for
<strong>nginx</strong> that is distributed with
Fedora. It is located
<tt>/usr/share/nginx/html/404.html</tt></p>
<p>You should customize this error page for your own
site or edit the <tt>error_page</tt> directive in
the <strong>nginx</strong> configuration file
<tt>/etc/nginx/nginx.conf</tt>.</p>
</div>
</div>
<div class="logos">
<a href="http://nginx.net/"><img
src="/nginx-logo.png"
alt="[ Powered by nginx ]"
width="121" height="32" /></a>
<a href="http://fedoraproject.org/"><img
src="/poweredby.png"
alt="[ Powered by Fedora ]"
width="88" height="31" /></a>
</div>
</div>
</body>
</html>
These articles were helpful:
https://hub.docker.com/_/centos
https://stackoverflow.com/questions/36617368/docker-centos-7-with-systemctl-failed-to-mount-tmpfs-cgroup
Sunday, October 4, 2020
PostgreSQL - get position of second delimiter in string
select description,
position(';' in description) from public.cherryshoe;
select description,
(char_length(split_part(description, ';', 1))
+ char_length(split_part(description, ';', 2))
+ 2) from public.cherryshoe;
select description,
(char_length(split_part(description, ';', 1))
+ char_length(split_part(description, ';', 2))
+ char_length(split_part(description, ';', 3))
+ 3) from public.cherryshoe;
Tuesday, September 1, 2020
Running jackd command line works, but not via systemd because [Cannot allocate memory]
I am working on a personal project with my husband (also a software dev), where he got the video saving piece working with a raspberry pi, python, and picamera. Audio wasn't working yet, so I decided to take that piece on.
Environment:
Raspberry Pi 2 Model B
Raspberry Pi Camera Module V2
Raspberry Pi USB WiFi Adapter
Raspberry Pi OS 10
USB Microphone
Python 3.7.3
systemctl --version => systemd 241 (241)
I'm currently working with JACK audio server and jack_capture to capture audio files with JACK. The jackd2 package installation included the /etc/security/limits.d/audio.conf.disabled file, which when activated, enables realtime permissions and memlock permissions to the audio group. You can activate it via the following command or rename the file without the .disabled extension at the end. Make sure to logout and login again for the audio server to have the configuration recognized - no need to reboot/restart.
dpkg-reconfigure -p high jackd
I installed jackd2 via the following commands:# audio server
sudo apt -y install jackd2
# modify /etc/security/limits.d/audio.conf to bring realtime priorities to the audio group (which is usually fine for a single user desktop usage)
sudo mv /etc/security/limits.d/audio.conf.disabled /etc/security/limits.d/audio.conf
The audio.conf file configures the following; I verified the default pi user is in the audio group.
@audio - rtprio 95
@audio - memlock unlimited
I installed jack_capture via the following commands. The liblo-tools were necessary for me to remote control jack_capture via UDP port with OSC (Open Sound Control) messages.
# audio capture
sudo apt -y install liblo-tools
sudo apt -y install jack-capture
Starting jackd with python via command line worked great! When I started the python script as a service with systemd this error kept occurring:
Cannot lock down 13589280 byte memory area (Cannot allocate memory)
Why?! I read through numerous jackd configuration articles. Running command line was fine. Checking "ulimit -l" was unlimited (as the audio.conf file told the machine to do).
I stumbled upon this and this article (looked very old but it still applied for my problem!) that pointed me in the right direction that "When using the RPM or Debian packages on systems that use systemd, system limits must be specified via systemd." Essentially running jackd with systemd ignores the audio.conf file, so you must set it manually with systemd.
# The four settings under Service is for jackd support
# LimitRTPRIO and LimitMEMLOCK must be set in systemd service file, because when run as a service
# etc security limits.d audio.conf is not honored. MEMLOCK unlimited is infinity in systemd
[Service]
LimitRTPRIO=95
LimitMEMLOCK=infinity
Environment="DISPLAY=:0"
Environment="JACK_NO_AUDIO_RESERVATION=1"
Note:
- The LimitMEMLOCK is set to "infinity" for the systemd service, but in the audio.conf was set to "unlimited".
- The two environment variables DISPLAY and JACK_NO_AUDIO_RESERVATION were set to get rid of these two errors I had previous to the "Cannot allocate memory error".
To bypass device reservation via session bus, set JACK_NO_AUDIO_RESERVATION=1
Failed to connect to session bus for device reservation Unable to autolaunch a dbus-daemon without a $DISPLAY for X11
These additional articles were helpful:
Friday, August 14, 2020
React - access ref of child component from parent component
This is how to get access to the ref of a child component from a parent component. I am working with react-table so this example has references to that.
Environment:
CentOS Linux release 7.3.1611 (Core)
React 16.2
react-table 6.10.3
MyParent.js
const ALL_TOOLS = "";
const TOOL_1 = "tool1";
class MyParent extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
rows: [],
numPages: -1,
summary: null,
loading: false,
filter: ""
};
}
/**
* Allow the parent to get a reference to the child component
* @param {Object} table
*/
setRef = table => {
this.childRef = table;
};
/**
* Event handler for filter selection
* @param {String} filter
*/
onFilterSelection = filter => {
// must put this.fetchData in callback because need to guarantee filter will be updated
// before calling
this.setState(
{
filter: filter
},
() => {
this.childRef.current.state.page = 0; // force react-table back to page 1
this.fetchData(this.childRef.current.state);
}
);
};
/**
* Fetches data for the table
* @param {Object} tableState: the react table current table
*/
fetchData = tableState => {
// show the loading overlay
this.setState({ loading: true });
const sortDir = tableState.sorted[0].desc ? "DESC" : "ASC";
const offset = tableState.page * tableState.pageSize;
...
};
render() {
const { error, rows, numPages, summary, loading } = this.state;
return (
<div className="MyParent">
{!error && (
<div className="content">
<div className="left-panel">
<ul>
<li
onClick={() => this.onFilterSelection(ALL_TOOLS)}
>All</li>
<li
onClick={() => this.onFilterSelection(TOOL_1)}
>Tool 1</li>
</ul>
</div>
<div className="right-panel">
<MyChildTable
setRef={this.setRef}
rows={rows}
numPages={numPages}
summary={summary}
loading={loading}
fetchData={this.fetchData}
/>
</div>
</div>
)}
</div>
);
}
}
export default MyParent;
MyChildTable.js
class MyChildTable extends React.PureComponent {
constructor(props) {
super(props);
// add ref to react table, and pass along to parent
const { setRef } = this.props;
this.table = React.createRef();
setRef(this.table);
}
render() {
const { rows, numPages, summary, loading, fetchData } = this.props;
const columns = [
...
];
return (
<div className="MyChildTable">
{summary}
<ReactTable
ref={this.table}
data={rows}
loading={loading}
pages={numPages}
columns={columns}
defaultSorted={[{ id: "tstamp", desc: true }]}
multiSort={false}
className="-striped -highlight"
showPagination={true}
showPaginationTop={true}
showPaginationBottom={true}
showPageSizeOptions={false} // Only allow 25
pageSizeOptions={[25]} // Only allow 25
defaultPageSize={25} // Only allow 25
manual
onFetchData={fetchData}
/>
</div>
);
}
}
export default MyChildTable;
Tuesday, July 28, 2020
Raspberry Pi set up for development
Environment:
Host: macOS Catalina
Raspberry Pi 2 Model B
Raspberry Pi USB WiFi Adapter (I have this one)
- Install Raspberry Pi OS image on an SD card: Used Raspberry Pi Imager rbi-imager for macOS, chose Raspberry Pi OS Lite (32-bit) since I didn’t want the desktop environment - https://www.raspberrypi.org/documentation/installation/installing-images/
- Boot into the Raspberry Pi and login
- Confirm the OS version. Mine is "Raspbian GNU/Linux 10 (buster)"
- Update settings for US. I did I1, then I4, then I3. I think I3 may be the only one I needed for my keyboard to recognize the double-quote key
sudo raspi-config Choose 4 Localisation Options Choose I1 Change Locale => en_US.UTF-8 UTF-8 Choose I3 Change Keyboard Layout => Generic 101-key PC => Other => English (US) (Country of Origin) => English (US) (Keyboard layout) => The default for the keyboard layout => No compose key => Choose I4 Change WLAN Country => US United States sudo reboot
- Configure Raspberry Pi to connect to WiFi
Backup file and copy to your home foldersudo cp /etc/wpa_supplicant/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf.orig cp /etc/wpa_supplicant/wpa_supplicant.conf ~
Add below to the end of the file. I had to add scan_ssid=1 because my SSID is not broadcasting. NOTE: If you have special characters in your password, e.g. double quote I didn't have to escape it but I had to add in key_mgmt=WPA-PSK config attribute (with no quotes).
vi ~/wpa_supplicant.conf network={ ssid="<ssid>" psk="<secret password>" scan_ssid=1 }
Copy updated file backsudo cp ~/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf
Verify if the “inet addr” is available on wlan0ifconfig wlan0
If not, reboot to connectsudo reboot
On reboot, I can see it says “My IP address is XXX.XXX.XX.XXXX”
To verify internet connectivity perform a wgetwget www.google.com
- Enable ssh on Raspberry Pi
sudo raspi-config Choose I5 Interfacing Options Choose P2 SSH => Yes [Didn’t need to reboot]
- Connect to Raspberry Pi via ssh from localhost, update macOS hosts file with Raspberry Pi IP
sudo vi /private/etc/hosts
Added this to bottom of file# raspberry pi XXX.XXX.XX.XXXX csrp
ssh to the Raspberry Pi
ssh pi@csrp
- Shutdown the Raspberry Pi
sudo halt
cat /etc/os-release