orgvm - virtual infrastructure orchestration workbook

Table of Contents

"Restate the problem in terms of the solution." -variously attributed

0.1. About OrgVM

This is an orgvm workbook. The sections provide summary, status, and technical detail in (increasingly, gradually) more "technical" terms. You can replace this section with a description of your solution or remove (or move) to plug OrgVM.

1. Capabilities

2. INPROGRESS State

2.1. TODO Feature Checklist

  • [ ] club together some working programs to pipline data
  • [ ] define file system users and groups
  • [ ] create file system structure(s)
  • [ ] write systemd scripts
  • [ ] setup source rendering (tangle/export)
  • [ ] setup build and integration (Makefile,…)
  • [-] publish (live demo at gw10.bru.st)

2.2. TODO Capability Checklist

A "functional cross-checklist" of working things to duplicate and migrate or integrate.

  • [ ] comic.chat - web view of public IRC (or whatever) as comix
    • [ ] websocket server
    • [ ] IRC to comic.chat relay+
    • [ ] comic.chat to web relay
    • [ ] discord to comic.chat relay
  • [-] crude-ci: drive commits on remote machines via messages from Savannah
    • [X] UDP to JSONL writer
    • [ ] JSONL to JSON CGI API
  • [ ] gliphy IRC relay
  • [ ] samplicator
    • [ ] install samplicator
    • [ ] start-samplicator.sh

3. TODO Licensing and Documentation

Render some top-level documentation right here…

4. INPROGRESS Programs

4.1. start-samplicator

#!/bin/sh

SAMPLICATE=/home/corwin/build/samplicator-1.3.8rc1/samplicate
FROM_PORT=17971
TO_LIST="127.0.0.1/19790 127.0.0.1/19769"
LOG_FILE=/petroglyph/samlicator.log

nohup $SAMPLICATE -p $FROM_PORT \
      $(echo `for h in $TO_LIST ; do echo $h ; done`) \
      >$LOG_FILE 2>&1 &

4.2. udp-line-receiver

4.2.1. Documentation

  1. Design
    Write commit meesages sent via UDP from git post-receive hooks
    to JSONL log files per "project", as defined by each message.
    
    Messages contain five items, space separated:
      PROJECT CONTEXT REVISION AUTHOR MESSAGE
    
    PROJECT:  the repository name
    CONTEXT:  the branch/tag
    REVISION: the new commit hash (given as $newrev)
    AUTHOR:   the local name of the author (not the commiter)
    MESSAGE:  the first line of the commit message
    
  2. Usage
    udp-line-receiver [options] [FOLDER]
    
     Most common options:
       -man             full documentation
       -basedir         root for auto-selected log folder
       -any-project     accept logs for any project
       -project <name>  accept logs for project <name>
       -port <number>   set the port to listen on
    

4.2.2. Program Headers

The "Sh-bang" line helps the operation system launch the right interpreter. Additional boilerplate (or "standard text") identifies the author and designates the program as free software by specifying a free software license (the GNU Public License).

#!/usr/bin/env perl
# 
# 

Load required packages.

use strict;
use warnings qw[all];
use feature qw[say];
use IO::Async::Loop;
use IO::Async::Socket;
use JSON::MaybeXS;
use LWP;
use URI::Escape;
use File::Basename qw[basename dirname];
use Getopt::Long;
use Pod::Usage;

Do not buffer output to STDOUT or STDERR.

#avoid buffering output;
STDOUT->autoflush;
STDERR->autoflush;

4.2.3. Start-up processing

Setup defaults and handle environment variables and command-line arguments.

## vars

# the port to listen on
my $port = $ENV{PORT} || 17970;
# access controls
# accept any name or IP that exists, when empty accept any
my %accept_hosts = ();

# reject from any name or IP that exists
my %reject_hosts = ();

  # keys are values to accept for project, when empty accept any
  my @projects = qw[emacs erc dungeon test-proj cc-test-proj];
  my ( $PROGRAM ) = basename $0, qw[.pl];
  my $VERSION = '0.0.1';
  my $DEBUG = 1; # any truthy value for more output, sent to STDOUT
  my ( $default_output_path ) = ( dirname $0 ) . q(/log);
  my $man = 0;
  my $help = 0;

  GetOptions(
      'help|?' => \$help, man => \$man,

      'program=s' => \$PROGRAM,
      'version=s' => \$VERSION,
      'debug!'    => \$DEBUG,

      'port=i' => \$port,
      'accept=s' => \%accept_hosts,
      'reject=s' => \%reject_hosts,
      'project=s' => \@projects,
      'any-project' => sub { @projects = () },
      'basedir=s' => \$default_output_path,
  ) or pod2usage(2);
  pod2usage(1) if $help;
  pod2usage(-exitval => 0, -verbose => 2) if $man;

  my ( $output_path ) = @ARGV ? (@ARGV) : $default_output_path;

  # output path must exist
  unless (-e $output_path && -r _) {
    die qq(Missing or unreadable output path "$output_path"\n).
        qq(Usage:  $0 [ /path/to/jsonl ]\n);
  }

  # cache existance of reject and accept lists
  my %projects = map { $_ => 1 } map { split /[,;\s]/ } @projects;
  my $accept_any_project = %projects < 1;

  my $no_accept_list = %accept_hosts < 1;
  my $no_acls_exist = $no_accept_list && %reject_hosts < 1;

4.2.4. Main loop

## start
my $loop = IO::Async::Loop->new;

my $socket = IO::Async::Socket->new(
   on_recv => sub {
      my ( $self, $dgram, $addr ) = @_;
      # ZZZ: fail2ban?

      # parse the datagram
      chomp($dgram);
      $DEBUG and warn 'RECV:', $dgram, "\n";

      # source ADDR check
      unless ($no_acls_exist or not exists $reject_hosts{$addr}
              && ($no_acls_exist or exists $accept_hosts{$addr})) {
        $DEBUG and warn 'RJCT:',$addr,qq[\Q$dgram\E],"\n";
        return;
      }

      my ( $project, $context, $revision, $author, $logline ) =
        split /[\s]+/ms, $dgram, 5;

      # content validations
      unless ($project and $context and $revision and $author and $logline
              and $revision =~ /^\b[0-9a-f]{5,40}$/
              and ($accept_any_project
                   or exists $projects{$project})) {
        $DEBUG and warn 'RJCT:',$addr,qq[\Q$dgram\E],"\n";
        return;
      }

      my $jsonl_file = sprintf( '%s/%s.jsonl', $output_path, $project );
      open my $fh, '>>', $jsonl_file
        or do {
          warn qq[ERR opening [[$jsonl_file][$@ $?]]:$dgram\n];
          return;
        };

      # touch-ups

      # rtrim commit message
      $logline =~ s/[\s.]+$//m;

      # ltrim all leading semi-colon, star, and space like chars
      $logline =~ s/^[\s;*]+//s;

      my $line  = encode_json({ project  => $project,
                                context  => $context,
                                revision => $revision,
                                author   => $author,
                                message  => $logline,
                              })
        . "\n";
      $DEBUG and warn qq(JSON:$line);

      $fh->print( $line ) or warn qq[ERR writing [[$jsonl_file][$@ $?]]:$dgram\n]
    }
);
$loop->add( $socket );

print "Starting..";

$socket->bind( service => $port, socktype => 'dgram' )
  ->then(sub{ say "listening on $port" })->get;

$loop->run;

4.2.5. Manual

__DATA__

=head1 NAME

udp-line-receiver - receive via UDP and write a jsonl per project

=head1 SYNOPSIS



=head1 ALL OPTIONS

=over 8

=item B<-any-project>

Enable logging for any project.

=item B<-basedir>

When no FOLDER is given, sets the base directory for a folder of JSON
logs containing one file per project for which at least one message
has been accepted.

=item B<-help>

Print a brief help message and exits.

=item B<-man>

Prints the manual page and exits.

=item B<-no-debug>

Disble debugging messages

=item B<-port>

Defines the UDP port to listen on, by default 17970.

=item B<-project NAME>

Enable logging for project NAME.

=item B<-program NAME>

Set the program name to NAME.

=item B<-version VERSION>

Set the project version to VERSION

=back

=head1 DESCRIPTION

Read from a UDP port and write accepted message into a JSONL file
based on the value of the first field of the message.



=head1 LICENSE



=cut

4.3. tailjsonl

4.3.1. Documentation

  1. Design
    return the notification detail for latest revision of given project
    
  2. Usage

4.3.2. Start-up Processing

First there are some repetitive bits that we can skip looking at for now; see Boilerplate or the generated file for detail.

  1. Include Libraries
    use strict;
    use warnings qw[all];
    use feature qw[say];
    use CGI;
    #use CGI::Carp qw[fatalsToBrowser];
    use File::Basename qw[basename];
    use Getopt::Long;
    use Pod::Usage;
    
  2. Declare Variables
    my ($PROGRAM) = basename $0, qw[.pl .cgi];
    my $VERSION = '0.0.50';
    
    my $data_path = $ENV{PWD};
    
    # both of these patterns are anchored at both ends
    # neither one allows single quote or backslash
    my $value_re  = qr(^([a-zA-Z0-9_.-]+)$);
    my $branch_re = qr(^([/a-zA-Z0-9_.-]+)$);
    my ($help, $man);
    my $DEBUG = 1;
    
    my ( @projects, %projects ) = qw[emacs dungeon testproj test-proj cc-testproj];
    
  3. Handle Command-line

    Process command line options.

    sub no_dot_or_backslash (\$) {
        my $sref = shift;
        return sub {
            my ($field,$value) = @_;
            die qq("$field cannot contain dot or backslash")
                if $value =~ m<[.\\]>;
            $$sref = \$value;
        }
    }
    
    GetOptions(
        'help|?' => \$help, man => \$man,
    
        'program=s' => \$PROGRAM,
        'version=s' => \$VERSION,
        'debug!'    => \$DEBUG,
    
        'project=s' => \@projects,
        'any-project' => sub { @projects = () },
    
        'basedir=s' => \$data_path,
    
        value_re => no_dot_or_backslash $value_re,
        branch_re => no_dot_or_backslash $branch_re,
    ) or pod2usage(2);
    pod2usage(1) if $help;
    pod2usage(-exitval => 0, -verbose => 2) if $man;
    
  4. Sanity Checks

    Sanity checks before starting

    # create a hash look-up table for given valid projects
    # don't check existance in case of creation after we start
    %projects = map {
      $_ => 1
    } grep /$value_re/, map {
      split /[,:;\s]+/
    } @projects;
    
    # optimize: store the fact of any empty project list
    my $allow_any_project = 0 == %projects;
    
    # an empty project list means a wide open query
    # if we have values in @projects then --any-project
    # was not used or --project was also used; ask for clarity.
    unless( %projects or $allow_any_project and not @projects ) {
        die qq(STOP possible uninteded unrestricted query enabled\n) .
            qq(use --any-project and avoid --project to clarify.);
    }
    

4.3.3. Main Program

sub fail_with ($$) {
  my ( $q, $status ) = @_;
  print $q->header( -status => $status );
  exit;
}
my $q = new CGI;

my ($project) = ($q->param('project') || $q->param('p') || ''

                ) =~ $value_re;
my $data_file = $data_path . '/' . $project . '.jsonl';

fail_with $q, q(401 Bad project)
  unless $project
    and ($allow_any_project || exists $projects{$project})
    and -r $data_file;

my ($filter) = ($q->param('branch') || $q->param('b') || ''
               ) =~ $branch_re;

my $command = $filter ? qq(grep -F '"context":"$filter"' $data_file | tail -1)
                      : qq(tail -1 $data_file);

my ($prop) = ($q->param('property') || $q->param('prop') || $q->param('P') || ''
             ) =~ $value_re;

$command .= qq( | jq -r '.$prop') if $prop;

my $result = `($command)  2>/dev/null`;

print $q->header( -type => q(application/json) ), $result || 'null'

5. INPROGRESS Development

Programs to run within Emacs.

5.1. HTML rendering

To render HTML from this document place your cursor (point, in Emacs terms) into the code below and press C-c C-c (control+c, twice).

(org-html-export-to-html)

5.2. Project source rendering

To render project sources from this document place your cursor (point, in Emacs terms) into the code below and press C-c C-c (control+c, twice).

(org-babel-tangle)

6. Integration (in shell)

These programs run from the command line and shell-scripts.

6.1. build.sh

#!/usr/bin/sh
emacs --batch -l build.el

6.2. tangle-README.sh

#!/usr/bin/sh
emacs --batch -eval '(progn (setopt org-safe-remote-resources '"'"'("\\`https://fniessen\\.github\\.io/org-html-themes/org/theme-readtheorg\\.setup\\'"'"'" "\\`https://fniessen\\.github\\.io/org-html-themes/org/theme-bigblow\\.setup\\'"'"'")) (find-file "README.org") (org-babel-tangle))'

6.3. build.el

;;; build.el --- build README.md from README.org     -*- lexical-binding: t; -*-
;; Author: Corwin Brust <corwin@bru.st>
;; 
;;; Commentary:

;; tangle sources then build README.{md,html,txt} from README.org

;;; Code:

(require 'ox-md)  ;; maybe something to install ox-md from MELPA?
(require 'htmlize "/var/c2e2/orgvm/site-lisp/htmlize/htmlize.el" t)

(setopt org-safe-remote-resources
        '("\\`https://fniessen\\.github\\.io/org-html-themes/org/theme-readtheorg\\.setup\\'"
          "\\`https://fniessen\\.github\\.io/org-html-themes/org/theme-bigblow\\.setup\\'"))
(org-babel-do-load-languages
 'org-babel-load-languages
 '((C     . t)
   (ditaa . t)
   (dot   . t)
   ;; (http  . t) ; local source
   (emacs-lisp . t)
   (latex . t)
   ;(ruby . t)
   (js    . t)
   (perl  . t)
   (plantuml . t)
   (shell . t)
   ;; (typescript . t) ; not always loaded
   ))
(save-excursion
  (find-file "README.org")
  (org-babel-tangle)
  (org-md-export-to-markdown)
  (org-html-export-to-html)
  ;;(org-latex-export-to-pdf) ;; ugly, omit for now
  (org-ascii-export-to-ascii))

(provide 'build)
;;; build.el ends here

7. Boilerplate

The "Sh-bang" line helps the operation system launch the right interpreter.

#!/usr/bin/env perl
# 

This additional "boilerplate" (or "standard text") which identifies the author and designates the program as free software by specifying a free software license (the GNU Public License).

Copyright 2025 Corwin Brust <corwin@bru.st>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/.

Author: Corwin Brust

Created: 2025-08-10 Sun 23:01

Validate