delivering static files

Delivering static files is supported by core4 with CoreStaticFileHandler. The following example demonstrates the publication of a private sphinx documentation site granting access to selected roles.

The example starts from scratch with the setup of a new core4 project. If you know how to setup a new core4 project continue with xxxxxx.

project setup

Change to your favorite development folder, e.g. ~/PycharmProjects and setup the project with:

coco --init home4 "core4 internal housekeeping project" --yes

This creates the following directory structure:

.
├── enter_env
├── home4
│   ├── home4.yaml
│   └── __init__.py
├── MANIFEST.in
├── README.md
├── requirements.txt
├── setup.py
└── tests
    └── __init__.py

sphinx setup

Enter the Python virtual environment, create a directory docs to host your sphinx project and setup sphinx with:

source home4/enter_env
mkdir -p home4/docs
cd home4/docs
sphinx-quickstart

Answer a couple of questions and select the defaults if you are unsure. This will create the following directory structure inside your ./home4/docs folder:

.
├── build
├── Makefile
└── source
    ├── conf.py
    ├── index.rst
    ├── _static
    └── _templates

build documentation

Let’s build the documentation with:

make html

implement document server

We will create a new .CoreApiContainer to deliver the built HTML documents. Create the following directory inside the package directory (cd ../home4; mkdir -p home4/api/v1):

home4
├── api
│   └── v1
├── docs
├── home4.yaml
└── __init__.py

Create a Python package and file server.py inside ./home4/api/v1 with the following commands:

touch home4/api/__init__.py
touch home4/api/v1/__init__.py
touch home4/api/v1/server.py

Put the following code into server.py:

from core4.api.v1.application import CoreApiContainer
from core4.api.v1.request.static import CoreStaticFileHandler


class DevopsManualServer(CoreApiContainer):
    rules = [
        (r'/devops', CoreStaticFileHandler, {
            "path": "/docs/build/html",
            "title": "devops manual",
            "protected": True
        }),
    ]


if __name__ == '__main__':
    from core4.api.v1.tool.functool import serve
    serve(DevopsManualServer)

automated build with cadmin

We will use core4 deployment tool cadmin to automatically build this documentation if an upgrade is available. See cadmin. This requires a package.json inside home4/docs:

{
  "name": "devops-docs",
  "core4": {
    "build_command" : [
      "/usr/bin/python3 -m venv .venv_build",
      ".venv_build/bin/pip install sphinx sphinx-rtd-theme",
      "rm -Rf ./dist",
      ".venv_build/bin/sphinx-build -b html source build/html",
      "rm -Rf .venv_build"
    ],
    "dist": "./dist"
  }
}

preparation

First you will have to create two users.

from requests import post, get

# login as admin
login = get("http://devops:5001/core4/api/v1/login?username=admin&password=hans")
token = login.json()["data"]["token"]
# create user1
rv = post("http://devops:5001/core4/api/v1/roles",
          headers={"Authorization": "Bearer " + token},
          json={
              "name": "user1",
              "realname": "Test User 1",
              "email": "test1@plan-net.com",
              "password": "very secret",
              "role": ["standard_user"]
          })
assert rv.status_code == 200
# create user2
rv = post("http://devops:5001/core4/api/v1/roles",
          headers={"Authorization": "Bearer " + token},
          json={
              "name": "user2",
              "realname": "Test User 2",
              "email": "test2@plan-net.com",
              "password": "very secret",
              "role": ["standard_user"]
          })
assert rv.status_code == 200

Verify both users exists with the role endpoint.

rv = get("http://devops:5001/core4/api/v1/roles",
          headers={"Authorization": "Bearer " + token})

To simulate a chat between both users, open two terminals and start the simple terminal chat client script.

"""
chat - simple chat terminal client

Usage:
  chat HOSTNAME USERNAME [PASSWORD] [--ssl]

Options:
  -h --help  Show this screen
  --ssl      use secure protocol (https/wss)
"""

import json
from getpass import getpass
from threading import Thread

from docopt import docopt
from requests import get
from websocket import create_connection


def listening(ws):
    while True:
        data = json.loads(ws.recv())
        if "channel" in data:
            print("{} > {}".format(data["author"], data["data"]))


def main():
    args = docopt(__doc__, help=True)

    if args["PASSWORD"] is None:
        args["PASSWORD"] = getpass("enter password: ")

    LOGIN_URL = "{HOSTNAME:s}/core4/api/v1/login" \
                "?username={USERNAME:s}&password={PASSWORD:s}"
    SOCKET_URL = "{HOSTNAME:s}/core4/api/v1/event?token={token:s}"

    if args["--ssl"]:
        url = "https://"
        ws = "wss://"
    else:
        url = "http://"
        ws = "ws://"

    # login
    login_url = url + LOGIN_URL.format(**args)
    login = get(login_url, verify=False)
    assert login.status_code == 200
    token = login.json()["data"]["token"]
    print("> login")

    # connect
    ws_url = ws + SOCKET_URL.format(token=token, **args)
    ws = create_connection(ws_url)
    assert ws.connected
    print("> web socket connected")

    # announce interest
    ws.send(json.dumps({"type": "interest", "data": ["message", "system"]}))

    thread = Thread(target=listening, args=(ws,))
    thread.start()

    while True:
        message = input()
        ws.send(json.dumps(
            {"type": "message", "channel": "message", "text": message}))

if __name__ == '__main__':
    main()

Before starting the script, ensure the CoreApiServer is up and running with:

cd core4
source enter_env
python core4/api/v1/server.py

In a second terminal, start the script for _user1_ with:

cd docs/source/example
python chat.py devops:5001 user1 "very secret"

In a third terminal, start the script for _user2_ with:

cd docs/source/example
python chat.py devops:5001 user2 "very secret"