<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Code Drunk Debug Sober]]></title><description><![CDATA[Tools and Ideas I use in my Software Consultancy]]></description><link>https://codedrunkdebugsober.com/</link><image><url>https://codedrunkdebugsober.com/favicon.png</url><title>Code Drunk Debug Sober</title><link>https://codedrunkdebugsober.com/</link></image><generator>Ghost 2.9</generator><lastBuildDate>Sat, 29 Feb 2020 15:39:16 GMT</lastBuildDate><atom:link href="https://codedrunkdebugsober.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[PyZMQ Paranoid Pirate]]></title><description><![CDATA[Python implementation of Paranoid Pirate to do RPC calls.]]></description><link>https://codedrunkdebugsober.com/pyzmq-paranoid-pirate/</link><guid isPermaLink="false">5e46c0d7f7fa930001360f8a</guid><category><![CDATA[zeromq]]></category><category><![CDATA[python]]></category><category><![CDATA[pyzmq]]></category><dc:creator><![CDATA[James O'Carroll]]></dc:creator><pubDate>Fri, 14 Feb 2020 16:29:27 GMT</pubDate><content:encoded><![CDATA[<h1 id="just-show-me-the-code-">Just show me the code!</h1><p>Sure, here you go: <a href="https://github.com/stack-head/codedrunkdebugsober/tree/master/zmq">PyZMQ Paranoid Pirate</a></p><h1 id="considering-zeromq-for-your-application">Considering ZeroMQ for your application</h1><p>I sometimes get the opportunity to work with ZeroMQ professionally. ZeroMQ is a very powerful and versatile tool, which is great, but comes with the tradeoff of needing additional development work and testing to ensure your application code is using it correctly. If you're considering using ZeroMQ, you should first ask yourself if one of it's competing technologies will work. If you have sufficient hardware and can use an external piece of hardware or a cloud service to run something like RabbitMQ, ActiveMQ, or MQTT, I generally recommend you do it. These technologies are going to just be easier to develop against, and you'll save a good chunk of development time using them instead. That being said, if your use case involves distributed networking without a dedicated central hub, or specifically requires a very quick messaging rate, pull in ZeroMQ.</p><h1 id="zeromq-dealer-router">ZeroMQ DEALER/ROUTER</h1><p>If you're reading this, you should already have some knowledge on ZeroMQ works and it's multiple forms of communication. For this article, we're going to focus on the DEALER/ROUTER pattern. The official documentation for ZeroMQ is better than what I can write, so reference these articles for more information on what the DEALER/ROUTER pattern is and how it works:</p><p><a href="http://zguide.zeromq.org/php:chapter3#toc24">ZeroMQ asynchronous client/server pattern</a><br><a href="http://wiki.zeromq.org/tutorials:dealer-and-router">Dealer and Router socket explanation</a></p><p>The key thing you'll need to take away for this article is to know that DEALER/ROUTER ZeroMQ sockets allow for reliable bi-directional communication. If your use case doesn't need bidirectional communication, you can use a simpler ZeroMQ pattern-Publish/Subscribe or Request/Reply. These work as advertised, but I often find that typical use cases demand an initial handshake before sending information, and to be alerted if their target fails for some reason. The ZeroMQ developers recognized this need long ago, and describe a solution with DEALER/ROUTER called the <a href="http://zguide.zeromq.org/php:chapter4#Robust-Reliable-Queuing-Paranoid-Pirate-Pattern">Paranoid Pirate</a></p><p>That link is it will describe the primary benefit of the paranoid pirate-keeping a heartbeat in addition to client/worker communications gives you a ZMQ-style way of letting clients and workers introduce themselves to each other, and an established way for these partners to halt/resume communications. You can even get a core <a href="http://zguide.zeromq.org/py:ppworker">Python code sample</a>.</p><p>The bad news is that while the ZeroMQ documentation is readable and does a great job describing the ZeroMQ protocol, but doesn't feature many application level examples-there are some snippets that show you how to correctly set up communications, but larger examples are left to higher-level libraries that wrap around ZeroMQ. These higher level libraries like <a href="https://pyzmq.readthedocs.io/en/latest/">PyZMQ</a> or <a href="https://github.com/zeromq/zmqpp">zmqpp</a> in turn explain their own usage well, but don't really provide an example implementation of any of the higher level patterns like the paranoid pirate. So, let's implement the paranoid pirate in PyZMQ.</p><h1 id="a-pyzmq-implementation-of-the-zeromq-paranoid-pirate-pattern">A PyZMQ implementation of the ZeroMQ Paranoid Pirate pattern</h1><p>We'll start with an intentionally simplified version of the Paranoid Pirate-the "full" implementation would include a single client and multiple workers, but to keeps things simple here we'll stick to just a single worker. Additionally, we'll make the example a bit easier to visualize by implementing an RPC-style interface-the client will issue a request to the worker, and the worker will check that request against a set of known procedures it has, and will either return a result or throw an exception. For the most part, the sample code is documented well, so to understand what's going on I'm mostly going to direct you to look at the code. Still, let's look at a few key parts and see what outcome we get when we run the sample.</p><h2 id="let-s-run-it-">Let's run it!</h2><p>I've included a few scripts that should make running this code easier. First, take a look at run_demo_xero_in_docker_env.sh</p><pre><code>#!/bin/bash

set -eu
set -o pipefail

docker-compose build &amp;&amp; docker-compose up --abort-on-container-exit \
    codedrunk_xero_uniclient \
    codedrunk_xero_uniworker
</code></pre><p>I've included some dockerfiles that will build the example and run some integration tests to make sure things are working correctly. If everything builds correctly, you should see an output like this:</p><pre><code>Building codedrunk_xero_base
Step 1/16 : FROM ubuntu:18.04
 ---&gt; ccc6e87d482b
Step 2/16 : ENV DEBIAN_FRONTEND noninteractive
 ---&gt; Using cache
 ---&gt; 05583fe42b89
...
Step 16/16 : RUN (cd demo_xero &amp;&amp; pip3 install .)
 ---&gt; Using cache
 ---&gt; f63b97161944

Successfully built f63b97161944
Successfully tagged stackhead/codedrunk_xero_base:latest
Building codedrunk_xero_uniclient
Step 1/2 : FROM stackhead/codedrunk_xero_base
 ---&gt; f63b97161944
Step 2/2 : CMD ["demo_xerouniclient", "tcp://*:5550", "ping", "--count", "20"]
 ---&gt; Running in 7d5aa7ad7cf5
Removing intermediate container 7d5aa7ad7cf5
 ---&gt; ba084294f665

Successfully built ba084294f665
Successfully tagged stackhead/codedrunk_xero_uniclient:latest
Building codedrunk_xero_uniworker
Step 1/2 : FROM stackhead/codedrunk_xero_base
 ---&gt; f63b97161944
Step 2/2 : CMD ["demo_xerouniworker", "tcp://codedrunk_xero_uniclient:5550"]
 ---&gt; Using cache
 ---&gt; 3b83047e3d4b

Successfully built 3b83047e3d4b
Successfully tagged stackhead/codedrunk_xero_uniworker:latest
Starting codedrunk_xero_uniworker   ... done
Recreating codedrunk_xero_uniclient ... done
Attaching to codedrunk_xero_uniworker, codedrunk_xero_uniclient
codedrunk_xero_uniworker    | 03:49:43.094 asyncio      DEBUG    [MainThread  ] Using selector: EpollSelector
codedrunk_xero_uniworker    | 03:49:43.095 asyncio      DEBUG    [MainThread  ] Using selector: EpollSelector
codedrunk_xero_uniclient    | 03:49:43.162 asyncio      DEBUG    [MainThread  ] Using selector: EpollSelector
codedrunk_xero_uniclient    | 03:49:43.162 asyncio      DEBUG    [MainThread  ] Using selector: EpollSelector
codedrunk_xero_uniclient    | 03:49:43.280 xero.uni.uniclient INFO     [uniclientthread] _register_worker
codedrunk_xero_uniclient    | 03:49:43.280 xero.uni.uniclient DEBUG    [uniclientthread] worker.registerWorker for 'b'\x00k\x8bEg'' is connected.
codedrunk_xero_uniclient    | 03:49:43.281 demo_xero.demo_xerouniclient DEBUG    [uniclientthread] partial msg: 'started'
codedrunk_xero_uniclient    | 03:49:43.282 demo_xero.demo_xerouniclient DEBUG    [MainThread  ] rpc reply: 'pong'
...
codedrunk_xero_uniclient    | 03:49:43.292 demo_xero.demo_xerouniclient DEBUG    [uniclientthread] partial msg: 'started'
codedrunk_xero_uniclient    | 03:49:43.292 demo_xero.demo_xerouniclient DEBUG    [MainThread  ] rpc reply: 'pong'
codedrunk_xero_uniclient    | 03:49:43.293 demo_xero.demo_xerouniclient DEBUG    [uniclientthread] partial msg: 'started'
codedrunk_xero_uniclient    | 03:49:43.293 demo_xero.demo_xerouniclient DEBUG    [MainThread  ] rpc reply: 'pong'
codedrunk_xero_uniclient    | Starting client
codedrunk_xero_uniclient    | waiting for worker
codedrunk_xero_uniclient    | Shutting down
codedrunk_xero_uniclient exited with code 0
Aborting on container exit...
Stopping codedrunk_xero_uniworker   ... done
</code></pre><p>You should find the dockerfiles have installed the example python packages and are running the basic "ping" command as quickly as possible.</p><p>If you want to run the samples directly on your dev machine, install both xero and demo_xero via:</p><pre><code>pip3 install -e ./xero
pip3 install -e ./demo_xero
</code></pre><p>In one terminal, run the worker:</p><pre><code>./bin/demo_xerouniworker tcp://127.0.0.1:5550
</code></pre><p>In a second terminal, run any of the commands in demo_xero/run_examples.sh:</p><pre><code>#!/bin/bash

set -eu
set -o pipefail

# Simplest example-call RPC with no arguments and get single string as a reply
./bin/demo_xerouniclient tcp://127.0.0.1:5550 ping

# Similar, call RPC with no arguments and ensure the "reply" comes in, but no data returned
./bin/demo_xerouniclient tcp://127.0.0.1:5550 return_none

# Call an RPC that just does an equality comparison. Simple example to demonstrate how to pass arguments to worker RPC
./bin/demo_xerouniclient tcp://127.0.0.1:5550 compare --args '"apple", "orange"'

# Effectively the same thing, but showing how to use named arguments.
./bin/demo_xerouniclient tcp://127.0.0.1:5550 compare --kwargs '"str1":"apple", "str2":"orange"'

# Call an RPC that doesn't actually exist, and make sure the worker is able to handle the problem cleanly while returning
# the actual exception that is raised.
./bin/demo_xerouniclient tcp://127.0.0.1:5550 invalid_request

# The slow_ functions are serviced by an "actor" model. slow_succeed will succeed after the provided timeout,
# slow_fail will fail after the provided timeout.
./bin/demo_xerouniclient tcp://127.0.0.1:5550 slow_succeed --args 5 --timeout 20
./bin/demo_xerouniclient tcp://127.0.0.1:5550 slow_fail --args 5 --timeout 20
</code></pre><h1 id="a-warning-about-pyzmq">A warning about PyZMQ</h1><p>The PyZMQ project seems to be under pretty active development, which is great. However, it looks like they're working with a fairly challenging dependency environment as they try to incorporate/maintain compatibility with Facebook's Tornado module. I'd say they're doing rather well, but it's still easy to inadvertently create a system that seems to work reliable for tens of seconds, or even minutes, but will later stop dropping messages for seemingly no reason. This can be mitigated by making a particular effort to version match your environment, PyZMQ, and tornado once you have something that works reliably. The PyZMQ team also keeps a good listing of errata/upgrade headaches and their solutions listed in their documentation, so if you have reliability issues with PyZMQ I'd carefully read <a href="https://pyzmq.readthedocs.io/en/latest/eventloop.html">their explanation of PyZMQ and the event loop</a>. Whenever I've had problems with this sample breaking, it is because the PyZMQ team had to make updates under the hood to manage the different async libraries they're compatible with, and the problem could be fixed by reading the <a href="https://pyzmq.readthedocs.io/en/latest/changelog.html">changelog</a> and updating accordingly.</p><h1 id="summary">Summary</h1><p>Thanks for reading, this should give you some ideas on how to use PyZMQ with some other modern python libraries to do robust messaging. This sample hasn't been battle-hardened on a production system, but it does includes some tests that should keep it running on your dev machine. Here's a link to the full implementation again: <a href="https://github.com/stack-head/codedrunkdebugsober/tree/master/zmq">PyZMQ Paranoid Pirate</a>.</p>]]></content:encoded></item><item><title><![CDATA[End to end Maven Repository Proxy with Nexus 3 and Docker]]></title><description><![CDATA[<p>Speed up Java development with an on-site proxy Maven repository.</p><p>Out of the box, modern Java development can be pretty resource intenstive. On a typical Java development project, I may have to download gigabytes worth of dependencies, source packages, javadocs, and the Eclipse IDE on top in order to just</p>]]></description><link>https://codedrunkdebugsober.com/end-to-end-maven-proxy-with-nexus-3-and-docker/</link><guid isPermaLink="false">5c9f91d3b5824400011f34b3</guid><category><![CDATA[maven]]></category><category><![CDATA[java]]></category><category><![CDATA[nexus 3]]></category><dc:creator><![CDATA[James O'Carroll]]></dc:creator><pubDate>Sun, 31 Mar 2019 03:45:22 GMT</pubDate><content:encoded><![CDATA[<p>Speed up Java development with an on-site proxy Maven repository.</p><p>Out of the box, modern Java development can be pretty resource intenstive. On a typical Java development project, I may have to download gigabytes worth of dependencies, source packages, javadocs, and the Eclipse IDE on top in order to just get started.  Multiply that by the number of developers you have on your project and add a CI server building and rebuilding the package everytime you push an update, and you've got some real time and bandwidth getting eaten as you develop.  Similar to how you can cache <a href="https://codedrunkdebugsober.com/end-to-end-p2-repository-mirror-with-package-drone-and-docker/">dependencies for OSGi bundles</a> with a repsotory manager on your local network, you can use Nexus 3 to set up a Maven repository proxy.  Set up correctly, your development machine will check against your local Maven repository to satisfy your build dependencies before reaching out to the internet.  Better yet, this local cache will automatically grow to match the dependencies you're attempting to include-you won't need to manually specify what packages you want to save, only the remote repositories that you should potentially proxy.</p>
<p>I'm assuming you have a working knowledge of Docker.  A Docker image will be provided for you, but I'm counting on you to know how to deploy it to your server, and how to run it.  I'll also be showing you how to let Maven know about the proxy server, but I'll also assume you have a Maven project to build.  After all, why would you care about speeding up your Maven builds, if you're not using Maven already?</p>
<h1 id="settingupnexus3">Setting up Nexus 3</h1>
<p>We're going to use <a href="https://help.sonatype.com/repomanager3">Nexus 3</a> as our proxy, happily the community version of Nexus 3 supports Maven proxy repositories out of the box.  Even better, Sonatype keeps a <a href="https://hub.docker.com/r/sonatype/nexus3/">Dockerfile maintained for Nexus3</a>, so we don't have to write one.  I've <a href="https://gist.github.com/stack-head/65c53df5ed43ae2369e02fbdaae26405">provided a Docker Compose file</a> to match my personal perferences.  Get it up and running on your local server.  If you're using the provided docker compose file, the Nexus UI will become available on port 8084.  You'll also have to give it a little time after starting the container before the UI will actually become available.</p>
<h1 id="configuringnexus3proxies">Configuring Nexus 3 Proxies</h1>
<p>You'll have to sign in before you can do anything further, the default username and password are admin/admin123.  This would be a great opportunity to set a new admin password.  Once you've logged in, a gear symbol will become available at the top toolbar.  Select it, and then select the Repository-&gt;Repositories section on the left toolbar.</p>
<h2 id="thedefaultmavencentralproxyrepository">The default maven-central proxy repository</h2>
<p>The default installation of Nexus 3 comes with a number of built-in repositories:<br>
<img src="https://codedrunkdebugsober.com/content/images/2019/03/nexus3_default_repositories.png" alt="nexus3_default_repositories"><br>
Lucky for us, it looks like one of the default repositories is a proxy for maven central.  Click on it to take a closer look:<br>
<img src="https://codedrunkdebugsober.com/content/images/2019/03/nexus3_default_maven_central_proxy.png" alt="nexus3_default_maven_central_proxy"></p>
<p>This tells us all of the settings we'll need to make other proxies.</p>
<h2 id="makeajcenterproxyrepository">Make a jcenter proxy repository</h2>
<p>Return to the repositories page, and choose the &quot;Create Repository&quot; button on top.  When presented with the list of repository recipes, choose &quot;maven2 (proxy)&quot;.<br>
You'll be presented with a new page, similar to the maven central configuration page we saw earlier.  You'll need to consider these fields:</p>
<ol>
<li>Name: set this to a unique ID, I just name mine to match the name of the remote maven proxy.  We'll just call it jcenter.</li>
<li>URL: Just to make it easy to track, match this URL against the Name.  In this case, we'll set it to http://&lt;server_ip_address&gt;:8084/repository/jcenter/</li>
<li>Maven 2-&gt;What type of artifacts does this repository store?  Maven repositories can store release artifacts, snapshot artifacts, or mixed (both release and shapshot).  To the best of my knowledge, there's no reason to not choose mixed, so go with that.</li>
<li>Proxy-&gt;Remote Storage.  This will have to match the location of the remote repository.  You can figure this out by looking at your maven build logs, but for jcenter you'll want <a href="https://jcenter.bintray.com/">https://jcenter.bintray.com/</a></li>
</ol>
<p>That's it!  Press Save and this proxy is ready to go.  If you're following along, your page will look like this:<br>
<img src="https://codedrunkdebugsober.com/content/images/2019/03/nexus3_jcenter_proxy.png" alt="nexus3_jcenter_proxy"></p>
<p>By the way, the default maven central repository is set to only proxy Release packages, if you want to also support snapshot packages you'll have to delete and remake the repository with the updated settings.</p>
<h2 id="configuringmaventoreferenceyourproxies">Configuring Maven to reference your proxies</h2>
<p>With Nexus 3 ready to go, all that's left is to let Maven know about it.  You'll typically want to edit Maven's settings.xml file to accomplish this.  settings.xml is typically located at ~/.m2/settings.xml, it may or may not already exist on your development system.  In order to let maven know your proxied repositories, you simply add an entry like this:</p>
<pre><code>&lt;mirrors&gt;
  &lt;mirror&gt;
    &lt;id&gt;maven-central&lt;/id&gt;
    &lt;name&gt;maven-central&lt;/name&gt;
    &lt;url&gt;http://192.168.1.101:8084/repository/maven-central/&lt;/url&gt;
    &lt;mirrorOf&gt;central&lt;/mirrorOf&gt;
  &lt;/mirror&gt;
  &lt;mirror&gt;
    &lt;id&gt;jcenter-mirror&lt;/id&gt;
    &lt;name&gt;jcenter-mirror&lt;/name&gt;
    &lt;url&gt;http://192.168.1.101:8084/repository/jcenter/&lt;/url&gt;
    &lt;mirrorOf&gt;jcenter&lt;/mirrorOf&gt;
  &lt;/mirror&gt;
&lt;/mirrors&gt;
</code></pre>
<p>This assumes your Nexus 3 instance is available at 192.168.1.101, port 8084.  It's also assumes in your project's pom or targetfile you have a reference to the mirrored repositories marked with IDs &quot;central&quot; and &quot;jcenter&quot;.  Maven isn't smart enough to check mirrors by url or content, it's relying on you to correctly match up <id> and <mirrorof> values.</mirrorof></id></p>
<h1 id="howdoiknowitsworking">How do I know it's working?</h1>
<p>This is easy:mvn clean install<br>
If you have everyting configured correctly, you can watch dependency bundle downloads in the build log, and you'll see them coming from your mirror's local IP address instead of the remote repository.<br>
That's about it-Nexus 3 does a lot of the lifting for you, so this isn't too hard to set up.  Good luck have fun.</p>
]]></content:encoded></item><item><title><![CDATA[End to end P2 Repository Mirror with Package Drone and Docker]]></title><description><![CDATA[<p>Speed up OSGi bundle development with an on-site P2 repository.</p>
<p>One of the challenges with software consulting is your client may have security policies that are well intentioned, but as a side-effect make development onerous.  I've faced situations where I've needed to use a client's proprietary VPN solution to access</p>]]></description><link>https://codedrunkdebugsober.com/end-to-end-p2-repository-mirror-with-package-drone-and-docker/</link><guid isPermaLink="false">5c89783ab5824400011f32f3</guid><category><![CDATA[package drone]]></category><category><![CDATA[P2]]></category><category><![CDATA[maven]]></category><category><![CDATA[java]]></category><dc:creator><![CDATA[James O'Carroll]]></dc:creator><pubDate>Thu, 14 Mar 2019 02:34:03 GMT</pubDate><content:encoded><![CDATA[<p>Speed up OSGi bundle development with an on-site P2 repository.</p>
<p>One of the challenges with software consulting is your client may have security policies that are well intentioned, but as a side-effect make development onerous.  I've faced situations where I've needed to use a client's proprietary VPN solution to access their intellectual assets, but that same VPN will make accessing third-party repositories, software updates, and generally anything from the internet a drag.  In the past, I've done development on a Java OSGi project, and the added latency when checking against a dozen Maven and P2 repositories was killing my ability to get things done.  I found that package drone was a reasonable solution to set up mirrors of remote P2 repositories, and because I couldn't find a good tutorial on how to set one up, I decided to write one.  This setup can work well for a single developer, but it especially pays off if you have multiple developers in one office all working on the same project-having a mirror of your dependencies on the local network can go a long way in cutting down build time and bandwidth.</p>
<p>I also assume that if you're reading this, you know what a repository mirror is and why you might want one, if that isn't the case check out <a href="https://maven.apache.org/guides/mini/guide-mirror-settings.html">Apache's guide to mirror settings</a> to learn more.</p>
<p>This article assumes that you have a working knowledge of Docker.  The example code will do the heavy lifting for you, but explaning how to build and run Docker containers has already been done by better writers, so I'm not going to redo it here.  I'm also assuming that you're successfully building an OSGi bundle with Maven, if that's not working for you yet than you don't need to worry about speeding up your build time.</p>
<h1 id="gettingpackagedroneupandrunning">Getting Package Drone up and running</h1>
<p>The official Package Drone <a href="https://hub.docker.com/r/ctron/package-drone/dockerfile">Dockerfile</a> is in good shape, but is getting a little dated.  We need to make light modifications to the Dockerfile to properly support mirrored repositories, so we'll take the opportunity to update the Dockerfile as well:</p>
<script src="https://gist.github.com/stack-head/e3a15f54caa2c1e69670a053f3475fb1.js"></script>
<p>One key change we're making is to modify the setting drone.web.maxListSize.<br>
It's default value of 10000 is typically fine, but if we mirror a number of P2 repositories, each with hundreds or thousands of individual packages, we can blow through that limit pretty easily.  In practice, I've found 20000 to be a good limit.</p>
<p>Get Package Drone up and running in any way you see fit, for the rest of this article I'll assume you're running it with the docker environment I provide <a href="https://github.com/stack-head/codedrunkdebugsober/tree/master/p2_repository">here</a>.</p>
<h1 id="configurepackagedrone">Configure Package Drone</h1>
<h2 id="createpackagedroneuser">Create Package Drone User</h2>
<p>Package Drone will create an admin user to initially login, you'll need to look at the docker logs to view it:</p>
<pre><code class="language-bash">docker logs package_drone
</code></pre>
<p>You'll see a snippet in the text output that looks like:<br>
=================== Admin&gt;&gt; ===================<br>
User: admin<br>
Password: 0123456789abcdef<br>
=================== &lt;&lt;Admin ===================</p>
<p>Use those credentials to log in to Package Drone's Web UI.  If you're using the docker setup I've provided, it'll be available on http://<machine-ip-address>:8083<br>
By design, the Admin user can't do much.  Best thing is to just use the admin account to create a new user with whatever credentials you want.  After creating the user, be sure to edit it, and assign the ADMIN and MANAGER roles to it.  Logout of the admin user, re-login in back as your new user.</machine-ip-address></p>
<h2 id="createadeploygroup">Create a Deploy Group</h2>
<ol>
<li>From the Package Drone toolbar, choose Administration-&gt;Deploy Keys.  Press the &quot;Add Group&quot; button, and provide &quot;Upload&quot; as the Group Name.</li>
<li>You'll also need to create a key to be associated with the group.  From the Deploy Groups UI menu, click on the group's ID (it'll be a <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">UUID</a>), and choose &quot;Create key&quot;.<br>
Name the key deploy, and you'll be rewarded with a screen like this:<br>
<img src="https://codedrunkdebugsober.com/content/images/2019/03/1_deploy_key.png" alt="1_deploy_key"></li>
</ol>
<p>Your password will be different from mine, note it down because we'll need it later.</p>
<p>You're ready to make your mirror repo!</p>
<h1 id="creatingap2mirrorchannel">Creating a P2 Mirror Channel</h1>
<p>Package Drone organizes the services it supports in &quot;Channels&quot;.  Channels can be individually accessed by their URL, and each can individual properties or actions that can be configured.  The only tough part here is that Package Drone mostly supports configuration through the UI-you can import and export configurations, but there isn't a great way to programmatically configure things.  So, through the UI we go:</p>
<p>From the main menu, create a new channel.  Call it P2Mirror and designate it as a Plain channel:<img src="https://codedrunkdebugsober.com/content/images/2019/03/2_create_channel.png" alt="2_create_channel"></p>
<h2 id="configuringthechannelsdeploykeys">Configuring the Channel's Deploy Keys</h2>
<p>Each Channel needs to configured with a Deploy Group to manage who can upload changes to the Channel.  Go to the P2Mirror Channel configuration page, select the &quot;Deploy Keys&quot; tab, and use the UI to add the &quot;Upload&quot; Group to the channel:<br>
<img src="https://codedrunkdebugsober.com/content/images/2019/03/3_channel_deploy_group.png" alt="3_channel_deploy_group"></p>
<h2 id="mirrorthep2repositories">Mirror the P2 repositories</h2>
<p>With the Channel created and capable of receiving files via http upload, we can use eclipse's built-in capability to mirror P2 repositories, and upload the repo's contents to the Channel.  A full script to make this easy <a href="https://github.com/stack-head/codedrunkdebugsober/tree/master/p2_repository">is available in the repo</a>, but here are the highlights:</p>
<p>Eclipse has a built-in application to mirror the metadata and contents of any P2 repository.  All we have to do is call it and provide an output directory to store the contents:</p>
<pre><code class="language-bash"># Download the metadata (content.jar)
/opt/eclipse/eclipse -nosplash -verbose -application org.eclipse.equinox.p2.metadata.repository.mirrorApplication -source &quot;$2&quot; -destination ./repo

# Download the artifacts
/opt/eclipse/eclipse -nosplash -verbose -application org.eclipse.equinox.p2.artifact.repository.mirrorApplication -source &quot;$2&quot; -destination ./repo
</code></pre>
<p>After zipping up the captured P2 repo, we can just use curl to upload it to Package Drone:</p>
<pre><code class="language-bash">EPOCH=$(date +%s)
curl -X PUT --data-binary @repo.zip &quot;http://$DEPLOY_USERNAME:$DEPLOY_PASSWORD@$PACKAGE_DRONE_URL/api/v3/upload/plain/channel/$P2_MIRROR_NAME/$1.zip?filesystem:name=$1&amp;filesystem:epoch=$EPOCH&quot;
</code></pre>
<p>The HTTP parameters of the curl call are attributes that will be assigned to the uploaded file in the Channel, the format is &quot;namespace:key&quot;=value.  These values will become important later as we configure the Package Drone Channel to manage it's own contents.  In further detail, we're creating a namespace called &quot;filesystem&quot;, and assigning the key &quot;name&quot; to a simple name we define, and the key &quot;epoch&quot; to the current datetime.  There is no special connotation to the &quot;filesystem&quot; namespace, it's just a sensible string value that we're choosing.</p>
<p>Run the script replicate_p2_repos.sh, or your version of it to mirror the repos to Package Drone.  Be prepared, you're probably going to be downloading multiple gigabytes here, so you can expect this script to run for some time.</p>
<h2 id="configuringtriggers">Configuring Triggers</h2>
<p>One limitation we still have is that if we upload multiple copies of the same mirrored repo to Package Drone, Package Drone will now have multiple copies of the same repo stored.  We really only want one copy of each mirror available, so if we run the mirroring script again it would be ideal if Package Drone would automatically delete the old copies of the mirrors.  This can be accomplished via the Channel's triggers.  By default, Package Drone has a &quot;Channel cleanup&quot; trigger that can be run everytime the contents of the repo has changed.  We'll configure the trigger so that if the Channel has two files with the same name, it will delete the older version.</p>
<p>Select the Triggers button and add a &quot;Post operation&quot; trigger, add a &quot;Channel cleanup&quot; trigger.</p>
<p>The UI for the channel triggers might not initially be intuitive-the trigger will have some aggregator values that look like suggestions, but are actually already enabled.<br>
Additionally, it's not enough to write in new aggregator and sorter names.  You must press the accompanying button to lock in the setting.</p>
<p>First, remove the default aggregators by selecting the 'x' next to each of their names.<br>
In the trigger's settings, set:</p>
<ul>
<li>Aggregator: <code>filesystem:name</code></li>
<li>Sorter: <code>filesystem:epoch</code>, select sorting order by pressing the &quot;Add ascending&quot; down arrow (it has the longer bars at the bottom).</li>
<li>Number of entries: <code>1</code></li>
<li>Only root artifacts</li>
<li>Ignore artifacts which don't have all aggregator fields.<br>
Configured correctly, the trigger description should read:<br>
<code>Group all artifacts by: filesystem:name then sort by the values of: filesystem:epoch then delete all but the last 1 entries of each group. Only root artifacts will be processed. Artifacts which are missing an aggregator field will be ignored.</code><br>
<img src="https://codedrunkdebugsober.com/content/images/2019/03/4_set_post_triggers.png" alt="4_set_post_triggers"></li>
</ul>
<h2 id="configuringmaventoreferenceyourmirroredrepo">Configuring Maven to reference your mirrored repo</h2>
<p>With a working P2 Mirror repository running, all that's left is to let Maven know about it.  There's a number of different ways to reference remote repositories and mirrors, but the simplest way is to create or edit your version of settings.xml.  settings.xml is typically located at ~/.m2/settings.xml, it may or may not already exist on your development system.  In order to let maven know about available an P2 mirror, you simply add an entry like this:</p>
<pre><code>&lt;mirror&gt;
  &lt;id&gt;oxygen-drone&lt;/id&gt;
  &lt;mirrorOf&gt;oxygen&lt;/mirrorOf&gt;
  &lt;url&gt;http://192.168.1.101:8083/unzip/newestByName/P2Mirror/oxygen.zip&lt;/url&gt;
  &lt;layout&gt;p2&lt;/layout&gt;
  &lt;mirrorOfLayouts&gt;p2&lt;/mirrorOfLayouts&gt;
&lt;/mirror&gt;
</code></pre>
<p>This assumes your Package Drone instance is available at 192.168.1.101, port 8083, and the channel is still called P2Mirror.  It's also assumes in your project's pom or targetfile you have a reference to the mirrored repository marked with an ID &quot;oxygen&quot; - Maven isn't smart enough to check mirrors by url or content, it's relying on you to correctly match up <id> and <mirrorof> values.</mirrorof></id></p>
<p>Note: if you want, you can put an entry like this in settings.xml</p>
<pre><code># NOT recommended!!!
&lt;profiles&gt;
  &lt;profile&gt;
    &lt;id&gt;development&lt;/id&gt;
    &lt;repositories&gt;
  	&lt;repository&gt;
  	  &lt;id&gt;oxygen&lt;/id&gt;
  	  &lt;url&gt;http://download.eclipse.org/releases/oxygen/201709271000&lt;/url&gt;
  	  &lt;layout&gt;p2&lt;/layout&gt;
  	  &lt;releases&gt;
  	    &lt;updatePolicy&gt;daily&lt;/updatePolicy&gt;
  	  &lt;/releases&gt;
  	&lt;/repository&gt;
    &lt;/repositories&gt;
  &lt;/profile&gt;
&lt;/profiles&gt;
&lt;activeProfiles&gt;
  &lt;activeProfile&gt;development&lt;/activeProfile&gt;
&lt;/activeProfiles&gt;
</code></pre>
<p>This can be a quick and visible way of associating a P2 repo with it's mirror.  However, adding this in your settings.xml file effectively adds this repo as a package source to ALL projects you're building in the machine, which will force it to do more package resolution than is necessary, and will probably end up giving you package conflicts.  So don't do it.  Instead, you'll have to go through your project's pom.xml files or Eclipse Target Definition file, and manually set the ID field of all your referenced P2 repositories.</p>
<h1 id="howdoiknowitsworking">How do I know it's working?</h1>
<p>This is easy:mvn clean install<br>
If you have everyting configured correctly, you can watch dependency bundle downloads in the build log, and you'll see them coming from your mirror's local IP address instead of the remote repository.</p>
<h1 id="wrappingupandsources">Wrapping up and sources</h1>
<p>The Dockerfile environment and the script to mirror the remote repositories can be found <a href="https://github.com/stack-head/codedrunkdebugsober/tree/master/p2_repository">here</a>.  Good luck have fun.</p>
<p></p>]]></content:encoded></item></channel></rss>