Sunday, March 14, 2021

CSS - divs side by side flexbox examples

Below are examples with <div>'s side by side styled with flexbox:

  1. two <div>'s with equal widths
  2. two <div>'s one with fixed width and other expanding width
  3. three <div>'s with defined percentage widths


cherryshoe.html

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="cherryshoe.css">
<title>cherryshoe</title>
</head>
<body>
    <h3>Flexbox - divs side by side</h3>
    <p>Example 1: equal widths</p>
    <div class="container-equal">
        <div class="container-equal-child">Left 50%</div>
        <div class="container-equal-child">Right 50%</div>
    </div>
    <p>Example 2: left taking up remaining, right fixed with</p>
    <div class="container-different">
        <div class="container-different-left">Left will expand to fill space</div>
        <div class="container-different-right">Right fixed width</div>
    </div>
    <p>Example 3: with defined percentage widths</p>
    <div class="container-thirds">
        <div class="container-thirds container-thirds-child-left">Left with 20%</div>
        <div class="container-thirds container-thirds-child-center">Center with 30%</div>
        <div class="container-thirds container-thirds-child-right">Right with 50%</div>
    </div>
</body>
</html>

cherryshoe.css

.container-equal {
  display: flex; /* define flex container with default row directioin */
}
.container-equal-child{
  flex: 1;  /* grow */
  background: orange;
}
.container-equal-child:first-child{
  flex: 1;  /* grow */
  background: yellow;
 }

.container-different {
  display: flex;
}
.container-different-left{
  flex: 1;  /* grow */
  background: yellow;
}
.container-different-right {
  flex: 0 0 250px; /* flex-grow 0, flex-shrink 0, flex-basis with fixed width */
  background: orange;
}

.container-thirds {
  display: flex;
  width: 100%; 
  background: yellow;
}
.container-thirds .container-thirds-child-left {
  width: 20%; 
}
.container-thirds .container-thirds-child-center {
  width: 30%; 
  background: orange;
}
.container-thirds .container-thirds-child-right {
  width: 50%; 
  background: pink;
}

Flexbox is supported in modern browsers:



Saturday, February 13, 2021

React tether-component - get access to renderTarget element

Problem

The renderTarget (anchor) is a div that displays a default color.  When the renderTarget is clicked, it opens up the renderElement, which is a react-color component.  

I need to be able to choose a new color with react-color and then display it on the anchor (renderTarget).  

Environment:

CentOS Linux release 7.3.1611 (Core)

React 17.1

react-tether 2.0.7

react-color 2.19.3

Solution

I wasn't able to find a way to get access to either the renderTarget or renderElement directly.  The references to those are internal to the <TetherComponent>.  To gain access to the renderTarget element, I added a React ref in the constructor to the <TetherComponent>:

this.tetherComponentRef 

and assigned it when defining the <TetherComponent>, and accessed with:

this.tetherComponentRef._tetherInstance.target

The full solution is here:

import React from "react";
import { CompactPicker } from "react-color";
import TetherComponent from "react-tether";

const defaultOptions = {
  attachment: "top left",
  targetAttachment: "top left"
};

class CherryShoeColorPicker extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);

    this.tetherComponentRef = React.createRef();
  }

  /**
   * Handler when the CompactPicker color is changed
* @param {Object} colorObject */ handleChange(colorObject) { const { open } = this.state; this.tetherComponentRef._tetherInstance.target.style.fill = colorObject.hex; this.setState({ open: !open }); } /** * Handler when the anchor element is clicked */ handleClick() { const { open } = this.state; this.setState({ open: !open }); } render() { const { color, tetherOptions } = this.props; const { open } = this.state; const tetherOptions = tetherOptions ? { ...defaultOptions, ...tetherOptions } : defaultOptions; return ( <div> <TetherComponent {...tetherOptions} ref={tether => (this.tetherComponentRef = tether)} // renderTarget: This is what the item will be tethered to, make sure to attach the ref renderTarget={ref => ( <div ref={ref} onClick={this.handleClick} style={{ backgroundColor: color, height: "10px", width: "10px" }} ></div> )} // renderElement: this item will be tethered to the the component returned by renderTarget renderElement={ref => open && ( <CompactPicker ref={ref} color={color} onChangeComplete={this.handleChange} /> ) } /> </div> ); } } CherryShoeColorPicker.defaultProps = { tetherOptions: {} }; export default CherryShoeColorPicker;

You can also pass in JSX as props for use in either the renderTarget or renderElement.  The key thing is that you need to pass the ref argument into the renderTarget/renderElement function, and that the ref needs to be attached to the highest element in the DOM tree.  The highest element must already be defined when defining <TetherComponent>, it cannot be passed in as JSX from a prop.

These articles were helpful:

https://www.npmjs.com/package/react-tether

https://reactjs.org/docs/refs-and-the-dom.html

https://github.com/danreeves/react-tether/issues/1

Sunday, January 17, 2021

Node.js exec with promisify

I'm working on a project where Nodejs is used for the backend and redis is used for caching application data.  Here's an example of making a shell command to delete redis cache keys using child_process exec and promisify.  I find it easier (and more intuitive) to use async/await than to use callbacks.

Environment:
CentOS Linux release 7.3.1611 (Core)
node v14.9.0

// 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
};

These articles were helpful:
https://stackabuse.com/executing-shell-commands-with-node-js/ https://stackoverflow.com/questions/20643470/execute-a-command-line-binary-with-node-js

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:

docker-compose-cs.yml
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

There's many articles with examples of getting the first or last position of a delimiter in a string. Here's an example of getting the position of the second delimiter in a string. 

Environment: 
PostgreSQL 10.12

Get the position of the first delimiter ';' from the public.cherryshoe.description column:
select description, 
position(';' in description) from public.cherryshoe; 

Get the position of the second delimiter ';'.   Add char_length of each split_part - since we want the position, need two char_length's.  Add an additional length of 2 for each split_part function that is used to account for each of the two delimiters:
select description, 
(char_length(split_part(description, ';', 1))
+ char_length(split_part(description, ';', 2)) 
+ 2) from public.cherryshoe;

Get the position of the third delimiter ';'.  Add char_length of each split_part - since we want the position, need three char_length's.  Add an additional length of 3 for each split_part function that is used to account for each of the three delimiters:
select description, 
(char_length(split_part(description, ';', 1)) 
+ char_length(split_part(description, ';', 2)) 
+ char_length(split_part(description, ';', 3)) 
+ 3) from public.cherryshoe;

So on and so forth...

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.   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: