From 7cd2dbd5cd0c07c3cca716ae54a35eb052425917 Mon Sep 17 00:00:00 2001 From: Muzammil Abdul Rehman Date: Tue, 4 Apr 2023 14:23:51 +0500 Subject: [PATCH] [FMP] Basic Function Mapper Example --- func-mapper-deb/Makefile | 103 ++++++++++++++++++ func-mapper-deb/Readme.md | 29 +++++ func-mapper-deb/debian/compat | 1 + func-mapper-deb/debian/control | 12 ++ .../debian/funkpkg.funcmapper.logrotate | 9 ++ .../debian/funkpkg.funcmapper.service | 21 ++++ func-mapper-deb/debian/funkpkg.postinst | 19 ++++ func-mapper-deb/debian/funkpkg.sysuser | 1 + func-mapper-deb/debian/rules | 36 ++++++ func-mapper-deb/dev.py | 10 ++ func-mapper-deb/funcmapper/__init__.py | 2 + func-mapper-deb/funcmapper/functions.py | 11 ++ func-mapper-deb/funcmapper/funk.py | 10 ++ func-mapper-deb/funcmapper/logger/__init__.py | 40 +++++++ func-mapper-deb/funcmapper/maps.py | 8 ++ func-mapper-deb/pytest.ini | 5 + func-mapper-deb/requirements.txt | 1 + func-mapper-deb/setup.py | 41 +++++++ func-mapper-deb/test_requirements.txt | 1 + func-mapper-deb/tests/test_funk.py | 10 ++ 20 files changed, 370 insertions(+) create mode 100644 func-mapper-deb/Makefile create mode 100644 func-mapper-deb/Readme.md create mode 100644 func-mapper-deb/debian/compat create mode 100644 func-mapper-deb/debian/control create mode 100644 func-mapper-deb/debian/funkpkg.funcmapper.logrotate create mode 100644 func-mapper-deb/debian/funkpkg.funcmapper.service create mode 100644 func-mapper-deb/debian/funkpkg.postinst create mode 100644 func-mapper-deb/debian/funkpkg.sysuser create mode 100644 func-mapper-deb/debian/rules create mode 100644 func-mapper-deb/dev.py create mode 100644 func-mapper-deb/funcmapper/__init__.py create mode 100644 func-mapper-deb/funcmapper/functions.py create mode 100644 func-mapper-deb/funcmapper/funk.py create mode 100644 func-mapper-deb/funcmapper/logger/__init__.py create mode 100644 func-mapper-deb/funcmapper/maps.py create mode 100644 func-mapper-deb/pytest.ini create mode 100644 func-mapper-deb/requirements.txt create mode 100644 func-mapper-deb/setup.py create mode 100644 func-mapper-deb/test_requirements.txt create mode 100644 func-mapper-deb/tests/test_funk.py diff --git a/func-mapper-deb/Makefile b/func-mapper-deb/Makefile new file mode 100644 index 0000000..40fe4f5 --- /dev/null +++ b/func-mapper-deb/Makefile @@ -0,0 +1,103 @@ + +# shell variables +SHELL := /bin/bash # use this instead of /bin/sh +DATE := $(shell date -u +"%Y-%m-%d") +PWD := $(shell pwd) +DISTRO := $(shell lsb_release -cs) + +# Development only variables +DEV_VENV_PATH := ~/funcmapper/venv + + +# Get the build/release version +ifdef PKG_VERSION +PKG_VERSION := $(PKG_VERSION) +else +PKG_VERSION := 0.0.1-0 +endif + +# program name +PERIODIC_PRINTER = periodicprinter + +# directory containing the final debian file (this can be relative or absolute, default is '..') +DEB_OUT_DIR = dist + +# Package Info +PKG_USER = funkuser +PKG_NAME = funkpkg + +# Install locations +PATH_PKG_LOGS = /var/log/funcmapper +PKG_USER = funcmapper # note: this same as debian/funkpkg.sysuser +PKG_SERVICE = funcmapper +PY_INSTALL_SUFFIX = funcmapper + +# exports +export DEB_OUT_DIR +export PATH_PKG_LOGS +export PKG_SERVICE +export PKG_USER +export PY_INSTALL_SUFFIX + +# Makefile +.PHONY: all test clean deb-clean format lint deb install dev-setup +all: clean ${PERIODIC_PRINTER} + +clean: deb-clean wheel-clean + -rm -rf .tox + +# setup a development virtualenv +dev-setup: +# setup a virtualenv + python3 -m venv ${DEV_VENV_PATH} + ${DEV_VENV_PATH}/bin/pip3 install -r ${MKFILE_DIR}/requirements.txt + ${DEV_VENV_PATH}/bin/pip3 install -r ${MKFILE_DIR}/test_requirements.txt + +# test the code +test: + tox --recreate + +# wheel +wheel: wheel-clean wheel-build wheel-inspect + +wheel-clean: + -rm -rf dist/*.whl + +wheel-build: + python3 setup.py -q bdist_wheel + +wheel-inspect: + echo WHEELCONTENTS:START + cp -v dist/${PKG_NAME}*.whl dist/package.zip + unzip -l dist/package.zip + echo WHEELCONTENTS:END + + +# debian building +# note: we create a deb changelog here because it's an example. Ideally, the changelog file should be only incremented with `dch -i` +deb: deb-clean deb-changelog-create deb-build + +deb-clean: + -rm -f dist/*.deb + -rm -fv dist/* + -rm -rf debian/${PKG_NAME} + -rm -rf debian/.debhelper/ + +deb-clean-changelog: + -rm -f debian/changelog + +deb-changelog-create: deb-clean-changelog + dch --create --package ${PKG_NAME} -D ${DISTRO} -v ${PKG_VERSION} "Release: ${PKG_VERSION}" + +deb-build: +# check build deps + dpkg-checkbuilddeps +# build +# --buildinfo-option is passed to dpkg-genbuildinfo +# --changes-option is passed to dpkg-genchanges +# dpkg-buildpackage --unsigned-source --unsigned-changes --buildinfo-option="-u${DEB_OUT_DIR}" --changes-option="-u${DEB_OUT_DIR}" + dpkg-buildpackage --unsigned-source --unsigned-changes + +buildcontainer: + docker build -t ppbuilder . + docker run --rm --volume "${PWD}:/pkgbuilddir" --workdir="/pkgbuilddir" -it ppbuilder diff --git a/func-mapper-deb/Readme.md b/func-mapper-deb/Readme.md new file mode 100644 index 0000000..eda112a --- /dev/null +++ b/func-mapper-deb/Readme.md @@ -0,0 +1,29 @@ +# Simple Debian Packaging + +A basic example of debian packaging with dh-sysuser (to create a user). + +```sh +# Use docker to build container (it contains all build dependencies) +# (for ease of use, it's a make command shortcut) +make buildcontainer + +# make the debian +make deb + +# check the contents of the debian +dpkg --contents ../*.deb +dpkg -I ../*.deb + +# install the deb on the target machine (example) +apt install -y ../periodicprinterpkg_0.0.1-0_amd64.deb +``` + +## Note for Windows Docker users +Please make sure that you are using `LF` line ending and not `CRLF` for *all* files in `debian/` directory, otherwise, you will run into errors. + +Example error (or `postinst` failures): +``` +: No such file or directory +cc -o .o +cc: fatal error: no input files +``` diff --git a/func-mapper-deb/debian/compat b/func-mapper-deb/debian/compat new file mode 100644 index 0000000..b1bd38b --- /dev/null +++ b/func-mapper-deb/debian/compat @@ -0,0 +1 @@ +13 diff --git a/func-mapper-deb/debian/control b/func-mapper-deb/debian/control new file mode 100644 index 0000000..23a174e --- /dev/null +++ b/func-mapper-deb/debian/control @@ -0,0 +1,12 @@ +Source: funkpkg +Section: unknown +Maintainer: Muzammil Abdul Rehman +Build-Depends: debhelper (>=13), dh-sysuser +Standards-Version: 4.0.0 +Homepage: github.com/muzammilar + +Package: funkpkg +Architecture: any +Pre-Depends: ${misc:Pre-Depends} +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: funcmapper is an example python project diff --git a/func-mapper-deb/debian/funkpkg.funcmapper.logrotate b/func-mapper-deb/debian/funkpkg.funcmapper.logrotate new file mode 100644 index 0000000..e1fd40f --- /dev/null +++ b/func-mapper-deb/debian/funkpkg.funcmapper.logrotate @@ -0,0 +1,9 @@ +/var/log/funcmapper/*.log +{ + maxsize 5M + weekly + rotate 5 + compress + notifempty + missingok +} diff --git a/func-mapper-deb/debian/funkpkg.funcmapper.service b/func-mapper-deb/debian/funkpkg.funcmapper.service new file mode 100644 index 0000000..b06faaa --- /dev/null +++ b/func-mapper-deb/debian/funkpkg.funcmapper.service @@ -0,0 +1,21 @@ +[Unit] +Description=Sample Mapping Function. +ConditionPathExists=/opt/venv/funcmapper/bin/funk +# make sure we have network connectivity (not that this program needs it) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +# user and group +User=funkuser +Group=funkuser +TimeoutStartSec=45 +TimeoutStopSec=75 +ExecStart=/opt/venv/funcmapper/bin/funk +# restart service on failures +Restart=on-failure +RestartSec=10s + +[Install] +WantedBy=multi-user.target diff --git a/func-mapper-deb/debian/funkpkg.postinst b/func-mapper-deb/debian/funkpkg.postinst new file mode 100644 index 0000000..bfb2dcd --- /dev/null +++ b/func-mapper-deb/debian/funkpkg.postinst @@ -0,0 +1,19 @@ +#!/bin/bash +set -xe + +# Add the marker for postinst scripts generated by debhelpers (i.e. systemd and sysuser in this case). +# See the following for state diagrams of pre/postinst scripts: https://wiki.debian.org/MaintainerScripts +# See the manpages of dh_installdeb for details: https://manpages.ubuntu.com/manpages/jammy/en/man1/dh_installdeb.1.html + +#DEBHELPER# + +# Add user-defined postinst script. +# Change ownership of the logs directory to the user + +if [ "$1" = "configure" ] || [ "$1" = "abort-remove" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] ; then + + # create a logs directory (and set ownership) + mkdir -p #ENV.PATH_PKG_LOGS# + chown -R #ENV.PKG_USER# #ENV.PATH_PKG_LOGS# + +fi diff --git a/func-mapper-deb/debian/funkpkg.sysuser b/func-mapper-deb/debian/funkpkg.sysuser new file mode 100644 index 0000000..234bb39 --- /dev/null +++ b/func-mapper-deb/debian/funkpkg.sysuser @@ -0,0 +1 @@ +funkuser diff --git a/func-mapper-deb/debian/rules b/func-mapper-deb/debian/rules new file mode 100644 index 0000000..d8cb65f --- /dev/null +++ b/func-mapper-deb/debian/rules @@ -0,0 +1,36 @@ +#!/usr/bin/make -f + +# We check distribution here because Makefile can contain linting and tests which don't need distro name +DISTRO := $(shell lsb_release -cs) + +ifndef DISTRO +$(error DISTRO is not set, make sure you have `lsb-release` package installed) +endif + +ifndef PKG_NAME +$(error PKG_NAME is not set, should be exported by project Makefile) +endif + +# use verbose logging to easily debug builds +DH_VERBOSE = 1 + +# set the root path for dh-virtualenv +export DH_VIRTUALENV_INSTALL_ROOT=/opt/venv + +# dh-sysuser for creating a user (when package is installed) +# dh-virtualenv creates a debian package for python +%: + dh $@ --with sysuser,python-virtualenv --python=/usr/bin/python3 --install-suffix ${PY_INSTALL_SUFFIX} + +# override the output directory of the binary to be `dist` instead of `..` +# Note that providing an explicit filename doesn't work when there are more than one debs +override_dh_builddeb: + dh_builddeb --destdir=dist/ --filename=${PKG_NAME} + +# install systemd service file. Use a different name from the package itself +override_dh_installsystemd: + dh_installsystemd --name=${PKG_SERVICE} + +# add logrotate support to the package +override_dh_installlogrotate: + dh_installlogrotate --name=${PKG_SERVICE} diff --git a/func-mapper-deb/dev.py b/func-mapper-deb/dev.py new file mode 100644 index 0000000..464cace --- /dev/null +++ b/func-mapper-deb/dev.py @@ -0,0 +1,10 @@ +#!/path/to/funcmapper/virtualenv/bin/python +""" +An entry point for testing funcmapper in local development setup. +""" + +import funcmapper + +# This function is mostly used for local testing of the funcmapper code +if __name__ == "__main__": + funcmapper.funk.main() diff --git a/func-mapper-deb/funcmapper/__init__.py b/func-mapper-deb/funcmapper/__init__.py new file mode 100644 index 0000000..65ee980 --- /dev/null +++ b/func-mapper-deb/funcmapper/__init__.py @@ -0,0 +1,2 @@ + +from . import funk diff --git a/func-mapper-deb/funcmapper/functions.py b/func-mapper-deb/funcmapper/functions.py new file mode 100644 index 0000000..02f6761 --- /dev/null +++ b/func-mapper-deb/funcmapper/functions.py @@ -0,0 +1,11 @@ +"""A list of functions to be mapped""" + + +def rails(name: str, length: str) -> str: + pass + +def cylinders(name: str, length: str, diamter: str = None) -> str: + pass + +def oranges(**kwargs): + pass diff --git a/func-mapper-deb/funcmapper/funk.py b/func-mapper-deb/funcmapper/funk.py new file mode 100644 index 0000000..58efdf0 --- /dev/null +++ b/func-mapper-deb/funcmapper/funk.py @@ -0,0 +1,10 @@ + +import funcmapper.maps as funkmap + +def main(): + mappers = [ + ("rails", "inter-gelactic", "62 inches", "", "other") + ] + + for func, first, second, *_ in mappers: + print(funkmap.maps[func](first, second)) diff --git a/func-mapper-deb/funcmapper/logger/__init__.py b/func-mapper-deb/funcmapper/logger/__init__.py new file mode 100644 index 0000000..49e8303 --- /dev/null +++ b/func-mapper-deb/funcmapper/logger/__init__.py @@ -0,0 +1,40 @@ +"""Setup handlers for basic logging""" + +import logging + + +def get_logger(loggername: str) -> logging.Logger: + """Either create a new logger or gets an existing logger for a given name + + :param str loggername: user defined unique ID of the logger + :return logging.Logger: logging handler + """ + return logging.getLogger(loggername) + + +def configure_file_logging(loggername: str, loglevel: str = "debug", + logfilepath: str = "/tmp/funkmapper.log"): + """Sets up a basic logging configuration using a watched file handler + + :param str loggername: user defined unique ID of the logger + :param str loglevel: user defined logging level, defaults to "debug" + :param str logfilepath: the absolute path of the logfile, defaults to "/tmp/funkmapper.log" + """ + formatter = logging.Formatter("%(asctime)s {%(pathname)s:%(lineno)d} " + "[%(funcName)s] %(levelname)s - %(message)s") + + # get/get a logger + logger = get_logger(loggername) + + # remove logger if it already exits to prevent dual write + for handler in logger.handlers: + logger.removeHandler(handler) + handler.close() # remove the handler (and the files) + + # Write to file + logfile_handler = logging.handlers.WatchedFileHandler(logfilepath) + logfile_handler.setFormatter(formatter) + + # set logging level + logger.setLevel(logging.getLevelName(loglevel.upper())) + logger.addHandler(logfile_handler) diff --git a/func-mapper-deb/funcmapper/maps.py b/func-mapper-deb/funcmapper/maps.py new file mode 100644 index 0000000..160bcd9 --- /dev/null +++ b/func-mapper-deb/funcmapper/maps.py @@ -0,0 +1,8 @@ + +from funcmapper import functions as funks + +maps = { + "rails": funks.rails, + "cylinders": funks.cylinders, + "oranges": funks.oranges, +} diff --git a/func-mapper-deb/pytest.ini b/func-mapper-deb/pytest.ini new file mode 100644 index 0000000..b1b6908 --- /dev/null +++ b/func-mapper-deb/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +markers = + unit: mark a test as a unit test. + integration: mark a test as an integration. + dev: mark a test for development testing. diff --git a/func-mapper-deb/requirements.txt b/func-mapper-deb/requirements.txt new file mode 100644 index 0000000..7888d40 --- /dev/null +++ b/func-mapper-deb/requirements.txt @@ -0,0 +1 @@ +requests>2.18,<3.0 # python requests library diff --git a/func-mapper-deb/setup.py b/func-mapper-deb/setup.py new file mode 100644 index 0000000..4f2e743 --- /dev/null +++ b/func-mapper-deb/setup.py @@ -0,0 +1,41 @@ +import os +from setuptools import find_packages +from setuptools import setup + +#NOTE: Using setup.py is the legacy format. It's better to use setup.ini +# since that avoids the circular dependency issue for wheel building + +PACKAGE_NAME = "funkpkg" +LICENSE_NAME = "MIT" + +def version(): + if "PKG_VERSION" in os.environ and os.environ["PKG_VERSION"]: + return os.environ["PKG_VERSION"] + return "0.0.1-0" + +setup( + name=PACKAGE_NAME, + version=version(), + description="Function Mapping Example", + long_description="""Fake Long Description.""", + url="https://example.com", + author="Muzammil Abdul Rehman", + author_email="foo@bar.com", + license=LICENSE_NAME, + classifiers=[ + "Development Status :: 1", + "Programming Language :: Python :: 3", + ], + install_requires=[ + "requests>2.18,<3.0" + ], + package_dir={"funcmapper": "funcmapper"}, + packages=find_packages(), + include_package_data=True, + data_files=[], + entry_points={ + "console_scripts": [ + "funk=funcmapper.funk:main", + ] + }, +) diff --git a/func-mapper-deb/test_requirements.txt b/func-mapper-deb/test_requirements.txt new file mode 100644 index 0000000..79f5c07 --- /dev/null +++ b/func-mapper-deb/test_requirements.txt @@ -0,0 +1 @@ +pytest~=7.2 diff --git a/func-mapper-deb/tests/test_funk.py b/func-mapper-deb/tests/test_funk.py new file mode 100644 index 0000000..edaa929 --- /dev/null +++ b/func-mapper-deb/tests/test_funk.py @@ -0,0 +1,10 @@ +"""Test Fuction Mapping""" + +import pytest + +# Mark this file as unit testing only +mark = pytest.mark.unit + +def test_funk(): + """Basic Funk Testing""" + pass