Chromebook: Using Logitech Unifying Receiver


If you have a decent Logitech keyboard/mouse, chances are you might also have a Logitech Unifying Receiver, which is a small USB dongle that allows multiple Logitech devices to be connected to a single computer. This is useful if your institution disables the Bluetooth connectivity on the institution-managed Chromebook. However, if your institution also prevents you from installing Logitech Unifying for Chrome extension, how can you still get this to work on your Chromebook without thinking about quitting your job?


The trick here is to use your Mac/Windows computer to get the devices added to the Logitech Unifying Receiver first before transferring the USB receiver to your Chromebook.

Attach the Logitech Unifying Receiver to your Mac/Windows computer. If your modern computer only has USB-C ports, use a “USB-C to USB” adapter.

Install Logitech Options.

From Logitech Options, add the keyboard/mouse as Unifying devices by clicking on the ADD UNIFYING DEVICE button.

Once added, attach the USB receiver to your Chromebook.

Note: If your keyboard/mouse has the multi-device switch, you can use the same set of devices on both Mac/Windows computer and Chromebook. In this case, you may configure Switch #1 using Bluetooth (for Mac/Windows computer) and Switch #2 using Logitech Unifying Receiver (for Chromebook).

GCP + Container Registry: Pushing/Pulling Images


You want to push a new image to Google Container Registry (GCR) or pull an existing image from GCR.


Pushing a New Image to GCR

Prepare your Dockerfile.

FROM alpine:3.7

# some content...

Create an image and tag it with a path pointing to GCR within a project.

There are several variations of GCR’s hostname (ex:,,, etc) depending on the data center’s location.

The GCR path has the following format: [HOSTNAME]/[PROJECT-ID]/[IMAGE].

docker build -t .

Log into GCP.

gcloud auth login

Register gcloud as a Docker credential helper.

gcloud auth configure-docker

Push the image to GCR.

docker push

View pushed image.

gcloud container images list-tags

78b36c0b456d  latest  2019-03-07T16:19:53

The repository and image can also be viewed in GCP Console.

Image in GCR

Pulling an Existing Image from GCR

Proceed with the authentication process if it is not done already.

gcloud auth login
gcloud auth configure-docker

Pull the image from GCR.

docker pull

Ansible: Handling Multiple Hosts via SSH


To run Ansible playbook in multiple hosts via SSH.


Configuring SSH environment

Ensure SSH keypair exists on the current machine (ex: ~/.ssh/id_rsa for private key and ~/.ssh/ for public key). If you do not have one, create one:


Copy the public key (ex: ~/.ssh/ to each remote host’s ~/.ssh/authorized_keys. If this file doesn’t exist, create it.

Ensure the current machine’s .ssh/ directory and file have correct permission.

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

In the current machine’s /etc/hosts, add all remote hosts.      localhost  # current machine  donkeykong # remote host 1  supermario # remote host 2

In each remote host, enable the remote login and grant yourself the access to this service.

Enabling Remote Login on Mac

Test SSH connection to remote host to ensure they work first before working on Ansible playbook.

ssh user@donkeykong
ssh user@supermario

Creating Ansible Playbook

Create ansible.cfg and define the location of inventory file.

inventory = inventory.yml

Create inventory.yml and define both localhost and remote hosts.

      ansible_connection: local
      ansible_user: user
      ansible_ssh_private_key_file: ~/.ssh/id_rsa
      ansible_user: user
      ansible_ssh_private_key_file: ~/.ssh/id_rsa

Run a test to ensure the connection to remote hosts are successful.

ansible all -i inventory.yml -m ping

If successful, the output looks something like this:

localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
donkeykong | SUCCESS => {
    "changed": false,
    "ping": "pong"
supermario | SUCCESS => {
    "changed": false,
    "ping": "pong"

Create main.yml with a very simple task.

- name: all-hosts
  hosts: all
    - name: Capture current dir
      shell: pwd
      register: output

    - name: Display output
      debug: msg='{{ output.stdout }}'

Run the playbook.

ansible-playbook main.yml

If successful, the output looks something like this:

PLAY [all-hosts] *******************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [localhost]
ok: [donkeykong]
ok: [supermario]

TASK [Capture current dir] *********************************************************************************************
changed: [localhost]
changed: [donkeykong]
changed: [supermario]

TASK [Display output] **************************************************************************************************
ok: [localhost] => {
    "msg": "/Users/user/myshittycode"
ok: [donkeykong] => {
    "msg": "/Users/user"
ok: [supermario] => {
    "msg": "/Users/user"

PLAY RECAP *************************************************************************************************************
donkeykong                 : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
supermario                 : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Controlling the Hosts

Sometimes, you want finer controls on what tasks to be ran in certain hosts.

To run in just one host (ex: donkeykong):

- name: one-host
  hosts: donkeykong
    - ...

To run in all remote hosts except localhost:

- name: all-hosts-except-localhost
  hosts: all:!localhost
    - ...