mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-29 03:52:38 +02:00
6815 lines
No EOL
763 KiB
XML
6815 lines
No EOL
763 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexis Métaireau - code</title><link href="https://blog.notmyidea.org/" rel="alternate"></link><link href="https://blog.notmyidea.org/feeds/code.atom.xml" rel="self"></link><id>https://blog.notmyidea.org/</id><updated>2023-10-18T00:00:00+02:00</updated><entry><title>Using DISTINCT in Parent-Child Relationships</title><link href="https://blog.notmyidea.org/using-distinct-in-parent-child-relationships.html" rel="alternate"></link><published>2023-10-18T00:00:00+02:00</published><updated>2023-10-18T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-10-18:/using-distinct-in-parent-child-relationships.html</id><summary type="html"><p>Let&#8217;s say you have a model defined like this, with a Parent and a Child&nbsp;table:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Parent</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">&quot;parent&quot;</span>
|
||
<span class="nb">id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
|
||
<span class="n">childs</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="s2">&quot;Child&quot;</span><span class="p">]]</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span><span class="n">back_populates</span><span class="o">=</span><span class="s2">&quot;parent&quot;</span><span class="p">)</span>
|
||
|
||
|
||
<span class="k">class</span> <span class="nc">Child</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">&quot;child&quot;</span>
|
||
<span class="nb">id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">parent_id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column …</span></code></pre></div></summary><content type="html"><p>Let&#8217;s say you have a model defined like this, with a Parent and a Child&nbsp;table:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Parent</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">&quot;parent&quot;</span>
|
||
<span class="nb">id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
|
||
<span class="n">childs</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="s2">&quot;Child&quot;</span><span class="p">]]</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span><span class="n">back_populates</span><span class="o">=</span><span class="s2">&quot;parent&quot;</span><span class="p">)</span>
|
||
|
||
|
||
<span class="k">class</span> <span class="nc">Child</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">&quot;child&quot;</span>
|
||
<span class="nb">id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">parent_id</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">(</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s2">&quot;parent.id&quot;</span><span class="p">))</span>
|
||
<span class="n">parent</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="s2">&quot;Parent&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span><span class="n">back_populates</span><span class="o">=</span><span class="s2">&quot;children&quot;</span><span class="p">)</span>
|
||
|
||
<span class="n">born_at</span><span class="p">:</span> <span class="n">Mapped</span><span class="p">[</span><span class="n">datetime</span><span class="p">]</span> <span class="o">=</span> <span class="n">mapped_column</span><span class="p">()</span>
|
||
</code></pre></div>
|
||
|
||
<p>I&#8217;ve tried many ways, with complex subqueries and the like, before finding out <a href="https://www.sqlitetutorial.net/sqlite-distinct/">the <span class="caps">DISTINCT</span> <span class="caps">SQL</span> statement</a>.</p>
|
||
<p>So, if you want to retrieve the parent with it&#8217;s more recent child, you can do it like&nbsp;this:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">results</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">db</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">Parent</span><span class="p">,</span> <span class="n">Child</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">Child</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">distinct</span><span class="p">(</span><span class="n">Parent</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="n">Parent</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="n">desc</span><span class="p">(</span><span class="n">Child</span><span class="o">.</span><span class="n">born_at</span><span class="p">))</span>
|
||
<span class="o">.</span><span class="n">all</span><span class="p">()</span>
|
||
<span class="p">)</span>
|
||
</code></pre></div></content><category term="code"></category><category term="SQL"></category><category term="SQLAlchemy"></category><category term="Python"></category></entry><entry><title>Convert string to duration</title><link href="https://blog.notmyidea.org/convert-string-to-duration.html" rel="alternate"></link><published>2023-10-11T00:00:00+02:00</published><updated>2023-10-11T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-10-11:/convert-string-to-duration.html</id><summary type="html"><p>I found myself wanting to convert a string to a duration (int), for some&nbsp;configuration.</p>
|
||
<p>Something you can call like&nbsp;this:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">string_to_duration</span><span class="p">(</span><span class="s2">&quot;1d&quot;</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="s2">&quot;days&quot;</span><span class="p">)</span>
|
||
<span class="n">string_to_duration</span><span class="p">(</span><span class="s2">&quot;1d&quot;</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="s2">&quot;hours&quot;</span><span class="p">)</span>
|
||
<span class="n">string_to_duration</span><span class="p">(</span><span class="s2">&quot;3m&quot;</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="s2">&quot;hours&quot;</span><span class="p">)</span>
|
||
<span class="n">string_to_duration</span><span class="p">(</span><span class="s2">&quot;3m&quot;</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="s2">&quot;minutes&quot;</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<p>The code&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Literal</span>
|
||
|
||
<span class="k">def</span> <span class="nf">string_to_duration</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">target</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&quot;days …</span></code></pre></div></summary><content type="html"><p>I found myself wanting to convert a string to a duration (int), for some&nbsp;configuration.</p>
|
||
<p>Something you can call like&nbsp;this:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">string_to_duration</span><span class="p">(</span><span class="s2">&quot;1d&quot;</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="s2">&quot;days&quot;</span><span class="p">)</span>
|
||
<span class="n">string_to_duration</span><span class="p">(</span><span class="s2">&quot;1d&quot;</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="s2">&quot;hours&quot;</span><span class="p">)</span>
|
||
<span class="n">string_to_duration</span><span class="p">(</span><span class="s2">&quot;3m&quot;</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="s2">&quot;hours&quot;</span><span class="p">)</span>
|
||
<span class="n">string_to_duration</span><span class="p">(</span><span class="s2">&quot;3m&quot;</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="s2">&quot;minutes&quot;</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<p>The code&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Literal</span>
|
||
|
||
<span class="k">def</span> <span class="nf">string_to_duration</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">target</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&quot;days&quot;</span><span class="p">,</span> <span class="s2">&quot;hours&quot;</span><span class="p">,</span> <span class="s2">&quot;minutes&quot;</span><span class="p">]):</span>
|
||
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Convert a string to a number of hours, days or minutes&quot;&quot;&quot;</span>
|
||
<span class="n">num</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">isdigit</span><span class="p">,</span> <span class="n">value</span><span class="p">)))</span>
|
||
|
||
<span class="c1"># It&#39;s not possible to convert from a smaller unit to a greater one:</span>
|
||
<span class="c1"># - hours and minutes cannot be converted to days</span>
|
||
<span class="c1"># - minutes cannot be converted to hours</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">target</span> <span class="o">==</span> <span class="s2">&quot;days&quot;</span> <span class="ow">and</span> <span class="p">(</span><span class="s2">&quot;h&quot;</span> <span class="ow">in</span> <span class="n">value</span> <span class="ow">or</span> <span class="s2">&quot;m&quot;</span> <span class="ow">in</span> <span class="n">value</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;mo&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)))</span> <span class="ow">or</span> <span class="p">(</span>
|
||
<span class="n">target</span> <span class="o">==</span> <span class="s2">&quot;hours&quot;</span> <span class="ow">and</span> <span class="s2">&quot;m&quot;</span> <span class="ow">in</span> <span class="n">value</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;mo&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
|
||
<span class="p">):</span>
|
||
<span class="n">msg</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="s2">&quot;Durations cannot be converted from a smaller to a greater unit. &quot;</span>
|
||
<span class="sa">f</span><span class="s2">&quot;(trying to convert &#39;</span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2">&#39; to </span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">)&quot;</span>
|
||
<span class="p">)</span>
|
||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Consider we&#39;re converting to minutes, do the eventual multiplication at the end.</span>
|
||
<span class="k">if</span> <span class="s2">&quot;h&quot;</span> <span class="ow">in</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="n">num</span> <span class="o">=</span> <span class="n">num</span> <span class="o">*</span> <span class="mi">60</span>
|
||
<span class="k">elif</span> <span class="s2">&quot;d&quot;</span> <span class="ow">in</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="n">num</span> <span class="o">=</span> <span class="n">num</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">24</span>
|
||
<span class="k">elif</span> <span class="s2">&quot;w&quot;</span> <span class="ow">in</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="n">num</span> <span class="o">=</span> <span class="n">num</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">7</span>
|
||
<span class="k">elif</span> <span class="s2">&quot;mo&quot;</span> <span class="ow">in</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="n">num</span> <span class="o">=</span> <span class="n">num</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">30</span> <span class="c1"># considers 30d in a month</span>
|
||
<span class="k">elif</span> <span class="s2">&quot;y&quot;</span> <span class="ow">in</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="n">num</span> <span class="o">=</span> <span class="n">num</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">365</span>
|
||
<span class="k">elif</span> <span class="s2">&quot;m&quot;</span> <span class="ow">in</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="n">num</span> <span class="o">=</span> <span class="n">num</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Invalid duration value&quot;</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="n">target</span> <span class="o">==</span> <span class="s2">&quot;hours&quot;</span><span class="p">:</span>
|
||
<span class="n">num</span> <span class="o">=</span> <span class="n">num</span> <span class="o">/</span> <span class="mi">60</span>
|
||
<span class="k">elif</span> <span class="n">target</span> <span class="o">==</span> <span class="s2">&quot;days&quot;</span><span class="p">:</span>
|
||
<span class="n">num</span> <span class="o">=</span> <span class="n">num</span> <span class="o">/</span> <span class="mi">60</span> <span class="o">/</span> <span class="mi">24</span>
|
||
|
||
<span class="k">return</span> <span class="n">num</span>
|
||
</code></pre></div></content><category term="code"></category><category term="python"></category><category term="conversion"></category></entry><entry><title>llm command-line tips</title><link href="https://blog.notmyidea.org/llm-command-line-tips.html" rel="alternate"></link><published>2023-09-27T00:00:00+02:00</published><updated>2023-09-27T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-09-27:/llm-command-line-tips.html</id><summary type="html"><p>I&#8217;m using <a href="https://llm.datasette.io">llm</a> more and more, and today I had to find back prompts I used in the past. Here is a command I&#8217;ve been using, which allows me to filter the results based on what I want. It leverages <a href="https://sqlutils.datasette.io">sql-utils</a>, a cli tool which is able to …</p></summary><content type="html"><p>I&#8217;m using <a href="https://llm.datasette.io">llm</a> more and more, and today I had to find back prompts I used in the past. Here is a command I&#8217;ve been using, which allows me to filter the results based on what I want. It leverages <a href="https://sqlutils.datasette.io">sql-utils</a>, a cli tool which is able to talk to a <span class="caps">SQLITE</span> database and answer in json, and <a href="https://github.com/jqlang/jq">jq</a> a command-line tool capable of doing requests for&nbsp;json.</p>
|
||
<p>All in all, it&#8217;s pretty satisfying to use. I finally got a simple way to query databases! I&#8217;m also using <a href="https://github.com/charmbracelet/glow">glow</a>, which is capable of transforming markdown into a better version on the&nbsp;terminal.</p>
|
||
<div class="highlight"><pre><span></span><code>sqlite-utils<span class="w"> </span><span class="s2">&quot;</span><span class="k">$(</span>llm<span class="w"> </span>logs<span class="w"> </span>path<span class="k">)</span><span class="s2">&quot;</span><span class="w"> </span><span class="s2">&quot;SELECT * FROM responses WHERE prompt LIKE &#39;%search%&#39;&quot;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>jq<span class="w"> </span><span class="s1">&#39;.[].response&#39;</span><span class="w"> </span>-r<span class="w"> </span><span class="p">|</span><span class="w"> </span>glow
|
||
</code></pre></div>
|
||
|
||
<p>Which got me a colored response&nbsp;:-)</p></content><category term="code"></category><category term="python"></category><category term="llm"></category><category term="bash"></category><category term="sqlite"></category></entry><entry><title>Setting up a IRC Bouncer with ZNC</title><link href="https://blog.notmyidea.org/setting-up-a-irc-bouncer-with-znc.html" rel="alternate"></link><published>2023-09-27T00:00:00+02:00</published><updated>2023-09-27T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-09-27:/setting-up-a-irc-bouncer-with-znc.html</id><summary type="html"><p>It&#8217;s been a while since I&#8217;ve used <span class="caps">IRC</span>, but I needed to connect to it today to discuss around <a href="https://docs.peewee-orm.com">Peewee</a>.</p>
|
||
<p>The main issue with <span class="caps">IRC</span> is that you need to be connected to see the answer, and to get the context of the conversation. Unless&#8230; you set up …</p></summary><content type="html"><p>It&#8217;s been a while since I&#8217;ve used <span class="caps">IRC</span>, but I needed to connect to it today to discuss around <a href="https://docs.peewee-orm.com">Peewee</a>.</p>
|
||
<p>The main issue with <span class="caps">IRC</span> is that you need to be connected to see the answer, and to get the context of the conversation. Unless&#8230; you set up a&nbsp;bouncer.</p>
|
||
<p>The bouncer is named <a href="https://znc.in"><span class="caps">ZNC</span></a>, and the <span class="caps">IRC</span> client I use is <a href="https://weechat.org">Weechat</a>.</p>
|
||
<p>So, that&#8217;s what I&nbsp;did:</p>
|
||
<h2 id="installation-of-znc">Installation of <span class="caps">ZNC</span></h2>
|
||
<div class="highlight"><pre><span></span><code>apt<span class="w"> </span>install<span class="w"> </span>znc
|
||
sudo<span class="w"> </span>-u<span class="w"> </span>_znc<span class="w"> </span>/usr/bin/znc<span class="w"> </span>--datadir<span class="o">=</span>/var/lib/znc<span class="w"> </span>--makeconf
|
||
sudo<span class="w"> </span>systemctl<span class="w"> </span><span class="nb">enable</span><span class="w"> </span>znc
|
||
</code></pre></div>
|
||
|
||
<p>You can answer the questions asked by <code>--makeconf</code>, it will generate you a configuration file like this (stored in <code>/var/lib/znc/configurations/znc.conf</code>):</p>
|
||
<div class="highlight"><pre><span></span><code>AnonIPLimit<span class="w"> </span>=<span class="w"> </span>10
|
||
AuthOnlyViaModule<span class="w"> </span>=<span class="w"> </span>false
|
||
ConfigWriteDelay<span class="w"> </span>=<span class="w"> </span>0
|
||
ConnectDelay<span class="w"> </span>=<span class="w"> </span>5
|
||
HideVersion<span class="w"> </span>=<span class="w"> </span>false
|
||
LoadModule<span class="w"> </span>=<span class="w"> </span>webadmin
|
||
MaxBufferSize<span class="w"> </span>=<span class="w"> </span>500
|
||
ProtectWebSessions<span class="w"> </span>=<span class="w"> </span>true
|
||
SSLCertFile<span class="w"> </span>=<span class="w"> </span>/var/lib/znc/znc.pem
|
||
SSLDHParamFile<span class="w"> </span>=<span class="w"> </span>/var/lib/znc/znc.pem
|
||
SSLKeyFile<span class="w"> </span>=<span class="w"> </span>/var/lib/znc/znc.pem
|
||
ServerThrottle<span class="w"> </span>=<span class="w"> </span>30
|
||
Version<span class="w"> </span>=<span class="w"> </span>1.8.2
|
||
|
||
<span class="nt">&lt;Listener</span><span class="w"> </span><span class="err">listener0</span><span class="nt">&gt;</span>
|
||
<span class="w"> </span>AllowIRC<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>AllowWeb<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>IPv4<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>IPv6<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>Port<span class="w"> </span>=<span class="w"> </span>6697
|
||
<span class="w"> </span>SSL<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>URIPrefix<span class="w"> </span>=<span class="w"> </span>/
|
||
<span class="nt">&lt;/Listener&gt;</span>
|
||
|
||
<span class="nt">&lt;User</span><span class="w"> </span><span class="err">alexis</span><span class="nt">&gt;</span>
|
||
<span class="w"> </span>Admin<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>Allow<span class="w"> </span>=<span class="w"> </span>*
|
||
<span class="w"> </span>AltNick<span class="w"> </span>=<span class="w"> </span>alexis_
|
||
<span class="w"> </span>AppendTimestamp<span class="w"> </span>=<span class="w"> </span>false
|
||
<span class="w"> </span>AuthOnlyViaModule<span class="w"> </span>=<span class="w"> </span>false
|
||
<span class="w"> </span>AutoClearChanBuffer<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>AutoClearQueryBuffer<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>BindHost<span class="w"> </span>=<span class="w"> </span>skate.notmyidea.org
|
||
<span class="w"> </span>ChanBufferSize<span class="w"> </span>=<span class="w"> </span>50
|
||
<span class="w"> </span>DenyLoadMod<span class="w"> </span>=<span class="w"> </span>false
|
||
<span class="w"> </span>DenySetBindHost<span class="w"> </span>=<span class="w"> </span>false
|
||
<span class="w"> </span>Ident<span class="w"> </span>=<span class="w"> </span>alexis
|
||
<span class="w"> </span>JoinTries<span class="w"> </span>=<span class="w"> </span>10
|
||
<span class="w"> </span>LoadModule<span class="w"> </span>=<span class="w"> </span>chansaver
|
||
<span class="w"> </span>LoadModule<span class="w"> </span>=<span class="w"> </span>controlpanel
|
||
<span class="w"> </span>MaxJoins<span class="w"> </span>=<span class="w"> </span>0
|
||
<span class="w"> </span>MaxNetworks<span class="w"> </span>=<span class="w"> </span>1
|
||
<span class="w"> </span>MaxQueryBuffers<span class="w"> </span>=<span class="w"> </span>50
|
||
<span class="w"> </span>MultiClients<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>Nick<span class="w"> </span>=<span class="w"> </span>alexis
|
||
<span class="w"> </span>NoTrafficTimeout<span class="w"> </span>=<span class="w"> </span>180
|
||
<span class="w"> </span>PrependTimestamp<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>QueryBufferSize<span class="w"> </span>=<span class="w"> </span>50
|
||
<span class="w"> </span>QuitMsg<span class="w"> </span>=<span class="w"> </span>See<span class="w"> </span>you<span class="w"> </span>:)
|
||
<span class="w"> </span>RealName<span class="w"> </span>=<span class="w"> </span>N/A
|
||
<span class="w"> </span>StatusPrefix<span class="w"> </span>=<span class="w"> </span>*
|
||
<span class="w"> </span>TimestampFormat<span class="w"> </span>=<span class="w"> </span>[%H:%M:%S]
|
||
|
||
<span class="w"> </span><span class="nt">&lt;Network</span><span class="w"> </span><span class="err">liberachat</span><span class="nt">&gt;</span>
|
||
<span class="w"> </span>FloodBurst<span class="w"> </span>=<span class="w"> </span>9
|
||
<span class="w"> </span>FloodRate<span class="w"> </span>=<span class="w"> </span>2.00
|
||
<span class="w"> </span>IRCConnectEnabled<span class="w"> </span>=<span class="w"> </span>true
|
||
<span class="w"> </span>JoinDelay<span class="w"> </span>=<span class="w"> </span>0
|
||
<span class="w"> </span>LoadModule<span class="w"> </span>=<span class="w"> </span>simple_away
|
||
<span class="w"> </span>RealName<span class="w"> </span>=<span class="w"> </span>N/A
|
||
<span class="w"> </span>Server<span class="w"> </span>=<span class="w"> </span>irc.libera.chat<span class="w"> </span>+6697
|
||
<span class="w"> </span>TrustAllCerts<span class="w"> </span>=<span class="w"> </span>false
|
||
<span class="w"> </span>TrustPKI<span class="w"> </span>=<span class="w"> </span>true
|
||
|
||
<span class="w"> </span><span class="nt">&lt;Chan</span><span class="w"> </span><span class="err">#peewee</span><span class="nt">&gt;</span>
|
||
<span class="w"> </span><span class="nt">&lt;/Chan&gt;</span>
|
||
<span class="w"> </span><span class="nt">&lt;/Network&gt;</span>
|
||
|
||
<span class="w"> </span><span class="nt">&lt;Pass</span><span class="w"> </span><span class="err">password</span><span class="nt">&gt;</span>
|
||
<span class="w"> </span>Hash<span class="w"> </span>=<span class="w"> </span>REDACTED
|
||
<span class="w"> </span>Method<span class="w"> </span>=<span class="w"> </span>SHA256
|
||
<span class="w"> </span>Salt<span class="w"> </span>=<span class="w"> </span>REDACTED
|
||
<span class="w"> </span><span class="nt">&lt;/Pass&gt;</span>
|
||
<span class="nt">&lt;/User&gt;</span>
|
||
</code></pre></div>
|
||
|
||
<p>You can access a web interface on the exposed port. I had to make a change in my Firefox configuration, in <code>about:config</code>, set <code>network.security.ports.banned.override</code> to <code>6697</code>, otherwise, Firefox prevents you from connecting to these ports (which might actually be a good&nbsp;idea).</p>
|
||
<h2 id="weechat-configuration">Weechat&nbsp;configuration</h2>
|
||
<p>Now, to use this in weechat, here are some useful commands. First, get the fingerprint of the <span class="caps">SSL</span> certificate generated on your&nbsp;server:</p>
|
||
<div class="highlight"><pre><span></span><code>cat<span class="w"> </span>/var/log/znc/znc.pem<span class="w"> </span><span class="p">|</span><span class="w"> </span>openssl<span class="w"> </span>x509<span class="w"> </span>-sha512<span class="w"> </span>-fingerprint<span class="w"> </span>-noout<span class="w"> </span><span class="p">|</span><span class="w"> </span>tr<span class="w"> </span>-d<span class="w"> </span><span class="s1">&#39;:&#39;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>tr<span class="w"> </span><span class="s1">&#39;A-Z&#39;</span><span class="w"> </span><span class="s1">&#39;a-z&#39;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>cut<span class="w"> </span>-d<span class="w"> </span><span class="o">=</span><span class="w"> </span>-f<span class="w"> </span><span class="m">2</span>
|
||
</code></pre></div>
|
||
|
||
<p>Then, in weechat&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>/server add znc host/6697 -tls -username=&lt;username&gt; -password=&lt;yourpass&gt; -autoconnect
|
||
/set irc.server.znc.tls_fingerprint &lt;fingerprint-goes-here&gt;
|
||
/connect znc
|
||
</code></pre></div>
|
||
|
||
<p>And you should be all&nbsp;set!</p>
|
||
<p>Resources : <a href="https://wiki.znc.in/Weechat">The <span class="caps">ZNC</span> Wiki on Weechat</a> and the <a href="https://wiki.debian.org/ZNC">Debian page on <span class="caps">ZNC</span></a></p></content><category term="code"></category><category term="ZNC"></category><category term="Weechat"></category><category term="IRC"></category></entry><entry><title>How to run the vigogne model locally</title><link href="https://blog.notmyidea.org/how-to-run-the-vigogne-model-locally.html" rel="alternate"></link><published>2023-09-22T00:00:00+02:00</published><updated>2023-09-22T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-09-22:/how-to-run-the-vigogne-model-locally.html</id><summary type="html">
|
||
<p><a href="https://github.com/bofenghuang/vigogne">Vigogne</a> is a <span class="caps">LLM</span> model based on <span class="caps">LLAMA2</span>, but trained with french data. As I&#8217;m working mostly in french, it might be useful. The current models that I can get locally are in&nbsp;english.</p>
|
||
<p>The information I&#8217;ve found online are scarse and not so easy to follow, so …</p></summary><content type="html">
|
||
<p><a href="https://github.com/bofenghuang/vigogne">Vigogne</a> is a <span class="caps">LLM</span> model based on <span class="caps">LLAMA2</span>, but trained with french data. As I&#8217;m working mostly in french, it might be useful. The current models that I can get locally are in&nbsp;english.</p>
|
||
<p>The information I&#8217;ve found online are scarse and not so easy to follow, so here is a step by step tutorial you can follow. I&#8217;m using <a href="https://pipenv.pypa.io/en/latest/">pipenv</a> almost everywhere now, it&#8217;s so easy&nbsp;:-)</p>
|
||
<div class="highlight"><pre><span></span><code>llm<span class="w"> </span>install<span class="w"> </span>-U<span class="w"> </span>llm-llama-cpp
|
||
wget<span class="w"> </span>https://huggingface.co/TheBloke/Vigogne-2-7B-Chat-GGUF/resolve/main/vigogne-2-7b-chat.Q4_K_M.gguf
|
||
llm<span class="w"> </span>llama-cpp<span class="w"> </span>add-model<span class="w"> </span>vigogne-2-7b-chat.Q4_K_M.gguf<span class="w"> </span>-a<span class="w"> </span>vigogne
|
||
llm<span class="w"> </span>models<span class="w"> </span>default<span class="w"> </span>vigogne
|
||
</code></pre></div></content><category term="code"></category><category term="llm"></category></entry><entry><title>Creating a simple command line to post snippets on Gitlab</title><link href="https://blog.notmyidea.org/creating-a-simple-command-line-to-post-snippets-on-gitlab.html" rel="alternate"></link><published>2023-09-18T00:00:00+02:00</published><updated>2023-09-18T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-09-18:/creating-a-simple-command-line-to-post-snippets-on-gitlab.html</id><summary type="html">
|
||
<p>I&#8217;m trying to get away from Github, and one thing that I find useful is the <a href="https://gist.github.com">gist</a> utility they&#8217;re providing. Seems that gitlab provides a similar&nbsp;tool.</p>
|
||
<p>You can use it using <a href="https://python-gitlab.readthedocs.io/">python-gitlab</a>:</p>
|
||
<div class="highlight"><pre><span></span><code>pipx<span class="w"> </span>install<span class="w"> </span>python-gitlab
|
||
</code></pre></div>
|
||
|
||
<p>And then&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>gitlab<span class="w"> </span>snippet<span class="w"> </span>create<span class="w"> </span>--title<span class="o">=</span><span class="s2">&quot;youpi&quot;</span><span class="w"> </span>--file-name<span class="o">=</span><span class="s2">&quot;snip.py&quot;</span><span class="w"> </span>--content<span class="w"> </span>snip …</code></pre></div></summary><content type="html">
|
||
<p>I&#8217;m trying to get away from Github, and one thing that I find useful is the <a href="https://gist.github.com">gist</a> utility they&#8217;re providing. Seems that gitlab provides a similar&nbsp;tool.</p>
|
||
<p>You can use it using <a href="https://python-gitlab.readthedocs.io/">python-gitlab</a>:</p>
|
||
<div class="highlight"><pre><span></span><code>pipx<span class="w"> </span>install<span class="w"> </span>python-gitlab
|
||
</code></pre></div>
|
||
|
||
<p>And then&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>gitlab<span class="w"> </span>snippet<span class="w"> </span>create<span class="w"> </span>--title<span class="o">=</span><span class="s2">&quot;youpi&quot;</span><span class="w"> </span>--file-name<span class="o">=</span><span class="s2">&quot;snip.py&quot;</span><span class="w"> </span>--content<span class="w"> </span>snip.py<span class="w"> </span>--visibility<span class="o">=</span><span class="s2">&quot;public&quot;</span>
|
||
</code></pre></div>
|
||
|
||
<p>I now wanted a small bash script which will just get the name of the file and infer all the parameters. I asked <span class="caps">GPT</span>-4, and iterated on its&nbsp;answer.</p>
|
||
<p>Here&#8217;s the resulting bash&nbsp;script:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/bash</span>
|
||
|
||
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span>
|
||
<span class="k">then</span>
|
||
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;Please provide a filename&quot;</span>
|
||
<span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">1</span>
|
||
<span class="k">fi</span>
|
||
|
||
<span class="nv">file</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span>
|
||
<span class="nv">base</span><span class="o">=</span><span class="k">$(</span>basename<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$file</span><span class="s2">&quot;</span><span class="k">)</span>
|
||
<span class="nv">title</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$base</span><span class="s2">&quot;</span>
|
||
<span class="nv">visibility</span><span class="o">=</span><span class="s2">&quot;public&quot;</span>
|
||
|
||
<span class="c1"># Use `cat` to fetch the content of the file</span>
|
||
<span class="nv">content</span><span class="o">=</span><span class="k">$(</span>cat<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$file</span><span class="s2">&quot;</span><span class="k">)</span>
|
||
|
||
<span class="nv">result</span><span class="o">=</span><span class="k">$(</span>gitlab<span class="w"> </span>snippet<span class="w"> </span>create<span class="w"> </span>--title<span class="o">=</span><span class="s2">&quot;</span><span class="nv">$title</span><span class="s2">&quot;</span><span class="w"> </span>--file-name<span class="o">=</span><span class="s2">&quot;</span><span class="nv">$title</span><span class="s2">&quot;</span><span class="w"> </span>--content<span class="o">=</span><span class="s2">&quot;</span><span class="nv">$content</span><span class="s2">&quot;</span><span class="w"> </span>--visibility<span class="o">=</span><span class="s2">&quot;</span><span class="nv">$visibility</span><span class="s2">&quot;</span><span class="k">)</span>
|
||
|
||
<span class="nv">id</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$result</span><span class="s2">&quot;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>awk<span class="w"> </span><span class="s1">&#39;/id: / { print $2 }&#39;</span><span class="k">)</span>
|
||
<span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;https://gitlab.com/-/snippets/</span><span class="nv">$id</span><span class="s2">&quot;</span>
|
||
</code></pre></div>
|
||
|
||
<p>I can now do <code>snip README.md</code> and that will create the snippet for me&nbsp;:-)</p></content><category term="code"></category></entry><entry><title>Creating an online space to share markdown files</title><link href="https://blog.notmyidea.org/creating-an-online-space-to-share-markdown-files.html" rel="alternate"></link><published>2023-09-17T00:00:00+02:00</published><updated>2023-09-17T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-09-17:/creating-an-online-space-to-share-markdown-files.html</id><summary type="html">
|
||
<p>I wanted to create a space on my server where I can upload markdown files and have them rendered directly, for them to be shared with other&nbsp;people.</p>
|
||
<p>I stumbled on <a href="https://github.com/ukarim/ngx_markdown_filter_module">the markdown module for nginx</a> which does exactly what I want, but seemed to ask for compilation of nginx …</p></summary><content type="html">
|
||
<p>I wanted to create a space on my server where I can upload markdown files and have them rendered directly, for them to be shared with other&nbsp;people.</p>
|
||
<p>I stumbled on <a href="https://github.com/ukarim/ngx_markdown_filter_module">the markdown module for nginx</a> which does exactly what I want, but seemed to ask for compilation of nginx, which wasn&#8217;t exactly what I wanted in terms of maintainability (it would make it complicated to update&nbsp;it)</p>
|
||
<p>I then thought that the <a href="https://caddyserver.com/">Caddy</a> server does that by default, and so I&#8217;ve tested it out. Turns out it&#8217;s not, but it offers ways to do this thanks to its template&nbsp;mecanism.</p>
|
||
<p>It also, <a href="https://caddyserver.com/docs/automatic-https">setups automatically and transparently <span class="caps">SSL</span> certificates</a> for you (using Let&#8217;s Encrypt!), so I wanted to have a&nbsp;look.</p>
|
||
<p>Here is the Caddy configuration file I&#8217;m now using&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>md.notmyidea.org {
|
||
root <span class="gs">* /home/caddy/md.notmyidea.org</span>
|
||
<span class="gs"> rewrite *</span> /index.html
|
||
file_server
|
||
templates
|
||
encode zstd gzip
|
||
|
||
}
|
||
</code></pre></div>
|
||
|
||
<p>And the&nbsp;template:</p>
|
||
<div class="highlight"><pre><span></span><code>{{$pathParts := splitList &quot;/&quot; .OriginalReq.URL.Path}}
|
||
{{$markdownFilename := default &quot;index&quot; (slice $pathParts 1 | join &quot;/&quot;)}}
|
||
|
||
{{if not (fileExists $markdownFilename)}}
|
||
{{httpError 404}}
|
||
{{end}}
|
||
|
||
{{$markdownFile := (include $markdownFilename | splitFrontMatter)}}
|
||
<span class="cp">&lt;!DOCTYPE html&gt;</span>
|
||
<span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
|
||
<span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
|
||
<span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>{{ $markdownFilename }}<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
|
||
<span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
|
||
<span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
|
||
{{ markdown $markdownFile.Body }}
|
||
<span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
|
||
<span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
|
||
</code></pre></div>
|
||
|
||
<p>This is a minimalistic version, but it works&nbsp;:-)</p></content><category term="code"></category></entry><entry><title>Conversion d’un fichier svg en favicon.ico</title><link href="https://blog.notmyidea.org/conversion-dun-fichier-svg-en-faviconico.html" rel="alternate"></link><published>2023-09-13T00:00:00+02:00</published><updated>2023-09-13T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-09-13:/conversion-dun-fichier-svg-en-faviconico.html</id><summary type="html">
|
||
<p>Il y a plusieurs sites qui permettent de faire ça automatiquement, mais j&#8217;aime bien faire les choses depuis mon terminal, voici donc une commande qui permet de faire ça simplement, en utilisant <a href="https://imagemagick.org/">ImageMagick</a>. Merci à <a href="https://gist.github.com/azam/3b6995a29b9f079282f3">ce&nbsp;gist</a></p>
|
||
<div class="highlight"><pre><span></span><code>convert<span class="w"> </span>-density<span class="w"> </span>256x256<span class="w"> </span>-background<span class="w"> </span>transparent<span class="w"> </span>favicon.svg<span class="w"> </span>-define<span class="w"> </span>icon:auto-resize<span class="w"> </span>-colors<span class="w"> </span><span class="m">256 …</span></code></pre></div></summary><content type="html">
|
||
<p>Il y a plusieurs sites qui permettent de faire ça automatiquement, mais j&#8217;aime bien faire les choses depuis mon terminal, voici donc une commande qui permet de faire ça simplement, en utilisant <a href="https://imagemagick.org/">ImageMagick</a>. Merci à <a href="https://gist.github.com/azam/3b6995a29b9f079282f3">ce&nbsp;gist</a></p>
|
||
<div class="highlight"><pre><span></span><code>convert<span class="w"> </span>-density<span class="w"> </span>256x256<span class="w"> </span>-background<span class="w"> </span>transparent<span class="w"> </span>favicon.svg<span class="w"> </span>-define<span class="w"> </span>icon:auto-resize<span class="w"> </span>-colors<span class="w"> </span><span class="m">256</span><span class="w"> </span>favicon.ico
|
||
</code></pre></div></content><category term="code"></category></entry><entry><title>Découverte de nouveaux outils pour le développement: LLM, Helix et plus</title><link href="https://blog.notmyidea.org/decouverte-de-nouveaux-outils-pour-le-developpement-llm-helix-et-plus.html" rel="alternate"></link><published>2023-09-12T00:00:00+02:00</published><updated>2023-09-12T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-09-12:/decouverte-de-nouveaux-outils-pour-le-developpement-llm-helix-et-plus.html</id><summary type="html">
|
||
<h2 id="llm"><span class="caps">LLM</span></h2>
|
||
<ul>
|
||
<li><a href="https://localai.io/model-compatibility/">LocalAI</a> permet de faire tourner des modèles en local avec la même <span class="caps">API</span> <span class="caps">HTTP</span> que celle&nbsp;d&#8217;OpenAI</li>
|
||
<li><a href="https://github.com/bofenghuang/vigogne">Le modèle Vigogne</a> est un modèle entrainé (<em>fine-tuned</em>) avec des données en Français. Notamment <a href="https://huggingface.co/bofenghuang/vigogne-2-7b-chat/tree/v1.0">ce modèle</a>qui prends <span class="caps">LLAMA2</span> en&nbsp;entrée.</li>
|
||
<li><a href="https://python.langchain.com/docs/get_started/introduction.html">LangChain</a> semble être un framework pour travailler avec les différents …</li></ul></summary><content type="html">
|
||
<h2 id="llm"><span class="caps">LLM</span></h2>
|
||
<ul>
|
||
<li><a href="https://localai.io/model-compatibility/">LocalAI</a> permet de faire tourner des modèles en local avec la même <span class="caps">API</span> <span class="caps">HTTP</span> que celle&nbsp;d&#8217;OpenAI</li>
|
||
<li><a href="https://github.com/bofenghuang/vigogne">Le modèle Vigogne</a> est un modèle entrainé (<em>fine-tuned</em>) avec des données en Français. Notamment <a href="https://huggingface.co/bofenghuang/vigogne-2-7b-chat/tree/v1.0">ce modèle</a>qui prends <span class="caps">LLAMA2</span> en&nbsp;entrée.</li>
|
||
<li><a href="https://python.langchain.com/docs/get_started/introduction.html">LangChain</a> semble être un framework pour travailler avec les différents concepts utiles. A&nbsp;voir.</li>
|
||
</ul>
|
||
<p>Pour la première fois, j&#8217;ai commencé à utiliser un peu plus l&#8217;outil <a href="https://llm.datasette.io">llm</a> pour m&#8217;aider dans les tâches de&nbsp;programmation. </p>
|
||
<p>!! warning
|
||
J&#8217;utilise actuellement par défaut le modèle en ligne d&#8217;OpenAI &#8220;<span class="caps">GTP4</span>&#8221;, à travers leur <span class="caps">API</span>. Cela me pose des problèmes éthiques, mais mon approche est pour le moment de voir le type de résultats que j&#8217;obtiens pour ensuite comparer avec des modèles locaux type <span class="caps">LLAMA2</span>.</p>
|
||
<p>Deux choses que j&#8217;ai trouvées utiles&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>git<span class="w"> </span>diff<span class="w"> </span><span class="p">|</span><span class="w"> </span>llm<span class="w"> </span><span class="s2">&quot;write me a commit message&quot;</span>
|
||
git<span class="w"> </span>diff<span class="w"> </span><span class="p">|</span><span class="w"> </span>llm<span class="w"> </span><span class="s2">&quot;find a branch name&quot;</span>
|
||
</code></pre></div>
|
||
|
||
<p>Qui peuvent faciliter la vie plutôt que de chercher à décrire des choses manuellement (le &#8220;branch-name&#8221;, je le trouve particulièrement&nbsp;utile)</p>
|
||
<p>J&#8217;ai aussi trouvé l&#8217;idée intéressante d&#8217;automatiser l&#8217;écrite de tests, avec pytest. Bien sur ça ne fait pas tout, mais ça donne un template de départ qui est intéressant&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>cat<span class="w"> </span>code/test_file.py<span class="w"> </span><span class="p">|</span><span class="w"> </span>llm<span class="w"> </span><span class="s2">&quot;write tests for this, using pytest&quot;</span>
|
||
</code></pre></div>
|
||
|
||
<p>J&#8217;ai par exemple demandé à llm de me lister 20 titres pour cet article&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>cat<span class="w"> </span>~/dev/til.notmyidea.org/content/<span class="k">$(</span>date<span class="w"> </span>+%F<span class="k">)</span>.md<span class="w"> </span><span class="p">|</span><span class="w"> </span>llm<span class="w"> </span><span class="s2">&quot;Liste moi 20 titres pour cet article. Quelque chose de simple&quot;</span>
|
||
<span class="m">1</span>.<span class="w"> </span><span class="s2">&quot;Découverte de nouveaux outils pour le développement: LLM, Helix et plus&quot;</span>
|
||
<span class="m">2</span>.<span class="w"> </span><span class="s2">&quot;Explorer les modèles LLM pour la génération de code&quot;</span>
|
||
<span class="m">3</span>.<span class="w"> </span><span class="s2">&quot;L&#39;amélioration du développement grâce à LLM et Helix&quot;</span>
|
||
<span class="m">4</span>.<span class="w"> </span><span class="s2">&quot;Introduction à l&#39;éditeur Helix et à LLM&quot;</span>
|
||
<span class="m">5</span>.<span class="w"> </span><span class="s2">&quot;Boostez votre productivité de développement avec LLM et Helix&quot;</span>
|
||
<span class="m">6</span>.<span class="w"> </span><span class="s2">&quot;Comment j&#39;intègre LLM et Helix dans mon flux de travail de développement&quot;</span>
|
||
<span class="m">7</span>.<span class="w"> </span><span class="s2">&quot;Les avantages de l&#39;utilisation de LLM pour la programmation&quot;</span>
|
||
<span class="m">8</span>.<span class="w"> </span><span class="s2">&quot;Approches innovantes pour le développement de code avec LLM&quot;</span>
|
||
<span class="m">9</span>.<span class="w"> </span><span class="s2">&quot;L&#39;avenir du développement de code avec LLM et Helix&quot;</span>
|
||
<span class="m">10</span>.<span class="w"> </span><span class="s2">&quot;Optimisation des tâches de programmation avec LLM&quot;</span>
|
||
<span class="m">11</span>.<span class="w"> </span><span class="s2">&quot;Ouvrir de nouvelles possibilités de codage avec LLM et Helix&quot;</span>
|
||
<span class="m">12</span>.<span class="w"> </span><span class="s2">&quot;Utiliser des modèles LLM pour améliorer la qualité du code&quot;</span>
|
||
</code></pre></div>
|
||
|
||
<h2 id="helix">Helix</h2>
|
||
<p>Je me mets à tester un peu plus l&#8217;éditeur Helix. Je suis un habitué de Vim pour toutes les petites modifications que je fais depuis mon terminal, et Helix semble&nbsp;intéressant.</p>
|
||
<ul>
|
||
<li><code>x</code>pour sélectionner une ligne (<code>xd</code> pour sélectionner et&nbsp;supprimer)</li>
|
||
<li><code>ma</code> pour sélectionner &#8220;around&#8221; quelque chose. <code>maw</code> pour sélectionner le&nbsp;mot</li>
|
||
<li><code>c</code> pour effacer la sélection et passer en mode&nbsp;insertion.</li>
|
||
</ul>
|
||
<h2 id="divers">Divers</h2>
|
||
<blockquote>
|
||
<p>J’ai fait confiance, j’ai appris.
|
||
— <a href="https://d%C3%A9tour.studio">Thomas</a></p>
|
||
</blockquote>
|
||
<p>J&#8217;aime beaucoup ce que ça dit. Faire confiance est peut-être nécessaire, même si on est déçu au final, on aura au moins appris. Ça me&nbsp;touche.</p></content><category term="code"></category></entry><entry><title>Running the Gitlab CI locally</title><link href="https://blog.notmyidea.org/running-the-gitlab-ci-locally.html" rel="alternate"></link><published>2023-08-19T00:00:00+02:00</published><updated>2023-08-19T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-08-19:/running-the-gitlab-ci-locally.html</id><summary type="html">
|
||
<p>Sometimes, I need to change how the continuous integration is setup, and I find
|
||
myself pushing to a branch to test if my changes are working. Oftentimes, it
|
||
takes me multiple commits to find the correct configuration, which is…&nbsp;suboptimal.</p>
|
||
<p>I discovered today <a href="https://github.com/firecow/gitlab-ci-local">Gitlab <span class="caps">CI</span>
|
||
local</a> which makes it possible …</p></summary><content type="html">
|
||
<p>Sometimes, I need to change how the continuous integration is setup, and I find
|
||
myself pushing to a branch to test if my changes are working. Oftentimes, it
|
||
takes me multiple commits to find the correct configuration, which is…&nbsp;suboptimal.</p>
|
||
<p>I discovered today <a href="https://github.com/firecow/gitlab-ci-local">Gitlab <span class="caps">CI</span>
|
||
local</a> which makes it possible to
|
||
run the <span class="caps">CI</span> actions locally, without having to push to the remote <span class="caps">CI</span>. The same
|
||
thing exists for <a href="https://github.com/nektos/act">Microsoft Github</a>.</p>
|
||
<p>Under the hood, it&#8217;s using Docker, so you need to have it running on your
|
||
system, but once it&#8217;s done, you just have to issue a simple command to see the
|
||
results. Very helpful&nbsp;:-)</p>
|
||
<p>Here is an example&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gitlab-ci-local<span class="w"> </span><span class="nb">test</span>
|
||
parsing<span class="w"> </span>and<span class="w"> </span>downloads<span class="w"> </span>finished<span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">41</span><span class="w"> </span>ms
|
||
<span class="nb">test</span><span class="w"> </span>starting<span class="w"> </span>python:3.8-alpine<span class="w"> </span><span class="o">(</span><span class="nb">test</span><span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>copied<span class="w"> </span>to<span class="w"> </span>docker<span class="w"> </span>volumes<span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">4</span>.05<span class="w"> </span>s
|
||
<span class="nb">test</span><span class="w"> </span>$<span class="w"> </span>apk<span class="w"> </span>update<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span>apk<span class="w"> </span>add<span class="w"> </span>make<span class="w"> </span>libsass<span class="w"> </span>gcc<span class="w"> </span>musl-dev<span class="w"> </span>g++
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>fetch<span class="w"> </span>https://dl-cdn.alpinelinux.org/alpine/v3.18/main/aarch64/APKINDEX.tar.gz
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>fetch<span class="w"> </span>https://dl-cdn.alpinelinux.org/alpine/v3.18/community/aarch64/APKINDEX.tar.gz
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>v3.18.3-55-g2ee93b9273a<span class="w"> </span><span class="o">[</span>https://dl-cdn.alpinelinux.org/alpine/v3.18/main<span class="o">]</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>v3.18.3-56-g4a3b0382caa<span class="w"> </span><span class="o">[</span>https://dl-cdn.alpinelinux.org/alpine/v3.18/community<span class="o">]</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>OK:<span class="w"> </span><span class="m">19939</span><span class="w"> </span>distinct<span class="w"> </span>packages<span class="w"> </span>available
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">1</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>libgcc<span class="w"> </span><span class="o">(</span><span class="m">12</span>.2.1_git20220924-r10<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">2</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>libstdc++<span class="w"> </span><span class="o">(</span><span class="m">12</span>.2.1_git20220924-r10<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">3</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>libstdc++-dev<span class="w"> </span><span class="o">(</span><span class="m">12</span>.2.1_git20220924-r10<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">4</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>zstd-libs<span class="w"> </span><span class="o">(</span><span class="m">1</span>.5.5-r4<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">5</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>binutils<span class="w"> </span><span class="o">(</span><span class="m">2</span>.40-r7<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">6</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>libgomp<span class="w"> </span><span class="o">(</span><span class="m">12</span>.2.1_git20220924-r10<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">7</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>libatomic<span class="w"> </span><span class="o">(</span><span class="m">12</span>.2.1_git20220924-r10<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">8</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>gmp<span class="w"> </span><span class="o">(</span><span class="m">6</span>.2.1-r3<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">9</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>isl26<span class="w"> </span><span class="o">(</span><span class="m">0</span>.26-r1<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">10</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>mpfr4<span class="w"> </span><span class="o">(</span><span class="m">4</span>.2.0_p12-r0<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">11</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>mpc1<span class="w"> </span><span class="o">(</span><span class="m">1</span>.3.1-r1<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">12</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>gcc<span class="w"> </span><span class="o">(</span><span class="m">12</span>.2.1_git20220924-r10<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">13</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>musl-dev<span class="w"> </span><span class="o">(</span><span class="m">1</span>.2.4-r1<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">14</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>libc-dev<span class="w"> </span><span class="o">(</span><span class="m">0</span>.7.2-r5<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">15</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>g++<span class="w"> </span><span class="o">(</span><span class="m">12</span>.2.1_git20220924-r10<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">16</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>libsass<span class="w"> </span><span class="o">(</span><span class="m">3</span>.6.5-r0<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">(</span><span class="m">17</span>/17<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>make<span class="w"> </span><span class="o">(</span><span class="m">4</span>.4.1-r1<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Executing<span class="w"> </span>busybox-1.36.1-r2.trigger
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>OK:<span class="w"> </span><span class="m">246</span><span class="w"> </span>MiB<span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">55</span><span class="w"> </span>packages
|
||
<span class="nb">test</span><span class="w"> </span>$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>-r<span class="w"> </span>requirements.txt
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>pelican
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>pelican-4.8.0-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">1</span>.4<span class="w"> </span>MB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">1</span>.4/1.4<span class="w"> </span>MB<span class="w"> </span><span class="m">539</span>.9<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>markdown
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>Markdown-3.4.4-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">94</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">94</span>.2/94.2<span class="w"> </span>kB<span class="w"> </span><span class="m">540</span>.1<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>typogrify
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>typogrify-2.0.7.tar.gz<span class="w"> </span><span class="o">(</span><span class="m">12</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Preparing<span class="w"> </span>metadata<span class="w"> </span><span class="o">(</span>setup.py<span class="o">)</span>:<span class="w"> </span>started
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Preparing<span class="w"> </span>metadata<span class="w"> </span><span class="o">(</span>setup.py<span class="o">)</span>:<span class="w"> </span>finished<span class="w"> </span>with<span class="w"> </span>status<span class="w"> </span><span class="s1">&#39;done&#39;</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>pelican-search
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>pelican_search-1.1.0-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">6</span>.6<span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>pelican-neighbors
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>pelican_neighbors-1.2.0-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">16</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>pelican-webassets
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>pelican_webassets-2.0.0-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">5</span>.8<span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>libsass
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>libsass-0.22.0.tar.gz<span class="w"> </span><span class="o">(</span><span class="m">316</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">316</span>.3/316.3<span class="w"> </span>kB<span class="w"> </span><span class="m">552</span>.1<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Preparing<span class="w"> </span>metadata<span class="w"> </span><span class="o">(</span>setup.py<span class="o">)</span>:<span class="w"> </span>started
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Preparing<span class="w"> </span>metadata<span class="w"> </span><span class="o">(</span>setup.py<span class="o">)</span>:<span class="w"> </span>finished<span class="w"> </span>with<span class="w"> </span>status<span class="w"> </span><span class="s1">&#39;done&#39;</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>docutils&gt;<span class="o">=</span><span class="m">0</span>.16
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>docutils-0.20.1-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">572</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">572</span>.7/572.7<span class="w"> </span>kB<span class="w"> </span><span class="m">549</span>.2<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>rich&gt;<span class="o">=</span><span class="m">10</span>.1
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>rich-13.5.2-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">239</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">239</span>.7/239.7<span class="w"> </span>kB<span class="w"> </span><span class="m">485</span>.3<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>jinja2&gt;<span class="o">=</span><span class="m">2</span>.7
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>Jinja2-3.1.2-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">133</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">133</span>.1/133.1<span class="w"> </span>kB<span class="w"> </span><span class="m">342</span>.6<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>pytz&gt;<span class="o">=</span><span class="m">2020</span>.1
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>pytz-2023.3-py2.py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">502</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">502</span>.3/502.3<span class="w"> </span>kB<span class="w"> </span><span class="m">547</span>.3<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>pygments&gt;<span class="o">=</span><span class="m">2</span>.6
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>Pygments-2.16.1-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">1</span>.2<span class="w"> </span>MB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">1</span>.2/1.2<span class="w"> </span>MB<span class="w"> </span><span class="m">551</span>.4<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>unidecode&gt;<span class="o">=</span><span class="m">1</span>.1
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>Unidecode-1.3.6-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">235</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">235</span>.9/235.9<span class="w"> </span>kB<span class="w"> </span><span class="m">554</span>.2<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>blinker&gt;<span class="o">=</span><span class="m">1</span>.4
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>blinker-1.6.2-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">13</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>python-dateutil&gt;<span class="o">=</span><span class="m">2</span>.8
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>python_dateutil-2.8.2-py2.py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">247</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">247</span>.7/247.7<span class="w"> </span>kB<span class="w"> </span><span class="m">235</span>.7<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>feedgenerator&gt;<span class="o">=</span><span class="m">1</span>.9
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>feedgenerator-2.1.0-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">21</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>importlib-metadata&gt;<span class="o">=</span><span class="m">4</span>.4
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>importlib_metadata-6.8.0-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">22</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>smartypants&gt;<span class="o">=</span><span class="m">1</span>.8.3
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>smartypants-2.0.1-py2.py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">9</span>.9<span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>rtoml&lt;<span class="m">0</span>.10.0,&gt;<span class="o">=</span><span class="m">0</span>.9.0
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>rtoml-0.9.0-cp38-cp38-musllinux_1_1_aarch64.whl<span class="w"> </span><span class="o">(</span><span class="m">846</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">846</span>.2/846.2<span class="w"> </span>kB<span class="w"> </span><span class="m">503</span>.7<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>webassets&lt;<span class="m">3</span>.0,&gt;<span class="o">=</span><span class="m">2</span>.0
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>webassets-2.0-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">142</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">142</span>.9/142.9<span class="w"> </span>kB<span class="w"> </span><span class="m">551</span>.8<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>zipp&gt;<span class="o">=</span><span class="m">0</span>.5
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>zipp-3.16.2-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">7</span>.2<span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>MarkupSafe&gt;<span class="o">=</span><span class="m">2</span>.0
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl<span class="w"> </span><span class="o">(</span><span class="m">30</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>six&gt;<span class="o">=</span><span class="m">1</span>.5
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>six-1.16.0-py2.py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">11</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>markdown-it-py&gt;<span class="o">=</span><span class="m">2</span>.2.0
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>markdown_it_py-3.0.0-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">87</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<span class="w"> </span><span class="m">87</span>.5/87.5<span class="w"> </span>kB<span class="w"> </span><span class="m">561</span>.7<span class="w"> </span>kB/s<span class="w"> </span>eta<span class="w"> </span><span class="m">0</span>:00:00
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>typing-extensions&lt;<span class="m">5</span>.0,&gt;<span class="o">=</span><span class="m">4</span>.0.0
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>typing_extensions-4.7.1-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">33</span><span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Collecting<span class="w"> </span>mdurl~<span class="o">=</span><span class="m">0</span>.1
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Downloading<span class="w"> </span>mdurl-0.1.2-py3-none-any.whl<span class="w"> </span><span class="o">(</span><span class="m">10</span>.0<span class="w"> </span>kB<span class="o">)</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Building<span class="w"> </span>wheels<span class="w"> </span><span class="k">for</span><span class="w"> </span>collected<span class="w"> </span>packages:<span class="w"> </span>typogrify,<span class="w"> </span>libsass
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Building<span class="w"> </span>wheel<span class="w"> </span><span class="k">for</span><span class="w"> </span>typogrify<span class="w"> </span><span class="o">(</span>setup.py<span class="o">)</span>:<span class="w"> </span>started
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Building<span class="w"> </span>wheel<span class="w"> </span><span class="k">for</span><span class="w"> </span>typogrify<span class="w"> </span><span class="o">(</span>setup.py<span class="o">)</span>:<span class="w"> </span>finished<span class="w"> </span>with<span class="w"> </span>status<span class="w"> </span><span class="s1">&#39;done&#39;</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Created<span class="w"> </span>wheel<span class="w"> </span><span class="k">for</span><span class="w"> </span>typogrify:<span class="w"> </span><span class="nv">filename</span><span class="o">=</span>typogrify-2.0.7-py2.py3-none-any.whl<span class="w"> </span><span class="nv">size</span><span class="o">=</span><span class="m">13452</span><span class="w"> </span><span class="nv">sha256</span><span class="o">=</span>4ce329903e807671102eab7fd2bc49765b6efc3a4ae68c82053318b62789083c
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Stored<span class="w"> </span><span class="k">in</span><span class="w"> </span>directory:<span class="w"> </span>/root/.cache/pip/wheels/0b/e9/98/c888501e8dd2166da059e4f8418694de9b50b48a7192712be9
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Building<span class="w"> </span>wheel<span class="w"> </span><span class="k">for</span><span class="w"> </span>libsass<span class="w"> </span><span class="o">(</span>setup.py<span class="o">)</span>:<span class="w"> </span>started
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Building<span class="w"> </span>wheel<span class="w"> </span><span class="k">for</span><span class="w"> </span>libsass<span class="w"> </span><span class="o">(</span>setup.py<span class="o">)</span>:<span class="w"> </span>still<span class="w"> </span>running...
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Building<span class="w"> </span>wheel<span class="w"> </span><span class="k">for</span><span class="w"> </span>libsass<span class="w"> </span><span class="o">(</span>setup.py<span class="o">)</span>:<span class="w"> </span>finished<span class="w"> </span>with<span class="w"> </span>status<span class="w"> </span><span class="s1">&#39;done&#39;</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Created<span class="w"> </span>wheel<span class="w"> </span><span class="k">for</span><span class="w"> </span>libsass:<span class="w"> </span><span class="nv">filename</span><span class="o">=</span>libsass-0.22.0-cp38-abi3-linux_aarch64.whl<span class="w"> </span><span class="nv">size</span><span class="o">=</span><span class="m">13710320</span><span class="w"> </span><span class="nv">sha256</span><span class="o">=</span>3dcb4ce97c1aafc179a6343e0f312c17df88e56c4eb647ab54b09ead5ee00b92
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Stored<span class="w"> </span><span class="k">in</span><span class="w"> </span>directory:<span class="w"> </span>/root/.cache/pip/wheels/95/64/fa/47638d5037df216387cdc168e9871d5d9851fc995d636bd108
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Successfully<span class="w"> </span>built<span class="w"> </span>typogrify<span class="w"> </span>libsass
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Installing<span class="w"> </span>collected<span class="w"> </span>packages:<span class="w"> </span>webassets,<span class="w"> </span>smartypants,<span class="w"> </span>pytz,<span class="w"> </span>zipp,<span class="w"> </span>unidecode,<span class="w"> </span>typogrify,<span class="w"> </span>typing-extensions,<span class="w"> </span>six,<span class="w"> </span>rtoml,<span class="w"> </span>pygments,<span class="w"> </span>mdurl,<span class="w"> </span>MarkupSafe,<span class="w"> </span>libsass,<span class="w"> </span>feedgenerator,<span class="w"> </span>docutils,<span class="w"> </span>blinker,<span class="w"> </span>python-dateutil,<span class="w"> </span>markdown-it-py,<span class="w"> </span>jinja2,<span class="w"> </span>importlib-metadata,<span class="w"> </span>rich,<span class="w"> </span>markdown,<span class="w"> </span>pelican,<span class="w"> </span>pelican-webassets,<span class="w"> </span>pelican-search,<span class="w"> </span>pelican-neighbors
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Successfully<span class="w"> </span>installed<span class="w"> </span>MarkupSafe-2.1.3<span class="w"> </span>blinker-1.6.2<span class="w"> </span>docutils-0.20.1<span class="w"> </span>feedgenerator-2.1.0<span class="w"> </span>importlib-metadata-6.8.0<span class="w"> </span>jinja2-3.1.2<span class="w"> </span>libsass-0.22.0<span class="w"> </span>markdown-3.4.4<span class="w"> </span>markdown-it-py-3.0.0<span class="w"> </span>mdurl-0.1.2<span class="w"> </span>pelican-4.8.0<span class="w"> </span>pelican-neighbors-1.2.0<span class="w"> </span>pelican-search-1.1.0<span class="w"> </span>pelican-webassets-2.0.0<span class="w"> </span>pygments-2.16.1<span class="w"> </span>python-dateutil-2.8.2<span class="w"> </span>pytz-2023.3<span class="w"> </span>rich-13.5.2<span class="w"> </span>rtoml-0.9.0<span class="w"> </span>six-1.16.0<span class="w"> </span>smartypants-2.0.1<span class="w"> </span>typing-extensions-4.7.1<span class="w"> </span>typogrify-2.0.7<span class="w"> </span>unidecode-1.3.6<span class="w"> </span>webassets-2.0<span class="w"> </span>zipp-3.16.2
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>WARNING:<span class="w"> </span>Running<span class="w"> </span>pip<span class="w"> </span>as<span class="w"> </span>the<span class="w"> </span><span class="s1">&#39;root&#39;</span><span class="w"> </span>user<span class="w"> </span>can<span class="w"> </span>result<span class="w"> </span><span class="k">in</span><span class="w"> </span>broken<span class="w"> </span>permissions<span class="w"> </span>and<span class="w"> </span>conflicting<span class="w"> </span>behaviour<span class="w"> </span>with<span class="w"> </span>the<span class="w"> </span>system<span class="w"> </span>package<span class="w"> </span>manager.<span class="w"> </span>It<span class="w"> </span>is<span class="w"> </span>recommended<span class="w"> </span>to<span class="w"> </span>use<span class="w"> </span>a<span class="w"> </span>virtual<span class="w"> </span>environment<span class="w"> </span>instead:<span class="w"> </span>https://pip.pypa.io/warnings/venv
|
||
<span class="nb">test</span><span class="w"> </span>&gt;
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">[</span>notice<span class="o">]</span><span class="w"> </span>A<span class="w"> </span>new<span class="w"> </span>release<span class="w"> </span>of<span class="w"> </span>pip<span class="w"> </span>is<span class="w"> </span>available:<span class="w"> </span><span class="m">23</span>.0.1<span class="w"> </span>-&gt;<span class="w"> </span><span class="m">23</span>.2.1
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="o">[</span>notice<span class="o">]</span><span class="w"> </span>To<span class="w"> </span>update,<span class="w"> </span>run:<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>--upgrade<span class="w"> </span>pip
|
||
<span class="nb">test</span><span class="w"> </span>$<span class="w"> </span>make<span class="w"> </span>publish
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span><span class="s2">&quot;pelican&quot;</span><span class="w"> </span><span class="s2">&quot;/gcl-builds/content&quot;</span><span class="w"> </span>-o<span class="w"> </span><span class="s2">&quot;/gcl-builds/public&quot;</span><span class="w"> </span>-s<span class="w"> </span><span class="s2">&quot;/gcl-builds/publishconf.py&quot;</span>
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>Done:<span class="w"> </span>Processed<span class="w"> </span><span class="m">5</span><span class="w"> </span>articles,<span class="w"> </span><span class="m">0</span><span class="w"> </span>drafts,<span class="w"> </span><span class="m">0</span><span class="w"> </span>hidden<span class="w"> </span>articles,<span class="w"> </span><span class="m">2</span><span class="w"> </span>pages,<span class="w"> </span><span class="m">0</span><span class="w"> </span>hidden<span class="w"> </span>pages
|
||
<span class="nb">test</span><span class="w"> </span>&gt;<span class="w"> </span>and<span class="w"> </span><span class="m">0</span><span class="w"> </span>draft<span class="w"> </span>pages<span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">0</span>.50<span class="w"> </span>seconds.
|
||
<span class="nb">test</span><span class="w"> </span>finished<span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">6</span><span class="w"> </span>min
|
||
PASS<span class="w"> </span><span class="nb">test</span>
|
||
</code></pre></div></content><category term="code"></category><category term="gitlab"></category><category term="docker"></category></entry><entry><title>ArchLinux et mise à jour du keyring</title><link href="https://blog.notmyidea.org/archlinux-et-mise-a-jour-du-keyring.html" rel="alternate"></link><published>2023-08-18T00:00:00+02:00</published><updated>2023-08-18T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-08-18:/archlinux-et-mise-a-jour-du-keyring.html</id><summary type="html">
|
||
<p>Pour les mises à jour Arch, j&#8217;utilise <a href="https://github.com/Jguer/yay">yay</a>. Je
|
||
ne fais les mises à jour que de manière semi-régulière, et parfois après une
|
||
longue période je me retrouve avec des soucis de clé qui ne sont plus à jour ou&nbsp;manquantes.</p>
|
||
<p>Avec une utilisation fréquente du système, aucun problème …</p></summary><content type="html">
|
||
<p>Pour les mises à jour Arch, j&#8217;utilise <a href="https://github.com/Jguer/yay">yay</a>. Je
|
||
ne fais les mises à jour que de manière semi-régulière, et parfois après une
|
||
longue période je me retrouve avec des soucis de clé qui ne sont plus à jour ou&nbsp;manquantes.</p>
|
||
<p>Avec une utilisation fréquente du système, aucun problème ne se pose car un
|
||
service s&#8217;occupe de faire la mise à jour des clés de manière&nbsp;automatique.</p>
|
||
<p>Pour résoudre le souci, il suffit de mettre à jour le paquet
|
||
<code>archlinux-keyring</code>, comme décrit <a href="https://wiki.archlinux.org/title/Pacman/Package_signing">dans la page Wiki qui va
|
||
bien</a>.</p>
|
||
<div class="highlight"><pre><span></span><code>sudo pacman -S archlinux-keyring
|
||
</code></pre></div></content><category term="code"></category><category term="arch-linux"></category></entry><entry><title>Python packaging with Hatch, pipx and Zsh environment variables</title><link href="https://blog.notmyidea.org/python-packaging-with-hatch-pipx-and-zsh-environment-variables.html" rel="alternate"></link><published>2023-08-17T00:00:00+02:00</published><updated>2023-08-17T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-08-17:/python-packaging-with-hatch-pipx-and-zsh-environment-variables.html</id><summary type="html">
|
||
<p>It&#8217;s been a while I didn&#8217;t packaged something new. I recently remembered an old
|
||
package of mine that needed some attention :
|
||
<a href="https://gitlab.com/almet/debts">debts</a>. It&#8217;s now time to package it, so I
|
||
discovered <a href="https://hatch.pypa.io/">hatch</a></p>
|
||
<p>hatch new&nbsp;&#8212;init</p>
|
||
<p>This does the heavy-lifting for you, actually porting the <code>setup.py</code> files …</p></summary><content type="html">
|
||
<p>It&#8217;s been a while I didn&#8217;t packaged something new. I recently remembered an old
|
||
package of mine that needed some attention :
|
||
<a href="https://gitlab.com/almet/debts">debts</a>. It&#8217;s now time to package it, so I
|
||
discovered <a href="https://hatch.pypa.io/">hatch</a></p>
|
||
<p>hatch new&nbsp;&#8212;init</p>
|
||
<p>This does the heavy-lifting for you, actually porting the <code>setup.py</code> files to the
|
||
new way of packaging with python (with a <code>pyproject.toml</code> file)</p>
|
||
<p>Then <code>hatch shell</code> will create a development environment, install dependencies,
|
||
check the <code>pyproject.toml</code> file in one command, and give you a shell to test
|
||
whatever you need to&nbsp;test.</p>
|
||
<h2 id="isolating-system-packages">Isolating system&nbsp;packages</h2>
|
||
<p>I discovered that <a href="https://github.com/pypa/pipx">pipx</a> is a convenient way to
|
||
install user-facing applications on my system. I use multiple virtual
|
||
environments for my different projects, but not for the install that are used&nbsp;system-wide.</p>
|
||
<p>pipx seems to solve this, and avoid using <code>sudo pip install x</code>.</p>
|
||
<h2 id="manipulating-env-variables-with-zsh">Manipulating env variables with&nbsp;Zsh</h2>
|
||
<p>I use <a href="https://www.zsh.org/">Zsh</a> as my main shell for years, and I just
|
||
discovered that it&#8217;s possible to manipulate environment variables in an easy&nbsp;way.</p>
|
||
<p>If you&#8217;re like me, you never remember how to add something to your path. You
|
||
can actually use <code>+=</code>, like&nbsp;this:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="nv">path</span><span class="o">+=(</span><span class="s1">&#39;/Users/alexis/.local/bin&#39;</span><span class="o">)</span>
|
||
<span class="nb">export</span><span class="w"> </span>PATH
|
||
</code></pre></div></content><category term="code"></category><category term="python"></category><category term="packaging"></category><category term="zsh"></category></entry><entry><title>Profiling and speeding up Django and Pytest</title><link href="https://blog.notmyidea.org/profiling-and-speeding-up-django-and-pytest.html" rel="alternate"></link><published>2023-08-16T00:00:00+02:00</published><updated>2023-08-16T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2023-08-16:/profiling-and-speeding-up-django-and-pytest.html</id><summary type="html">
|
||
<p><a href="https://yaal.coop/">Éloi</a> made <a href="https://github.com/spiral-project/ihatemoney/issues/1214">a pull request on
|
||
IHateMoney</a> to
|
||
speedup the tests, with some great tooling for pytest that I wasn&#8217;t aware&nbsp;of:</p>
|
||
<ul>
|
||
<li><a href="https://pypi.org/project/pytest-xdist/">pytest-xdist</a> allows to run tests in
|
||
parallel, using <code>-n auto</code></li>
|
||
<li><a href="https://pypi.org/project/pytest-profiling/">pytest-profiling</a> makes it easy
|
||
to get the call stack and time the function calls that take most …</li></ul></summary><content type="html">
|
||
<p><a href="https://yaal.coop/">Éloi</a> made <a href="https://github.com/spiral-project/ihatemoney/issues/1214">a pull request on
|
||
IHateMoney</a> to
|
||
speedup the tests, with some great tooling for pytest that I wasn&#8217;t aware&nbsp;of:</p>
|
||
<ul>
|
||
<li><a href="https://pypi.org/project/pytest-xdist/">pytest-xdist</a> allows to run tests in
|
||
parallel, using <code>-n auto</code></li>
|
||
<li><a href="https://pypi.org/project/pytest-profiling/">pytest-profiling</a> makes it easy
|
||
to get the call stack and time the function calls that take most of the&nbsp;time.</li>
|
||
<li>You can them analyse the <code>.prof</code> files with
|
||
<a href="https://pypi.org/project/snakeviz/">Snakeviz</a></li>
|
||
</ul>
|
||
<p>So, I spent some time using these on the tests for <a href="https://chariotte.fr">La
|
||
Chariotte</a>, because they were&nbsp;slow.</p>
|
||
<p>I found two things&nbsp;:</p>
|
||
<ul>
|
||
<li>Login calls are costly in the test, and it&#8217;s possible to speed things up&nbsp;;</li>
|
||
<li>On my machine, calls to resolve my hostname were slow, using 5s during the
|
||
tests for a lookup that wasn&#8217;t even&nbsp;useful.</li>
|
||
</ul>
|
||
<h2 id="changing-the-hashing-algorithm-to-speedup-tests">Changing the hashing algorithm to speedup&nbsp;tests</h2>
|
||
<p>By default, Django uses a slow (but secure !) hashing mechanism for checking
|
||
the user credentials. In the tests, we don&#8217;t need this security, but we need
|
||
the&nbsp;speed.</p>
|
||
<p>Changing them to use <span class="caps">MD5</span> turns out to be a way to greatly speed them up! Here
|
||
is how to do it with a pytest fixture&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">autouse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="k">def</span> <span class="nf">password_hasher_setup</span><span class="p">(</span><span class="n">settings</span><span class="p">):</span>
|
||
<span class="c1"># Use a weaker password hasher during tests, for speed</span>
|
||
<span class="n">settings</span><span class="o">.</span><span class="n">PASSWORD_HASHERS</span> <span class="o">=</span> <span class="p">[</span>
|
||
<span class="s2">&quot;django.contrib.auth.hashers.MD5PasswordHasher&quot;</span><span class="p">,</span>
|
||
<span class="p">]</span>
|
||
</code></pre></div>
|
||
|
||
<h2 id="speeding-dns-lookups">Speeding <span class="caps">DNS</span>&nbsp;lookups</h2>
|
||
<p>I&#8217;m currently using a MacOSX machine, and for for whatever reason, the local
|
||
lookup was not configured properly on my machine. I don&#8217;t think I did anything
|
||
specific to get this wrong, so it might be your case too. Calls to resolve the
|
||
local domain were tooking&nbsp;5s.</p>
|
||
<p>If the answer to <code>scutil --get LocalHostName</code>, <code>hostname</code> and <code>scutil --get
|
||
HostName</code> differ, then you might be in this case. Here is the fix&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>scutil<span class="w"> </span>--set<span class="w"> </span>HostName<span class="w"> </span>&lt;YourHostName&gt;
|
||
</code></pre></div></content><category term="code"></category><category term="django"></category><category term="pytest"></category></entry><entry><title>Installation de Mosquitto, InfluxDB, Telegraf et Grafana</title><link href="https://blog.notmyidea.org/installation-de-mosquitto-influxdb-telegraf-et-grafana.html" rel="alternate"></link><published>2022-08-29T00:00:00+02:00</published><updated>2022-08-29T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2022-08-29:/installation-de-mosquitto-influxdb-telegraf-et-grafana.html</id><summary type="html"><p>Récemment, on a m&#8217;a demandé un petit coup de main pour aider à l&#8217;installation d&#8217;une pile logicielle qui permet de stocker des données temporelles et en faire des&nbsp;graphiques.</p>
|
||
<p>Voici donc quelques notes prises durant l&#8217;installation du système, concues pour que des personnes qui n&#8217;y …</p></summary><content type="html"><p>Récemment, on a m&#8217;a demandé un petit coup de main pour aider à l&#8217;installation d&#8217;une pile logicielle qui permet de stocker des données temporelles et en faire des&nbsp;graphiques.</p>
|
||
<p>Voici donc quelques notes prises durant l&#8217;installation du système, concues pour que des personnes qui n&#8217;y connaissent pas grand chose puissent s&#8217;y&nbsp;retrouver.</p>
|
||
<p>L&#8217;objectif, c&#8217;est d&#8217;avoir des cartes Arduino qui envoient des données de manière régulière sur un système qui va nous permettre de les stocker et d&#8217;en faire des&nbsp;graphiques.</p>
|
||
<p>Pour ça, nous allons utiliser&nbsp;:</p>
|
||
<ul>
|
||
<li>Un <em>Broker</em> <a href="https://mosquitto.org/">Mosquitto</a> qui va permettre de receptionner les données depuis les différents <em>clients</em>, puis de les dispatcher à qui en a besoin&nbsp;;</li>
|
||
<li>Une base de données <a href="https://www.influxdata.com/products/influxdb-overview/">InfluxDB</a>, qui permet de stocker des données temporelles&nbsp;;</li>
|
||
<li>Un <em>agent</em> <a href="https://www.influxdata.com/time-series-platform/telegraf/">Telegraf</a> qui va prendre les données du broker et les stocker dans la base de données&nbsp;InfluxDB.</li>
|
||
<li><a href="https://grafana.com/">Grafana</a>, une <em>application web</em> qui permet de visualiser les données stockées dans&nbsp;InfluxDB.</li>
|
||
</ul>
|
||
<p>Voici donc un document qui résume les étapes que j&#8217;ai suivies pour mettre en place les différents élements utiles&nbsp;:</p>
|
||
<h2 id="installer-et-se-connecter-au-serveur">Installer et se connecter au&nbsp;serveur</h2>
|
||
<p>Dans notre cas, on est passé par un <span class="caps">VPS</span> chez <span class="caps">OVH</span>, qui tourne sous <a href="https://www.debian.org/">Debian 11</a>, qui a le mérite d&#8217;être une distribution Linux stable, reconnue et (relativement) simple à&nbsp;utiliser.</p>
|
||
<p>Dans un terminal, vous pouvez vous connecter en utilisant la ligne de commande suivante&nbsp;:</p>
|
||
<p><em>Les lignes suivantes sont des lignes d&#8217;invite de commande, on les rencontre assez souvent dans les documentations sur le web. Le signe <code>$</code> signifie le début de la ligne de commande. Le signe <code>#</code> signifie le début des&nbsp;commentaires.</em></p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>ssh<span class="w"> </span>utilisateur@adresseip
|
||
</code></pre></div>
|
||
|
||
<p>Une fois connecté, on va mettre à jour les logiciels qui sont présents sur le&nbsp;serveur. </p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>apt<span class="w"> </span>update<span class="w"> </span><span class="c1"># mise à jour des dépôts (la liste des logiciels).</span>
|
||
$<span class="w"> </span>sudo<span class="w"> </span>apt<span class="w"> </span>upgrade<span class="w"> </span><span class="c1"># mise à jour des logiciels.</span>
|
||
</code></pre></div>
|
||
|
||
<h2 id="configurer-les-dns">Configurer les <span class="caps">DNS</span></h2>
|
||
<p>Nous allons avoir besoin de deux sous domaines qui redirigent vers le serveur. Bien sur, il faut adapter <code>ndd.tld</code> et le remplacer par votre nom de domaine&nbsp;:</p>
|
||
<ol>
|
||
<li>moquitto.ndd.tld</li>
|
||
<li>graphs.ndd.tld</li>
|
||
</ol>
|
||
<p>Pour faire ça, chez <span class="caps">OVH</span> ça se passe dans la console de « <span class="caps">OVH</span> Cloud », direction « Noms de domaines », et puis il faut rajouter deux enregistrements de type « A » qui pointent vers l&#8217;adresse <span class="caps">IP</span> du&nbsp;serveur.</p>
|
||
<p>En temps normal, l&#8217;adresse <span class="caps">IP</span> vous est fournie par <span class="caps">OVH</span>. Si vous avez un doute, vous pouvez l&#8217;obtenir depuis le serveur avec la commande <code>ip a</code>.</p>
|
||
<h2 id="installer-mosquitto">Installer&nbsp;Mosquitto</h2>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>mosquitto<span class="w"> </span><span class="c1"># installation depuis les dépots officiels</span>
|
||
</code></pre></div>
|
||
|
||
<p>Une fois installé, <a href="https://mosquitto.org/documentation/authentication-methods/">il faut sécuriser l&#8217;installation avec un utilisateur et un mot de passe</a>.</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>mosquitto_passwd<span class="w"> </span>-c<span class="w"> </span>/etc/mosquitto/passwd<span class="w"> </span>&lt;username&gt;
|
||
</code></pre></div>
|
||
|
||
<p>Ensuite dans le fichier de configuration il faut spécifier où est le fichier qui contient les mots de passe. Pour éditer je recommande l&#8217;utilisation de l&#8217;éditeur de texte <code>nano</code>. </p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>nano<span class="w"> </span>/etc/mosquitto/mosquitto.conf
|
||
</code></pre></div>
|
||
|
||
<p>Voici les lignes à rajouter&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>listener 1883
|
||
password_file /etc/mosquitto/passwd
|
||
</code></pre></div>
|
||
|
||
<p>Puis il faut relancer le service mosquitto&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>systemctl<span class="w"> </span>restart<span class="w"> </span>mosquitto
|
||
</code></pre></div>
|
||
|
||
<p>Avant de pouvoir utiliser mosquitto, il faut <a href="https://docs.ovh.com/gb/en/dedicated/firewall-network/">régler le firewall</a> de chez <span class="caps">OVH</span> pour qu&#8217;il accepte de laisser passer les messages pour le broker <span class="caps">MQTT</span>.</p>
|
||
<p>Il faut ajouter une règle dans le Firewall qui laisse passer toutes les connections <span class="caps">TCP</span>, avec l&#8217;option « établie&nbsp;».</p>
|
||
<h3 id="verifions-que-tout-fonctionne-comme-prevu">Vérifions que tout fonctionne comme prévu&nbsp;:</h3>
|
||
<p>Dans une console,&nbsp;écoutons…</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>mosquitto_sub<span class="w"> </span>-h<span class="w"> </span>mosquitto.ndd.tld<span class="w"> </span>-p<span class="w"> </span><span class="m">1883</span><span class="w"> </span>-u<span class="w"> </span>&lt;username&gt;<span class="w"> </span>-P<span class="w"> </span>&lt;password&gt;<span class="w"> </span>-t<span class="w"> </span>topic
|
||
</code></pre></div>
|
||
|
||
<p>Et dans une autre envoyons un message&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>mosquitto_pub<span class="w"> </span>-h<span class="w"> </span>mosquitto.ndd.tld<span class="w"> </span>-p<span class="w"> </span><span class="m">1883</span><span class="w"> </span>-u<span class="w"> </span>&lt;username&gt;<span class="w"> </span>-P<span class="w"> </span>&lt;password&gt;<span class="w"> </span>-t<span class="w"> </span>topic<span class="w"> </span>-m<span class="w"> </span><span class="m">30</span>
|
||
</code></pre></div>
|
||
|
||
<p>Vous deviez voir « 30 » apparaitre dans la première console. Si c&#8217;est bon, tout fonctionne&nbsp;!</p>
|
||
<h2 id="installation-dinfluxdb-et-telegraf">Installation d&#8217;InfluxDB et&nbsp;Telegraf</h2>
|
||
<p>Coup de bol, InfluxDB propose directement des packets pour Debian, sur leur dépot, qu&#8217;il faut donc ajouter en suivant ces quelques lignes&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>-y<span class="w"> </span>gnupg2<span class="w"> </span>curl<span class="w"> </span>wget
|
||
wget<span class="w"> </span>-qO-<span class="w"> </span>https://repos.influxdata.com/influxdb.key<span class="w"> </span><span class="p">|</span><span class="w"> </span>sudo<span class="w"> </span>apt-key<span class="w"> </span>add<span class="w"> </span>-
|
||
<span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;deb https://repos.influxdata.com/debian </span><span class="k">$(</span>lsb_release<span class="w"> </span>-cs<span class="k">)</span><span class="s2"> stable&quot;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>sudo<span class="w"> </span>tee<span class="w"> </span>/etc/apt/sources.list.d/influxdb.list
|
||
sudo<span class="w"> </span>apt<span class="w"> </span>update
|
||
</code></pre></div>
|
||
|
||
<p>Puis <code>sudo apt install influxdb telegraf</code> pour&nbsp;l&#8217;installer.</p>
|
||
<p>Ensuite, vous pouvez le lancer maintenant et indiquer au système de le lancer tout seul au démarrage&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>systemctl<span class="w"> </span><span class="nb">enable</span><span class="w"> </span>--now<span class="w"> </span>influxdb
|
||
$<span class="w"> </span>sudo<span class="w"> </span>systemctl<span class="w"> </span><span class="nb">enable</span><span class="w"> </span>--now<span class="w"> </span>telegraf
|
||
</code></pre></div>
|
||
|
||
<h3 id="configuration-de-telegraf">Configuration de&nbsp;Telegraf</h3>
|
||
<p>Telegraf permet de faire le lien entre les messages envoyés sur le broker <span class="caps">MQTT</span> et la base de données&nbsp;InfluxDB.</p>
|
||
<p>Ici, il faut rentrer un peu plus dans le vif du sujet, et ça dépends des messages que vous avez à&nbsp;stocker.</p>
|
||
<p>Dans notre cas, nous avons trois types de messages&nbsp;:</p>
|
||
<ul>
|
||
<li>/BatVoltage,&nbsp;int</li>
|
||
<li>/Temperature,&nbsp;int</li>
|
||
<li>/<span class="caps">GPS</span>,&nbsp;string</li>
|
||
</ul>
|
||
<p>Voici un fichier de configuration, qui reste à modifier en fonction des&nbsp;données.</p>
|
||
<div class="highlight"><pre><span></span><code><span class="k">[global_tags]</span>
|
||
<span class="k">[agent]</span>
|
||
<span class="w"> </span><span class="na">interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;10s&quot;</span>
|
||
<span class="w"> </span><span class="na">round_interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">true</span>
|
||
<span class="w"> </span><span class="na">metric_batch_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">1000</span>
|
||
<span class="w"> </span><span class="na">metric_buffer_limit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">10000</span>
|
||
<span class="w"> </span><span class="na">collection_jitter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;0s&quot;</span>
|
||
<span class="w"> </span><span class="na">flush_interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;10s&quot;</span>
|
||
<span class="w"> </span><span class="na">flush_jitter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;0s&quot;</span>
|
||
<span class="w"> </span><span class="na">precision</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;0s&quot;</span>
|
||
<span class="w"> </span><span class="na">hostname</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;&quot;</span>
|
||
<span class="w"> </span><span class="na">omit_hostname</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">false</span>
|
||
|
||
<span class="k">[[outputs.influxdb]]</span>
|
||
<span class="w"> </span><span class="na">urls</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">[&quot;http://127.0.0.1:8086&quot;]</span>
|
||
<span class="w"> </span><span class="na">database</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;telegraf&quot;</span>
|
||
|
||
<span class="k">[[inputs.mqtt_consumer]]</span>
|
||
<span class="w"> </span><span class="na">servers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">[&quot;tcp://127.0.0.1:1883&quot;]</span>
|
||
<span class="w"> </span><span class="na">name_override</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;mqtt_consumer_float&quot;</span>
|
||
<span class="w"> </span><span class="na">topics</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">[</span>
|
||
<span class="w"> </span><span class="na">&quot;Topic/BatVoltage&quot;,</span>
|
||
<span class="w"> </span><span class="na">&quot;Topic/Temperature&quot;,</span>
|
||
<span class="w"> </span><span class="na">]</span>
|
||
<span class="w"> </span><span class="na">username</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;&lt;username&gt;&quot;</span>
|
||
<span class="w"> </span><span class="na">password</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;&lt;password&gt;&quot;</span>
|
||
<span class="w"> </span><span class="na">data_format</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;value&quot;</span>
|
||
<span class="w"> </span><span class="na">data_type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;integer&quot;</span>
|
||
</code></pre></div>
|
||
|
||
<h2 id="installation-de-grafana">Installation de&nbsp;Grafana</h2>
|
||
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>-y<span class="w"> </span>apt-transport-https
|
||
sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>-y<span class="w"> </span>software-properties-common<span class="w"> </span>wget
|
||
sudo<span class="w"> </span>wget<span class="w"> </span>-q<span class="w"> </span>-O<span class="w"> </span>/usr/share/keyrings/grafana.key<span class="w"> </span>https://packages.grafana.com/gpg.key
|
||
<span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;deb [signed-by=/usr/share/keyrings/grafana.key] https://packages.grafana.com/oss/deb stable main&quot;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>sudo<span class="w"> </span>tee<span class="w"> </span>-a<span class="w"> </span>/etc/apt/sources.list.d/grafana.list
|
||
sudo<span class="w"> </span>apt<span class="w"> </span>update
|
||
sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>grafana
|
||
sudo<span class="w"> </span>/bin/systemctl<span class="w"> </span>daemon-reload
|
||
sudo<span class="w"> </span>/bin/systemctl<span class="w"> </span><span class="nb">enable</span><span class="w"> </span>grafana-server
|
||
sudo<span class="w"> </span>/bin/systemctl<span class="w"> </span>start<span class="w"> </span>grafana-server
|
||
</code></pre></div>
|
||
|
||
<h3 id="nginx">Nginx</h3>
|
||
<div class="highlight"><pre><span></span><code>sudo apt install nginx certbot python3-certbot-nginx
|
||
</code></pre></div>
|
||
|
||
<p>Puis il faut créer un fichier de configuration dans <code>/etc/nginx/sites-enabled/graphs.ndd.tld</code> avec le contenu suivant&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="k">map</span><span class="w"> </span><span class="nv">$http_upgrade</span><span class="w"> </span><span class="nv">$connection_upgrade</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="kn">default</span><span class="w"> </span><span class="s">upgrade</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kn">&#39;&#39;</span><span class="w"> </span><span class="s">close</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="k">upstream</span><span class="w"> </span><span class="s">grafana</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="kn">server</span><span class="w"> </span><span class="n">localhost</span><span class="p">:</span><span class="mi">3000</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="k">server</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="kn">listen</span><span class="w"> </span><span class="mi">80</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kn">server_name</span><span class="w"> </span><span class="s">graphs.ndd.tld</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kn">root</span><span class="w"> </span><span class="s">/usr/share/nginx/html</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kn">index</span><span class="w"> </span><span class="s">index.html</span><span class="w"> </span><span class="s">index.htm</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="kn">location</span><span class="w"> </span><span class="s">/</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Host</span><span class="w"> </span><span class="nv">$http_host</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kn">proxy_pass</span><span class="w"> </span><span class="s">http://grafana</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
|
||
<span class="w"> </span><span class="c1"># Proxy Grafana Live WebSocket connections.</span>
|
||
<span class="w"> </span><span class="kn">location</span><span class="w"> </span><span class="s">/api/live/</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="kn">proxy_http_version</span><span class="w"> </span><span class="mi">1</span><span class="s">.1</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Upgrade</span><span class="w"> </span><span class="nv">$http_upgrade</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Connection</span><span class="w"> </span><span class="nv">$connection_upgrade</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Host</span><span class="w"> </span><span class="nv">$http_host</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kn">proxy_pass</span><span class="w"> </span><span class="s">http://grafana</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<p>Une fois ces fichiers de configuration en place, il faut penser à la mise en place du <span class="caps">SSL</span> qui permet d&#8217;avoir une connexion sécurisée&nbsp;(https).</p>
|
||
<p>Il suffit de lancer cette ligne de commande et de suivre les questions posées&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>sudo certbot --nginx
|
||
</code></pre></div>
|
||
|
||
<p>Voilà ! A ce moment là, tout doit être fonctionnel, il ne reste plus qu&#8217;à configurer le Grafana pour grapher les données enregistrées dans&nbsp;InfluxDB.</p></content><category term="code"></category><category term="Administration Système"></category></entry><entry><title>Groupement d’achats & partage d’expérience</title><link href="https://blog.notmyidea.org/groupement-dachats-partage-dexperience.html" rel="alternate"></link><published>2018-03-03T00:00:00+01:00</published><updated>2018-03-03T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2018-03-03:/groupement-dachats-partage-dexperience.html</id><summary type="html"><p>Il y a quelques années, on s&#8217;est motivé entre copains copines pour créer un groupement&nbsp;d&#8217;achat.</p>
|
||
<p>L&#8217;idée est&nbsp;simple:</p>
|
||
<ul>
|
||
<li>commander en gros, pour faire baisser les&nbsp;prix</li>
|
||
<li>se passer d&#8217;intermédiaires et favoriser les circuits&nbsp;courts</li>
|
||
<li>aller à la rencontre des producteurs locaux et&nbsp;échanger</li>
|
||
</ul>
|
||
<p>Notre groupement …</p></summary><content type="html"><p>Il y a quelques années, on s&#8217;est motivé entre copains copines pour créer un groupement&nbsp;d&#8217;achat.</p>
|
||
<p>L&#8217;idée est&nbsp;simple:</p>
|
||
<ul>
|
||
<li>commander en gros, pour faire baisser les&nbsp;prix</li>
|
||
<li>se passer d&#8217;intermédiaires et favoriser les circuits&nbsp;courts</li>
|
||
<li>aller à la rencontre des producteurs locaux et&nbsp;échanger</li>
|
||
</ul>
|
||
<p>Notre groupement dessert actuellement 18 foyers et une 60aine de&nbsp;personnes.</p>
|
||
<p>Au fur et à mesure de la vie du groupement, on a développé quelques outils pour se simplifier la vie. Voici un retour d&#8217;expérience et quelques astuces / outils, au cas où l&#8217;envie vous prenne à vous aussi&nbsp;:)</p>
|
||
<h1 id="organisation">Organisation</h1>
|
||
<p>On organise environs trois ou quatre distributions par an. Le <em>modus operandi</em> est le&nbsp;suivant:</p>
|
||
<ul>
|
||
<li>chaque product·eur·rice à un·e référent·e, qui s&#8217;occupe de faire le&nbsp;lien;</li>
|
||
<li>une personne est désignée pour coordonner la&nbsp;distribution;</li>
|
||
<li>4 semaines avant la distribution, les référent·e·s mettent à jour les prix / produits dans le tableau de&nbsp;commandes;·e·</li>
|
||
<li>3 semaines avant la distribution, les commandes sont&nbsp;ouvertes;</li>
|
||
<li>2 semaines avant la distribution, les commandes sont&nbsp;closes;</li>
|
||
<li>Les référent·e·s ont ensuite deux semaines pour récupérer les commandes pour la&nbsp;distribution</li>
|
||
</ul>
|
||
<h1 id="quels-produits">Quels produits&nbsp;?</h1>
|
||
<p>On essaye d&#8217;avoir uniquement des produits qui se conservent (on a également quelques autres produits plus frais, mais avec d&#8217;autres&nbsp;modalités).</p>
|
||
<p>Entre autres: bières, légumes secs, conserves, jus, miel, pâtes, semoule, café, vinaigres, pommes de terre, oignons, huiles,&nbsp;farines.</p>
|
||
<p>On essaye de faire du local puis du bio au plus proche plutôt que de trouver nécessairement les prix les plus bas. C&#8217;est une discussion qui revient assez
|
||
souvent, et donc un point à évoquer lors de la création pour avoir une posture
|
||
claire sur le sujet (tout le monde n&#8217;est pas animé par la même éthique&nbsp;!).</p>
|
||
<h1 id="paiements">Paiements</h1>
|
||
<p>Pour les paiements, on utilise autant que possible des chèques. Chaque référent·e paye la·le product·rice·eur en son nom, et lui demande d&#8217;attendre la date de la distribution pour l&#8217;encaissement. La plupart des producteurs acceptent d&#8217;être payés sous&nbsp;quinzaine.</p>
|
||
<p>Le jour de la distribution, tout le monde apporte son chéquier. Nous avons mis
|
||
en place une moulinette qui s&#8217;occupe de faire la répartition des chèques automatiquement, chaque membre se retrouve à faire en moyenne un ou deux&nbsp;chèques.</p>
|
||
<p>Chaque référent·e est ainsi remboursé·e de la somme avancée, et chaque
|
||
membre du groupement d&#8217;achat paye ce qu&#8217;il doit payer. Nous n&#8217;avons
|
||
volontairement pas de structure juridique et pas de compte en banque. Les
|
||
paiements s&#8217;effectuent directement entre&nbsp;nous.</p>
|
||
<h1 id="transports">Transports</h1>
|
||
<p>Chaque référent·e commande les produits, puis ensuite s&#8217;occupe de les rapatrier. À Rennes, on a la chance d&#8217;avoir pas mal de producteurs aux alentours, donc c&#8217;est assez&nbsp;simple.</p>
|
||
<p>Le mieux est de ramener les produits juste un peu avant la distribution au lieu de distribution, ça permet d&#8217;éviter de les stocker trop longtemps, et d&#8217;éviter aux producteurs d&#8217;attendre trop longtemps avant d&#8217;encaisser les&nbsp;chèques.</p>
|
||
<p>Pour les grosses commandes, les voitures se remplissent bien, mais ma petite Clio suffit, que ce soit dit&nbsp;!</p>
|
||
<h1 id="la-distribution">La&nbsp;distribution</h1>
|
||
<p>Un peu en amont de la distribution, il faut organiser l&#8217;espace. Des tas par membre sont constitués pour faciliter les choses le jour de la&nbsp;distribution.</p>
|
||
<p>Le jour même, on se retrouve, on charge ses marchandises, on échange quelques chèques et on papote ! On en profite&nbsp;pour:</p>
|
||
<ul>
|
||
<li>discuter de la date de la prochaine&nbsp;distribution;</li>
|
||
<li>trouver une nouvelle personne pour la&nbsp;coordonner;</li>
|
||
<li>discuter de nouveaux&nbsp;produits;</li>
|
||
<li>refaire le&nbsp;monde;</li>
|
||
<li>changer de référents pour les&nbsp;producteurs.</li>
|
||
</ul>
|
||
<p>Et c&#8217;est reparti pour un tour&nbsp;;)</p>
|
||
<h1 id="nos-outils">Nos&nbsp;outils</h1>
|
||
<p>On utilise un tableur en ligne pour partager les prix et prendre les commandes. On a essayé d&#8217;utiliser <em>ethercalc</em> au début mais ça ne fonctionnait pas pour nous à l&#8217;époque (trop de petits bugs). On a donc préféré utiliser Google docs&nbsp;(ouch).</p>
|
||
<p>Il est d&#8217;ailleurs possible d&#8217;y intégrer de nouvelles fonctionnalités assez facilement, du coup Fred et Rémy ont planché sur un moyen d&#8217;automatiser la répartition des chèques (qu&#8217;on faisait dans un premier temps à la main - assez&nbsp;péniblement).</p>
|
||
<p>Le système n&#8217;est pas parfait mais fonctionne quand même assez bien&nbsp;!</p>
|
||
<p>Quelques ressources,&nbsp;donc:</p>
|
||
<ul>
|
||
<li><a href="https://gist.github.com/almet/8c77fafc9e487c02ded852ec4a91ae16">le code pour faire la répartition des&nbsp;chèques</a></li>
|
||
<li><a href="https://docs.google.com/spreadsheets/d/1bnPRSvf2Q2RDxKerWnEqUyJjuCFePnVMq6pWo8LeA_k/edit?usp=sharing">une version « à remplir » de notre tableau de commandes</a> (le mieux est d&#8217;en faire une copie&nbsp;!).</li>
|
||
</ul>
|
||
<p>Bon groupement d&#8217;achat&nbsp;;)</p></content><category term="code"></category></entry><entry><title>Webnotes</title><link href="https://blog.notmyidea.org/webnotes.html" rel="alternate"></link><published>2018-02-25T00:00:00+01:00</published><updated>2018-02-25T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2018-02-25:/webnotes.html</id><summary type="html"><p>Quand je navigue en ligne, j&#8217;aime bien prendre des notes sur ce que je lis. C&#8217;est utile pour les retrouver plus tard. Il existe quelques outils pour ce genre de cas, mais j&#8217;ai vraiment eu du mal à trouver un outil qui faisais ce que je voulais …</p></summary><content type="html"><p>Quand je navigue en ligne, j&#8217;aime bien prendre des notes sur ce que je lis. C&#8217;est utile pour les retrouver plus tard. Il existe quelques outils pour ce genre de cas, mais j&#8217;ai vraiment eu du mal à trouver un outil qui faisais ce que je voulais, de la manière que je voulais, c&#8217;est à&nbsp;dire:</p>
|
||
<ul>
|
||
<li>enregistrer une sélection de texte ainsi que son contexte: heure, site&nbsp;web.</li>
|
||
<li>fonctionner sur&nbsp;Firefox;</li>
|
||
<li>stocker mes notes à un endroit que je contrôle (ce sont mes données, après tout&nbsp;!)</li>
|
||
<li>rester en dehors de mon chemin: je suis en train de lire, pas en train d&#8217;organiser mes&nbsp;notes.</li>
|
||
<li>automatiquement partager les notes sur une page&nbsp;web.</li>
|
||
</ul>
|
||
<p>J&#8217;ai donc pris un peu de temps pour fabriquer mon outil de prises de notes, que j&#8217;ai baptisé « Webnotes ». C&#8217;est <a href="https://addons.mozilla.org/en-US/firefox/addon/wwebnotes/">une extension Firefox</a>, qui se configure assez simplement, et qui stocke les données dans une instance de <a href="http://kinto-storage.org/">Kinto</a>.</p>
|
||
<p><img src="https://github.com/almet/webnotes/blob/master/webnotes.gif?raw=true" /></p>
|
||
<p>C&#8217;est aussi simple que sélectionner du texte, faire « clic droit » puis « save as webnote », entrer un tag et le tour est joué&nbsp;!</p>
|
||
<p>Mes notes sont disponibles <a href="https://notes.notmyidea.org">sur notes.notmyidea.org</a>, et voici <a href="https://github.com/almet/webnotes">le lien vers les sources</a>, si ça vous intéresse de regarder comment ça fonctionne&nbsp;!</p></content><category term="code"></category></entry><entry><title>Comment est-ce que vous générez vos formulaires ?</title><link href="https://blog.notmyidea.org/comment-est-ce-que-vous-generez-vos-formulaires.html" rel="alternate"></link><published>2016-05-31T00:00:00+02:00</published><updated>2016-05-31T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2016-05-31:/comment-est-ce-que-vous-generez-vos-formulaires.html</id><summary type="html"><p><span class="caps">TL</span>; <span class="caps">DR</span>: Je viens à peine de <em>releaser</em> la première version d&#8217;un service de génération de formulaires.
|
||
Allez jeter un coup d&#8217;œil sur <a href="https://www.fourmilieres.net">https://www.fourmilieres.net</a></p>
|
||
<p><em>En février 2012, je parlais ici <a href="https://blog.notmyidea.org/carto-forms.html">d&#8217;un service de génération de formulaires</a>.
|
||
Depuis, pas mal d&#8217;eau à coulé sous …</em></p></summary><content type="html"><p><span class="caps">TL</span>; <span class="caps">DR</span>: Je viens à peine de <em>releaser</em> la première version d&#8217;un service de génération de formulaires.
|
||
Allez jeter un coup d&#8217;œil sur <a href="https://www.fourmilieres.net">https://www.fourmilieres.net</a></p>
|
||
<p><em>En février 2012, je parlais ici <a href="https://blog.notmyidea.org/carto-forms.html">d&#8217;un service de génération de formulaires</a>.
|
||
Depuis, pas mal d&#8217;eau à coulé sous les ponts, on est passé par pas mal d&#8217;étapes pour
|
||
finalement arriver à une première version de ce service de génération de
|
||
formulaires (à la </em>google forms<em>).</em></p>
|
||
<p>En tant qu&#8217;organisateurs d&#8217;évènements (petits et gros), je me retrouve souvent
|
||
dans une situation ou je dois créer des formulaires pour recueillir des
|
||
informations. Actuellement, la meilleure solution disponible est <em>Google Forms</em>,
|
||
mais celle ci à plusieurs problèmes, à commencer par le fait que le code n&#8217;est
|
||
pas libre et que les données sont stockées chez&nbsp;Google.</p>
|
||
<p>La plupart du temps, le besoin est assez simple: je veux spécifier quelques
|
||
questions, et donner un lien à mes amis pour qu&#8217;ils puissent y répondre.
|
||
Je reviens ensuite plus tard pour voir la liste des réponses&nbsp;apportées.</p>
|
||
<p><img alt="Capture de l'interface de création du formulaire" src="https://blog.notmyidea.org/images/formbuilder/formbuilder-build.png"></p>
|
||
<h2 id="fonctionnalites">Fonctionnalités</h2>
|
||
<p>Il existe pas mal de solutions techniques qui essayent de répondre à la même
|
||
problématique, mais la plupart d&#8217;entre elles sont assez souvent compliquées,
|
||
nécessitent de se créer un compte, et/ou ne vous laisse pas la main libre sur
|
||
les données générées, voire le code est assez difficile à faire évoluer ou à&nbsp;déployer.</p>
|
||
<p>Je voulais donc quelque chose de simple à utiliser <em>et</em> pour les créateurs de
|
||
formulaires <em>et</em> pour les utilisateurs finaux. Pas de chichis, juste quelques
|
||
vues, et des URLs à sauvegarder une fois l&#8217;opération&nbsp;terminée.</p>
|
||
<p><img alt="Capture de l'écran avec les URLs générées" src="https://blog.notmyidea.org/images/formbuilder/formbuilder-created.png">
|
||
<img alt="Capture d'écran d'un exemple de formulaire" src="https://blog.notmyidea.org/images/formbuilder/formbuilder-form.png"></p>
|
||
<h3 id="pas-de-compte">Pas de&nbsp;compte</h3>
|
||
<p>Vous n&#8217;avez pas besoin d&#8217;avoir un compte sur le site pour commencer à l&#8217;utiliser.
|
||
Vous créez simplement un nouveau formulaire puis envoyez le lien à vos amis pour
|
||
qu&#8217;eux puissent à leur tour le&nbsp;remplir.</p>
|
||
<p><img alt="Capture de la page d'accueil, ou aucun compte n'est requis" src="https://blog.notmyidea.org/images/formbuilder/formbuilder-welcome.png"></p>
|
||
<h3 id="gardez-la-main-sur-vos-donnees">Gardez la main sur vos&nbsp;données</h3>
|
||
<p>Une fois que vous avez récupéré les réponses à vos questions, vous pouvez
|
||
récupérer les données sur votre machines dans un fichier <code>.csv</code>.</p>
|
||
<p><img alt="Capture de la page de resultats, il est possible de télécharger en CSV." src="https://blog.notmyidea.org/images/formbuilder/formbuilder-results.png"></p>
|
||
<h3 id="api"><span class="caps">API</span></h3>
|
||
<p>L&#8217;ensemble des données sont en fait stockées dans <a href="https://kinto.readthedocs.org">Kinto</a>
|
||
qui est interrogeable très facilement en <span class="caps">HTTP</span>. Ce qui fait qu&#8217;il est très facile de
|
||
réutiliser les formulaires que vous avez construits (ou leurs réponses) depuis
|
||
d&#8217;autres&nbsp;outils.</p>
|
||
<h3 id="auto-hebergeable">Auto-hébergeable</h3>
|
||
<p>Un des objectifs de ce projet est de vous redonner la main sur vos données.
|
||
Bien sur, vous pouvez utiliser l&#8217;instance qui est mise à votre disposition sur
|
||
<a href="https://www.fourmilieres.net">wwww.fourmilieres.net</a>, mais vous pouvez
|
||
également l&#8217;héberger vous même très
|
||
simplement, et vous êtes d&#8217;ailleurs fortement encouragés à le faire ! Notre
|
||
objectif n&#8217;est pas de stocker l&#8217;ensemble des formulaires du monde, mais de
|
||
(re)donner le contrôle aux utilisateurs&nbsp;!</p>
|
||
<h2 id="on-commence-petit">On commence&nbsp;petit…</h2>
|
||
<p>Cette <em>release</em> n&#8217;est (bien sur) pas parfaite, et il reste encore pas mal de
|
||
travail sur cet outil, mais je pense qu&#8217;il s&#8217;agit d&#8217;une base de travail
|
||
intéressante pour un futur où Google n&#8217;a pas la main sur toutes nos&nbsp;données.</p>
|
||
<p>La liste des champs supportés est pour l&#8217;instant assez faible (Texte court,
|
||
Texte long, Oui/Non, choix dans une liste) mais elle à vocation à s&#8217;étendre, en
|
||
fonction des besoins de&nbsp;chacun.</p>
|
||
<p>J&#8217;ai d&#8217;ailleurs créé <a href="https://www.fourmilieres.net/#/form/cfd878264cec4ed2">un formulaire pour que vous puissiez me faire part de vos
|
||
retours</a>, n&#8217;hésitez pas&nbsp;!</p>
|
||
<h2 id="et-euh-comment-ca-marche">Et, euh, comment ça marche&nbsp;?</h2>
|
||
<p>Le <em>formbuilder</em>, comme j&#8217;aime l&#8217;appeler se compose en fin de compte de deux
|
||
parties&nbsp;distinctes:</p>
|
||
<ul>
|
||
<li><a href="https://kinto.readthedocs.org">Kinto</a>, un service qui stocke
|
||
des données coté serveur et qui les expose via des <strong>APIs <span class="caps">HTTP</span></strong></li>
|
||
<li><a href="https://github.com/kinto/formbuilder">Le formbuilder</a>, une application
|
||
JavaScript qui ne tourne que coté client (dans votre navigateur) qui permet
|
||
de construire les formulaires et d&#8217;envoyer les données sur les <em>APIs</em> coté&nbsp;serveur.</li>
|
||
</ul>
|
||
<p>Au niveau de la <em>stack</em> technique, le <strong>formbuilder</strong> est codé en ReactJS. Un
|
||
des points techniques intéressants du projet est qu&#8217;il génère en fin de compte du
|
||
<a href="http://jsonschema.net/"><span class="caps">JSON</span> Schema</a>, un format de validation de données <em><span class="caps">JSON</span></em>.</p>
|
||
<p>Donc, reprenons! Vous arrivez sur la page d&#8217;accueil puis cliquez sur
|
||
&#8220;Create a new form&#8221;, puis vous vous retrouvez face à une interface ou vous pouvez
|
||
ajouter des champs de formulaire. Une fois ce travail effectué, vous appuyez sur
|
||
&#8220;Create the&nbsp;form&#8221;.</p>
|
||
<ul>
|
||
<li>Le <span class="caps">JSON</span> Schema est alors envoyé au serveur Kinto, qui l&#8217;utilisera pour valider
|
||
les données qu&#8217;il recevra par la&nbsp;suite.</li>
|
||
<li>Ce <span class="caps">JSON</span> Schema sera aussi utilisé lors de l&#8217;affichage du formulaire aux
|
||
personnes qui le&nbsp;remplissent.</li>
|
||
<li>Un jeton d&#8217;accès est généré et ajouté à l&#8217;<span class="caps">URL</span>, il s&#8217;agit de l&#8217;identifiant du&nbsp;formulaire.</li>
|
||
<li>Un second jeton d&#8217;accès administrateur et généré, il vous faut le garder de
|
||
coté pour avoir accès aux&nbsp;réponses.</li>
|
||
</ul>
|
||
<p>Bref, en espérant que ça vous serve ! Un petit pas dans la direction des données
|
||
rendues à leurs utilisateurs&nbsp;!</p></content><category term="code"></category></entry><entry><title>Avez vous confiance en SSL?</title><link href="https://blog.notmyidea.org/avez-vous-confiance-en-ssl.html" rel="alternate"></link><published>2016-03-25T00:00:00+01:00</published><updated>2016-03-25T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2016-03-25:/avez-vous-confiance-en-ssl.html</id><summary type="html"><p>Dans le cadre <a href="http://autodefense-numerique.readthedocs.org/en/latest/">des ateliers d&#8217;autodéfense numérique</a>,
|
||
j&#8217;ai passé un peu de temps à creuser sur l&#8217;utilisation de <span class="caps">SSL</span> puisque
|
||
contrairement à ce que la plupart des personnes ont encore tendance à croire,
|
||
le petit cadenas (qui prouve qu&#8217;une connexion <span class="caps">SSL</span> est en cours) n&#8217;est …</p></summary><content type="html"><p>Dans le cadre <a href="http://autodefense-numerique.readthedocs.org/en/latest/">des ateliers d&#8217;autodéfense numérique</a>,
|
||
j&#8217;ai passé un peu de temps à creuser sur l&#8217;utilisation de <span class="caps">SSL</span> puisque
|
||
contrairement à ce que la plupart des personnes ont encore tendance à croire,
|
||
le petit cadenas (qui prouve qu&#8217;une connexion <span class="caps">SSL</span> est en cours) n&#8217;est
|
||
<strong>absolument</strong> pas&nbsp;suffisant.</p>
|
||
<p>Allez hop, c&#8217;est parti&nbsp;pour:</p>
|
||
<ul>
|
||
<li>un tour d&#8217;horizon du fonctionnement de&nbsp;SSl</li>
|
||
<li>quelques moyens contourner cette &#8220;protection&#8221; en faisant une attaque en&nbsp;pratique</li>
|
||
<li>un tour des solutions existantes actuellement et de pourquoi je ne les trouve
|
||
pas vraiment&nbsp;satisfaisantes.</li>
|
||
</ul>
|
||
<h2 id="comment-fonctionne-ssl">Comment fonctionne <span class="caps">SSL</span>?</h2>
|
||
<p>Pour expliquer les problèmes de <span class="caps">SSL</span>, j&#8217;ai d&#8217;abord besoin d&#8217;expliquer comment
|
||
tout ça&nbsp;fonctionne.</p>
|
||
<p><span class="caps">SSL</span> repose sur l&#8217;utilisation de certificats, qui sont générés par des autorités
|
||
de certification (<em>Certificate Authority</em> que je nomme <em><span class="caps">CA</span></em> dans la suite de&nbsp;l&#8217;article).</p>
|
||
<p>Les certificats <span class="caps">SSL</span> permettent deux&nbsp;choses:</p>
|
||
<ul>
|
||
<li>De garantir que les communications entre les navigateurs (vous) et les sites
|
||
Web ne sont connues que du détenteur du certificat du site et de vous&nbsp;même.</li>
|
||
<li>De garantir que le site sur lequel vous vous connectez est bien celui que
|
||
vous&nbsp;imaginez.</li>
|
||
</ul>
|
||
<p>Le navigateur, lors d&#8217;une visite d&#8217;un site, va télécharger le certificat
|
||
associé puis vérifier que le certificat en question a bien été généré par un
|
||
des <em><span class="caps">CA</span></em> en qui il a&nbsp;confiance.</p>
|
||
<p>Imaginons maintenant qu&#8217;une des <em><span class="caps">CA</span></em> essaye de savoir ce qui s&#8217;échange entre
|
||
mon navigateur et le site de ma banque (protégé par <span class="caps">SSL</span>). Comment cela se
|
||
passerait il&nbsp;?</p>
|
||
<p>N&#8217;importe quel <em><span class="caps">CA</span></em> peut donc générer des certificats pour n&#8217;importe quel site,
|
||
et le navigateur vérifierait, lui, que le certificat a bien été généré par une
|
||
<em><span class="caps">CA</span></em>.</p>
|
||
<p>Tout cela ne poserait pas de soucis si les <em><span class="caps">CA</span></em> étaient gérés de manière fiable,
|
||
mais il s&#8217;agit d&#8217;un travail compliqué, et certains <em><span class="caps">CA</span></em> ont par le passé montré
|
||
des&nbsp;faiblesses.</p>
|
||
<p>Par exemple, <a href="https://en.wikipedia.org/wiki/DigiNotar">DigiNotar</a> (un <em><span class="caps">CA</span></em> des Pays-Bas)
|
||
a été compromise et les attaquant.e.s ont pu générer des certificats <span class="caps">SSL</span>
|
||
frauduleux, ce qui leur a permis d&#8217;attaquer des sites tels que Facebook ou&nbsp;GMail.</p>
|
||
<p>Vous pouvez retrouver une liste des risques et menaces autour des <em><span class="caps">CA</span></em> <a href="http://wiki.cacert.org/Risk/History">sur le
|
||
wiki de CACert</a>.</p>
|
||
<h2 id="attaque-de-lhomme-du-milieu-avec-ssl">Attaque de l&#8217;homme du milieu avec <span class="caps">SSL</span></h2>
|
||
<p>A force de dire que c&#8217;était très facile à faire, j&#8217;ai eu envie d&#8217;essayer
|
||
d&#8217;espionner des connections protégées par <span class="caps">SSL</span>, et effectivement c&#8217;est
|
||
carrément flippant tellement c&#8217;est&nbsp;simple.</p>
|
||
<p>En l&#8217;espace de quelques minutes, il est possible de faire une <em>attaque de
|
||
l&#8217;homme du milieu</em> en utilisant par exemple un outil nommé <a href="http://docs.mitmproxy.org/en/stable">mitm-proxy</a>.</p>
|
||
<p>Pour déchiffrer l&#8217;ensemble du trafic <span class="caps">SSL</span>, j&#8217;ai simplement eu à lancer quelques
|
||
commandes et avoir un <em><span class="caps">CA</span></em> dans lequel le navigateur de la victime a confiance.
|
||
Je l&#8217;ai ajouté dans le navigateur cible pour simuler que je l&#8217;avais déjà
|
||
(c&#8217;est le cas si un des 1200 <span class="caps">CA</span> se fait pirater, ce qui me semble une surface
|
||
d&#8217;attaque assez&nbsp;large).</p>
|
||
<p>Je les colle ici si ça vous&nbsp;intéresse:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>aptitude<span class="w"> </span>install<span class="w"> </span>mitmproxy
|
||
$<span class="w"> </span>mitm-proxy<span class="w"> </span>-T<span class="w"> </span>--host
|
||
</code></pre></div>
|
||
|
||
<p>Il faut faire croire à votre victime que vous êtes la passerelle vers
|
||
l&#8217;extérieur et à la passerelle que vous êtes la&nbsp;victime:</p>
|
||
<div class="highlight"><pre><span></span><code>arpspoof<span class="w"> </span>-i<span class="w"> </span>wlan0<span class="w"> </span>-t<span class="w"> </span>victime<span class="w"> </span>gateway
|
||
arpspoof<span class="w"> </span>-i<span class="w"> </span>wlan0<span class="w"> </span>-t<span class="w"> </span>gateway<span class="w"> </span>victime
|
||
</code></pre></div>
|
||
|
||
<p>Puis dire à notre fausse passerelle de rediriger le trafic des ports 80 et 443
|
||
vers notre&nbsp;proxy:</p>
|
||
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>sysctl<span class="w"> </span>-w<span class="w"> </span>net.ipv4.ip_forward<span class="o">=</span><span class="m">1</span>
|
||
sudo<span class="w"> </span>iptables<span class="w"> </span>-t<span class="w"> </span>nat<span class="w"> </span>-A<span class="w"> </span>PREROUTING<span class="w"> </span>-i<span class="w"> </span>wlan0<span class="w"> </span>-p<span class="w"> </span>tcp<span class="w"> </span>--dport<span class="w"> </span><span class="m">443</span><span class="w"> </span>-j<span class="w"> </span>REDIRECT<span class="w"> </span>--to-port<span class="w"> </span><span class="m">4443</span>
|
||
sudo<span class="w"> </span>iptables<span class="w"> </span>-t<span class="w"> </span>nat<span class="w"> </span>-A<span class="w"> </span>PREROUTING<span class="w"> </span>-i<span class="w"> </span>wlan0<span class="w"> </span>-p<span class="w"> </span>tcp<span class="w"> </span>--dport<span class="w"> </span><span class="m">80</span><span class="w"> </span>-j<span class="w"> </span>REDIRECT<span class="w"> </span>--to-port<span class="w"> </span><span class="m">4443</span>
|
||
</code></pre></div>
|
||
|
||
<p>Et paf, <strong>on voit tout ce qui passe entre la machine et le serveur <span class="caps">SSL</span></strong>. On peut
|
||
d&#8217;ailleurs même imaginer faire tourner ces quelques commandes sur un
|
||
raspberry pi, pour aller encore plus&nbsp;vite…</p>
|
||
<h3 id="key-pinning-dans-les-navigateurs">Key-pinning dans les&nbsp;navigateurs</h3>
|
||
<p>Actuellement, n&#8217;importe quel <em><span class="caps">CA</span></em> peut générer des certificats pour
|
||
n&#8217;importe quel site, et c&#8217;est en grande partie ce qui pose souci. Une des
|
||
manières de faire évoluer la situation est d&#8217;épingler les certificats de
|
||
certains sites directement dans les&nbsp;navigateurs.</p>
|
||
<p>Cette approche a le mérite de fonctionner très bien <a href="https://dxr.mozilla.org/mozilla-central/source/security/manager/ssl/StaticHPKPins.h?from=StaticHPKPins.h">pour un petit nombre de
|
||
sites critiques (Google, Facebook, etc)</a>.</p>
|
||
<h3 id="http-public-key-pinning-hpkp"><span class="caps">HTTP</span> Public Key Pinning (<span class="caps">HPKP</span>)</h3>
|
||
<p><a href="https://developer.mozilla.org/en/docs/Web/Security/Public_Key_Pinning"><em><span class="caps">HTTP</span> Public Key Pinning</em></a>
|
||
est également une solution de <em>pinning</em> qui permet d&#8217;établir une confiance lors
|
||
de la première connexion avec le site. C&#8217;est ce qu&#8217;on appelle du <em>Trust on First
|
||
Use</em> ou <em><span class="caps">TOFU</span></em>.</p>
|
||
<p>Le navigateur va alors mettre ces informations dans un cache et vérifiera que
|
||
les certificats correspondent bien lors des prochaines&nbsp;visites.</p>
|
||
<p><em><span class="caps">HPKP</span></em> est disponible dans Firefox depuis Janvier 2015 et dans Chrome
|
||
depuis Octobre&nbsp;2015.</p>
|
||
<h3 id="certificate-transparency-des-journaux-auditables">Certificate transparency: des journaux&nbsp;auditables</h3>
|
||
<p>Une autre approche est celle proposée par <em>certificate transparency</em>:</p>
|
||
<blockquote>
|
||
<p>Certificate Transparency aims to remedy these certificate-based threats by
|
||
making the issuance and existence of <span class="caps">SSL</span> certificates open to scrutiny by
|
||
domain owners, CAs, and domain&nbsp;users.</p>
|
||
<p>&#8212; <a href="https://www.certificate-transparency.org/what-is-ct">Certificate&nbsp;Transparency</a></p>
|
||
</blockquote>
|
||
<p>Autrement dit, avec ce système les <em><span class="caps">CA</span></em> doivent rendre public le fait qu&#8217;ils
|
||
aient signé de nouveaux certificats intermédiaires. La signature est ajoutée à
|
||
un journal sur lequel il n&#8217;est possible que&nbsp;d&#8217;écrire.</p>
|
||
<p>Les navigateurs vont alors vérifier que les certificats utilisés sont bien des
|
||
certificats qui ont été ajoutés au&nbsp;journal.</p>
|
||
<p>Ici, toute l&#8217;intelligence est dans la vérification de ces journaux, qui
|
||
permettent donc de valider/invalider des certificats racines ou&nbsp;intermédiaires.</p>
|
||
<p>Il me semble donc qu&#8217;il serait possible d&#8217;ajouter un certificat frauduleux le
|
||
temps d&#8217;une attaque (et celui ci serait détecté et supprimé&nbsp;ensuite).</p>
|
||
<p><em>Certificate-Transparency</em> n&#8217;est donc pas une solution contre une écoute
|
||
globale mise en place par les gouvernements par&nbsp;exemple.</p>
|
||
<p>Si vous lisez bien l&#8217;anglais, je vous invite à aller lire
|
||
<a href="http://security.stackexchange.com/a/52838">cette description du problème et de la solution</a>
|
||
que je trouve très bien&nbsp;écrite.</p>
|
||
<h3 id="dane-dnssec"><span class="caps">DANE</span> + <span class="caps">DNSSEC</span></h3>
|
||
<blockquote>
|
||
<p>The <span class="caps">DANE</span> working group has developed a framework for securely
|
||
retrieving keying information from the <span class="caps">DNS</span> [<span class="caps">RFC6698</span>]. This
|
||
framework allows secure storing and looking up server public key
|
||
information in the <span class="caps">DNS</span>. This provides a binding between a domain
|
||
name providing a particular service and the key that can be used
|
||
to establish encrypted connection to that&nbsp;service.</p>
|
||
<p>&#8212; <a href="https://datatracker.ietf.org/wg/dane/charter/">Dane <span class="caps">WG</span></a></p>
|
||
</blockquote>
|
||
<p>Une autre solution est appelée &#8220;<span class="caps">DANE</span>&#8221; et repose par dessus le protocole
|
||
<em><span class="caps">DNSSEC</span></em>.</p>
|
||
<p>Je connais assez mal <em><span class="caps">DNSSEC</span></em> donc j&#8217;ai passé un peu de temps à lire des
|
||
documents. L&#8217;impression finale que ça me laisse est que le problème est
|
||
exactement le même que pour <span class="caps">SSL</span>: un certain nombre de personnes détiennent les
|
||
clés et toute la sécurité repose sur cette confiance. Or il est possible que
|
||
ces clés soient détenues par des personnes non dignes de&nbsp;confiance.</p>
|
||
<blockquote>
|
||
<p>Secure <span class="caps">DNS</span> (<span class="caps">DNSSEC</span>) uses cryptographic digital signatures signed with a
|
||
trusted public key certificate to determine the authenticity of data.
|
||
&#8212;&nbsp;https://en.wikipedia.org/wiki/DNS_spoofing</p>
|
||
</blockquote>
|
||
<p>Et&nbsp;aussi:</p>
|
||
<blockquote>
|
||
<p>It is widely believed[1] that securing the <span class="caps">DNS</span> is critically important for
|
||
securing the Internet as a whole, but deployment of <span class="caps">DNSSEC</span> specifically has
|
||
been hampered (As of 22 January 2010) by several&nbsp;difficulties:</p>
|
||
<ul>
|
||
<li>The need to design a backward-compatible standard that can scale to the
|
||
size of the&nbsp;Internet</li>
|
||
<li>Prevention of &#8220;zone enumeration&#8221; (see below) where&nbsp;desired</li>
|
||
<li>Deployment of <span class="caps">DNSSEC</span> implementations across a wide variety of <span class="caps">DNS</span> servers
|
||
and resolvers&nbsp;(clients)</li>
|
||
<li>Disagreement among implementers over who should own the top-level domain
|
||
root keys Overcoming the perceived complexity of <span class="caps">DNSSEC</span> and <span class="caps">DNSSEC</span>&nbsp;deployment</li>
|
||
</ul>
|
||
</blockquote>
|
||
<h2 id="solutions-basees-sur-la-blockchain">Solutions basées sur la&nbsp;blockchain</h2>
|
||
<p>Une dernière piste semble être l&#8217;utilisation de la <em>blockchain</em> pour distribuer
|
||
des clés par&nbsp;site.</p>
|
||
<p>La solution <em>DNSChain</em> me paraissait tout d&#8217;abord un bon point de départ mais
|
||
la lecture de <a href="https://www.indolering.com/okturtles-dnschain-unblock-us">quelques critiques</a>
|
||
et interventions du développeur du projet m&#8217;ont fait changer&nbsp;d&#8217;avis.</p>
|
||
<p>Reste encore la piste de <em>Namecoin Control</em> que je n&#8217;ai pas encore creusée.
|
||
Peut-être pour un prochain billet. Toute piste de réflexion est bien sur la
|
||
bienvenue sur ces&nbsp;sujets!</p></content><category term="code"></category></entry><entry><title>Retours sur un atelier ZeroNet</title><link href="https://blog.notmyidea.org/retours-sur-un-atelier-zeronet.html" rel="alternate"></link><published>2016-03-17T00:00:00+01:00</published><updated>2016-03-17T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2016-03-17:/retours-sur-un-atelier-zeronet.html</id><summary type="html"><p>Mardi dernier se tenait <a href="http://biblio.insa-rennes.fr/crypto">une <em>cryptoparty</em></a> dans les locaux de l&#8217;<span class="caps">INSA</span> de&nbsp;Rennes.</p>
|
||
<p>L&#8217;évènement s&#8217;étant rempli au delà de toutes les espérances, on m&#8217;a proposé de
|
||
venir y tenir un atelier, que j&#8217;ai proposé sur <a href="https://zeronet.io">ZeroNet</a>, un
|
||
petit projet fort sympathique qui pourrait devenir une …</p></summary><content type="html"><p>Mardi dernier se tenait <a href="http://biblio.insa-rennes.fr/crypto">une <em>cryptoparty</em></a> dans les locaux de l&#8217;<span class="caps">INSA</span> de&nbsp;Rennes.</p>
|
||
<p>L&#8217;évènement s&#8217;étant rempli au delà de toutes les espérances, on m&#8217;a proposé de
|
||
venir y tenir un atelier, que j&#8217;ai proposé sur <a href="https://zeronet.io">ZeroNet</a>, un
|
||
petit projet fort sympathique qui pourrait devenir une nouvelle manière de
|
||
distribuer le Web, permettant notamment d&#8217;éviter la&nbsp;censure.</p>
|
||
<p>Avant toute autre chose, merci énormément à l&#8217;équipe de la bibliothèque de
|
||
l&#8217;<span class="caps">INSA</span> pour l&#8217;organisation de cet évènement qui à une réelle portée&nbsp;politique.</p>
|
||
<h2 id="un-peu-dhistoire">Un peu&nbsp;d&#8217;histoire</h2>
|
||
<p>Il me semble que Tim Bernes Lee (l&#8217;inventeur du Web) avait prévu le Web comme un
|
||
protocole décentralisé. Chacun hébergerait ses données et les servirait aux
|
||
autres, qui pourraient alors y&nbsp;accéder.</p>
|
||
<p>Avec ce fonctionnement, impossible alors d&#8217;accéder à des sites si leur auteur
|
||
n&#8217;est pas en ligne. Qu&#8217;à cela ne tienne, on s&#8217;est mis à avoir des machines qui
|
||
restent connectées au réseau 24 heures par jour. Et puis une machine ne
|
||
suffisant plus, on a eu des fermes de machines dans des <em>data centers</em> etc afin
|
||
de supporter les milliers d&#8217;utilisateurs des&nbsp;sites.</p>
|
||
<h2 id="un-web-decentralise">Un Web&nbsp;décentralisé</h2>
|
||
<p>ZeroNet permet (entre autres) de répondre à ce problème en proposant une manière alternative de <strong>distribuer le Web</strong>, en pair à pair. Lors d&#8217;une visite d&#8217;un&nbsp;site:</p>
|
||
<ol>
|
||
<li>Vous contactez un <em>tracker</em> BitTorrent pour connaitre la liste des autres
|
||
visiteurs du site (les <em>pairs</em>).</li>
|
||
<li>Vous demandez aux <em>pairs</em> de vous donner les fichiers du&nbsp;site.</li>
|
||
<li>Vous validez que les fichiers servis sont bien les bons (en vérifiant la
|
||
signature&nbsp;attachée).</li>
|
||
</ol>
|
||
<p>N&#8217;importe quel visiteur devient alors un <em>pair</em>, qui sert le site aux autres&nbsp;visiteurs.</p>
|
||
<p>Parmi les nombreux avantages de cette approche, je note particulièrement&nbsp;que:</p>
|
||
<ul>
|
||
<li>Il est très difficile de censurer un site — Il est sur l&#8217;ensemble des machines
|
||
des&nbsp;visiteurs.</li>
|
||
<li>Les attaques par <em>fingerprinting</em> sont impossibles: le navigateur Web se
|
||
connecte à un serveur <em>proxy</em>&nbsp;local.</li>
|
||
<li>Vous détenez directement vos données et (par design) ne les donnez pas à des
|
||
silos (Facebook, Google,&nbsp;etc.)</li>
|
||
</ul>
|
||
<p>Si vous êtes interessés par une démonstration rapide, j&#8217;ai enregistré une vidéo
|
||
de 10 minutes où je parle en anglais avec une voix très&nbsp;grave.</p>
|
||
<video controls="" src="http://alexis.notmyidea.org/zeronet.webm" width=800></video>
|
||
|
||
<h2 id="atelier">Atelier</h2>
|
||
<p>Pour l&#8217;atelier, j&#8217;ai choisi de faire une présentation rapide du projet (<a href="https://blog.notmyidea.org/docs/zeronet-presentation-fr.pdf">j&#8217;ai
|
||
traduit les slides</a> anglais
|
||
pour l&#8217;occasion — <a href="https://docs.google.com/presentation/d/158C_-V1ueNaaKHMBMBgGOVhunb9xrXzB3hC_g1N53c0/edit?usp=sharing">accès aux sources</a>)
|
||
avant d&#8217;installer ZeroNet sur les machines et de l&#8217;utiliser pour publier un&nbsp;site.</p>
|
||
<h3 id="partager-sur-le-reseau-local">Partager sur le réseau&nbsp;local</h3>
|
||
<p>Nous avons eu des soucis à cause du réseau (un peu congestionné) sur lequel
|
||
les ports utilisés pour la discussion entre <em>pairs</em> étaient fermés. Il est bien
|
||
sur possible de faire tourner le tout de manière indépendante du reste du réseau,
|
||
mais je n&#8217;avais pas prévu le&nbsp;coup.</p>
|
||
<p>Voici donc comment faire pour contourner le&nbsp;souci:</p>
|
||
<ol>
|
||
<li>Installer et lancer un <em>tracker</em> BitTorrent (De manière surprenante,
|
||
<a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=685575">rien n&#8217;est packagé pour debian pour l&#8217;instant</a>)
|
||
J&#8217;ai choisi d&#8217;installer <a href="http://erdgeist.org/arts/software/opentracker/#build-instructions">OpenTracker</a></li>
|
||
<li>Ensuite lancer ZeroNet avec des options&nbsp;spécifiques.</li>
|
||
</ol>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>python<span class="w"> </span>zeronet.py<span class="w"> </span>--trackers<span class="w"> </span>udp://localhost:6969<span class="w"> </span>--ip_external<span class="w"> </span><span class="m">192</span>.168.43.207
|
||
$<span class="w"> </span>python<span class="w"> </span>zeronet.py<span class="w"> </span>--trackers<span class="w"> </span>udp://192.168.43.207:6969<span class="w"> </span>--ip_external<span class="w"> </span><span class="m">192</span>.168.43.172
|
||
</code></pre></div>
|
||
|
||
<p>Il est nécessaire de spécifier l&#8217;adresse <span class="caps">IP</span> externe que chaque nœud expose pour
|
||
éviter qu&#8217;elle n&#8217;essaye d&#8217;aller la trouver par elle même: nous voulons l&#8217;adresse
|
||
du réseau local, et non pas l&#8217;adresse&nbsp;internet.</p>
|
||
<p>La prochaine fois je tenterais de venir avec un HotSpot Wifi et un tracker
|
||
BitTorrent dans la&nbsp;poche!</p>
|
||
<h2 id="questions-reponses">Questions /&nbsp;Réponses</h2>
|
||
<p>Il y avait quelques questions intéressantes auxquelles je n&#8217;ai pas toujours su
|
||
répondre sur le moment. Après quelques recherches, je rajoute des détails&nbsp;ici.</p>
|
||
<h3 id="torrent-tor-breche-de-secu">Torrent + Tor = brèche de sécu&nbsp;?</h3>
|
||
<p>Il me semblait avoir entendu parler de problèmes de <em>dé-anonymisation</em>
|
||
<a href="https://hal.inria.fr/file/index/docid/471556/filename/TorBT.pdf">lors de l&#8217;utilisation de BitTorrent par dessus Tor</a>.</p>
|
||
<blockquote>
|
||
<p>Dans certains cas, certains clients torrents (uTorrent, BitSpirit, etc)
|
||
écrivent directement votre adresse <span class="caps">IP</span> dans l&#8217;information qui est envoyée
|
||
au tracker et/ou aux autres pairs.
|
||
—&nbsp;https://blog.torproject.org/blog/bittorrent-over-tor-isnt-good-idea</p>
|
||
</blockquote>
|
||
<p><a href="https://github.com/HelloZeroNet/ZeroNet/issues/274">Ce n&#8217;est pas le cas de ZeroNet</a>, ce qui évacue le&nbsp;souci.</p>
|
||
<h3 id="zeromail-cest-lent-non">ZeroMail, c&#8217;est lent non&nbsp;?</h3>
|
||
<p>Une des applications de démo, <em>ZeroMail</em>, propose un mécanisme qui permet de
|
||
s&#8217;envoyer des messages chiffrés sur un réseau pair à pair. L&#8217;approche choisie
|
||
est de chiffrer les messages avec la clé du destinataire et de le mettre dans
|
||
un <em>pot commun</em>. Tout le monde essaye de déchiffrer tous les messages, mais ne
|
||
peut déchiffrer que les&nbsp;siens.</p>
|
||
<p>Cela permet de ne <strong>pas</strong> fuiter de méta-données, <a href="https://blog.notmyidea.org/les-problemes-de-pgp.html">à l&#8217;inverse de <span class="caps">PGP</span></a>.</p>
|
||
<p>Je n&#8217;ai en fait pas de réponse claire à donner à cette question: l&#8217;auteur de
|
||
ZeroNet me disait que <span class="caps">10MB</span> (la limite de taille d&#8217;un site, par défaut)
|
||
correspondait à beaucoup de place pour stocker des messages, et qu&#8217;il était
|
||
possible de supprimer les anciens messages une fois qu&#8217;ils sont lus par&nbsp;exemple.</p>
|
||
<p>Une autre solution à laquelle je pensait était de créer un <em>ZeroSite</em> pour
|
||
chaque récipient, mais on connait à ce moment là le nombre de messages qu&#8217;un
|
||
utilisateur peut&nbsp;recevoir.</p>
|
||
<p>Je vois plusieurs problèmes avec le design actuel de ZeroMail (il me semble
|
||
assez facile d&#8217;y faire un déni de service par exemple). A&nbsp;creuser.</p>
|
||
<h3 id="comment-heberger-des-tres-gros-sites">Comment héberger des très gros sites&nbsp;?</h3>
|
||
<p>Par exemple, comment faire pour héberger Wikipedia&nbsp;?</p>
|
||
<p>Il semble que la meilleure manière de faire serait de séparer Wikipedia en
|
||
un tas de petites ressources (par catégorie par ex.). Les gros médias pourraient
|
||
être considérés optionnels (et donc téléchargés uniquement à la&nbsp;demande)</p>
|
||
<h3 id="est-ce-quon-a-vraiment-besoin-dun-tracker">Est-ce qu&#8217;on à vraiment besoin d&#8217;un tracker&nbsp;?</h3>
|
||
<p>Le support d&#8217;une <span class="caps">DHT</span> <a href="https://github.com/HelloZeroNet/ZeroNet/issues/57">est souhaité</a>,
|
||
mais pour l&#8217;instant pas encore implémenté. L&#8217;utilisation de la <span class="caps">DHT</span> BitTorrent
|
||
n&#8217;est pas une option puisque <a href="https://github.com/HelloZeroNet/ZeroNet/issues/57">Tor ne supporte pas <span class="caps">UDP</span></a>.</p></content><category term="code"></category></entry><entry><title>Service de nuages : Garantir l’intégrité des données via des signatures</title><link href="https://blog.notmyidea.org/service-de-nuages-garantir-lintegrite-des-donnees-via-des-signatures-fr.html" rel="alternate"></link><published>2016-03-01T00:00:00+01:00</published><updated>2016-03-01T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2016-03-01:/service-de-nuages-garantir-lintegrite-des-donnees-via-des-signatures-fr.html</id><summary type="html"><p class="first last">Comment garantir l&#8217;intégrité des données en utilisant les&nbsp;signatures.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p>Dans le cadre du projet <a class="reference external" href="https://wiki.mozilla.org/Firefox/Go_Faster">Go Faster</a>, nous souhaitons distribuer des
|
||
mises à jour de parties de <em>Firefox</em> de manière séparée des mises à jour majeures
|
||
(qui ont lieu toutes les 6&nbsp;semaines).</p>
|
||
<p>Les données que nous souhaitons mettre à jour sur les clients sont multiples.
|
||
Entre autres, nous souhaitons gérer <a class="reference external" href="https://blog.mozilla.org/security/2015/03/03/revoking-intermediate-certificates-introducing-onecrl/">la mise à jour des listes de révocation
|
||
(<span class="caps">CRL</span>) de certificats <span class="caps">SSL</span></a>.</p>
|
||
<p>Il est évidemment nécessaire de s&#8217;assurer que les données qui sont téléchargées
|
||
sur les client sont légitimes : que personne ne tente d&#8217;invalider des
|
||
certificats alors qu&#8217;ils sont valides, et que l&#8217;ensemble des mises à jour sont
|
||
bel et bien récupérées sur le&nbsp;client.</p>
|
||
<p>La signature garantit qu&#8217;une mise à jour contient tous les enregistrements, mais il
|
||
est toujours possible de bloquer l&#8217;accès au service (par exemple avec le <em>china
|
||
great firewall</em>).</p>
|
||
<p>Ce mécanisme fonctionne pour les listes de certificats à révoquer, mais pas
|
||
uniquement. Nous comptons réutiliser ce même fonctionnement dans le futur pour
|
||
la mise à jour d&#8217;autres parties de Firefox, et vous pouvez également en tirer
|
||
parti pour d&#8217;autres cas&nbsp;d&#8217;utilisation.</p>
|
||
<p>Nous souhaitons utiliser <a class="reference external" href="https://kinto.readthedocs.org">Kinto</a> afin
|
||
de distribuer ces jeux de données. Un des avantages est que l&#8217;on peut
|
||
facilement <em>cacher</em> les collections derrière un <span class="caps">CDN</span>.</p>
|
||
<p>Par contre, nous ne souhaitons pas que les clients fassent
|
||
confiance aveuglément, ni au serveur Kinto, ni au <span class="caps">CDN</span>.</p>
|
||
<p>Effectivement, un attaquant, contrôlant l&#8217;un ou l&#8217;autre, pourrait
|
||
alors envoyer les mises à jour qu&#8217;il souhaite à l&#8217;ensemble des clients
|
||
ou supprimer des certificats révoqués. Imaginez le carnage&nbsp;!</p>
|
||
<p>Afin de résoudre ce problème, considérons les conditions&nbsp;suivantes:</p>
|
||
<ul class="simple">
|
||
<li>La personne qui a le pouvoir de mettre à jour les <span class="caps">CRL</span> (<em>l&#8217;updater</em>)
|
||
a accès à une cle de signature (ou mieux, <a class="reference external" href="https://fr.wikipedia.org/wiki/Hardware_Security_Module">un <span class="caps">HSM</span></a>) qui lui permet de
|
||
signer la&nbsp;collection;</li>
|
||
<li>Le pendant public de ce certificat est stocké et distribué dans&nbsp;Firefox;</li>
|
||
<li>Le <em>hashing</em> et la <em>signature</em> sont faits côté client pour éviter certains
|
||
vecteurs d&#8217;attaque (si un attaquant a la main sur le serveur Kinto par&nbsp;exemple).</li>
|
||
</ul>
|
||
<p>Le chiffrement à sens unique, aussi appellé <em>hashing</em> est un moyen de toujours
|
||
obtenir le même résultat à partir de la même&nbsp;entrée.</p>
|
||
<div class="section" id="premier-envoi-de-donnees-sur-kinto">
|
||
<h2>Premier envoi de données sur&nbsp;Kinto</h2>
|
||
<p>L&#8217;ensemble des données est récupéré depuis une source <em>sécurisée</em> puis mis dans
|
||
une collection <span class="caps">JSON</span>. Chaque élément contient un identifiant unique généré sur
|
||
le&nbsp;client.</p>
|
||
<p>Par exemple, un enregistrement peut ressembler à&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;b7dded96-8df0-8af8-449a-8bc47f71b4c4&quot;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s2">&quot;fingerprint&quot;</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;11:D5:D2:0A:9A:F8:D9:FC:23:6E:5C:5C:30:EC:AF:68:F5:68:FB:A3&quot;</span><span class="p">}</span>
|
||
</pre></div>
|
||
<p>Le <em>hash</em> de la collection est ensuite calculé, signé puis envoyé au serveur
|
||
(voir plus bas pour les&nbsp;détails).</p>
|
||
<p>La signature est déportée sur un service qui ne s&#8217;occupe que de ça, puisque la
|
||
sécurité du certificat qui s&#8217;occupe des signatures est extrêmement&nbsp;importante.</p>
|
||
</div>
|
||
<div class="section" id="comment-verifier-l-integrite-des-donnees">
|
||
<h2>Comment vérifier l&#8217;intégrité des données&nbsp;?</h2>
|
||
<p>Premièrement, il faut récupérer l&#8217;ensemble des enregistrements présents sur
|
||
le serveur, ainsi que le <em>hash</em> et la signature&nbsp;associée.</p>
|
||
<p>Ensuite, vérifier la signature du <em>hash</em>, pour s&#8217;assurer que celui-ci provient
|
||
bien d&#8217;un tiers de&nbsp;confiance.</p>
|
||
<p>Finalement, recalculer le <em>hash</em> localement et valider qu&#8217;il correspond bien à
|
||
celui qui a été&nbsp;signé.</p>
|
||
</div>
|
||
<div class="section" id="ajouter-de-nouvelles-donnees">
|
||
<h2>Ajouter de nouvelles&nbsp;données</h2>
|
||
<p>Pour l&#8217;ajout de nouvelles données, il est nécessaire de s&#8217;assurer que les
|
||
données que l&#8217;on a localement sont valides avant de faire quoi que ce soit&nbsp;d&#8217;autre.</p>
|
||
<p>Une fois ces données validées, il suffit de procéder comme la première fois, et
|
||
d&#8217;envoyer à nouveau le <em>hash</em> de la collection au&nbsp;serveur.</p>
|
||
</div>
|
||
<div class="section" id="comment-calculer-ce-hash">
|
||
<h2>Comment calculer ce hash&nbsp;?</h2>
|
||
<p>Pour calculer le <em>hash</em> de la collection, il est nécessaire&nbsp;:</p>
|
||
<ol class="arabic simple">
|
||
<li>D&#8217;ordonner l&#8217;ensemble des éléments de la collection (par leur id)&nbsp;;</li>
|
||
<li>Pour chaque élément, sérialiser les champs qui nous intéressent (les
|
||
concaténer clé +&nbsp;valeur)</li>
|
||
<li>Calculer le <em>hash</em> depuis la&nbsp;sérialisation.</li>
|
||
</ol>
|
||
<p>Nous sommes encore incertains de la manière dont le hash va être calculé. Les <a class="reference external" href="https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41"><span class="caps">JSON</span> Web Signature</a> semblent
|
||
une piste intéressante. En attendant, une implementation naïve en python
|
||
pourrait ressembler à ceci&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">json</span>
|
||
<span class="kn">import</span> <span class="nn">hashlib</span>
|
||
|
||
<span class="n">data</span> <span class="o">=</span> <span class="p">[</span>
|
||
<span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;b7dded96-8df0-8af8-449a-8bc47f71b4c4&quot;</span><span class="p">,</span>
|
||
<span class="s2">&quot;fingerprint&quot;</span><span class="p">:</span> <span class="s2">&quot;11:D5:D2:0A:9A:F8:D9:FC:23:6E:5C:5C:30:EC:AF:68:F5:68:FB:A3&quot;</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;dded96b7-8f0d-8f8a-49a4-7f771b4c4bc4&quot;</span><span class="p">,</span>
|
||
<span class="s2">&quot;fingerprint&quot;</span><span class="p">:</span> <span class="s2">&quot;33:6E:5C:5C:30:EC:AF:68:F5:68:FB:A3:11:D5:D2:0A:9A:F8:D9:FC&quot;</span><span class="p">}]</span>
|
||
|
||
<span class="n">m</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha256</span><span class="p">()</span>
|
||
<span class="n">m</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">True</span><span class="p">))</span>
|
||
<span class="n">collection_hash</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</content><category term="code"></category></entry><entry><title>Let’s Encrypt + HAProxy</title><link href="https://blog.notmyidea.org/lets-encrypt-haproxy.html" rel="alternate"></link><published>2016-02-11T00:00:00+01:00</published><updated>2016-02-11T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2016-02-11:/lets-encrypt-haproxy.html</id><summary type="html"><p><em>Note : Cet article n&#8217;est plus à jour. Il est maintenant (2018) possible d&#8217;installer des certificats <span class="caps">SSL</span> Let&#8217;s Encrypt d&#8217;une manière beaucoup plus simple, en utilisant certbot (et le plugin nginx <code>certbot --nginx</code>).</em></p>
|
||
<blockquote>
|
||
<p>It’s time for the Web to take a big step forward in terms …</p></blockquote></summary><content type="html"><p><em>Note : Cet article n&#8217;est plus à jour. Il est maintenant (2018) possible d&#8217;installer des certificats <span class="caps">SSL</span> Let&#8217;s Encrypt d&#8217;une manière beaucoup plus simple, en utilisant certbot (et le plugin nginx <code>certbot --nginx</code>).</em></p>
|
||
<blockquote>
|
||
<p>It’s time for the Web to take a big step forward in terms of security
|
||
and privacy. We want to see <span class="caps">HTTPS</span> become the default. Let’s Encrypt
|
||
was built to enable that by making it as easy as possible to get and
|
||
manage&nbsp;certificates.</p>
|
||
<p>&#8212; <a href="https://letsencrypt.org/">Let&#8217;s&nbsp;Encrypt</a></p>
|
||
</blockquote>
|
||
<p>Depuis début Décembre, la nouvelle <em>autorité de certification</em> Let&#8217;s
|
||
Encrypt est passée en version <em>Beta</em>. Les certificats <span class="caps">SSL</span> sont un moyen
|
||
de 1. chiffrer la communication entre votre navigateur et le serveur et
|
||
2. un moyen d&#8217;être sur que le site Web auquel vous accédez est celui
|
||
auquel vous pensez vous connecter (pour éviter des <a href="https://fr.wikipedia.org/wiki/Attaque_de_l'homme_du_milieu">attaques de l&#8217;homme
|
||
du milieu</a>).</p>
|
||
<p>Jusqu&#8217;à maintenant, il était nécessaire de payer une entreprise pour
|
||
faire en sorte d&#8217;avoir des certificats qui évitent d&#8217;avoir ce genre
|
||
d&#8217;erreurs dans vos&nbsp;navigateurs:</p>
|
||
<p><img alt="Message de firefox lorsque une connexion n'est pas
|
||
sécurisée." src="%7Bfilename%7D/static/unsecure-connection.png"></p>
|
||
<p>Maintenant, grâce à Let&#8217;s Encrypt il est possible d&#8217;avoir des
|
||
certificats <span class="caps">SSL</span> <strong>gratuits</strong>, ce qui représente un grand pas en avant
|
||
pour la sécurité de nos&nbsp;communications.</p>
|
||
<p>Je viens de mettre en place un procédé (assez simple) qui permet de
|
||
configurer votre serveur pour générer des certificats <span class="caps">SSL</span> valides avec
|
||
Let&#8217;s Encrypt et le répartiteur de charge
|
||
<a href="http://www.haproxy.org/">HAProxy</a>.</p>
|
||
<p>Je me suis basé pour cet article sur
|
||
d&#8217;<a href="https://blog.infomee.fr/p/letsencrypt-haproxy">autres</a>
|
||
<a href="http://blog.victor-hery.com/article22/utiliser-let-s-encrypt-avec-haproxy">articles</a>,
|
||
dont je vous recommande la lecture pour un complément&nbsp;d&#8217;information.</p>
|
||
<h2 id="validation-des-domaines-par-lets-encrypt">Validation des domaines par Let&#8217;s&nbsp;Encrypt</h2>
|
||
<p>Je vous passe les détails d&#8217;installation du client de Let&#8217;s Encrypt, qui
|
||
sont <a href="https://github.com/letsencrypt/letsencrypt#installation">très bien expliqués sur leur
|
||
documentation</a>.</p>
|
||
<p>Une fois installé, vous allez taper une commande qui va ressembler&nbsp;à:</p>
|
||
<div class="highlight"><pre><span></span><code>letsencrypt-auto certonly --renew-by-default
|
||
--webroot -w /home/www/letsencrypt-requests/ \
|
||
-d hurl.kinto-storage.org \
|
||
-d forums.kinto-storage.org
|
||
</code></pre></div>
|
||
|
||
<p>Le <em>webroot</em> est l&#8217;endroit ou les preuves de détention du domaine vont
|
||
être&nbsp;déposées.</p>
|
||
<p>Lorsque les serveurs de Let&#8217;s Encrypt vont vouloir vérifier que vous
|
||
êtes bien à l&#8217;origine des demandes de certificats, ils vont envoyer une
|
||
requête <span class="caps">HTTP</span> sur <code>http://domaine.org/.well-known/acme-challenge</code>, ou il
|
||
voudra trouver des informations qu&#8217;il aura généré via la commande
|
||
<code>letsencrypt-auto</code>.</p>
|
||
<p>J&#8217;ai choisi de faire une règle dans haproxy pour diriger toutes les
|
||
requêtes avec le chemin <code>.well-known/acme-challenge</code> vers un <em>backend</em>
|
||
nginx qui sert des fichiers statiques (ceux contenus dans
|
||
<code>/home/www/letsencrypt-requests/</code>).</p>
|
||
<p>Voici la section de la configuration de HAProxy (et <a href="https://github.com/almet/infra/blob/master/haproxy/haproxy.cfg#L63-L72">la configuration
|
||
complete</a>
|
||
si ça peut être&nbsp;utile):</p>
|
||
<div class="highlight"><pre><span></span><code>frontend http
|
||
bind 0.0.0.0:80
|
||
mode http
|
||
default_backend nginx_server
|
||
|
||
acl letsencrypt_check path_beg /.well-known/acme-challenge
|
||
use_backend letsencrypt_backend if letsencrypt_check
|
||
|
||
redirect scheme https code 301 if !{ ssl_fc } !letsencrypt_check
|
||
|
||
backend letsencrypt_backend
|
||
http-request set-header Host letsencrypt.requests
|
||
dispatch 127.0.0.1:8000
|
||
</code></pre></div>
|
||
|
||
<p>Et celle de <span class="caps">NGINX</span>:</p>
|
||
<div class="highlight"><pre><span></span><code>server {
|
||
listen 8000;
|
||
server_name letsencrypt.requests;
|
||
root /home/www/letsencrypt-requests;
|
||
}
|
||
</code></pre></div>
|
||
|
||
<h2 id="installation-des-certificats-dans-haproxy">Installation des certificats dans&nbsp;HAProxy</h2>
|
||
<p>Vos certificats <span class="caps">SSL</span> devraient être générés dans <code>/etc/letsencrypt/live</code>,
|
||
mais ils ne sont pas au format attendu par haproxy. Rien de grave, la
|
||
commande suivant convertit l&#8217;ensemble des certificats en une version
|
||
compatible avec&nbsp;HAProxy:</p>
|
||
<div class="highlight"><pre><span></span><code>cat /etc/letsencrypt/live/domaine.org/privkey.pem /etc/letsencrypt/live/domaine.org/fullchain.pem &gt; /etc/ssl/letsencrypt/domaine.org.pem
|
||
</code></pre></div>
|
||
|
||
<p>Et ensuite dans la configuration de haproxy, pour le (nouveau)
|
||
<em>frontend</em>&nbsp;https:</p>
|
||
<div class="highlight"><pre><span></span><code>bind 0.0.0.0:443 ssl no-sslv3 crt /etc/ssl/letsencrypt
|
||
</code></pre></div>
|
||
|
||
<p>Faites bien attention à avoir un <em>frontend</em> https pour tous vos sites en
|
||
<span class="caps">HTTPS</span>. <a href="https://github.com/almet/infra/blob/master/haproxy/haproxy.cfg#L38-L60">Pour moi cela ressemble à
|
||
ça</a>.</p>
|
||
<p>Une fois tout ceci fait, redémarrez votre service haproxy et zou&nbsp;!</p>
|
||
<h2 id="automatisation">Automatisation</h2>
|
||
<p>Pour automatiser un peu tout ça, j&#8217;ai choisi de faire ça comme&nbsp;suit:</p>
|
||
<ul>
|
||
<li>Un fichier domaine dans <code>letsencrypt/domains/domain.org</code> qui
|
||
contient le script <code>letsencrypt</code>.</li>
|
||
<li>Un fichier d&#8217;installation de certificats dans
|
||
<code>letsencrypt/install-certs.sh</code> qui s&#8217;occupe d&#8217;installer les
|
||
certificats déjà&nbsp;générés.</li>
|
||
</ul>
|
||
<p>Et voila ! <a href="https://github.com/almet/infra/">Le tout est dans un dépot
|
||
github</a>, si jamais ça peut vous servir,
|
||
tant mieux&nbsp;!</p></content><category term="code"></category></entry><entry><title>Ateliers d’autodéfense numérique</title><link href="https://blog.notmyidea.org/ateliers-dautodefense-numerique.html" rel="alternate"></link><published>2016-01-14T00:00:00+01:00</published><updated>2016-01-14T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2016-01-14:/ateliers-dautodefense-numerique.html</id><summary type="html"><p>Il y a huit mois, je me rendais compte de l&#8217;importance du choix des
|
||
outils pour faire face à la surveillance généralisée, et notamment en
|
||
rapport au chiffrement des données. Une de mes envies de l&#8217;époque était
|
||
l&#8217;animation&nbsp;d&#8217;ateliers.</p>
|
||
<blockquote>
|
||
<p>Je compte&nbsp;donc:</p>
|
||
<ul>
|
||
<li>Organiser des ateliers de …</li></ul></blockquote></summary><content type="html"><p>Il y a huit mois, je me rendais compte de l&#8217;importance du choix des
|
||
outils pour faire face à la surveillance généralisée, et notamment en
|
||
rapport au chiffrement des données. Une de mes envies de l&#8217;époque était
|
||
l&#8217;animation&nbsp;d&#8217;ateliers.</p>
|
||
<blockquote>
|
||
<p>Je compte&nbsp;donc:</p>
|
||
<ul>
|
||
<li>Organiser des ateliers de sensibilisation aux outils de
|
||
communication, envers mes&nbsp;proches;</li>
|
||
<li>Utiliser la communication chiffrée le plus souvent possible, au
|
||
moins pour rendre le déchiffrement des messages plus longue,
|
||
&#8220;noyer le&nbsp;poisson&#8221;.</li>
|
||
</ul>
|
||
<p>&#8212; <a href="http://blog.notmyidea.org/chiffrement.html">Chiffrement</a></p>
|
||
</blockquote>
|
||
<p>J&#8217;ai mis un peu de temps à mettre le pied à l&#8217;étrier, mais je ressors
|
||
finalement du premier atelier que j&#8217;ai co-animé avec geb, auprès d&#8217;un
|
||
public de&nbsp;journalistes.</p>
|
||
<p>Pour cette première édition l&#8217;idée était à la fois d&#8217;aller à la
|
||
rencontre d&#8217;un public que je connais mal, de leur donner des outils pour
|
||
solutionner les problèmes auxquels ils font parfois face, et de me faire
|
||
une idée de ce que pouvait être un atelier sur l&#8217;autodéfense&nbsp;numérique.</p>
|
||
<p>L&#8217;objectif pour ce premier atelier était&nbsp;de:</p>
|
||
<ol>
|
||
<li>Échanger autour des besoins et <strong>faire ressortir des histoires</strong> ou
|
||
le manque d&#8217;outillage / connaissances à posé problème, dans des
|
||
situations&nbsp;concrètes;</li>
|
||
<li>Se rendre compte des &#8220;conduites à risque&#8221;, <strong>faire peur</strong> aux
|
||
personnes formées pour qu&#8217;elles se rendent compte de l&#8217;état actuel
|
||
des&nbsp;choses;</li>
|
||
<li><strong>Proposer des solutions concrètes</strong> aux problèmes soulevés, ainsi
|
||
que le minimum de connaissance théorique pour les&nbsp;appréhender.</li>
|
||
</ol>
|
||
<h2 id="1-faire-ressortir-les-problemes">1. Faire ressortir les&nbsp;problèmes</h2>
|
||
<p>Afin de faire ressortir les problèmes, nous avons choisi de constituer
|
||
des petits groupes de discussion, afin de faire des &#8220;Groupes d&#8217;Interview
|
||
Mutuels&#8221;, ou &#8220;<span class="caps">GIM</span>&#8221;:</p>
|
||
<blockquote>
|
||
<p>l’animateur invite les participants à se regrouper par trois, avec des
|
||
personnes qu’on connaît moins puis invite chacun à livrer une
|
||
expérience vécue en lien avec le thème de la réunion et les deux
|
||
autres à poser des questions leur permettant de bien saisir ce qui a
|
||
été&nbsp;vécu.</p>
|
||
<p>&#8212; «<a href="http://www.scoplepave.org/pour-s-ecouter">Pour s&#8217;écouter</a>», <span class="caps">SCOP</span>
|
||
Le&nbsp;Pavé.</p>
|
||
</blockquote>
|
||
<p>De ces <em>GIMs</em> nous avons pu ressortir quelques histoires, gravitant
|
||
autour&nbsp;de:</p>
|
||
<ul>
|
||
<li><strong>La protection des sources (d&#8217;information)</strong>: Comment faire pour
|
||
aider quelqu&#8217;un à faire &#8220;fuiter&#8221; des données depuis l&#8217;intérieur
|
||
d&#8217;une entreprise&nbsp;?</li>
|
||
<li><strong>Le chiffrement de ses données</strong>: Comment éviter de faire &#8220;fuiter&#8221;
|
||
des données importantes lors d&#8217;une perquisition de matériel&nbsp;?</li>
|
||
</ul>
|
||
<h2 id="2-faire-peur">2. Faire&nbsp;peur</h2>
|
||
<p>Un des premiers objectifs est de faire peur, afin que tout le monde se
|
||
rende compte à quel point il est facile d&#8217;accéder à certaines données.
|
||
<a href="http://blog.barbayellow.com/">Grégoire</a> m&#8217;avait conseillé quelques
|
||
petites accroches qui ont ma foi bien&nbsp;marché:</p>
|
||
<p>J&#8217;ai demandé aux présent.e.s&nbsp;de:</p>
|
||
<ul>
|
||
<li>donner leur mot de passe à voix haute devant les autres: a priori
|
||
personne ne le&nbsp;fera;</li>
|
||
<li>venir se connecter à leur compte email depuis mon ordinateur. J&#8217;ai
|
||
piégé une personne, qui est venu pour taper son mot de&nbsp;passe.</li>
|
||
</ul>
|
||
<p>Cela à été un bon moyen de parler de l&#8217;importance des traces que l&#8217;on
|
||
peut laisser sur un ordinateur, et de la confiance qu&#8217;il faut avoir dans
|
||
le matériel que l&#8217;on utilise, à fortiori si ce ne sont pas les&nbsp;vôtres.</p>
|
||
<p>Pour continuer à leur faire peur, après une brève explication de ce
|
||
qu&#8217;est <span class="caps">SSL</span> nous avons montré comment il était facile de scruter le
|
||
réseau à la recherche de mots de passe en&nbsp;clair.</p>
|
||
<h2 id="3-proposer-des-solutions-concretes">3. Proposer des solutions&nbsp;concrêtes</h2>
|
||
<p>Une fois que tout le monde avait pleinement pris sonscience des
|
||
problématiques et n&#8217;osait plus utiliser son ordinateur ou son
|
||
téléphone, on à commencé à parler de quelques solutions. Plusieurs
|
||
approches étaient possibles ici, nous avons choisi de présenter quelques
|
||
outils qui nous semblaient répondre aux&nbsp;attentes:</p>
|
||
<ul>
|
||
<li>On a expliqué ce qu&#8217;était <a href="https://tails.boum.org">Tails</a>, et
|
||
comment l&#8217;utiliser et le&nbsp;dupliquer.</li>
|
||
<li>On a pu faire un tour des outils existants sur Tails, notamment
|
||
autour de l&#8217;<em>anonymisation</em> de fichiers et la suppression effective
|
||
de&nbsp;contenus.</li>
|
||
<li>Certaines personnes ont pu créer une clé tails avec la persistance
|
||
de&nbsp;configurée.</li>
|
||
<li>Nous nous sommes connectés au réseau
|
||
<a href="https://www.torproject.org">Tor</a> et testé que nos adresses <span class="caps">IP</span>
|
||
changeaient bien à la&nbsp;demande.</li>
|
||
<li>Nous avons utilisé <a href="https://crypto.cat">CryptoCat</a> par dessus Tor,
|
||
afin de voir comment avoir une conversation confidentielle dans
|
||
laquelle il est possible d&#8217;échanger des&nbsp;fichiers.</li>
|
||
</ul>
|
||
<h2 id="retours">Retours</h2>
|
||
<p>D&#8217;une manière générale, pour une formation de trois heures et demi, je
|
||
suis assez content de l&#8217;exercice, et de l&#8217;ensemble des sujets que nous
|
||
avons pu couvrir. Il y a beaucoup de place pour l&#8217;amélioration,
|
||
notamment en amont (j&#8217;avais par exemple oublié d&#8217;amener avec moi
|
||
suffisamment de clés <span class="caps">USB</span> pour utiliser&nbsp;Tails).</p>
|
||
<p>La plupart des retours qu&#8217;on a pu avoir jusqu&#8217;à maintenant sont
|
||
positifs, et il y a l&#8217;envie d&#8217;aller plus loin sur l&#8217;ensemble de ces&nbsp;sujets.</p>
|
||
<h2 id="la-suite">La&nbsp;suite</h2>
|
||
<p>Il y a beaucoup de sujets que nous n&#8217;avons pas abordés, ou uniquement
|
||
survolés, à cause du manque de temps disponible. Idéalement, il faudrait
|
||
au moins une journée entière pour couvrir quelques sujets plus en détail
|
||
(on peut imaginer avoir une partie théorique le matin et une partie
|
||
pratique l&#8217;après-midi par&nbsp;exemple).</p>
|
||
<p>J&#8217;ai choisi volontairement de ne pas aborder le chiffrement des messages
|
||
via <span class="caps">PGP</span> parce que <a href="%7Bfilename%7D2015.05.pgp-problemes.rst">je pense que la protection que ce média propose n&#8217;est
|
||
pas suffisante</a>, mais je suis
|
||
en train de revenir sur ma décision: il pourrait être utile de présenter
|
||
l&#8217;outil, à minima, en insistant sur certaines de ses&nbsp;faiblesses.</p>
|
||
<p>Un compte twitter à été créé recemment autour des crypto-party à Rennes,
|
||
si vous êtes interessés, <a href="https://twitter.com/CryptoPartyRNS">allez jeter un coup
|
||
d&#8217;œil</a>!</p>
|
||
<p>Je n&#8217;ai pas trouvé de ressources disponibles par rapport à des plans de
|
||
formation sur le sujet, j&#8217;ai donc décidé de publier les nôtres, afin de
|
||
co-construire avec d&#8217;autres des plans de&nbsp;formation.</p>
|
||
<p>Ils sont pour l&#8217;instant disponibles <a href="http://autodefense-numerique.readthedocs.org/en/latest/">sur Read The
|
||
Docs</a>. Tous les
|
||
retours sont évidemment les bienvenus&nbsp;!</p></content><category term="code"></category></entry><entry><title>Le mail doit-il mourir ?</title><link href="https://blog.notmyidea.org/le-mail-doit-il-mourir.html" rel="alternate"></link><published>2015-11-24T00:00:00+01:00</published><updated>2015-11-24T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-11-24:/le-mail-doit-il-mourir.html</id><summary type="html"><p>J&#8217;utilise quotidiennement le protocole email, tant bien que mal, tout en sachant que l&#8217;ensemble de mes messages passent en clair sur le réseau pour la plupart de mes conversations, puisque trop peu de monde utilise le chiffrement des&nbsp;messages.</p>
|
||
<p>Et même si j&#8217;arrive à convaincre certains de …</p></summary><content type="html"><p>J&#8217;utilise quotidiennement le protocole email, tant bien que mal, tout en sachant que l&#8217;ensemble de mes messages passent en clair sur le réseau pour la plupart de mes conversations, puisque trop peu de monde utilise le chiffrement des&nbsp;messages.</p>
|
||
<p>Et même si j&#8217;arrive à convaincre certains de mes proches à installer <span class="caps">PGP</span>, je ne suis pas satisfait du résultat: les méta-données (qui contacte qui à quel
|
||
moment, et pour lui dire quoi) transitent de toute manière, elles, en clair, à la vue de&nbsp;tous.</p>
|
||
<p>Ce problème est lié directement au protocole email: il est <em>necessaire</em> de faire fuiter ces meta-données (au moins le destinataire) pour avoir un protocole
|
||
mail&nbsp;fonctionnel.</p>
|
||
<p>Le mail répond à un besoin de communication asynchrone qui permet des conversations plus réfléchies qu&#8217;un simple chat (miaou). Il est tout à fait possible d&#8217;utiliser certaines technologies existantes afin de construire le futur de l&#8217;email, pour&nbsp;lequel:</p>
|
||
<ul>
|
||
<li>Les méta-données seraient chiffrées — Il n&#8217;est pas possible de savoir qui
|
||
communique avec qui, et&nbsp;quand;</li>
|
||
<li>Le chiffrement serait fort (et protégé d&#8217;une phrase de passe&nbsp;?);</li>
|
||
<li>La fuite d&#8217;une clé de chiffrement utilisée dans un échange ne permette pas de
|
||
déchiffrer l&#8217;ensemble des échanges (forward&nbsp;secrecy);</li>
|
||
<li>Il ne soit pas possible de réutiliser les données comme preuve pour
|
||
incriminer l&#8217;emmeteur du message&nbsp;(deniability);</li>
|
||
</ul>
|
||
<p>Avec au moins ces besoins en tête, il semble qu&#8217;une revue de l&#8217;ensemble des projets existants pointe du doigt vers <a href="https://github.com/agl/pond">pond</a>, ou vers <a href="https://www.whispersystems.org">Signal</a>.</p>
|
||
<p>Malheureusement, Pond est le projet d&#8217;une seule personne, qui veut plutôt utiliser ce code comme démonstration du concept en&nbsp;question.</p></content><category term="code"></category></entry><entry><title>Web distribution signing</title><link href="https://blog.notmyidea.org/web-distribution-signing.html" rel="alternate"></link><published>2015-10-12T00:00:00+02:00</published><updated>2015-10-12T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-10-12:/web-distribution-signing.html</id><summary type="html"><p><em>I&#8217;m not a crypto expert, nor pretend to be one. These are thoughts I
|
||
want to share with the crypto community to actually see if any solution
|
||
exists to solve this particular&nbsp;problem.</em></p>
|
||
<p>One <a href="http://www.tonyarcieri.com/whats-wrong-with-webcrypto">often pointed</a> flaw in
|
||
web-based cryptographic applications is the fact that there is no way …</p></summary><content type="html"><p><em>I&#8217;m not a crypto expert, nor pretend to be one. These are thoughts I
|
||
want to share with the crypto community to actually see if any solution
|
||
exists to solve this particular&nbsp;problem.</em></p>
|
||
<p>One <a href="http://www.tonyarcieri.com/whats-wrong-with-webcrypto">often pointed</a> flaw in
|
||
web-based cryptographic applications is the fact that there is no way to
|
||
trust online software distributions. Put differently, you don&#8217;t actually
|
||
trust the software authors but are rather trusting the software
|
||
distributors and certificate authorities&nbsp;(CAs).</p>
|
||
<p>I&#8217;ve been talking with a few folks in the past months about that and
|
||
they suggested me to publish something to discuss the matter. So here I&nbsp;come!</p>
|
||
<h2 id="the-problem-attack-vectors">The problem (Attack&nbsp;vectors)</h2>
|
||
<p>Let&#8217;s try to describe a few potential&nbsp;attacks:</p>
|
||
<p><em>Application Authors</em> just released a new version of their open source
|
||
web crypto messaging application. An <em>Indie Hoster</em> installs it on their
|
||
servers so a wide audience can actually use&nbsp;it.</p>
|
||
<p>Someone alters the files on <em>Indie Hoster</em> servers, effectively
|
||
replacing them with other <em>altered files</em> with less security properties
|
||
/ a backdoor. This someone could either be an <em>Evil Attacker</em> which
|
||
found its way trough, the <em>Indie Hoster</em> or a <span class="caps">CDN</span> which delivers the&nbsp;files,</p>
|
||
<p>Trusted <em>Certificate Authorities</em> (&#8220;governments&#8221; or &#8220;hacking team&#8221;) can
|
||
also trick the User Agents (i.e. Firefox) into thinking they&#8217;re talking
|
||
to <em>Indie Hoster</em> even though they&#8217;re actually talking to a different&nbsp;server.</p>
|
||
<p><strong>Altered files</strong> are then being served to the User Agents, and <em>Evil
|
||
Attacker</em> now has a way to actually attack the end&nbsp;users.</p>
|
||
<h2 id="problem-mitigation">Problem&nbsp;Mitigation</h2>
|
||
<p>Part of the problem is solved by the recently introduced <a href="https://w3c.github.io/webappsec/specs/subresourceintegrity/">Sub Resource
|
||
Integrity</a>
|
||
(<span class="caps">SRI</span>). To quote them: &#8220;[it] defines a mechanism by which user agents
|
||
may verify that a fetched resource has been delivered without unexpected&nbsp;manipulation.&#8221;.</p>
|
||
<p><span class="caps">SRI</span> is a good start, but isn&#8217;t enough: it ensures the assets (JavaScript
|
||
files, mainly) loaded from a specific <span class="caps">HTML</span> page are the ones the author
|
||
of the <span class="caps">HTML</span> page intends. However, <span class="caps">SRI</span> doesn&#8217;t allow the User Agent to
|
||
ensure the <span class="caps">HTML</span> page is the one he&nbsp;wants.</p>
|
||
<p>In other words, we miss a way to create trust between <em>Application
|
||
Authors</em> and <em>User Agents</em>. The User-Agent currently has to trust the
|
||
<em>Certificate Authorities</em> and the delivery (<em>Indie Hoster</em>).</p>
|
||
<p>For desktop software distribution: <em>Crypto Experts</em> audit the software,
|
||
sign it somehow and then this signature can be checked locally during
|
||
installation or runtime. It&#8217;s not automated, but at least it&#8217;s&nbsp;possible.</p>
|
||
<p>For web applications, we don&#8217;t have such a mechanism, but it should be
|
||
possible. Consider the&nbsp;following:</p>
|
||
<ul>
|
||
<li><em>App Authors</em> publish a new version of their software; They provide
|
||
a hash of each of their distributed files (including the <span class="caps">HTML</span>&nbsp;files);</li>
|
||
<li><em>Crypto Experts</em> audit these files and sign the hashes&nbsp;somehow;</li>
|
||
<li><em>User Agents</em> can chose to trust some specific <em>Crypto Experts</em>;</li>
|
||
<li>When a <em>User Agent</em> downloads files, it checks if they&#8217;re signed by
|
||
a trusted&nbsp;party.</li>
|
||
</ul>
|
||
<h2 id="chosing-who-you-trust">Chosing who you&nbsp;trust</h2>
|
||
<p>In terms of user experience, handling certificates is hard, and that&#8217;s
|
||
where the community matters. Distributions such as
|
||
<a href="https://tails.boom.org">Tails</a> could chose who they trust to verify the
|
||
files, and issue warnings / refuse to run the application in case files
|
||
aren&#8217;t&nbsp;verified.</p>
|
||
<p>But, as highligted earlier, CAs are hard to trust. A new instance of the
|
||
same <span class="caps">CA</span> system wouldn&#8217;t make that much differences, expect the fact that
|
||
distributions could ship with a set of trusted authorities (for which
|
||
revocation would still need to be taken care&nbsp;of).</p>
|
||
<blockquote>
|
||
<p>[&#8230;] users are vulnerable to MitM attacks by the authority, which
|
||
can vouch for, or be coerced to vouch for, false keys. This weakness
|
||
has been highlighted by recent <span class="caps">CA</span> scandals. Both schemes can also be
|
||
attacked if the authority does not verify keys before vouching for&nbsp;them.</p>
|
||
<p>&#8212; <a href="http://cacr.uwaterloo.ca/techreports/2015/cacr2015-02.pdf">SoK : Secure
|
||
Messaging</a>;</p>
|
||
</blockquote>
|
||
<p>It seems that some other systems could allow for something more&nbsp;reliable:</p>
|
||
<blockquote>
|
||
<p>Melara et al proposed <span class="caps">CONIKS</span>, using a series of chained commitments to
|
||
Merkle prefix trees to build a key directory [&#8230;] for which
|
||
individual users can efficiently verify the consistency of their own
|
||
entry in the directory without relying on a third&nbsp;party.</p>
|
||
<p>This “self- auditing log” approach makes the system partially have no
|
||
auditing required (as general auditing of non-equivocation is still
|
||
required) and also enables the system to be privacy preserving as the
|
||
entries in the directory need not be made public. This comes at a mild
|
||
bandwidth cost not reflected in our table, estimated to be about 10
|
||
kilobytes per client per day for&nbsp;self-auditing.</p>
|
||
<p>&#8212; <a href="http://cacr.uwaterloo.ca/techreports/2015/cacr2015-02.pdf">SoK : Secure
|
||
Messaging</a>;</p>
|
||
</blockquote>
|
||
<p>Now, I honestly have no idea if this thing solves the whole problem, and
|
||
I&#8217;m pretty sure this design has many security problems attached to&nbsp;it.</p>
|
||
<p>However, that&#8217;s a problem I would really like to see solved one day, so
|
||
here the start of the discussion, don&#8217;t hesitate to <a href="/pages/about.html">get in
|
||
touch</a>!</p>
|
||
<h2 id="addendum">Addendum</h2>
|
||
<p>It seems possible to increase the level a user has in a Web Application
|
||
by adding indicators in the User-Agent. For instance, when using an
|
||
application that&#8217;s actually signed by someone considered trustful by the
|
||
User-Agent (or the distributor of the User-Agent), a little green icon
|
||
could be presented to the User, so they know that they can be confident
|
||
about&nbsp;this.</p>
|
||
<p>A bit like User-Agents do for <span class="caps">SSL</span>, but for the actual signature of the
|
||
files being&nbsp;viewed.</p></content><category term="code"></category></entry><entry><title>Service de nuages : Pourquoi avons-nous fait Cliquet ?</title><link href="https://blog.notmyidea.org/pourquoi-cliquet" rel="alternate"></link><published>2015-07-14T00:00:00+02:00</published><updated>2015-07-14T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-07-14:/pourquoi-cliquet</id><summary type="html"><p class="first last">Basé sur Pyramid, Cliquet est un projet qui permet de se concentrer sur l&#8217;essentiel
|
||
lors de la conception&nbsp;d&#8217;APIs.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p><strong>tldr; Cliquet est un toolkit Python pour construire des APIs, qui implémente
|
||
les bonnes pratiques en terme de mise en production et de protocole <span class="caps">HTTP</span>.</strong></p>
|
||
<div class="section" id="les-origines">
|
||
<h2>Les&nbsp;origines</h2>
|
||
<p>L&#8217;objectif pour le premier trimestre 2015 était de construire un service de
|
||
stockage et de <a class="reference external" href="https://blog.notmyidea.org/service-de-nuages-fr.html">synchronisation de listes de lecture</a>.</p>
|
||
<p>Au démarrage du projet, nous avons tenté de rassembler toutes les bonnes pratiques
|
||
et recommandations, venant de différentes équipes et surtout des derniers projets&nbsp;déployés.</p>
|
||
<p>De même, nous voulions tirer parti du protocole de <em>Firefox Sync</em>, robuste et éprouvé,
|
||
pour la synchronisation des données&nbsp;«offline».</p>
|
||
<p>Plutôt qu&#8217;écrire un <a class="reference external" href="http://blog.octo.com/en/design-a-rest-api/">énième</a>
|
||
<a class="reference external" href="http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api">article</a> de blog,
|
||
nous avons préféré les rassembler dans ce qu&#8217;on a appellé «un&nbsp;protocole».</p>
|
||
<p>Comme pour l&#8217;architecture envisagée nous avions deux projets à construire, qui
|
||
devaient obéir globalement à ces mêmes règles, nous avons décidé de mettre en
|
||
commun l&#8217;implémentation de ce protocole et de ces bonnes pratiques dans un&nbsp;«toolkit».</p>
|
||
<p><em>Cliquet</em> est&nbsp;né.</p>
|
||
<img alt="Cliquet logo" class="align-center" src="https://blog.notmyidea.org/images/cliquet/cliquet-logo.png" />
|
||
<div class="section" id="les-intentions">
|
||
<h3>Les&nbsp;intentions</h3>
|
||
<blockquote class="epigraph">
|
||
Quelle structure <span class="caps">JSON</span> pour mon <span class="caps">API</span> ? Quelle syntaxe pour filtrer la liste
|
||
via la querystring ? Comment gérer les écritures concurrentes ?
|
||
Et synchroniser les données dans mon application cliente ?</blockquote>
|
||
<p>Désormais, quand un projet souhaite bénéficier d&#8217;une <span class="caps">API</span> <span class="caps">REST</span> pour stocker et consommer
|
||
des données, il est possible d&#8217;utiliser le <strong>protocole <span class="caps">HTTP</span></strong> proposé
|
||
et de se concentrer sur l&#8217;essentiel. Cela vaut aussi pour les clients, où
|
||
la majorité du code d&#8217;interaction avec le serveur est&nbsp;réutilisable.</p>
|
||
<blockquote class="epigraph">
|
||
Comment pouvons-nous vérifier que le service est opérationnel ? Quels indicateurs StatsD ?
|
||
Est-ce que Sentry est bien configuré ? Comment déployer une nouvelle version
|
||
sans casser les applications clientes ?</blockquote>
|
||
<p>Comme <em>Cliquet</em> fournit tout ce qui est nécessaire pour être conforme avec les
|
||
exigences de la <strong>mise en production</strong>, le passage du prototype au service opérationnel
|
||
est très rapide ! De base le service répondra aux attentes en terme supervision, configuration,
|
||
déploiement et dépréciation de version. Et si celles-ci évoluent, il suffira
|
||
de faire évoluer le&nbsp;toolkit.</p>
|
||
<blockquote class="epigraph">
|
||
Quel backend de stockage pour des documents <span class="caps">JSON</span> ? Comment faire si l&#8217;équipe
|
||
de production impose PostgreSQL ? Et si on voulait passer à Redis ou en
|
||
mémoire pour lancer les tests ?</blockquote>
|
||
<p>En terme d&#8217;implémentation, nous avons choisi de <strong>fournir des abstractions</strong>.
|
||
En effet, nous avions deux services dont le coeur consistait
|
||
à exposer un <em><span class="caps">CRUD</span></em> en <em><span class="caps">REST</span></em>, persistant des données <span class="caps">JSON</span> dans un backend.
|
||
Comme <em>Pyramid</em> et <em>Cornice</em> ne fournissent rien de tout prêt pour ça,
|
||
nous avons voulu introduire des classes de bases pour abstraire les notions
|
||
de resource <span class="caps">REST</span> et de backend de&nbsp;stockage.</p>
|
||
<p>Dans le but de tout rendre optionnel et «pluggable», <strong>tout est configurable</strong>
|
||
depuis le fichier <tt class="docutils literal">.ini</tt> de l&#8217;application. Ainsi tous les projets qui utilisent
|
||
le toolkit se déploieront de la même manière : seuls quelques éléments de configuration
|
||
les&nbsp;distingueront.</p>
|
||
<img alt="Une réunion à Paris..." class="align-center" src="https://blog.notmyidea.org/images/cliquet/cliquet-notes-whiteboard.jpg" />
|
||
</div>
|
||
</div>
|
||
<div class="section" id="le-protocole">
|
||
<h2>Le&nbsp;protocole</h2>
|
||
<blockquote class="epigraph">
|
||
Est-ce suffisant de parler d&#8217;«<span class="caps">API</span> <span class="caps">REST</span>» ? Est-ce bien nécessaire de
|
||
relire la spec <span class="caps">HTTP</span> à chaque fois ? Pourquoi réinventer un protocole complet
|
||
à chaque fois ?</blockquote>
|
||
<p>Quand nous développons un (micro)service Web, nous dépensons généralement beaucoup
|
||
trop d&#8217;énergie à (re)faire des choix&nbsp;(arbitraires).</p>
|
||
<p>Nul besoin de lister ici tout ce qui concerne la dimension
|
||
de la spécification <span class="caps">HTTP</span> pure, qui nous impose le format des headers,
|
||
le support de <span class="caps">CORS</span>, la négocation de contenus (types mime), la différence entre
|
||
authentification et autorisation, la cohérence des code&nbsp;status&#8230;</p>
|
||
<p>Les choix principaux du protocole concernent surtout&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li><strong>Les resources <span class="caps">REST</span></strong> : Les deux URLs d&#8217;une resource (pour la collection
|
||
et les enregistrements) acceptent des verbes et des headers&nbsp;précis.</li>
|
||
<li><strong>Les formats</strong> : le format et la structure <span class="caps">JSON</span> des réponses est imposé, ainsi
|
||
que la pagination des listes ou la syntaxe pour filtrer/trier les resources via la <a class="reference external" href="https://en.wikipedia.org/wiki/Query_string">querystring</a>.</li>
|
||
<li><strong>Les timestamps</strong> : un numéro de révision qui s&#8217;incrémente à chaque opération
|
||
d&#8217;écriture sur une collection&nbsp;d&#8217;enregistrements.</li>
|
||
<li><strong>La synchronisation</strong> : une série de leviers pour récupérer et renvoyer des
|
||
changements sur les données, sans perte ni collision, en utilisant les&nbsp;timestamps.</li>
|
||
<li><strong>Les permissions</strong> : les droits d&#8217;un utilisateur sur une collection ou un enregistrement
|
||
(<em>encore frais et sur le point d&#8217;être documenté</em>) <a class="footnote-reference" href="#footnote-1" id="footnote-reference-1">[1]</a>.</li>
|
||
<li><strong>Opérations par lot</strong>: une <span class="caps">URL</span> qui permet d&#8217;envoyer une série de requêtes
|
||
décrites en <span class="caps">JSON</span> et d&#8217;obtenir les réponses&nbsp;respectives.</li>
|
||
</ul>
|
||
<p>Dans la dimension opérationnelle du protocole, on trouve&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li><strong>La gestion de version</strong> : cohabitation de plusieurs versions en production,
|
||
avec alertes dans les entêtes pour la fin de vie des anciennes&nbsp;versions.</li>
|
||
<li><strong>Le report des requêtes</strong> : entêtes interprétées par les clients, activées en cas de
|
||
maintenance ou de surchage, pour ménager le&nbsp;serveur.</li>
|
||
<li><strong>Le canal d&#8217;erreurs</strong> : toutes les erreurs renvoyées par le serveur ont le même
|
||
format <span class="caps">JSON</span> et ont un numéro&nbsp;précis.</li>
|
||
<li><strong>Les utilitaires</strong> : URLs diverses pour répondre aux besoins exprimés par
|
||
l&#8217;équipe d&#8217;administrateurs (monitoring, metadonnées, paramètres&nbsp;publiques).</li>
|
||
</ul>
|
||
<p>Ce protocole est une compilation des bonnes pratiques pour les APIs <span class="caps">HTTP</span> (<em>c&#8217;est notre métier !</em>),
|
||
des conseils des administrateurs système dont c&#8217;est le métier de mettre à disposition des services
|
||
pour des millions d&#8217;utilisateurs et des retours d&#8217;expérience de l&#8217;équipe
|
||
de <em>Firefox Sync</em> pour la gestion de la concurrence et de&nbsp;l&#8217;«offline-first».</p>
|
||
<p>Il est <a class="reference external" href="http://cliquet.readthedocs.org/en/latest/api/index.html">documenté en détail</a>.</p>
|
||
<p>Dans un monde idéal, ce protocole serait versionné, et formalisé dans une <span class="caps">RFC</span>.
|
||
En rêve, il existerait même plusieurs implémentations avec des codes différentes
|
||
(Python, Go, Node, etc.). <a class="footnote-reference" href="#footnote-2" id="footnote-reference-2">[2]</a></p>
|
||
<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[1]</a></td><td>Voir notre <a class="reference external" href="https://blog.notmyidea.org/service-de-nuages-la-gestion-des-permissions-fr.html">article dédié sur les permissions</a></td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils footnote" frame="void" id="footnote-2" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-2">[2]</a></td><td>Rappel: nous sommes une toute petite équipe !</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="le-toolkit">
|
||
<h2>Le&nbsp;toolkit</h2>
|
||
<div class="section" id="choix-techniques">
|
||
<h3>Choix&nbsp;techniques</h3>
|
||
<p><em>Cliquet</em> implémente le protocole en Python (<em>2.7, 3.4+, pypy</em>), avec <a class="reference external" href="http://trypyramid.com/">Pyramid</a> <a class="footnote-reference" href="#footnote-3" id="footnote-reference-3">[3]</a>.</p>
|
||
<p><strong>Pyramid</strong> est un framework Web qui va prendre en charge tout la partie <span class="caps">HTTP</span>,
|
||
et qui s&#8217;avère pertinent aussi bien pour des petits projets que des plus&nbsp;ambitieux.</p>
|
||
<p><strong>Cornice</strong> est une extension de <em>Pyramid</em>, écrite en partie par Alexis et Tarek,
|
||
qui permet d&#8217;éviter d&#8217;écrire tout le code <em>boilerplate</em> quand on construit une
|
||
<span class="caps">API</span> <span class="caps">REST</span> avec&nbsp;Pyramid.</p>
|
||
<p>Avec <em>Cornice</em>, on évite de réécrire à chaque fois le code qui va
|
||
cabler les verbes <span class="caps">HTTP</span> aux méthodes, valider les entêtes, choisir le sérialiseur
|
||
en fonction des entêtes de négociation de contenus, renvoyer les codes <span class="caps">HTTP</span>
|
||
rigoureux, gérer les entêtes <span class="caps">CORS</span>, fournir la validation <span class="caps">JSON</span> à partir de&nbsp;schémas&#8230;</p>
|
||
<p><strong>Cliquet</strong> utilise les deux précédents pour implémenter le protocole et fournir
|
||
des abstractions, mais on a toujours <em>Pyramid</em> et <em>Cornice</em> sous la main pour
|
||
aller au delà de ce qui est proposé&nbsp;!</p>
|
||
<table class="docutils footnote" frame="void" id="footnote-3" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-3">[3]</a></td><td>Au tout début nous avons commencé une implémentation avec <em>Python-Eve</em>
|
||
(Flask), mais n&#8217;étions pas satisfaits de l&#8217;approche pour la configuration
|
||
de l&#8217;<span class="caps">API</span>. En particulier du côté magique.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="concepts">
|
||
<h3>Concepts</h3>
|
||
<p>Bien évidemment, les concepts du toolkit reflètent ceux du protocole mais il y
|
||
a des éléments&nbsp;supplémentaires:</p>
|
||
<ul class="simple">
|
||
<li><strong>Les backends</strong> : abstractions pour le stockage, le cache et les permissions
|
||
(<em>ex. PostgreSQL, Redis, en-mémoire, &#8230;</em>)</li>
|
||
<li><strong>La supervision</strong> : logging <span class="caps">JSON</span> et indicateurs temps-réel (<em>StatsD</em>) pour suivre les
|
||
performances et la santé du&nbsp;service.</li>
|
||
<li><strong>La configuration</strong> : chargement de la configuration depuis les variables
|
||
d&#8217;environnement et le fichier <tt class="docutils literal">.ini</tt></li>
|
||
<li><strong>La flexibilité</strong> : dés/activation ou substitution de la majorité des composants
|
||
depuis la&nbsp;configuration.</li>
|
||
<li><strong>Le profiling</strong> : utilitaires de développement pour trouver les <a class="reference external" href="https://fr.wiktionary.org/wiki/goulet_d%E2%80%99%C3%A9tranglement">goulets
|
||
d&#8217;étranglement</a>.</li>
|
||
</ul>
|
||
<img alt="Cliquet concepts" class="align-center" src="https://blog.notmyidea.org/images/cliquet/cliquet-concepts.png" />
|
||
<p>Proportionnellement, l&#8217;implémentation du protocole pour les resources <span class="caps">REST</span> est
|
||
la plus volumineuse dans le code source de <em>Cliquet</em>.
|
||
Cependant, comme nous l&#8217;avons décrit plus haut, <em>Cliquet</em> fournit tout un
|
||
ensemble d&#8217;outillage et de bonnes pratiques, et reste
|
||
donc tout à fait pertinent pour n&#8217;importe quel type d&#8217;<span class="caps">API</span>, même sans
|
||
manipulation de données&nbsp;!</p>
|
||
<p>L&#8217;objectif de la boîte à outils est de faire en sorte qu&#8217;un développeur puisse constuire
|
||
une application simplement, en étant sûr qu&#8217;elle réponde aux exigeances de la
|
||
mise en production, tout en ayant la possibilité de remplacer certaines parties
|
||
au fur et à mesure que ses besoins se&nbsp;précisent.</p>
|
||
<p>Par exemple, la persistence fournie par défault est <em>schemaless</em> (e.g <em><span class="caps">JSONB</span></em>),
|
||
mais rien n&#8217;empêcherait d&#8217;implémenter le stockage dans un modèle&nbsp;relationnel.</p>
|
||
<p>Comme les composants peuvent être remplacés depuis la configuration, il est
|
||
tout à fait possible d&#8217;étendre <em>Cliquet</em> avec des notions métiers ou des
|
||
codes exotiques ! Nous avons posé quelques idées dans <a class="reference external" href="http://cliquet.readthedocs.org/en/latest/ecosystem.html">la documentation
|
||
de l&#8217;éco-système</a>.</p>
|
||
<p>Dans les prochaines semaines, nous allons introduire la notion d&#8217;«évènements» (ou signaux),
|
||
qui permettraient aux extensions de s&#8217;interfacer beaucoup plus&nbsp;proprement.</p>
|
||
<p>Nous attachons beaucoup d&#8217;importance à la clareté du code, la pertinence des
|
||
<em>patterns</em>, des tests et de la documentation. Si vous avez des commentaires,
|
||
des critiques ou des interrogations, n&#8217;hésitez pas à <a class="reference external" href="https://github.com/mozilla-services/cliquet/issues">nous en faire part</a>&nbsp;!</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="cliquet-a-l-action">
|
||
<h2>Cliquet, à&nbsp;l&#8217;action.</h2>
|
||
<p>Nous avons écrit un <a class="reference external" href="http://cliquet.readthedocs.org/en/latest/quickstart.html">guide de démarrage</a>,
|
||
qui n&#8217;exige pas de connaître <em>Pyramid</em>.</p>
|
||
<p>Pour illustrer la simplicité et les concepts, voici quelques extraits&nbsp;!</p>
|
||
<div class="section" id="etape-1">
|
||
<h3>Étape&nbsp;1</h3>
|
||
<p>Activer <em>Cliquet</em>:</p>
|
||
<div class="highlight"><pre><span></span><span class="hll"><span class="kn">import</span> <span class="nn">cliquet</span>
|
||
</span><span class="kn">from</span> <span class="nn">pyramid.config</span> <span class="kn">import</span> <span class="n">Configurator</span>
|
||
|
||
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">global_config</span><span class="p">,</span> <span class="o">**</span><span class="n">settings</span><span class="p">):</span>
|
||
<span class="n">config</span> <span class="o">=</span> <span class="n">Configurator</span><span class="p">(</span><span class="n">settings</span><span class="o">=</span><span class="n">settings</span><span class="p">)</span>
|
||
|
||
<span class="hll"> <span class="n">cliquet</span><span class="o">.</span><span class="n">initialize</span><span class="p">(</span><span class="n">config</span><span class="p">,</span> <span class="s1">&#39;1.0&#39;</span><span class="p">)</span>
|
||
</span> <span class="k">return</span> <span class="n">config</span><span class="o">.</span><span class="n">make_wsgi_app</span><span class="p">()</span>
|
||
</pre></div>
|
||
<p>À partir de là, la plupart des outils de <em>Cliquet</em> sont activés et&nbsp;accessibles.</p>
|
||
<p>Par exemple, les URLs <em>hello</em> (<tt class="docutils literal">/v1/</tt>) ou <em>supervision</em> (<tt class="docutils literal">/v1/__heartbeat__</tt>).
|
||
Mais aussi les backends de stockage, de cache, etc.
|
||
qu&#8217;il est possible d&#8217;utiliser dans des vues classiques <em>Pyramid</em> ou <em>Cornice</em>.</p>
|
||
</div>
|
||
<div class="section" id="etape-2">
|
||
<h3>Étape&nbsp;2</h3>
|
||
<p>Ajouter des&nbsp;vues:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">global_config</span><span class="p">,</span> <span class="o">**</span><span class="n">settings</span><span class="p">):</span>
|
||
<span class="n">config</span> <span class="o">=</span> <span class="n">Configurator</span><span class="p">(</span><span class="n">settings</span><span class="o">=</span><span class="n">settings</span><span class="p">)</span>
|
||
|
||
<span class="n">cliquet</span><span class="o">.</span><span class="n">initialize</span><span class="p">(</span><span class="n">config</span><span class="p">,</span> <span class="s1">&#39;1.0&#39;</span><span class="p">)</span>
|
||
<span class="hll"> <span class="n">config</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="s2">&quot;myproject.views&quot;</span><span class="p">)</span>
|
||
</span> <span class="k">return</span> <span class="n">config</span><span class="o">.</span><span class="n">make_wsgi_app</span><span class="p">()</span>
|
||
</pre></div>
|
||
<p>Pour définir des resources <span class="caps">CRUD</span>, il faut commencer par définir un schéma,
|
||
avec <em>Colander</em>, et ensuite déclarer une&nbsp;resource:</p>
|
||
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">cliquet</span> <span class="kn">import</span> <span class="n">resource</span><span class="p">,</span> <span class="n">schema</span>
|
||
|
||
<span class="k">class</span> <span class="nc">BookmarkSchema</span><span class="p">(</span><span class="n">schema</span><span class="o">.</span><span class="n">ResourceSchema</span><span class="p">):</span>
|
||
<span class="n">url</span> <span class="o">=</span> <span class="n">schema</span><span class="o">.</span><span class="n">URL</span><span class="p">()</span>
|
||
|
||
<span class="hll"><span class="nd">@resource</span><span class="o">.</span><span class="n">register</span><span class="p">()</span>
|
||
</span><span class="hll"><span class="k">class</span> <span class="nc">Bookmark</span><span class="p">(</span><span class="n">resource</span><span class="o">.</span><span class="n">BaseResource</span><span class="p">):</span>
|
||
</span><span class="hll"> <span class="n">mapping</span> <span class="o">=</span> <span class="n">BookmarkSchema</span><span class="p">()</span>
|
||
</span></pre></div>
|
||
<p>Désormais, la resource <span class="caps">CRUD</span> est disponible sur <tt class="docutils literal">/v1/bookmarks</tt>, avec toutes
|
||
les fonctionnalités de synchronisation, filtrage, tri, pagination, timestamp, etc.
|
||
De base les enregistrements sont privés, par&nbsp;utilisateur.</p>
|
||
<div class="highlight"><pre><span></span><span class="err">$</span><span class="w"> </span><span class="err">h</span><span class="kc">tt</span><span class="err">p</span><span class="w"> </span><span class="err">GET</span><span class="w"> </span><span class="s2">&quot;http://localhost:8000/v1/bookmarks&quot;</span>
|
||
<span class="err">HTTP/</span><span class="mf">1.1</span><span class="w"> </span><span class="mi">200</span><span class="w"> </span><span class="err">OK</span>
|
||
<span class="err">...</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">&quot;data&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">&quot;url&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;http://cliquet.readthedocs.org&quot;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;cc103eb5-0c80-40ec-b6f5-dad12e7d975e&quot;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">&quot;last_modified&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1437034418940</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="etape-3">
|
||
<h3>Étape&nbsp;3</h3>
|
||
<p>Évidemment, il est possible choisir les <span class="caps">URLS</span>, les verbes <span class="caps">HTTP</span> supportés, de modifier
|
||
des champs avant l&#8217;enregistrement,&nbsp;etc.</p>
|
||
<div class="highlight"><pre><span></span><span class="hll"><span class="nd">@resource</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">collection_path</span><span class="o">=</span><span class="s1">&#39;/user/bookmarks&#39;</span><span class="p">,</span>
|
||
</span><span class="hll"> <span class="n">record_path</span><span class="o">=</span><span class="s1">&#39;/user/bookmarks/{{id}}&#39;</span><span class="p">,</span>
|
||
</span><span class="hll"> <span class="n">collection_methods</span><span class="o">=</span><span class="p">(</span><span class="s1">&#39;GET&#39;</span><span class="p">,))</span>
|
||
</span><span class="k">class</span> <span class="nc">Bookmark</span><span class="p">(</span><span class="n">resource</span><span class="o">.</span><span class="n">BaseResource</span><span class="p">):</span>
|
||
<span class="n">mapping</span> <span class="o">=</span> <span class="n">BookmarkSchema</span><span class="p">()</span>
|
||
|
||
<span class="hll"> <span class="k">def</span> <span class="nf">process_record</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">new</span><span class="p">,</span> <span class="n">old</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
</span><span class="hll"> <span class="k">if</span> <span class="n">old</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">new</span><span class="p">[</span><span class="s1">&#39;device&#39;</span><span class="p">]</span> <span class="o">!=</span> <span class="n">old</span><span class="p">[</span><span class="s1">&#39;device&#39;</span><span class="p">]:</span>
|
||
</span><span class="hll"> <span class="n">device</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;User-Agent&#39;</span><span class="p">)</span>
|
||
</span><span class="hll"> <span class="n">new</span><span class="p">[</span><span class="s1">&#39;device&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">device</span>
|
||
</span><span class="hll"> <span class="k">return</span> <span class="n">new</span>
|
||
</span></pre></div>
|
||
<p><a class="reference external" href="http://cliquet.readthedocs.org/en/latest/reference/resource.html">Plus d&#8217;infos dans la documentation dédiée</a>&nbsp;!</p>
|
||
<div class="admonition note">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">Il est possible de définir des resources sans validation de schema.
|
||
<a class="reference external" href="https://github.com/mozilla-services/kinto/blob/master/kinto/views/records.py">Voir le code source de Kinto</a>.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="etape-4-optionelle">
|
||
<h3>Étape 4&nbsp;(optionelle)</h3>
|
||
<p>Utiliser les abstractions de <em>Cliquet</em> dans une vue <em>Cornice</em>.</p>
|
||
<p>Par exemple, une vue qui utilise le backend de&nbsp;stockage:</p>
|
||
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">cliquet</span> <span class="kn">import</span> <span class="n">Service</span>
|
||
|
||
<span class="n">score</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;score&quot;</span><span class="p">,</span>
|
||
<span class="n">path</span><span class="o">=</span><span class="s1">&#39;/score/</span><span class="si">{game}</span><span class="s1">&#39;</span><span class="p">,</span>
|
||
<span class="n">description</span><span class="o">=</span><span class="s2">&quot;Store game score&quot;</span><span class="p">)</span>
|
||
|
||
<span class="nd">@score</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">schema</span><span class="o">=</span><span class="n">ScoreSchema</span><span class="p">)</span>
|
||
<span class="k">def</span> <span class="nf">post_score</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="n">collection_id</span> <span class="o">=</span> <span class="s1">&#39;scores-&#39;</span> <span class="o">+</span> <span class="n">request</span><span class="o">.</span><span class="n">match_dict</span><span class="p">[</span><span class="s1">&#39;game&#39;</span><span class="p">]</span>
|
||
<span class="n">user_id</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">authenticated_userid</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">validated</span> <span class="c1"># c.f. Cornice.</span>
|
||
|
||
<span class="hll"> <span class="n">storage</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">registry</span><span class="o">.</span><span class="n">storage</span>
|
||
</span><span class="hll"> <span class="n">record</span> <span class="o">=</span> <span class="n">storage</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">collection_id</span><span class="p">,</span> <span class="n">user_id</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
|
||
</span> <span class="k">return</span> <span class="n">record</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="vos-retours">
|
||
<h2>Vos&nbsp;retours</h2>
|
||
<p>N&#8217;hésitez pas à nous faire part de vos retours ! Cela vous a donné envie
|
||
d&#8217;essayer ? Vous connaissez un outil similaire ?
|
||
Y-a-t-il des points qui ne sont pas clairs ? Manque de cas d&#8217;utilisation concrets ?
|
||
Certains aspects mal pensés ? Trop contraignants ? Trop de magie ? Overkill&nbsp;?</p>
|
||
<p>Nous prenons&nbsp;tout.</p>
|
||
<div class="section" id="points-faibles">
|
||
<h3>Points&nbsp;faibles</h3>
|
||
<p>Nous sommes très fiers de ce que nous avons construit, en relativement peu
|
||
de temps. Et comme nous l&#8217;exposions dans l&#8217;article précédent (plus accessible), il y a du potentiel&nbsp;!</p>
|
||
<p>Cependant, nous sommes conscients d&#8217;un certain nombre de points
|
||
qui peuvent être vus comme des&nbsp;faiblesses.</p>
|
||
<ul class="simple">
|
||
<li><strong>La documentation d&#8217;<span class="caps">API</span></strong> : actuellement, nous n&#8217;avons pas de solution pour qu&#8217;un
|
||
projet qui utilise <em>Cliquet</em> puisse intégrer facilement toute
|
||
<a class="reference external" href="http://cliquet.readthedocs.org/en/latest/api/index.html">la documentation de l&#8217;<span class="caps">API</span></a>&nbsp;obtenue.</li>
|
||
<li><strong>La documentation</strong> : il est très difficile d&#8217;organiser la documentation, surtout
|
||
quand le public visé est aussi bien débutant qu&#8217;expérimenté. Nous sommes probablement
|
||
victimes du «<a class="reference external" href="https://en.wikipedia.org/wiki/Curse_of_knowledge">curse of knowledge</a>».</li>
|
||
<li><strong>Le protocole</strong> : on sent bien qu&#8217;on va devoir versionner le protocole. Au
|
||
moins pour le désolidariser des versions de <em>Cliquet</em>, si on veut aller au
|
||
bout de la philosophie et de&nbsp;l&#8217;éco-système.</li>
|
||
<li><strong>Le conservatisme</strong> : Nous aimons la stabilité et la robustesse. Mais surtout
|
||
nous ne sommes pas tout seuls et devons nous plier aux contraintes de la mise
|
||
en production ! Cependant, nous avons très envie de faire de l&#8217;async avec Python 3&nbsp;!</li>
|
||
<li><strong>Publication de versions</strong> : le revers de la médaille de la factorisation. Il
|
||
arrive qu&#8217;on préfère faire évoluer le toolkit (e.g. ajouter une option) pour
|
||
un point précis d&#8217;un projet. En conséquence, on doit souvent releaser les
|
||
projets en&nbsp;cascade.</li>
|
||
</ul>
|
||
</div>
|
||
<div class="section" id="quelques-questions-courantes">
|
||
<h3>Quelques questions&nbsp;courantes</h3>
|
||
<blockquote>
|
||
Pourquoi Python ?</blockquote>
|
||
<p>On prend beaucoup de plaisir à écrire du Python, et le calendrier annoncé
|
||
initialement était très serré: pas question de tituber avec une code
|
||
mal maitrisée&nbsp;!</p>
|
||
<p>Et puis, après avoir passé près d&#8217;un an sur un projet Node.js, l&#8217;équipe avait
|
||
bien envie de refaire du&nbsp;Python.</p>
|
||
<blockquote>
|
||
Pourquoi pas Django ?</blockquote>
|
||
<p>On y a pensé, surtout parce qu&#8217;il y a plusieurs fans de <em>Django <span class="caps">REST</span> Framework</em>
|
||
dans&nbsp;l&#8217;équipe.</p>
|
||
<p>On l&#8217;a écarté principalement au profit de la légèreté et la modularité de
|
||
<em>Pyramid</em>.</p>
|
||
<blockquote>
|
||
Pourquoi pas avec un framework asynchrone en Python 3+ ?</blockquote>
|
||
<p>Pour l&#8217;instant nos administrateurs système nous imposent des déploiements en
|
||
Python 2.7, à notre grand désarroi&nbsp;/o\</p>
|
||
<p>Pour <em>Reading List</em>, nous <a class="reference external" href="https://github.com/mozilla-services/readinglist/blob/1.7.0/readinglist/__init__.py#L19-L26">avions activé</a>
|
||
<em>gevent</em>.</p>
|
||
<p>Puisque l&#8217;approche consiste à implémenter un protocole bien déterminé, nous n&#8217;excluons
|
||
pas un jour d&#8217;écrire un <em>Cliquet</em> en <em>aiohttp</em> ou <em>Go</em> si cela s&#8217;avèrerait&nbsp;pertinent.</p>
|
||
<blockquote>
|
||
Pourquoi pas <span class="caps">JSON</span>-<span class="caps">API</span> ?</blockquote>
|
||
<p>Comme nous l&#8217;expliquions au retour des APIdays,
|
||
<span class="caps">JSON</span>-<span class="caps">API</span> est une spécification qui rejoint plusieurs de nos&nbsp;intentions.</p>
|
||
<p>Quand nous avons commencé le protocole, nous ne connaissions pas <span class="caps">JSON</span>-<span class="caps">API</span>.
|
||
Pour l&#8217;instant, comme notre proposition est beaucoup plus minimaliste, le
|
||
rapprochement n&#8217;a <a class="reference external" href="https://github.com/mozilla-services/cliquet/issues/254">pas dépassé le stade de la discussion</a>.</p>
|
||
<blockquote>
|
||
Est-ce que Cliquet est un framework <span class="caps">REST</span> pour Pyramid ?</blockquote>
|
||
<p>Non.</p>
|
||
<p>Au delà des classes de resources <span class="caps">CRUD</span> de Cliquet, qui implémentent un
|
||
protocole bien précis, il faut utiliser Cornice ou Pyramid&nbsp;directement.</p>
|
||
<blockquote>
|
||
Est-ce que Cliquet est suffisamment générique pour des projets hors Mozilla ?</blockquote>
|
||
<p>Premièrement, nous faisons en sorte que tout soit contrôlable depuis la
|
||
configuration <tt class="docutils literal">.ini</tt> pour permettre la dés/activation ou substitution des&nbsp;composants.</p>
|
||
<p>Si le protocole <span class="caps">HTTP</span>/<span class="caps">JSON</span> des resources <span class="caps">CRUD</span> vous satisfait,
|
||
alors Cliquet est probablement le plus court chemin pour construire une
|
||
application qui tient la&nbsp;route.</p>
|
||
<p>Mais l&#8217;utilisation des resources <span class="caps">CRUD</span> est facultative, donc Cliquet reste pertinent
|
||
si les bonnes pratiques en terme de mise en production ou les abstractions fournies
|
||
vous paraissent valables&nbsp;!</p>
|
||
<p>Cliquet reste un moyen simple d&#8217;aller très vite pour mettre sur pied
|
||
une application&nbsp;Pyramid/Cornice.</p>
|
||
<blockquote>
|
||
Est-ce que les resources <span class="caps">JSON</span> supporte les modèles relationnels complexes ?</blockquote>
|
||
<p>La couche de persistence fournie est très simple, et devrait
|
||
répondre à la majorité des cas d&#8217;utilisation où les données n&#8217;ont pas de&nbsp;relations.</p>
|
||
<p>En revanche, il est tout à fait possible de bénéficier de tous les aspects
|
||
du protocole en utilisant une classe <tt class="docutils literal">Collection</tt> maison, qui se chargerait
|
||
elle de manipuler les&nbsp;relations.</p>
|
||
<p>Le besoin de relations pourrait être un bon prétexte pour implémenter le
|
||
protocole avec Django <span class="caps">REST</span> Framework&nbsp;:)</p>
|
||
<blockquote>
|
||
Est-il possible de faire ci ou ça avec Cliquet ?</blockquote>
|
||
<p>Nous aimerions collecter des besoins pour écrire un ensemble de «recettes/tutoriels». Mais
|
||
pour ne pas travailler dans le vide, nous aimerions <a class="reference external" href="https://github.com/mozilla-services/cliquet/issues">connaitre vos idées</a> !
|
||
(<em>ex. brancher l&#8217;authentification Github, changer le format du logging <span class="caps">JSON</span>, stocker des
|
||
données cartographiques, &#8230;</em>)</p>
|
||
<blockquote>
|
||
Est-ce que Cliquet peut manipuler des fichiers ?</blockquote>
|
||
<p><a class="reference external" href="https://github.com/mozilla-services/cliquet/issues/236">Nous l&#8217;envisageons</a>,
|
||
mais pour l&#8217;instant nous attendons que le besoin survienne en interne pour se&nbsp;lancer.</p>
|
||
<p>Si c&#8217;est le cas, le protocole utilisé sera <a class="reference external" href="http://remotestorage.io/">Remote Storage</a>,
|
||
afin notamment de s&#8217;intégrer dans l&#8217;éco-système&nbsp;grandissant.</p>
|
||
<blockquote>
|
||
Est-ce que la fonctionnalité X va être implémentée ?</blockquote>
|
||
<p><em>Cliquet</em> est déjà bien garni. Plutôt qu&#8217;implémenter la fonctionnalité X,
|
||
il y a de grandes chances que nous agissions pour s&#8217;assurer que les abstractions
|
||
et les mécanismes d&#8217;extension fournis permettent de l&#8217;implémenter sous forme&nbsp;d&#8217;extension.</p>
|
||
</div>
|
||
</div>
|
||
</content><category term="code"></category></entry><entry><title>Service de nuages : Perspectives pour l’été</title><link href="https://blog.notmyidea.org/service-de-nuages-perspectives-pour-lete-fr.html" rel="alternate"></link><published>2015-07-07T00:00:00+02:00</published><updated>2015-07-07T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-07-07:/service-de-nuages-perspectives-pour-lete-fr.html</id><summary type="html"><p class="first last">Le travail en cours et les fonctionnalités à venir pour les prochains&nbsp;mois.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p>Mozilla a pour coutume d&#8217;organiser régulièrement des semaines de travail où tous les employés
|
||
sont réunis physiquement. Pour cette dernière édition, nous avons pu retrouver
|
||
nos collègues du monde entier à <a class="reference external" href="http://www.openstreetmap.org/node/268148288#map=4/50.12/-122.95">Whistler, en Colombie Britannique au Canada</a>&nbsp;!</p>
|
||
<img alt="«All Hands» talk about Lego, by &#64;davidcrob - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-talks.jpg" />
|
||
<p>Ce fût l&#8217;occasion pour notre équipe de se retrouver, et surtout de partager notre
|
||
vision et nos idées dans le domaine du stockage, afin de collecter des cas d&#8217;utilisation pour
|
||
notre solution <a class="reference external" href="https://kinto.readthedocs.org">Kinto</a>.</p>
|
||
<p>Dans cet article, nous passons en revue les pistes que nous avons pour
|
||
les prochains&nbsp;mois.</p>
|
||
<div class="section" id="ateliers-et-promotion">
|
||
<h2>Ateliers et&nbsp;promotion</h2>
|
||
<p>Nicolas a présenté <a class="reference external" href="https://github.com/mozilla-services/kinto.js">Kinto.js</a> dans un atelier dédié, avec comme support de
|
||
présentation le <a class="reference external" href="http://kintojs.readthedocs.org/en/latest/tutorial/">tutorial d&#8217;introduction</a>.</p>
|
||
<p>L&#8217;application résultante, pourtant toute simple, permet d&#8217;appréhender les
|
||
concepts de synchronisation de Kinto. Le tout sans installation prélable,
|
||
puisque Rémy a mis en place un <a class="reference external" href="https://kinto.dev.mozaws.net/v1/">serveur de dev effacé tous les jours</a>.</p>
|
||
<p>Nous avions mis un point d&#8217;honneur à faire du Vanilla.<span class="caps">JS</span>, déjà pour éviter les
|
||
combats de clochers autour des frameworks, mais aussi pour mettre en évidence qu&#8217;avec
|
||
<span class="caps">HTML5</span> et <span class="caps">ES6</span>, on n&#8217;était plus aussi démunis qu&#8217;il y a quelques&nbsp;années.</p>
|
||
<p>Ce petit atelier nous a permis de nous rendre compte qu&#8217;on avait encore de
|
||
grosses lacunes en terme de documentation, surtout en ce qui concerne
|
||
l&#8217;éco-système et la vision globale des projets (Kinto, Kinto.js, Cliquet, &#8230;).
|
||
Nous allons donc faire de notre mieux pour combler ce&nbsp;manque.</p>
|
||
<img alt="Kinto.js workshop - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-workshop.jpg" />
|
||
</div>
|
||
<div class="section" id="mozilla-payments">
|
||
<h2>Mozilla&nbsp;Payments</h2>
|
||
<p>Comme <a class="reference external" href="http://www.servicedenuages.fr/la-gestion-des-permissions">décrit précédemment</a>, nous avons mis en place un système de permissions pour répondre aux besoins de suivi des paiements et&nbsp;abonnements.</p>
|
||
<p>Pour ce projet, Kinto sera utilisé depuis une application Django, via un client&nbsp;Python.</p>
|
||
<p>Maintenant que les développements ont été livrés, il faut transformer l&#8217;essai, réussir l&#8217;intégration, l&#8217;hébergement et la montée en puissance. La solution doit être livrée à la fin de&nbsp;l&#8217;année.</p>
|
||
<div class="section" id="a-venir">
|
||
<h3>À&nbsp;venir</h3>
|
||
<p>Nous aimerions en profiter pour implémenter une fonctionnalité qui nous tient à coeur : la construction de la liste des enregistrements accessibles en lecture sur une collection&nbsp;partagée.</p>
|
||
<img alt="Whistler Alta Lake - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-lake.jpg" />
|
||
</div>
|
||
</div>
|
||
<div class="section" id="firefox-os-et-stockage">
|
||
<h2>Firefox <span class="caps">OS</span> et&nbsp;stockage</h2>
|
||
<p>Nous avons eu beaucoup d&#8217;échanges avec l&#8217;équipe de Firefox <span class="caps">OS</span>, avec qui nous avions
|
||
déjà eu l&#8217;occasion de collaborer, pour le <a class="reference external" href="https://github.com/mozilla-services/msisdn-gateway">serveur d&#8217;identification BrowserID par <span class="caps">SMS</span></a> et pour <a class="reference external" href="https://github.com/mozilla-services/loop-server">Firefox Hello</a>.</p>
|
||
<div class="section" id="in-app-sync">
|
||
<h3>In-App&nbsp;sync</h3>
|
||
<p>Kinto, la solution simple promue pour la synchronisation de données dans les applications
|
||
Firefox <span class="caps">OS</span> ? La classe ! C&#8217;est ce qu&#8217;on avait en tête depuis longtemps, déjà à
|
||
l&#8217;époque avec <a class="reference external" href="http://daybed.readthedocs.org/">Daybed</a>. Voici donc une belle opportunité à saisir&nbsp;!</p>
|
||
<p>Il va falloir expliciter les limitations et hypothèses simplificatrices de notre
|
||
solution, surtout en termes de gestion de la concurrence. Nous sommes persuadés
|
||
que ça colle avec la plupart des besoins, mais il ne faudrait pas décevoir&nbsp;:)</p>
|
||
<p>Le fait que <a class="reference external" href="https://github.com/daleharvey">Dale</a>, un des auteurs de <a class="reference external" href="http://pouchdb.com/">PouchDB</a> et <a class="reference external" href="https://github.com/michielbdejong">Michiel de Jong</a>, un des auteurs de <a class="reference external" href="http://remotestorage.io/">Remote Storage</a>, nous aient encouragés sur nos premiers pas nous a bien motivé&nbsp;!</p>
|
||
</div>
|
||
<div class="section" id="cut-the-rope">
|
||
<h3>Cut the&nbsp;Rope</h3>
|
||
<p>Kinto devrait être mis à profit pour synchroniser les paramètres et les scores
|
||
du <a class="reference external" href="http://mozilla.cuttherope.net/">jeu</a>. Un premier exercice et une première vitrine sympas&nbsp;!</p>
|
||
</div>
|
||
<div class="section" id="syncto">
|
||
<h3>« SyncTo&nbsp;»</h3>
|
||
<p><a class="reference external" href="https://docs.services.mozilla.com/storage/apis-1.5.html">Firefox Sync</a> est la solution qui permet de synchroniser les données de Firefox (favoris, extensions, historique, complétion des formulaires, mots de passe, &#8230;) entre plusieurs périphériques, de manière&nbsp;chiffrée.</p>
|
||
<p>L&#8217;implémentation du client en JavaScript est relativement complexe et date un peu maintenant.
|
||
Le code existant n&#8217;est pas vraiment portable dans <em>Firefox <span class="caps">OS</span></em> et les tentatives de réécriture
|
||
n&#8217;ont pas&nbsp;abouti.</p>
|
||
<p>Nous souhaitons implémenter un pont entre <em>Kinto</em> et <em>Firefox Sync</em>, de manière
|
||
à pouvoir utiliser le client <em>Kinto.js</em>, plus simple et plus moderne, pour récupérer
|
||
les contenus et les stocker dans IndexedDB. Le delta à implémenter côté serveur est faible car nous nous étions
|
||
inspirés du protocole déjà éprouvé de Sync. Côté client, il s&#8217;agira surtout de
|
||
câbler l&#8217;authentification BrowserId et la&nbsp;Crypto.</p>
|
||
<p>Alexis a sauté sur l&#8217;occasion pour commencer l&#8217;écriture d&#8217;<a class="reference external" href="https://github.com/mozilla-services/syncclient">un client python pour Firefox Sync</a>, qui servira de brique de base pour l&#8217;écriture du&nbsp;service.</p>
|
||
</div>
|
||
<div class="section" id="cloud-storage">
|
||
<h3>Cloud&nbsp;Storage</h3>
|
||
<p>Eden Chuang et Sean Lee ont présenté les avancées sur l&#8217;intégration de services de stockages
|
||
distants (<em>DropBox, Baidu Yun</em>) dans <em>Firefox <span class="caps">OS</span></em>. Actuellement, leur preuve de
|
||
concept repose sur <a class="reference external" href="https://fr.wikipedia.org/wiki/Filesystem_in_Userspace"><span class="caps">FUSE</span></a>.</p>
|
||
<p>Nous avons évidemment en tête d&#8217;introduire la notion de fichiers attachés dans
|
||
<em>Kinto</em>, en implémentant la specification
|
||
<a class="reference external" href="https://tools.ietf.org/html/draft-dejong-remotestorage-05">*Remote Storage*</a>,
|
||
mais pour l&#8217;instant les cas d&#8217;utilisations ne se sont pas encore présentés&nbsp;officiellement.</p>
|
||
</div>
|
||
<div class="section" id="a-venir-1">
|
||
<h3>À&nbsp;venir</h3>
|
||
<p>Nous serons probablement amenés à introduire la gestion de la concurrence dans
|
||
le client <span class="caps">JS</span>, en complément de ce qui a été fait sur le serveur, pour permettre
|
||
les écritures simultanées et synchronisation en tâche de&nbsp;fond.</p>
|
||
<p>Nous sommes par ailleurs perpétuellement preneurs de vos retours — et bien
|
||
entendu de vos contributions — tant sur le code <a class="reference external" href="https://github.com/mozilla-services/kinto/">serveur</a>
|
||
que <a class="reference external" href="https://github.com/mozilla-services/kinto.js/">client</a>&nbsp;!</p>
|
||
<img alt="Firefox OS Cloud Storage Presentation - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-cloud-storage.jpg" />
|
||
</div>
|
||
</div>
|
||
<div class="section" id="contenus-applicatifs-de-firefox">
|
||
<h2>Contenus applicatifs de&nbsp;Firefox</h2>
|
||
<p>Aujourd&#8217;hui Firefox a un cycle de release de six semaines. Un des objectifs
|
||
consiste à désolidariser certains contenus applicatifs de ces cycles
|
||
relativement longs (ex. <em>règles de securité, dictionnaires, traductions, &#8230;</em>) <a class="footnote-reference" href="#footnote-1" id="footnote-reference-1">[1]</a>.</p>
|
||
<p>Il s&#8217;agit de données <span class="caps">JSON</span> et binaire qui doivent être versionnées et synchronisées par
|
||
les navigateurs (<em>lecture seule</em>).</p>
|
||
<p>Il y a plusieurs outils officiels qui existent pour gérer ça (<em>Balrog</em>, <em>Shavar</em>, &#8230;),
|
||
et pour l&#8217;instant, aucun choix n&#8217;a été fait. Mais lors des conversations avec
|
||
l&#8217;équipe en charge du projet, ce fût vraiment motivant de voir que même pour
|
||
ce genre de besoins internes, <em>Kinto</em> est tout aussi pertinent&nbsp;!</p>
|
||
<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[1]</a></td><td>La bonne nouvelle c&#8217;est que toutes les fonctionnalités <em>third-party</em> qui ont
|
||
été intégrées récemment vont redevenir des <em>add-ons</em> \o/.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<img alt="Landscape - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-landscape.jpg" />
|
||
</div>
|
||
<div class="section" id="awesome-bar">
|
||
<h2>Awesome&nbsp;bar</h2>
|
||
<p>L&#8217;équipe <em>Firefox Labs</em>, le laboratoire qui élève des pandas roux en éprouvette,
|
||
serait vraiment intéressé par notre solution, notamment pour abreuver en données
|
||
un prototype pour améliorer <em>Awesome bar</em>, qui fusionnerait <span class="caps">URL</span>, historique et&nbsp;recherche.</p>
|
||
<p>Nous ne pouvons pas en dire beaucoup plus pour l&#8217;instant, mais les fonctionnalités
|
||
de collections d&#8217;enregistrements partagées entre utilisateurs de <em>Kinto</em>
|
||
correspondent parfaitement à ce qui est envisagé pour le futur du navigateur&nbsp;:)</p>
|
||
<div class="section" id="a-venir-2">
|
||
<h3>À&nbsp;venir</h3>
|
||
<p>Nous serons donc probablement amenés, avant de la fin de l&#8217;année, à introduire des
|
||
fonctionnalités d&#8217;indexation et de recherche <em>full-text</em> (comprendre <em>ElasticSearch</em>).
|
||
Cela rejoint nos plans précédents, puisque c&#8217;est quelque chose que nous avions dans
|
||
<em>Daybed</em>, et qui figurait sur notre feuille de route&nbsp;!</p>
|
||
<img alt="Firefox Labs Meeting - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-labs.jpg" />
|
||
</div>
|
||
</div>
|
||
<div class="section" id="browser-html">
|
||
<h2>Browser.html</h2>
|
||
<p>L&#8217;équipe <em>Recherche</em> explore les notions de plateforme, et travaille notamment
|
||
sur l&#8217;implémentation d&#8217;un navigateur en <span class="caps">JS</span>/<span class="caps">HTML</span> avec <em>React</em>:
|
||
<a class="reference external" href="https://github.com/mozilla/browser.html">browser.html</a></p>
|
||
<p><em>Kinto</em> correspond parfaitement aux attentes
|
||
de l&#8217;équipe pour synchroniser les données associées à un&nbsp;utilisateur.</p>
|
||
<p>Il pourrait s&#8217;agir de données de navigation (comme Sync), mais aussi de collections
|
||
d&#8217;enregistrements diverses, comme par exemple les préférences du navigateur
|
||
ou un équivalent à <em>Alexa.com Top 500</em> pour fournir la complétion d&#8217;<span class="caps">URL</span> sans
|
||
interroger le moteur de&nbsp;recherche.</p>
|
||
<p>L&#8217;exercice pourrait être poussé jusqu&#8217;à la synchronisation d&#8217;états <em>React</em>
|
||
entre périphériques (par exemple pour les&nbsp;onglets).</p>
|
||
<div class="section" id="a-venir-3">
|
||
<h3>À&nbsp;venir</h3>
|
||
<p>Si <em>browser.html</em> doit stocker des données de navigation, il faudra ajouter
|
||
des fonctionnalités de chiffrement sur le client <span class="caps">JS</span>. Ça tombe bien, c&#8217;est un
|
||
sujet passionant, et <a class="reference external" href="http://www.w3.org/TR/WebCryptoAPI/">il y a plusieurs standards</a>&nbsp;!</p>
|
||
<p>Pour éviter d&#8217;interroger le serveur à intervalle régulier afin de synchroniser les
|
||
changements, l&#8217;introduction des <a class="reference external" href="https://w3c.github.io/push-api/">*push notifications*</a> semble assez naturelle.
|
||
Il s&#8217;agirait alors de la dernière pierre qui manque à l&#8217;édifice pour obtenir
|
||
un «<em>Mobile/Web backend as a service</em>»&nbsp;complet.</p>
|
||
<img alt="Roadmap - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-roadmap.jpg" />
|
||
</div>
|
||
</div>
|
||
<div class="section" id="conclusion">
|
||
<h2>Conclusion</h2>
|
||
<p>Nous sommes dans une situation idéale, puisque ce que nous avions imaginé
|
||
sur <a class="reference external" href="https://github.com/mozilla-services/kinto/wiki/Roadmap">notre feuille de route</a> correspond à ce qui nous est demandé par les
|
||
différentes&nbsp;équipes.</p>
|
||
<p>L&#8217;enjeu consiste maintenant à se coordonner avec tout le monde, ne pas décevoir,
|
||
tenir la charge, continuer à améliorer et à faire la promotion du produit, se concentrer
|
||
sur les prochaines étapes et embarquer quelques contributeurs à nos cotés pour
|
||
construire une solution libre, générique, simple et auto-hébergeable pour le stockage
|
||
de données sur le Web&nbsp;:)</p>
|
||
<img alt="Friday Night Party - CC0" class="align-center" src="https://blog.notmyidea.org/images/whistler/whistler-top-roof.jpg" />
|
||
</div>
|
||
</content><category term="code"></category></entry><entry><title>Service de nuages : Achievement unlocked</title><link href="https://blog.notmyidea.org/service-de-nuages-achievement-unlocked-fr.html" rel="alternate"></link><published>2015-06-01T00:00:00+02:00</published><updated>2015-06-01T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-06-01:/service-de-nuages-achievement-unlocked-fr.html</id><summary type="html"><p class="first last">Après près de 3 mois intensifs, nous venons de <em>tagguer</em> Cliquet 2.0
|
||
et Kinto 1.0&nbsp;!</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p>Aujourd&#8217;hui, c&#8217;est jour de fête : nous venons de publier Cliquet <strong>2.0</strong>
|
||
<a class="footnote-reference" href="#footnote-1" id="footnote-reference-1">[1]</a> et Kinto <strong>1.0</strong> <a class="footnote-reference" href="#footnote-2" id="footnote-reference-2">[2]</a>.</p>
|
||
<blockquote class="epigraph">
|
||
<p>L&#8217;aboutissement de 3 années de R&amp;D&nbsp;!</p>
|
||
<p class="attribution">&mdash;Rémy</p>
|
||
</blockquote>
|
||
<p><a class="reference external" href="https://kinto.readthedocs.org/en/latest/">Kinto</a> est un service pour
|
||
stocker, synchroniser et partager des données arbitraires, attachées à un
|
||
compte Firefox (mais le système d&#8217;authentification est <em>pluggable</em>).</p>
|
||
<p><a class="reference external" href="https://cliquet.readthedocs.org/en/latest/">Cliquet</a> est une boite à outils pour faciliter l&#8217;implémentation de
|
||
micro-services <span class="caps">HTTP</span> tels que les APIs <em><span class="caps">REST</span></em> ayant des besoins de&nbsp;synchronisation.</p>
|
||
<p>Vous pouvez lire plus à propos des raisons qui nous ont poussé à proposer cette nouvelle solution
|
||
et de notre ambition sur <a class="reference external" href="http://www.servicedenuages.fr/eco-systeme-et-stockage-generique.html">http://www.servicedenuages.fr/eco-systeme-et-stockage-generique.html</a></p>
|
||
<p>Nous sommes fiers du travail que nous avons pu réaliser durant ces derniers
|
||
mois sur ces deux projets. Bien que la plupart du travail que nous ayons
|
||
réalisé pour le serveur de liste de lecture (Reading List) ait pu être utilisé,
|
||
beaucoup de parties ont été repensées et nous avons introduit des
|
||
fonctionnalités que l&#8217;on attendait depuis longtemps, comme la gestion des&nbsp;permissions.</p>
|
||
<p>Bien sur, exactement comme après un ré-aménagement de salon, on ne peut
|
||
s&#8217;empêcher de voir toutes les choses qui doivent toujours être améliorées,
|
||
notamment sur la documentation et les&nbsp;performances.</p>
|
||
<p>On peut déjà entrevoir à quoi l&#8217;écosystème va ressembler, et c&#8217;est prometteur.
|
||
Il y a déjà un client JavaScript <a class="footnote-reference" href="#footnote-3" id="footnote-reference-3">[3]</a> dont l&#8217;objectif est de synchroniser les
|
||
données locales du navigateur avec une instance de&nbsp;Kinto.</p>
|
||
<p>N&#8217;hésitez vraiment pas à nous solliciter pour discuter avec vous si vous avez
|
||
des problématiques proches : nous accueillons avec plaisir toutes sortes de
|
||
retours, que ce soit à propos du code, de la documentation, de la sécurité de
|
||
la solution ou de la manière de communiquer avec le monde extérieur. Si vous
|
||
souhaitez nous contacter, vous pouvez laisser un commentaire ici ou nous
|
||
contacter sur le canal <a class="reference external" href="irc://irc.mozilla.org/#storage">#storage</a> sur le réseau <span class="caps">IRC</span> de&nbsp;Mozilla.</p>
|
||
<p>Et ce n&#8217;est que le début ! Le futur se dessine dans notre feuille de route
|
||
<a class="footnote-reference" href="#footnote-4" id="footnote-reference-4">[4]</a>.</p>
|
||
<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[1]</a></td><td><strong>Cliquet</strong> est une boite à outils pour faciliter l&#8217;implémentation de
|
||
microservices <span class="caps">HTTP</span> tels que les APIs <em><span class="caps">REST</span></em> ayant des besoins de
|
||
synchronisation.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils footnote" frame="void" id="footnote-2" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-2">[2]</a></td><td><strong>Kinto</strong> est un service pour stocker, synchroniser et partager des données
|
||
arbitraires, attachées à un compte Firefox (mais le système d&#8217;authentification
|
||
est <em>pluggable</em>).</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils footnote" frame="void" id="footnote-3" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-3">[3]</a></td><td>Cliquetis, la bibliothèque JavaScript pour consommer l&#8217;<span class="caps">API</span> <span class="caps">HTTP</span> de Kinto —
|
||
<a class="reference external" href="https://github.com/mozilla-services/cliquetis">https://github.com/mozilla-services/cliquetis</a></td></tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils footnote" frame="void" id="footnote-4" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-4">[4]</a></td><td>La feuille de route de Kinto: <a class="reference external" href="https://github.com/mozilla-services/kinto/wiki/roadmap">https://github.com/mozilla-services/kinto/wiki/roadmap</a></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</content><category term="code"></category></entry><entry><title>Service de nuages : Stocker et interroger les permissions avec Kinto</title><link href="https://blog.notmyidea.org/service-de-nuages-stocker-et-interroger-les-permissions-avec-kinto-fr.html" rel="alternate"></link><published>2015-05-26T00:00:00+02:00</published><updated>2015-05-26T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-05-26:/service-de-nuages-stocker-et-interroger-les-permissions-avec-kinto-fr.html</id><summary type="html"><p class="first last">Comment faire pour stocker et interroger la base de données au sujet des permissions avec Kinto&nbsp;?</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p><strong>tl;dr: On a maintenant un super système de permission mais comment faire pour stocker et interroger ces permissions de manière efficace&nbsp;?</strong></p>
|
||
<div class="section" id="la-problematique">
|
||
<h2>La&nbsp;problématique</h2>
|
||
<p>Maintenant que nous avons défini un modèle de gestion des permissions
|
||
sur les objets qui nous satisfait, le problème est de stocker ces
|
||
permissions de manière efficace afin de pouvoir autoriser ou interdire
|
||
l&#8217;accès à un objet pour la personne qui fait la&nbsp;requête.</p>
|
||
<p>Chaque requête sur notre <span class="caps">API</span> va générer une ou plusieurs demandes
|
||
d&#8217;accès, il faut donc que la réponse soit très rapide sous peine
|
||
d&#8217;impacter la vélocité du&nbsp;service.</p>
|
||
</div>
|
||
<div class="section" id="obtenir-la-liste-des-principals-d-un-utilisateur">
|
||
<h2>Obtenir la liste des &#8220;principals&#8221; d&#8217;un&nbsp;utilisateur</h2>
|
||
<p>Les <em>principals</em> de l&#8217;utilisateur correspondent à son <tt class="docutils literal">user_id</tt>
|
||
ainsi qu&#8217;à la liste des identifiants des groupes dans lesquels il a
|
||
été&nbsp;ajouté.</p>
|
||
<p>Pour éviter de recalculer les <em>principals</em> de l&#8217;utilisateur à chaque
|
||
requête, le mieux reste de maintenir une liste des <em>principals</em> par&nbsp;utilisateur.</p>
|
||
<p>Ainsi lorsqu&#8217;on ajoute un utilisateur à un groupe, il faut bien penser
|
||
à ajouter le groupe à la liste des <em>principals</em> de&nbsp;l&#8217;utilisateur.</p>
|
||
<p>Ça se complexifie lorsqu&#8217;on ajoute un groupe à un&nbsp;groupe.</p>
|
||
<p>Dans un premier temps interdire l&#8217;ajout d&#8217;un groupe à un groupe est
|
||
une limitation qu&#8217;on est prêts à accepter pour simplifier le&nbsp;modèle.</p>
|
||
<p>L&#8217;avantage de maintenir la liste des <em>principals</em> d&#8217;un utilisateur
|
||
lors de la modification de cette liste c&#8217;est qu&#8217;elle est déjà
|
||
construite lors des lectures, qui sont dans notre cas plus fréquentes
|
||
que les&nbsp;écritures.</p>
|
||
<p>Cela nécessite de donner un identifiant unique aux groupes pour tous
|
||
les <em>buckets</em>.</p>
|
||
<p>Nous proposons de de les nommer avec leur <span class="caps">URI</span>:
|
||
<tt class="docutils literal">/buckets/blog/groups/moderators</tt></p>
|
||
</div>
|
||
<div class="section" id="obtenir-la-liste-des-principals-d-un-ace">
|
||
<h2>Obtenir la liste des &#8220;principals&#8221; d&#8217;un <span class="caps">ACE</span></h2>
|
||
<blockquote>
|
||
Rappel, un &#8220;<span class="caps">ACE</span>&#8221; est un <em>Access Control Entry</em>, un des éléments
|
||
d&#8217;une <span class="caps">ACL</span> (e.g. <em>modifier un enregistrement</em>).</blockquote>
|
||
<p>Avec le <a class="reference external" href="https://blog.notmyidea.org/service-de-nuages-la-gestion-des-permissions-fr.html">système de permissions choisi</a>, les permissions d&#8217;un
|
||
objet héritent de celle de l&#8217;objet&nbsp;parent.</p>
|
||
<p>Par exemple, avoir le droit d&#8217;écriture sur un <em>bucket</em> permet la
|
||
création des permissions et la modification de tous ses&nbsp;records.</p>
|
||
<p>Ce qui veut dire que pour obtenir la liste complète des <em>principals</em>
|
||
ayant une permission sur un objet, il faut regarder à plusieurs&nbsp;endroits.</p>
|
||
<p>Rémy a <a class="reference external" href="https://gist.github.com/Natim/77c8f61c1d42e476cef8#file-permission-py-L9-L52">décrit dans un gist la liste d&#8217;héritage de chaque permission</a>.</p>
|
||
<p>Prenons l&#8217;exemple de l&#8217;ajout d&#8217;un record dans une&nbsp;collection.</p>
|
||
<p>Le droit <tt class="docutils literal">records:create</tt> est obtenu si l&#8217;on a l&#8217;un des droits&nbsp;suivants:</p>
|
||
<ul class="simple">
|
||
<li><tt class="docutils literal">bucket:write</tt></li>
|
||
<li><tt class="docutils literal">collection:write</tt></li>
|
||
<li><tt class="docutils literal">records:create</tt></li>
|
||
</ul>
|
||
<p>Notre première idée était de stocker les permissions sur chaque objet
|
||
et de maintenir la liste exhaustive des permissions lors d&#8217;une
|
||
modification d&#8217;<span class="caps">ACL</span>. Cependant cela nécessitait de construire cette
|
||
liste lors de l&#8217;ajout d&#8217;un objet et de mettre à jour tout l&#8217;arbre lors
|
||
de sa suppression. (<em>Je vous laisse imaginer le nombre d&#8217;opérations
|
||
nécessaires pour ajouter un administrateur sur un *bucket</em> contenant
|
||
1000 collections avec 100000 records&nbsp;chacune.*)</p>
|
||
<p>La solution que nous avons désormais adoptée consiste à stocker les
|
||
<em>principals</em> de chaque <em><span class="caps">ACE</span></em> (<em>qui</em> a le droit de faire telle action
|
||
sur l&#8217;objet), et de faire l&#8217;union des <em><span class="caps">ACE</span></em> hérités, afin de les
|
||
croiser avec les <em>principals</em> de l&#8217;utilisateur&nbsp;:</p>
|
||
<blockquote>
|
||
(<span class="caps">ACE</span>(object, permission) ∪ inherited_ACE) ∩ <span class="caps">PRINCIPALS</span>(user)</blockquote>
|
||
<p>Par exemple l&#8217;<span class="caps">ACE</span>: <tt class="docutils literal">/buckets/blog/collections/article:records:create</tt> hérite de
|
||
l&#8217;<span class="caps">ACE</span> <tt class="docutils literal">/buckets/blog/collections/article:write</tt> et de <tt class="docutils literal">/buckets/blog:write</tt> :</p>
|
||
<blockquote>
|
||
(<span class="caps">ACE</span>(/buckets/blog/collections/article:records:create) ∪ <span class="caps">ACE</span>(/buckets/blog/collections/article:write) ∪ <span class="caps">ACE</span>(/buckets/blog:write)) ∩ <span class="caps">PRINCIPALS</span>(&#8216;fxa:alexis&#8217;)</blockquote>
|
||
</div>
|
||
<div class="section" id="recuperer-les-donnees-de-l-utilisateur">
|
||
<h2>Récupérer les données de&nbsp;l&#8217;utilisateur</h2>
|
||
<p>La situation se corse lorsqu&#8217;on souhaite limiter la liste des
|
||
<em>records</em> d&#8217;une collection à ceux accessibles pour l&#8217;utilisateur, car
|
||
on doit faire cette intersection pour tous les <em>records</em>.</p>
|
||
<p>Une première solution est de regarder si l&#8217;utilisateur est mentionné
|
||
dans les <em><span class="caps">ACL</span>*s du *bucket</em> ou de la <em>collection</em>:</p>
|
||
<p>Ensuite, si ce n&#8217;est pas le cas, alors on filtre les <em>records</em> pour
|
||
lesquels les <em>principals</em> correspondent à ceux de&nbsp;l&#8217;utilisateur.</p>
|
||
<div class="highlight"><pre><span></span><span class="n">principals</span> <span class="o">=</span> <span class="n">get_user_principals</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
|
||
<span class="n">can_read_all</span> <span class="o">=</span> <span class="n">has_read_perms</span><span class="p">(</span><span class="n">bucket_id</span><span class="p">,</span> <span class="n">collection_id</span><span class="p">,</span>
|
||
<span class="n">principals</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">can_read_all</span><span class="p">:</span>
|
||
<span class="n">records</span> <span class="o">=</span> <span class="n">get_all_records</span><span class="p">(</span><span class="n">bucket_id</span><span class="p">,</span> <span class="n">collection_id</span><span class="p">,</span>
|
||
<span class="n">filters</span><span class="o">=</span><span class="p">[</span><span class="o">...</span><span class="p">])</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">records</span> <span class="o">=</span> <span class="n">filter_read_records</span><span class="p">(</span><span class="n">bucket_id</span><span class="p">,</span> <span class="n">collection_id</span><span class="p">,</span>
|
||
<span class="n">principals</span><span class="o">=</span><span class="n">principals</span><span class="p">,</span>
|
||
<span class="n">filters</span><span class="o">=</span><span class="p">[</span><span class="o">...</span><span class="p">])</span>
|
||
</pre></div>
|
||
<p>Il faudra faire quelque chose de similaire pour la suppression
|
||
multiple, lorsqu&#8217;un utilisateur souhaitera supprimer des
|
||
enregistrements sur lesquels il a les droits de lecture mais pas&nbsp;d&#8217;écriture.</p>
|
||
</div>
|
||
<div class="section" id="le-modele-de-donnees">
|
||
<h2>Le modèle de&nbsp;données</h2>
|
||
<p>Pour avoir une idée des requêtes dans un backend <span class="caps">SQL</span>, voyons un peu ce
|
||
que donnerait le modèle de&nbsp;données.</p>
|
||
<div class="section" id="le-format-des-id">
|
||
<h3>Le format des <span class="caps">ID</span></h3>
|
||
<p>Utiliser des <span class="caps">URI</span> comme identifiant des objets présente de nombreux
|
||
avantages (lisibilité, unicité, cohérence avec les&nbsp;URLs)</p>
|
||
<ul class="simple">
|
||
<li>bucket: <tt class="docutils literal">/buckets/blog</tt></li>
|
||
<li>groupe: <tt class="docutils literal">/buckets/blog/group/moderators</tt></li>
|
||
<li>collection: <tt class="docutils literal">/buckets/blog/collections/articles</tt></li>
|
||
<li>record: <tt class="docutils literal"><span class="pre">/buckets/blog/collections/articles/records/02f3f76f-7059-4ae4-888f-2ac9824e9200</span></tt></li>
|
||
</ul>
|
||
</div>
|
||
<div class="section" id="les-tables">
|
||
<h3>Les&nbsp;tables</h3>
|
||
<p>Pour le stockage des principals et des&nbsp;permissions:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">user</span><span class="p">(</span><span class="n">id</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="n">principals</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">[]);</span>
|
||
<span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">perms</span><span class="p">(</span><span class="n">ace</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="n">principals</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">[]);</span>
|
||
</pre></div>
|
||
<p>La table <em>perms</em> va associer des <em>principals</em> à chaque <em><span class="caps">ACE</span></em>&nbsp;(e.g.&#8220;/buckets/blog:write&#8220;).</p>
|
||
<p>Pour le stockage des&nbsp;données:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">object</span><span class="p">(</span><span class="n">id</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="w"> </span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">write_principals</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">[],</span><span class="w"> </span><span class="n">read_principals</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">[]);</span>
|
||
</pre></div>
|
||
<p>La colonne <em>parent_id</em> permet de savoir à qui appartient l&#8217;objet
|
||
(e.g. groupe d&#8217;un <em>bucket</em>, collection d&#8217;un <em>bucket</em>, <em>record</em> d&#8217;une
|
||
collection,&nbsp;&#8230;).</p>
|
||
</div>
|
||
<div class="section" id="exemple-d-utilisateur">
|
||
<h3>Exemple&nbsp;d&#8217;utilisateur</h3>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;fxa:alexis&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">);</span>
|
||
|
||
<span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;fxa:natim&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{&quot;/buckets/blog/groups/moderators&quot;}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="exemple-d-objets">
|
||
<h3>Exemple&nbsp;d&#8217;objets</h3>
|
||
<div class="section" id="bucket">
|
||
<h4>Bucket</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">read_principals</span><span class="p">,</span><span class="w"> </span><span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;bucket&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="k">NULL</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{&quot;name&quot;: &quot;blog&quot;}&#39;</span><span class="p">::</span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;{&quot;fxa:alexis&quot;}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="group">
|
||
<h4>Group</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">read_principals</span><span class="p">,</span><span class="w"> </span><span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/groups/moderators&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;group&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{&quot;name&quot;: &quot;moderators&quot;, &quot;members&quot;: [&#39;</span><span class="n">fxa</span><span class="p">:</span><span class="n">natim</span><span class="s1">&#39;]}&#39;</span><span class="p">::</span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
<p>Ce groupe peut être gére par <tt class="docutils literal">fxa:alexis</tt> puisqu&#8217;il a la permission
|
||
<tt class="docutils literal">write</tt> dans le <em>bucket</em>&nbsp;parent.</p>
|
||
</div>
|
||
<div class="section" id="collection">
|
||
<h4>Collection</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">read_principals</span><span class="p">,</span><span class="w"> </span><span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/articles&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;collection&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{&quot;name&quot;: &quot;article&quot;}&#39;</span><span class="p">::</span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{&quot;system.Everyone&quot;}&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{&quot;/buckets/blog/groups/moderators&quot;}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
<p>Cette collection d&#8217;articles peut être lue par tout le monde,
|
||
et gérée par les membres du groupe <tt class="docutils literal">moderators</tt>, ainsi que
|
||
<tt class="docutils literal">fxa:alexis</tt>, via le <em>bucket</em>.</p>
|
||
</div>
|
||
<div class="section" id="records">
|
||
<h4>Records</h4>
|
||
<div class="highlight"><pre><span></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="k">type</span><span class="p">,</span><span class="w"> </span><span class="n">parent_id</span><span class="p">,</span><span class="w"> </span><span class="k">data</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">read_principals</span><span class="p">,</span><span class="w"> </span><span class="n">write_principals</span><span class="p">)</span>
|
||
<span class="k">VALUES</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/articles/records/02f3f76f-7059-4ae4-888f-2ac9824e9200&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;record&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/articles&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{&quot;name&quot;: &quot;02f3f76f-7059-4ae4-888f-2ac9824e9200&quot;,</span>
|
||
<span class="s1"> &quot;title&quot;: &quot;Stocker les permissions&quot;, ...}&#39;</span><span class="p">::</span><span class="n">JSONB</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;{}&#39;</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="interroger-les-permissions">
|
||
<h3>Interroger les&nbsp;permissions</h3>
|
||
<div class="section" id="obtenir-la-liste-des-principals-d-un-ace-1">
|
||
<h4>Obtenir la liste des &#8220;principals&#8221; d&#8217;un <span class="caps">ACE</span></h4>
|
||
<p>Comme vu plus haut, pour vérifier une permission, on fait l&#8217;union des
|
||
<em>principals</em> requis par les objets hérités, et on teste leur
|
||
intersection avec ceux de&nbsp;l&#8217;utilisateur:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">WITH</span><span class="w"> </span><span class="n">required_principals</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">unnest</span><span class="p">(</span><span class="n">principals</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">p</span>
|
||
<span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">perms</span>
|
||
<span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">ace</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog:write&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog:read&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/article:write&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/article:read&#39;</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="p">),</span>
|
||
<span class="w"> </span><span class="n">user_principals</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">unnest</span><span class="p">(</span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="k">user</span>
|
||
<span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;fxa:natim&#39;</span>
|
||
<span class="w"> </span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">user_principals</span><span class="w"> </span><span class="n">a</span>
|
||
<span class="w"> </span><span class="k">INNER</span><span class="w"> </span><span class="k">JOIN</span><span class="w"> </span><span class="n">required_principals</span><span class="w"> </span><span class="n">b</span>
|
||
<span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">p</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">p</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="filtrer-les-objets-en-fonction-des-permissions">
|
||
<h4>Filtrer les objets en fonction des&nbsp;permissions</h4>
|
||
<p>Pour filtrer les objets, on fait une simple intersection de liste
|
||
(<em>merci PostgreSQL</em>):</p>
|
||
<div class="highlight"><pre><span></span><span class="k">SELECT</span><span class="w"> </span><span class="k">data</span>
|
||
<span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="n">o</span><span class="p">,</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="n">u</span>
|
||
<span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">o</span><span class="p">.</span><span class="k">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;record&#39;</span>
|
||
<span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">o</span><span class="p">.</span><span class="n">parent_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;/buckets/blog/collections/article&#39;</span>
|
||
<span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="p">(</span><span class="n">o</span><span class="p">.</span><span class="n">read_principals</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">u</span><span class="p">.</span><span class="n">principals</span><span class="w"> </span><span class="k">OR</span>
|
||
<span class="w"> </span><span class="n">o</span><span class="p">.</span><span class="n">write_principals</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">u</span><span class="p">.</span><span class="n">principals</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">u</span><span class="p">.</span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;fxa:natim&#39;</span><span class="p">;</span>
|
||
</pre></div>
|
||
<p>Les listes s&#8217;indexent bien, notamment grâce aux <a class="reference external" href="http://www.postgresql.org/docs/current/static/indexes-types.html">index <span class="caps">GIN</span></a>.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="avec-redis">
|
||
<h3>Avec&nbsp;Redis</h3>
|
||
<p><em>Redis</em> présente plusieurs avantages pour ce genre de
|
||
problématiques. Notamment, il gère les <em>set</em> nativement (listes de
|
||
valeurs uniques), ainsi que les opérations d&#8217;intersection et&nbsp;d&#8217;union.</p>
|
||
<p>Avec <em>Redis</em> on peut écrire l&#8217;obtention des <em>principals</em> pour un <em><span class="caps">ACE</span></em>
|
||
comme cela&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span>SUNIONSTORE temp_perm:/buckets/blog/collections/articles:write permission:/buckets/blog:write permission:/buckets/blog/collections/articles:write
|
||
SINTER temp_perm:/buckets/blog/collections/articles:write principals:fxa:alexis
|
||
</pre></div>
|
||
<ul class="simple">
|
||
<li><tt class="docutils literal"><span class="caps">SUNIONSTORE</span></tt> permet de créer un set contenant les éléments de
|
||
l&#8217;union de tous les set suivants. Dans notre cas on le nomme
|
||
<tt class="docutils literal"><span class="pre">temp_perm:/buckets/blog/collections/articles:write</span></tt> et il contient
|
||
l&#8217;union des sets d&#8217;ACLs suivants:
|
||
- <tt class="docutils literal"><span class="pre">permission:/buckets/blog:write</span></tt>
|
||
- <tt class="docutils literal"><span class="pre">permission:/buckets/blog/collections/articles:write</span></tt></li>
|
||
<li><tt class="docutils literal"><span class="caps">SINTER</span></tt> retourne l&#8217;intersection de tous les sets passés en paramètres dans notre cas :
|
||
- <tt class="docutils literal"><span class="pre">temp_perm:/buckets/blog/collections/articles:write</span></tt>
|
||
- <tt class="docutils literal">principals:fxa:alexis</tt></li>
|
||
</ul>
|
||
<p>Plus d&#8217;informations sur :
|
||
- <a class="reference external" href="http://redis.io/commands/sinter">http://redis.io/commands/sinter</a>
|
||
- <a class="reference external" href="http://redis.io/commands/sunionstore">http://redis.io/commands/sunionstore</a></p>
|
||
<p>Si le set résultant de la commande <tt class="docutils literal"><span class="caps">SINTER</span></tt> n&#8217;est pas vide, alors
|
||
l&#8217;utilisateur possède la&nbsp;permission.</p>
|
||
<p>On peut ensuite supprimer la clé temporaire <tt class="docutils literal">temp_perm</tt>.</p>
|
||
<p>En utilisant <tt class="docutils literal"><span class="caps">MULTI</span></tt> on peut <a class="reference external" href="https://gist.github.com/Natim/77c8f61c1d42e476cef8#file-permission-py-L117-L124">même faire tout cela au sein d&#8217;une
|
||
transaction</a>
|
||
et garantir ainsi l&#8217;intégrité de la&nbsp;requête.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="conclusion">
|
||
<h2>Conclusion</h2>
|
||
<p>La solution a l&#8217;air simple mais nous a demandé beaucoup de réflexion
|
||
en passant par plusieurs&nbsp;propositions.</p>
|
||
<p>L&#8217;idée finale est d&#8217;avoir&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li>Un backend spécifique permettant de stocker les <em>principals</em> des
|
||
utilisateurs et des <em><span class="caps">ACE</span></em> (e.g. avec les sets Redis)&nbsp;;</li>
|
||
<li>La liste des principals read et write sur la table des&nbsp;objets.</li>
|
||
</ul>
|
||
<p>C&#8217;est dommage d&#8217;avoir le concept de permissions à deux endroits, mais
|
||
cela permet de connaître rapidement la permission d&#8217;un utilisateur sur
|
||
un objet et également de pouvoir récupérer tous les objets d&#8217;une
|
||
collection pour un utilisateur si celui-ci n&#8217;a pas accès à tous les
|
||
records de la collection, ou toutes les collections du&nbsp;bucket.</p>
|
||
</div>
|
||
</content><category term="code"></category></entry><entry><title>Les problèmes de PGP</title><link href="https://blog.notmyidea.org/les-problemes-de-pgp.html" rel="alternate"></link><published>2015-05-25T00:00:00+02:00</published><updated>2015-05-25T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-05-25:/les-problemes-de-pgp.html</id><summary type="html"><blockquote>
|
||
<p>Flip a bit in the communication between sender and recipient and they
|
||
will experience decryption or verification errors. How high are the
|
||
chances they will start to exchange the data in the clear rather than
|
||
trying to hunt down the man in the&nbsp;middle?</p>
|
||
<p>&#8212; <a href="http://secushare.org/PGP">http://secushare.org/<span class="caps">PGP</span></a></p>
|
||
</blockquote>
|
||
<p>Une fois …</p></summary><content type="html"><blockquote>
|
||
<p>Flip a bit in the communication between sender and recipient and they
|
||
will experience decryption or verification errors. How high are the
|
||
chances they will start to exchange the data in the clear rather than
|
||
trying to hunt down the man in the&nbsp;middle?</p>
|
||
<p>&#8212; <a href="http://secushare.org/PGP">http://secushare.org/<span class="caps">PGP</span></a></p>
|
||
</blockquote>
|
||
<p>Une fois passé l&#8217;euphorie du &#8220;il faut utiliser <span class="caps">PGP</span> pour l&#8217;ensemble de
|
||
nos communications&#8221;, j&#8217;ai réalisé lors de discussions que <span class="caps">PGP</span> avait
|
||
plusieurs problèmes, parmi&nbsp;ceux-ci:</p>
|
||
<ul>
|
||
<li>Les <em>meta données</em> (y compris le champ &#8220;sujet&#8221; de la conversation)
|
||
sont quand même échangées en clair (il est possible de savoir qu&#8217;un
|
||
message à été échangé entre telle et telle personne, a telle&nbsp;date);</li>
|
||
<li><span class="caps">PGP</span> se base sur un protocole de communication qui est lui non
|
||
chiffré, et il est donc facile de soit se tromper, soit dégrader le
|
||
mode de conversation vers une méthode non&nbsp;chiffrée;</li>
|
||
<li>Il est facile de connaître votre réseau social avec <span class="caps">PGP</span>, puisque
|
||
tout le principe est de signer les clés des personnes dont vous
|
||
validez&nbsp;l&#8217;identité;</li>
|
||
<li>En cas de fuite de votre clé privée, tous les messages que vous avez
|
||
chiffrés avec elle sont compromis. On dit que <span class="caps">PGP</span> ne fournit pas de
|
||
<em>forward secrecy</em>;</li>
|
||
<li>La découverte de la clé de pairs se passe souvent <em>en clair</em>, sans
|
||
utiliser une connexion &#8220;sécurisée&#8221; (<span class="caps">HTTPS</span>). Tout le monde peut donc
|
||
voir ces échanges et savoir de qui vous cherchez la&nbsp;clé;</li>
|
||
<li>Les discussions de groupes sont très difficiles: il faut chiffrer
|
||
pour chacun des destinataires (ou que ceux-ci partagent une paire de&nbsp;clés).</li>
|
||
</ul>
|
||
<p>Je suis en train de creuser à propos les alternatives à <span class="caps">PGP</span>, par exemple
|
||
<a href="https://pond.imperialviolet.org/">Pond</a>, qui lui ne construit pas par
|
||
dessus un standard déjà établi, et donc n&#8217;hérite pas de ses défauts
|
||
(mais pas non plus de son réseau déjà&nbsp;établi).</p>
|
||
<p>En attendant, quelques bonnes pratiques sur <span class="caps">PGP</span>&nbsp;;)</p>
|
||
<h2 id="bonnes-pratiques">Bonnes&nbsp;pratiques</h2>
|
||
<p>Il est en fait assez facile d&#8217;utiliser <span class="caps">PGP</span> de travers. Riseup à fait <a href="https://help.riseup.net/en/security/message-security/openpgp/best-practices">un
|
||
excellent
|
||
guide</a>
|
||
qui explique comment configurer son installation&nbsp;correctement.</p>
|
||
<ul>
|
||
<li>J&#8217;en ai déjà parlé, mais il faut absolument choisir des phrases de
|
||
passes suffisamment longues. Pas facile de les retenir, mais
|
||
indispensable. Vous pouvez aussi avoir un document chiffré avec une
|
||
clé que vous ne mettez jamais en ligne, qui contiens ces phrases de
|
||
passe, au cas ou vous les&nbsp;oubliez.</li>
|
||
<li>Générez des clés <span class="caps">RSA</span> de 4096 bits, en utilisant&nbsp;sha512;</li>
|
||
<li>Il faut utiliser une date d&#8217;expiration de nos clés suffisamment
|
||
proche (2 ans). Il est possible de repousser cette date si
|
||
nécessaire, par la&nbsp;suite.</li>
|
||
</ul>
|
||
<p>Parmi les choses les plus frappantes que j&#8217;ai&nbsp;rencontrées:</p>
|
||
<ul>
|
||
<li>Utiliser le <em>flag</em> –hidden-recipient avec <span class="caps">PGP</span> pour ne pas dévoiler
|
||
qui est le destinataire du&nbsp;message;</li>
|
||
<li>Ne pas envoyer les messages de brouillons sur votre serveur, ils le
|
||
seraient en clair&nbsp;!;</li>
|
||
<li>Utilisez <span class="caps">HPKS</span> pour communiquer avec les serveurs de clés, sinon tout
|
||
le trafic est en&nbsp;clair.</li>
|
||
</ul>
|
||
<p>Le <a href="https://bitmask.net/">projet Bitmask</a> vise lui à rendre les outils
|
||
de chiffrement d&#8217;échanges de messages et de <span class="caps">VPN</span> simples à utiliser,
|
||
encore quelque chose à&nbsp;regarder.</p>
|
||
<p>Enfin bref, y&#8217;a du&nbsp;taf.</p></content><category term="code"></category></entry><entry><title>Simplifier les preuves d’identités</title><link href="https://blog.notmyidea.org/simplifier-les-preuves-didentites.html" rel="alternate"></link><published>2015-05-11T00:00:00+02:00</published><updated>2015-05-11T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-05-11:/simplifier-les-preuves-didentites.html</id><summary type="html">
|
||
<ul>
|
||
<li>headline<br>
|
||
Qu&#8217;est-ce que Keybase.io et comment essayent-ils de simplifier la
|
||
création de preuves&nbsp;d&#8217;identité.</li>
|
||
</ul>
|
||
<p>L&#8217;un des problèmes non réellement résolu actuellement quant au
|
||
chiffrement des échanges est lié à l&#8217;authenticité des clés. Si quelqu&#8217;un
|
||
décide de publier une clé en mon nom, et en …</p></summary><content type="html">
|
||
<ul>
|
||
<li>headline<br>
|
||
Qu&#8217;est-ce que Keybase.io et comment essayent-ils de simplifier la
|
||
création de preuves&nbsp;d&#8217;identité.</li>
|
||
</ul>
|
||
<p>L&#8217;un des problèmes non réellement résolu actuellement quant au
|
||
chiffrement des échanges est lié à l&#8217;authenticité des clés. Si quelqu&#8217;un
|
||
décide de publier une clé en mon nom, et en utilisant mon adresse email,
|
||
cela lui est assez&nbsp;facile.</p>
|
||
<p>Il est donc nécessaire d&#8217;avoir des moyens de prouver que la clé publique
|
||
que j&#8217;utilise est réellement la&nbsp;mienne.</p>
|
||
<p>Traditionnellement, il est nécessaire de faire signer ma clé publique
|
||
par d&#8217;autres personnes, via une rencontre en personne ou des échanges
|
||
hors du réseau. C&#8217;est par exemple ce qui est réalisé lors des <a href="https://fr.wikipedia.org/wiki/Key_signing_party">Key
|
||
Signing parties</a>.</p>
|
||
<p>Une manière simple d&#8217;effectuer ces vérifications serait, en plus de
|
||
donner son adresse email, sa signature de clé, ou a minima de donner un
|
||
mot clé pour valider que les échanges proviennent bien de la bonne&nbsp;personne.</p>
|
||
<p><span class="caps">PGP</span> propose un mécanisme de signature des clés d&#8217;autrui, une fois celles
|
||
ci validées, ce qui permet de placer sa confiance dans les signataires
|
||
de la&nbsp;clé.</p>
|
||
<p><a href="https://keybase.io">Keybase.io</a> est un service qui vise à rendre la
|
||
création de ces preuves plus facile, en partant du principe qu&#8217;il est
|
||
possible d&#8217;utiliser différents moyens afin de prouver l&#8217;identité des
|
||
personnes. Par exemple, leurs comptes Twitter, GitHub ou leurs noms de
|
||
domaines. De la même manière qu&#8217;il est possible de signer (valider) les
|
||
clés de nos amis, il est possible de les &#8220;tracker&#8221; selon le jargon de&nbsp;keybase.</p>
|
||
<p>Donc, en somme, <em>Keybase.io</em> est un annuaire, qui tente de rendre plus
|
||
facile la création de preuves.&nbsp;Bien.</p>
|
||
<h2 id="quelques-points-dombre">Quelques points&nbsp;d&#8217;ombre</h2>
|
||
<p>Il s&#8217;agit d&#8217;une <em>startup</em> américaine, domiciliée dans le Delaware, qui
|
||
se trouve être un des paradis fiscaux qui <a href="https://fr.wikipedia.org/wiki/Delaware">est connu pour être un
|
||
paradis fiscal au coeur même des
|
||
États-Unis</a>. Je ne veux pas
|
||
faire de raccourcis trop rapides, bien évidemment, alors <a href="https://github.com/keybase/keybase-issues/issues/1569">j&#8217;ai ouvert un
|
||
ticket sur GitHub pour en savoir
|
||
plus</a> (après
|
||
tout, le fait d&#8217;être un paradis fiscal permet peut-être d&#8217;échapper à
|
||
certaines lois sur la requêtes de données). D&#8217;autant plus étonnant, la
|
||
startup n&#8217;a pour l&#8217;instant <a href="https://github.com/keybase/keybase-issues/issues/788">pas de <em>business
|
||
model</em></a> (ce qui en
|
||
un sens est assez rassurant, même si on peut se poser la question de
|
||
pourquoi faire une startup dans ces cas&nbsp;là).</p>
|
||
<p>Le service (bien qu&#8217;en Alpha), n&#8217;est pas mis à disposition sous licence
|
||
libre, ce qui pour l&#8217;instant empêche quiconque de créer son propre
|
||
serveur Keybase. <a href="https://github.com/keybase/">Une partie des composants, cependant, le sont (open
|
||
source)</a>.</p>
|
||
<p>J&#8217;ai du mal à croire en des initiatives qui veulent sauver le monde,
|
||
mais dans leur coin, je ne comprends pas pourquoi il n&#8217;y à pas de
|
||
documentation sur comment monter son propre serveur, ou comment les
|
||
aider à travailler sur la fédération. Mais bon, c&#8217;est pour l&#8217;instant une
|
||
initiative encore fraîche, et je lui laisse le bénéfice du&nbsp;doute.</p>
|
||
<p>Sur le long terme, une infrastructure comme <em>Keybase.io</em>, devra
|
||
évidemment être
|
||
<a href="https://github.com/keybase/keybase-issues/issues/162">distribuée</a>.</p>
|
||
<blockquote>
|
||
<p>We&#8217;ve been talking about a total decentralization, but we have to
|
||
solve a couple things, synchronization in particular. Right now
|
||
someone can mirror us and a client can trust a mirror just as easily
|
||
as the server at keybase.io, but there needs to be a way of announcing
|
||
proofs to any server and having them cooperate with each other. We&#8217;d
|
||
be so happy to get this&nbsp;right.</p>
|
||
<p>&#8212; <a href="http://chris.beams.io/posts/keybase/">Chris Coyne, co-founder of&nbsp;Keybase</a></p>
|
||
</blockquote>
|
||
<p>Afin de se &#8220;passer&#8221; de leur service centralisé, les preuves générées
|
||
(qui sont la force du système qu&#8217;ils mettent en place) pourraient être
|
||
exportées sur des serveurs de clés existants. C&#8217;est quelque chose
|
||
<a href="https://github.com/keybase/keybase-issues/issues/890">qu&#8217;ils souhaitent réaliser
|
||
.</a>.</p>
|
||
<p>Bref, une initiative quand même importante et utile, même si elle
|
||
soulève des questions qui méritent qu&#8217;on s&#8217;y attarde un&nbsp;brin.</p>
|
||
<p>Par ailleurs, <a href="https://leap.se/nicknym">d&#8217;autres projets qui visent des objectifs
|
||
similaires</a> existent, via le projet <span class="caps">LEAP</span>, mais
|
||
je n&#8217;ai pas encore&nbsp;creusé.</p></content><category term="code"></category></entry><entry><title>Phrases de passe et bonnes pratiques</title><link href="https://blog.notmyidea.org/phrases-de-passe-et-bonnes-pratiques.html" rel="alternate"></link><published>2015-05-09T00:00:00+02:00</published><updated>2015-05-09T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-05-09:/phrases-de-passe-et-bonnes-pratiques.html</id><summary type="html">
|
||
<ul>
|
||
<li>headline<br>
|
||
Communiquer de manière chiffrée n&#8217;est pas aisée, et nécessite de
|
||
mémoriser des phrases de passes complexes. Comment s&#8217;en sortir&nbsp;?</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p>Au contraire des autres mots de passe, les mots de passe
|
||
cryptographiques ont specifiquement besoin d&#8217;être longs et extremement
|
||
difficiles à deviner. La raison est qu&#8217;un …</p></blockquote></summary><content type="html">
|
||
<ul>
|
||
<li>headline<br>
|
||
Communiquer de manière chiffrée n&#8217;est pas aisée, et nécessite de
|
||
mémoriser des phrases de passes complexes. Comment s&#8217;en sortir&nbsp;?</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p>Au contraire des autres mots de passe, les mots de passe
|
||
cryptographiques ont specifiquement besoin d&#8217;être longs et extremement
|
||
difficiles à deviner. La raison est qu&#8217;un ordinateur (ou un cluster de
|
||
plusieurs ordinateurs) peut être programmé pour faire des trillions
|
||
d&#8217;essais de manière automatique. Si le mot de passe choisi est trop
|
||
faible ou construit d&#8217;une manière trop prédictible, cette attaque par
|
||
la force pourrait se revéler fructueuse en essayant toutes les&nbsp;possibilités.</p>
|
||
<p>&#8212; <a href="https://www.eff.org/wp/defending-privacy-us-border-guide-travelers-carrying-digital-devices">The Electronic Frontier
|
||
Foundation</a>
|
||
(traduction de mon&nbsp;fait)</p>
|
||
</blockquote>
|
||
<p>Comprendre les concepts et l&#8217;écosystème qui permettent d&#8217;avoir une vie
|
||
numérique chiffrée n&#8217;est pas quelque chose d&#8217;aisé.
|
||
<a href="https://emailselfdefense.fsf.org/fr/">Plusieurs</a>
|
||
<a href="http://www.controle-tes-donnees.net/outils/GnuPG.html">guides</a> ont été
|
||
écrits à ce propos, et pour autant je me rends compte que naïvement il
|
||
est possible de mal utiliser les outils&nbsp;existants.</p>
|
||
<blockquote>
|
||
<p>Utilisez un <em>bon</em> mot de passe pour votre session utilisateur et une
|
||
<em>bonne</em> phrase de passe pour proteger votre clé privée. Cette phrase
|
||
de passe est la partie la plus fragile de tout le&nbsp;système.</p>
|
||
<p>&#8212; La page de manuel de <span class="caps">GPG</span>.</p>
|
||
</blockquote>
|
||
<p>Une phrase de passe&nbsp;devrait:</p>
|
||
<ul>
|
||
<li>Être suffisamment longue pour être difficile à&nbsp;deviner;</li>
|
||
<li>Ne pas être une citation connue (littérature, livres sacrés&nbsp;etc);</li>
|
||
<li>Difficile à deviner même pour vos&nbsp;proches;</li>
|
||
<li>Facile à se souvenir et à&nbsp;taper;</li>
|
||
<li>être unique et non partagée entre différents sites / applications&nbsp;etc.</li>
|
||
</ul>
|
||
<p>Une des techniques consiste à utiliser des mots du dictionnaire,
|
||
sélectionnés de manière aléatoire, puis&nbsp;modifiés.</p>
|
||
<p><img alt="Trough 20 years of effort, we've succesfully trained everyone to use passwords that are hard for humans to remember, but easy for computers to guess" src="https://imgs.xkcd.com/comics/password_strength.png"></p>
|
||
<p>Micah Lee <a href="https://github.com/micahflee/passphrases">travaille également sur un
|
||
outil</a> qui vise à rendre la
|
||
mémorisation des phrases de passe plus aisée, de par leur répétition
|
||
avec des pauses de plus en plus&nbsp;longues.</p>
|
||
<p>Oui, ce n&#8217;est pas aussi simple que ce qu&#8217;il y parait. Pour ma part, j&#8217;ai
|
||
une copie en local de mes clés, dans un fichier chiffré avec une autre
|
||
clé que j&#8217;ai généré pour l&#8217;occasion et que je ne partagerait pas. J&#8217;ai
|
||
par ailleurs <a href="https://github.com/jamessan/vim-gnupg">configuré</a> mon
|
||
éditeur de texte pour pouvoir chiffrer les documents textes par&nbsp;défaut.</p>
|
||
<p>J&#8217;ai donc regénéré une nouvelle fois mes clés de travail et
|
||
personnelles, en utilisant des phrases de passe plus&nbsp;complexes.</p>
|
||
<p>Reste encore la question de la sauvegarde de ces clés privées de manière
|
||
chiffrée, que je n&#8217;ai pas encore résolue. Bref, tout cela me semble bien
|
||
compliqué pour réussir à l&#8217;expliquer à des novices, qui pour certains ne
|
||
sont même pas sur de l&#8217;intérêt de la&nbsp;chose.</p></content><category term="code"></category></entry><entry><title>Service de nuages : La gestion des permissions</title><link href="https://blog.notmyidea.org/service-de-nuages-la-gestion-des-permissions-fr.html" rel="alternate"></link><published>2015-05-01T00:00:00+02:00</published><updated>2015-05-01T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-05-01:/service-de-nuages-la-gestion-des-permissions-fr.html</id><summary type="html"><p class="first last">Démystification du vocabulaire des permissions et proposition d&#8217;implémentation pour&nbsp;Kinto</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p>Dans le cadre de la création d&#8217;un service de stockage de données personnelles
|
||
(Kinto), la gestion des permissions est un des gros challenges : qui doit avoir
|
||
accès à quoi, et comment le définir&nbsp;?</p>
|
||
<p><strong>tl;dr: Quelques retours sur le vocabulaire des systèmes de permission et sur nos idées pour l&#8217;implementation des permissions dans un stockage&nbsp;générique.</strong></p>
|
||
<div class="section" id="la-problematique">
|
||
<h2>La&nbsp;problématique</h2>
|
||
<p>La problématique est simple : des données sont stockées en ligne, et il
|
||
faut un moyen de pouvoir les partager avec d&#8217;autres&nbsp;personnes.</p>
|
||
<p>En regardant les cas d&#8217;utilisations, on se rend compte qu&#8217;on a plusieurs types
|
||
d&#8217;utilisateurs&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li>les utilisateurs &#8220;finaux&#8221; (vous)&nbsp;;</li>
|
||
<li>les applications qui interagissent en leurs&nbsp;noms.</li>
|
||
</ul>
|
||
<p>Tous les intervenants n&#8217;ont donc pas les mêmes droits : certains doivent
|
||
pouvoir lire, d&#8217;autres écrire, d&#8217;autres encore créer de nouveaux
|
||
enregistrements, et le contrôle doit pouvoir s&#8217;effectuer de manière fine : il
|
||
doit être possible de lire un enregistrement mais pas un autre, par&nbsp;exemple.</p>
|
||
<p>Nous sommes partis du constat que les solutions disponibles n&#8217;apportaient pas
|
||
une réponse satisfaisante à ces&nbsp;besoins.</p>
|
||
</div>
|
||
<div class="section" id="un-probleme-de-vocabulaire">
|
||
<h2>Un problème de&nbsp;vocabulaire</h2>
|
||
<p>Le principal problème rencontré lors des réflexions fût le&nbsp;vocabulaire.</p>
|
||
<p>Voici ci-dessous une explication des différents&nbsp;termes.</p>
|
||
<div class="section" id="le-concept-de-principal">
|
||
<h3>Le concept de « principal&nbsp;»</h3>
|
||
<p>Un <em>principal</em>, en sécurité informatique, est une entité qui peut être
|
||
authentifiée par un système informatique. <a class="footnote-reference" href="#footnote-1" id="footnote-reference-1">[1]</a> En Français il s&#8217;agit
|
||
du « commettant », l&#8217;acteur qui commet l&#8217;action (oui, le terme est conceptuel&nbsp;!)</p>
|
||
<p>Il peut s&#8217;agir aussi bien d&#8217;un individu, d&#8217;un ordinateur, d&#8217;un
|
||
service ou d&#8217;un groupe regroupant l&#8217;une de ces entités, ce qui
|
||
est plus large que le classique « <em>user id</em>&nbsp;».</p>
|
||
<p>Les permissions sont alors associées à ces <em>principals</em>.</p>
|
||
<p>Par exemple, un utilisateur est identifié de manière unique lors de la
|
||
connexion par le système d&#8217;authentification dont le rôle est de
|
||
définir une liste de <em>principals</em> pour l&#8217;utilisateur se&nbsp;connectant.</p>
|
||
<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[1]</a></td><td>Pour en savoir plus sur les <em>principals</em> :
|
||
<a class="reference external" href="https://en.wikipedia.org/wiki/Principal_%28computer_security%29">https://en.wikipedia.org/wiki/Principal_%28computer_security%29</a></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="la-difference-entre-role-et-groupe">
|
||
<h3>La différence entre rôle et&nbsp;groupe</h3>
|
||
<p>De but en blanc, il n&#8217;est pas évident de définir précisément la
|
||
différence entre ces deux concepts qui permettent d&#8217;associer
|
||
des permissions à un groupe de <em>principals</em>. <a class="footnote-reference" href="#footnote-2" id="footnote-reference-2">[2]</a></p>
|
||
<p>La différence est principalement sémantique. Mais on peut y voir une
|
||
différence dans la « direction » de la relation entre les deux&nbsp;concepts.</p>
|
||
<ul class="simple">
|
||
<li>Un rôle est une liste de permissions que l&#8217;on associe à un <em>principal</em>.</li>
|
||
<li>Un groupe est une liste de <em>principals</em> que l&#8217;on peut associer à une&nbsp;permission.</li>
|
||
</ul>
|
||
<table class="docutils footnote" frame="void" id="footnote-2" rules="none">
|
||
<colgroup><col class="label" /><col /></colgroup>
|
||
<tbody valign="top">
|
||
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-2">[2]</a></td><td>Plus d&#8217;informations :
|
||
<a class="reference external" href="http://stackoverflow.com/questions/7770728/group-vs-role-any-real-difference">http://stackoverflow.com/questions/7770728/group-vs-role-any-real-difference</a></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="la-difference-entre-permission-acl-ace">
|
||
<h3>La différence entre permission, <span class="caps">ACL</span>, <span class="caps">ACE</span></h3>
|
||
<blockquote class="epigraph">
|
||
<p>Une <span class="caps">ACL</span> est une liste d’Access Control Entry (<span class="caps">ACE</span>) ou entrée de contrôle d&#8217;accès
|
||
donnant ou supprimant des droits d&#8217;accès à une personne ou un&nbsp;groupe.</p>
|
||
<p class="attribution">&mdash;<a class="reference external" href="https://fr.wikipedia.org/wiki/Access_Control_List">https://fr.wikipedia.org/wiki/Access_Control_List</a></p>
|
||
</blockquote>
|
||
<p>Je dirais même plus, dans notre cas, « à un <em>principal</em> ». Par&nbsp;exemple:</p>
|
||
<div class="highlight"><pre><span></span>create_record: alexis,remy,tarek
|
||
</pre></div>
|
||
<p>Cet <span class="caps">ACE</span> donne la permission <tt class="docutils literal">create</tt> sur l&#8217;objet <tt class="docutils literal">record</tt> aux
|
||
utilisateurs Tarek, Rémy et&nbsp;Alexis.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="la-delegation-de-permissions">
|
||
<h2>La délégation de&nbsp;permissions</h2>
|
||
<p>Imaginez l&#8217;exemple suivant, où un utilisateur stocke deux types de données en
|
||
ligne&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li>des contacts&nbsp;;</li>
|
||
<li>une liste de tâches à faire qu&#8217;il peut associer à ses&nbsp;contacts.</li>
|
||
</ul>
|
||
<p>L&#8217;utilisateur a tous les droits sur ses&nbsp;données.</p>
|
||
<p>Cependant il utilise deux applications qui doivent elles avoir un accès restreint&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li>une application de gestion des contacts à qui il souhaite déléguer
|
||
la gestion intégrale de ses contacts : <tt class="docutils literal">contacts:write</tt> ;</li>
|
||
<li>une application de gestion des tâches à qui il souhaite déléguer la
|
||
gestion des tâches : <tt class="docutils literal">contacts:read tasks:write</tt></li>
|
||
</ul>
|
||
<p>Il souhaite que son application de contacts ne puisse pas accéder à
|
||
ses tâches et que son application de tâches ne puisse pas modifier ses
|
||
contacts existants, juste éventuellement en créer de&nbsp;nouveaux.</p>
|
||
<p>Il lui faut donc un moyen de déléguer certains de ses droits à un tiers&nbsp;(l&#8217;application).</p>
|
||
<p>C&#8217;est précisément le rôle des <a class="reference external" href="http://tools.ietf.org/html/rfc6749#page-23">scopes OAuth2</a>.</p>
|
||
<p>Lors de la connexion d&#8217;un utilisateur, une fenêtre lui demande quels
|
||
accès il veut donner à l&#8217;application qui va agir en son&nbsp;nom.</p>
|
||
<p>Le service aura ensuite accès à ces <em>scopes</em> en regardant le jeton
|
||
d&#8217;authentification utilisé. Cette information doit être
|
||
considérée comme une entrée utilisateur (c&#8217;est à dire qu&#8217;on ne peut
|
||
pas lui faire confiance). Il s&#8217;agit de ce que l&#8217;utilisateur&nbsp;souhaite.</p>
|
||
<p>Or, il est également possible que l&#8217;utilisateur n&#8217;ait pas accès aux données
|
||
qu&#8217;il demande. Le service doit donc utiliser deux niveaux de permissions :
|
||
celles de l&#8217;utilisateur, et celles qui ont été déléguées à&nbsp;l&#8217;application.</p>
|
||
</div>
|
||
<div class="section" id="espace-de-noms">
|
||
<h2>Espace de&nbsp;noms</h2>
|
||
<p>Dans notre implémentation initiale de <em>Kinto</em> (notre service de stockage en
|
||
construction), l&#8217;espace de nom était implicite : les données stockées étaient
|
||
nécessairement celles de l&#8217;utilisateur&nbsp;connecté.</p>
|
||
<p>Les données d&#8217;un utilisateur étaient donc cloisonnées et impossible à&nbsp;partager.</p>
|
||
<p>L&#8217;utilisation d&#8217;espaces de noms est une manière simple de gérer le partage des&nbsp;données.</p>
|
||
<p>Nous avons choisi de reprendre le modèle de GitHub et de Bitbucket, qui
|
||
utilisent les « organisations » comme espaces de&nbsp;noms.</p>
|
||
<p>Dans notre cas, il est possible de créer des &#8220;buckets&#8221;, qui correspondent à ces
|
||
espaces de noms. Un bucket est un conteneur de collections et de groupes&nbsp;utilisateurs.</p>
|
||
<p>Les ACLs sur ces collections peuvent être attribuées à certains groupes du
|
||
<em>bucket</em> ainsi qu&#8217;à d&#8217;autres <em>principals</em>&nbsp;directement.</p>
|
||
</div>
|
||
<div class="section" id="notre-proposition-d-api">
|
||
<h2>Notre proposition d&#8217;<span class="caps">API</span></h2>
|
||
<div class="section" id="les-objets-manipules">
|
||
<h3>Les objets&nbsp;manipulés</h3>
|
||
<p>Pour mettre en place la gestion des permissions, nous avons identifié les
|
||
objets suivants&nbsp;:</p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="23%" />
|
||
<col width="77%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td><strong>bucket</strong></td>
|
||
<td>On peut les voir comme des espaces de noms. Ils
|
||
permettent d&#8217;avoir différentes collections portant
|
||
le même nom mais stockées dans différents <em>buckets</em> de
|
||
manière à ce que les données soient distinctes.</td>
|
||
</tr>
|
||
<tr><td><strong>collection</strong></td>
|
||
<td>Une liste d&#8217;enregistrements.</td>
|
||
</tr>
|
||
<tr><td><strong>record</strong></td>
|
||
<td>Un enregistrement d&#8217;une collection.</td>
|
||
</tr>
|
||
<tr><td><strong>group</strong></td>
|
||
<td>Un groupe de commetants (« <em>principals</em> »).</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Pour la définition des ACLs, il y a une hiérarchie et les objets « héritent » des
|
||
ACLs de leur parents&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span> +---------------+
|
||
| Bucket |
|
||
+---------------+
|
||
+-----&gt;+ - id +&lt;---+
|
||
| | - permissions | |
|
||
| +---------------+ |
|
||
| |
|
||
| |
|
||
| |
|
||
| |
|
||
| |
|
||
+---+-----------+ +------+---------+
|
||
| Collection | | Group |
|
||
+---------------+ +----------------+
|
||
| - id | | - id |
|
||
| - permissions | | - members |
|
||
+------+--------+ | - permissions |
|
||
^ +----------------+
|
||
|
|
||
|
|
||
+------+---------+
|
||
| Record |
|
||
+----------------+
|
||
| - id |
|
||
| - data |
|
||
| - permissions |
|
||
+----------------+
|
||
</pre></div>
|
||
</div>
|
||
<div class="section" id="les-permissions">
|
||
<h3>Les&nbsp;permissions</h3>
|
||
<p>Pour chacun de ces objets nous avons identifié les permissions suivantes&nbsp;:</p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="23%" />
|
||
<col width="77%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Permission</th>
|
||
<th class="head">Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td><strong>read</strong></td>
|
||
<td>La permission de lire le contenu de
|
||
l&#8217;objet et de ses sous-objets.</td>
|
||
</tr>
|
||
<tr><td><strong>write</strong></td>
|
||
<td>La permission de modifier et
|
||
d&#8217;administrer un objet et ses sous-
|
||
objets. La permission <em>write</em> permet la
|
||
lecture, modification et suppression
|
||
d&#8217;un objet ainsi que la gestion de ses
|
||
permissions.</td>
|
||
</tr>
|
||
<tr><td><strong>create</strong></td>
|
||
<td>La permission de créer le sous-objet
|
||
spécifié. Par exemple:
|
||
<tt class="docutils literal">collections:create</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>À chaque permission spécifiée sur un objet est associée une liste de
|
||
<em>principals</em>.</p>
|
||
<p>Dans le cas de la permission <tt class="docutils literal">create</tt> on est obligé de spécifier
|
||
l&#8217;objet enfant en question car un objet peut avoir plusieurs types
|
||
d&#8217;enfants. Par exemple : <tt class="docutils literal">collections:create</tt>, <tt class="docutils literal">groups:create</tt>.</p>
|
||
<p>Nous n&#8217;avons pour l&#8217;instant pas de permission pour <cite>delete</cite> et <cite>update</cite>,
|
||
puisque nous n&#8217;avons pas trouvé de cas d&#8217;utilisation qui les nécessitent.
|
||
Quiconque avec le droit d&#8217;écriture peut donc supprimer un&nbsp;enregistrement.</p>
|
||
<p>Les permissions d&#8217;un objet sont héritées de son parent. Par exemple,
|
||
un enregistrement créé dans une collection accessible à tout le monde
|
||
en lecture sera lui aussi accessible à tout le&nbsp;monde.</p>
|
||
<p>Par conséquent, les permissions sont cumulées. Autrement dit, il n&#8217;est pas
|
||
possible qu&#8217;un objet ait des permissions plus restrictives que son&nbsp;parent.</p>
|
||
<p>Voici la liste exhaustive des permissions&nbsp;:</p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="21%" />
|
||
<col width="32%" />
|
||
<col width="47%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions associées</th>
|
||
<th class="head">Commentaire</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td>Configuration
|
||
(.ini)</td>
|
||
<td><cite>buckets:create</cite></td>
|
||
<td>Les <em>principals</em> ayant le droit
|
||
de créer un bucket sont définis
|
||
dans la configuration du serveur.
|
||
(<em>ex. utilisateurs authentifiés</em>)</td>
|
||
</tr>
|
||
<tr><td rowspan="4"><tt class="docutils literal">bucket</tt></td>
|
||
<td><cite>write</cite></td>
|
||
<td>C&#8217;est en quelque sorte le droit
|
||
d&#8217;administration du <em>bucket</em>.</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>C&#8217;est le droit de lire le contenu
|
||
de tous les objets du <em>bucket</em>.</td>
|
||
</tr>
|
||
<tr><td><cite>collections:create</cite></td>
|
||
<td>Permission de créer des
|
||
collections dans le <em>bucket</em>.</td>
|
||
</tr>
|
||
<tr><td><cite>groups:create</cite></td>
|
||
<td>Permission de créer des groupes
|
||
dans le <em>bucket</em>.</td>
|
||
</tr>
|
||
<tr><td rowspan="3"><tt class="docutils literal">collection</tt></td>
|
||
<td><cite>write</cite></td>
|
||
<td>Permission d&#8217;administrer tous les
|
||
objets de la collection.</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>Permission de consulter tous les
|
||
objets de la collection.</td>
|
||
</tr>
|
||
<tr><td><cite>records:create</cite></td>
|
||
<td>Permission de créer des nouveaux
|
||
enregistrement dans la collection.</td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal">record</tt></td>
|
||
<td><cite>write</cite></td>
|
||
<td>Permission de modifier ou de
|
||
partager l&#8217;enregistrement.</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>Permission de consulter
|
||
l&#8217;enregistrement.</td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal">group</tt></td>
|
||
<td><cite>write</cite></td>
|
||
<td>Permission d&#8217;administrer le
|
||
groupe</td>
|
||
</tr>
|
||
<tr><td><cite>read</cite></td>
|
||
<td>Permission de consulter les
|
||
membres du groupe.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="les-principals">
|
||
<h3>Les « <em>principals</em>&nbsp;»</h3>
|
||
<p>Les acteurs se connectant au service de stockage peuvent&nbsp;s&#8217;authentifier.</p>
|
||
<p>Ils reçoivent alors une liste de <em>principals</em>&nbsp;:</p>
|
||
<ul class="simple">
|
||
<li><tt class="docutils literal">Everyone</tt>: le <em>principal</em> donné à tous les acteurs (authentifiés ou pas)&nbsp;;</li>
|
||
<li><tt class="docutils literal">Authenticated</tt>: le <em>principal</em> donné à tous les acteurs authentifiés&nbsp;;</li>
|
||
<li>un <em>principal</em> identifiant l&#8217;acteur, par exemple <tt class="docutils literal">fxa:32aa95a474c984d41d395e2d0b614aa2</tt></li>
|
||
</ul>
|
||
<p>Afin d&#8217;éviter les collisions d&#8217;identifiants, le <em>principal</em> de l&#8217;acteur dépend
|
||
de son type d&#8217;authentification (<tt class="docutils literal">system</tt>, <tt class="docutils literal">basic</tt>, <tt class="docutils literal">ipaddr</tt>, <tt class="docutils literal">hawk</tt>,
|
||
<tt class="docutils literal">fxa</tt>) et de son identifiant (unique par&nbsp;acteur).</p>
|
||
<p>En fonction du <em>bucket</em> sur lequel se passe l&#8217;action, les groupes dont
|
||
fait partie l&#8217;utilisateur sont également ajoutés à sa liste de
|
||
<tt class="docutils literal">principals</tt>. <tt class="docutils literal">group:moderators</tt> par&nbsp;exemple.</p>
|
||
<p>Ainsi, si Bob se connecte avec <em>Firefox Accounts</em> sur le <em>bucket</em>
|
||
<tt class="docutils literal">servicedenuages_blog</tt> dans lequel il fait partie du groupe
|
||
<tt class="docutils literal">moderators</tt>, il aura la liste de <em>principals</em> suivante :
|
||
<tt class="docutils literal">Everyone, Authenticated, fxa:32aa95a474c984d41d395e2d0b614aa2, group:moderators</tt></p>
|
||
<p>Il est donc possible d&#8217;assigner une permission à Bob en utilisant l&#8217;un de
|
||
ces quatre <em>principals</em>.</p>
|
||
<div class="admonition note">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">Le <em>principal</em> <tt class="docutils literal">&lt;userid&gt;</tt> dépend du <em>back-end</em> d&#8217;authentification (e.g.
|
||
<tt class="docutils literal">github:leplatrem</tt>).</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="quelques-exemples">
|
||
<h3>Quelques&nbsp;exemples</h3>
|
||
<p><strong>Blog</strong></p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="35%" />
|
||
<col width="18%" />
|
||
<col width="46%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td><tt class="docutils literal">bucket:blog</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;blog</span> owner id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal">collection:articles</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal">group:moderators</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">record:569e28r98889</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;co-author</span> id&gt;</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Wiki</strong></p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="35%" />
|
||
<col width="18%" />
|
||
<col width="46%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Object</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td><tt class="docutils literal">bucket:wiki</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;wiki</span> administrator id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal">collection:articles</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Sondages</strong></p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="34%" />
|
||
<col width="31%" />
|
||
<col width="35%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td rowspan="2"><tt class="docutils literal">bucket:poll</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;admin</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">collection:create</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal"><span class="pre">collection:&lt;poll</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;poll</span> author id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">record:create</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Cartes&nbsp;colaboratives</strong></p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="34%" />
|
||
<col width="31%" />
|
||
<col width="35%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td rowspan="2"><tt class="docutils literal">bucket:maps</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;admin</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">collection:create</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal"><span class="pre">collection:&lt;map</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;map</span> author id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal"><span class="pre">record:&lt;record</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;maintainer</span> id&gt;</tt>
|
||
(<em>ex. event staff member
|
||
maintaining venues</em>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Plateformes</strong></p>
|
||
<p>Bien sûr, il y a plusieurs façons de modéliser les cas d&#8217;utilisation typiques.
|
||
Par exemple, on peut imaginer une plateforme de wikis (à la wikia.com), où les
|
||
wikis sont privés par défaut et certaines pages peuvent être rendues publiques&nbsp;:</p>
|
||
<table border="1" class="docutils">
|
||
<colgroup>
|
||
<col width="32%" />
|
||
<col width="30%" />
|
||
<col width="38%" />
|
||
</colgroup>
|
||
<thead valign="bottom">
|
||
<tr><th class="head">Objet</th>
|
||
<th class="head">Permissions</th>
|
||
<th class="head">Principals</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<tr><td rowspan="3"><tt class="docutils literal">bucket:freewiki</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;administrator</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">collection:create</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">group:create</tt></td>
|
||
<td><tt class="docutils literal">Authenticated</tt></td>
|
||
</tr>
|
||
<tr><td rowspan="2"><tt class="docutils literal"><span class="pre">collection:&lt;wiki</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">write</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">fxa:&lt;wiki</span> owner id&gt;</tt>,
|
||
<tt class="docutils literal"><span class="pre">group:&lt;editors</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal"><span class="pre">group:&lt;readers</span> id&gt;</tt></td>
|
||
</tr>
|
||
<tr><td><tt class="docutils literal"><span class="pre">record:&lt;page</span> id&gt;</tt></td>
|
||
<td><tt class="docutils literal">read</tt></td>
|
||
<td><tt class="docutils literal">Everyone</tt></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section" id="l-api-http">
|
||
<h3>L&#8217;<span class="caps">API</span> <span class="caps">HTTP</span></h3>
|
||
<p>Lors de la création d&#8217;un objet, l&#8217;utilisateur se voit
|
||
attribué la permission <tt class="docutils literal">write</tt> sur l&#8217;objet&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><span class="nf">PUT</span> <span class="nn">/v1/buckets/servicedenuages_blog</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||
<span class="na">Authorization</span><span class="o">:</span> <span class="l">Bearer 0b9c2625dc21ef05f6ad4ddf47c5f203837aa32ca42fced54c2625dc21efac32</span>
|
||
<span class="na">Accept</span><span class="o">:</span> <span class="l">application/json</span>
|
||
|
||
HTTP/1.1 201 Created
|
||
Content-Type: application/json; charset=utf-8
|
||
|
||
{
|
||
&quot;id&quot;: &quot;servicedenuages_blog&quot;,
|
||
&quot;permissions&quot;: {
|
||
&quot;write&quot;: [&quot;fxa:49d02d55ad10973b7b9d0dc9eba7fdf0&quot;]
|
||
}
|
||
}
|
||
</pre></div>
|
||
<p>Il est possible d&#8217;ajouter des permissions à l&#8217;aide de <tt class="docutils literal"><span class="caps">PATCH</span></tt> :</p>
|
||
<div class="highlight"><pre><span></span><span class="nf">PATCH</span> <span class="nn">/v1/buckets/servicedenuages_blog/collections/articles</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||
<span class="na">Authorization</span><span class="o">:</span> <span class="l">Bearer 0b9c2625dc21ef05f6ad4ddf47c5f203837aa32ca42fced54c2625dc21efac32</span>
|
||
<span class="na">Accept</span><span class="o">:</span> <span class="l">application/json</span>
|
||
|
||
{
|
||
&quot;permissions&quot;: {
|
||
&quot;read&quot;: [&quot;+system.Everyone&quot;]
|
||
}
|
||
}
|
||
|
||
HTTP/1.1 201 Created
|
||
Content-Type: application/json; charset=utf-8
|
||
|
||
{
|
||
&quot;id&quot;: &quot;servicedenuages_blog&quot;,
|
||
&quot;permissions&quot;: {
|
||
&quot;write&quot;: [&quot;fxa:49d02d55ad10973b7b9d0dc9eba7fdf0&quot;],
|
||
&quot;read&quot;: [&quot;system.Everyone&quot;]
|
||
}
|
||
}
|
||
</pre></div>
|
||
<p>Pour le <tt class="docutils literal"><span class="caps">PATCH</span></tt> nous utilisons la syntaxe préfixée par un <tt class="docutils literal">+</tt> ou
|
||
par un <tt class="docutils literal">-</tt> pour ajouter ou enlever des <em>principals</em> sur un <span class="caps">ACL</span>.</p>
|
||
<p>Il est également possible de faire un <tt class="docutils literal"><span class="caps">PUT</span></tt> pour réinitialiser les ACLs,
|
||
sachant que le <tt class="docutils literal"><span class="caps">PUT</span></tt> va ajouter l&#8217;utilisateur courant à la
|
||
liste automatiquement mais qu&#8217;il pourra se retirer avec un <tt class="docutils literal"><span class="caps">PATCH</span></tt>.
|
||
Ajouter l&#8217;utilisateur courant permet d&#8217;éviter les situations où plus
|
||
personne n&#8217;a accès aux&nbsp;données.</p>
|
||
<div class="admonition note">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">La permission <tt class="docutils literal">create</tt> est valable pour <tt class="docutils literal"><span class="caps">POST</span></tt> mais aussi pour <tt class="docutils literal"><span class="caps">PUT</span></tt>
|
||
lorsque l&#8217;enregistrement n&#8217;existe&nbsp;pas.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="le-cas-specifique-des-donnees-utilisateurs">
|
||
<h3>Le cas spécifique des données&nbsp;utilisateurs</h3>
|
||
<p>Une des fonctionnalités actuelles de <em>Kinto</em> est de pouvoir gérer des
|
||
collections d&#8217;enregistrements par&nbsp;utilisateur.</p>
|
||
<p>Sous <em>*nix</em> il est possible, pour une
|
||
application, de sauvegarder la configuration de l&#8217;utilisateur courant
|
||
dans son dossier personnel sans se soucier de l&#8217;emplacement sur
|
||
le disque en utilisant <tt class="docutils literal">~/</tt>.</p>
|
||
<p>Dans notre cas si une application souhaite sauvegarder les contacts d&#8217;un
|
||
utilisateur, elle peut utiliser le raccourci <tt class="docutils literal">~</tt> pour faire référence au
|
||
<em>bucket</em> <strong>personnel</strong> de l&#8217;utilisateur : <tt class="docutils literal"><span class="pre">/buckets/~/collections/contacts</span></tt></p>
|
||
<p>Cette <span class="caps">URL</span> retournera le code <tt class="docutils literal"><span class="caps">HTTP</span> 307</tt> vers le <em>bucket</em> de l&#8217;utilisateur courant&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/v1/buckets/~/collections/contacts/records</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||
|
||
{
|
||
&quot;name&quot;: &quot;Rémy&quot;,
|
||
&quot;emails&quot;: [&quot;remy@example.com&quot;],
|
||
&quot;phones&quot;: [&quot;+330820800800&quot;]
|
||
}
|
||
|
||
HTTP/1.1 307 Temporary Redirect
|
||
Location: /v1/buckets/fxa:49d02d55ad10973b7b9d0dc9eba7fdf0/collections/contacts/records
|
||
</pre></div>
|
||
<p>Ainsi il est tout à fait possible à Alice de partager ses contacts
|
||
avec Bob. Il lui suffit pour cela de donner la permission <tt class="docutils literal">read</tt> à
|
||
Bob sur sa collection et de donner l&#8217;<span class="caps">URL</span> complète
|
||
<tt class="docutils literal">/v1/buckets/fxa:49d02d55ad10973b7b9d0dc9eba7fdf0/collections/contacts/records</tt>
|
||
à&nbsp;Bob.</p>
|
||
</div>
|
||
<div class="section" id="la-delegation-des-permissions">
|
||
<h3>La délégation des&nbsp;permissions</h3>
|
||
<p>Dans le cas de <em>Kinto</em>, nous avons défini un format pour restreindre les
|
||
permissions via les scopes OAuth2:
|
||
<tt class="docutils literal"><span class="pre">storage:&lt;bucket_id&gt;:&lt;collection_id&gt;:&lt;permissions_list&gt;</span></tt>.</p>
|
||
<p>Ainsi, si on reprend l&#8217;exemple précédent de la liste de tâches, il est possible pour
|
||
Bob de créer un token OAuth spécifique avec les <em>scopes</em> suivants :
|
||
<tt class="docutils literal">profile storage:todolist:tasks:write <span class="pre">storage:~:contacts:read+records:create</span></tt></p>
|
||
<p>Donc, bien que Bob a la permission <tt class="docutils literal">write</tt> sur ses contacts,
|
||
l&#8217;application utilisant ce token pourra uniquement lire les contacts
|
||
existants et en ajouter de&nbsp;nouveaux.</p>
|
||
<p>Une partie de la complexité est donc de réussir à présenter ces <em>scopes</em> de
|
||
manière lisible à l&#8217;utilisateur, afin qu&#8217;il choisisse quelles permissions
|
||
donner aux applications qui agissent en son&nbsp;nom.</p>
|
||
<p>Voilà où nous en sommes de notre réflexion&nbsp;!</p>
|
||
<p>Si vous avez des choses à ajouter, des points de désaccord ou autres
|
||
réflexions, n&#8217;hésitez pas à nous interrompre pendant qu&#8217;il est encore temps&nbsp;!</p>
|
||
</div>
|
||
</div>
|
||
</content><category term="code"></category></entry><entry><title>Eco-système et stockage générique</title><link href="https://blog.notmyidea.org/eco-systeme-et-stockage-generique.html" rel="alternate"></link><published>2015-04-30T00:00:00+02:00</published><updated>2015-04-30T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-04-30:/eco-systeme-et-stockage-generique.html</id><summary type="html">
|
||
<p><strong>tl;dr Nous devons construire un service de suivi de paiements, et nous
|
||
hésitons à continuer à nous entêter avec notre propre solution de&nbsp;stockage/synchronisation.</strong></p>
|
||
<p>Comme nous l&#8217;écrivions <a href="https://blog.notmyidea.org/service-de-nuages-fr.html">dans l&#8217;article
|
||
précédent</a>, nous
|
||
souhaitons construire une solution de stockage générique. On refait
|
||
<a href="http://daybed.readthedocs.org">Daybed</a> chez Mozilla&nbsp;!</p>
|
||
<p>Notre objectif est …</p></summary><content type="html">
|
||
<p><strong>tl;dr Nous devons construire un service de suivi de paiements, et nous
|
||
hésitons à continuer à nous entêter avec notre propre solution de&nbsp;stockage/synchronisation.</strong></p>
|
||
<p>Comme nous l&#8217;écrivions <a href="https://blog.notmyidea.org/service-de-nuages-fr.html">dans l&#8217;article
|
||
précédent</a>, nous
|
||
souhaitons construire une solution de stockage générique. On refait
|
||
<a href="http://daybed.readthedocs.org">Daybed</a> chez Mozilla&nbsp;!</p>
|
||
<p>Notre objectif est simple: permettre aux développeurs d&#8217;application,
|
||
internes à Mozilla ou du monde entier, de faire persister et
|
||
synchroniser facilement des données associées à un&nbsp;utilisateur.</p>
|
||
<div id="storage-specs">
|
||
|
||
Les aspects de l&#8217;architecture qui nous semblent incontournables:
|
||
|
||
</div>
|
||
|
||
<ul>
|
||
<li>La solution doit reposer sur un protocole, et non sur une
|
||
implémentation&nbsp;;</li>
|
||
<li>L&#8217;auto-hébergement de l&#8217;ensemble doit être simplissime&nbsp;;</li>
|
||
<li>L&#8217;authentification doit être <em>pluggable</em>, voire décentralisée
|
||
(OAuth2, FxA, Persona)&nbsp;;</li>
|
||
<li>Les enregistrements doivent pouvoir être validés par le serveur&nbsp;;</li>
|
||
<li>Les données doivent pouvoir être stockées dans n&#8217;importe quel
|
||
backend&nbsp;;</li>
|
||
<li>Un système de permissions doit permettre de protéger des
|
||
collections, ou de partager des enregistrements de manière fine&nbsp;;</li>
|
||
<li>La résolution de conflits doit pouvoir avoir lieu sur le serveur&nbsp;;</li>
|
||
<li>Le client doit être pensé «*offline-first*»&nbsp;;</li>
|
||
<li>Le client doit pouvoir réconcilier les données simplement&nbsp;;</li>
|
||
<li>Le client doit pouvoir être utilisé aussi bien dans le navigateur
|
||
que côté serveur&nbsp;;</li>
|
||
<li>Tous les composants se doivent d´être simples et substituables&nbsp;facilement.</li>
|
||
</ul>
|
||
<p>La première question qui nous a été posée fût «*Pourquoi vous
|
||
n&#8217;utilisez pas PouchDB ou Remote Storage&nbsp;?*»</p>
|
||
<h2 id="remote-storage">Remote&nbsp;Storage</h2>
|
||
<p>Remote Storage est un standard ouvert pour du stockage par utilisateur.
|
||
<a href="http://tools.ietf.org/html/draft-dejong-remotestorage-04">La
|
||
specification</a>
|
||
se base sur des standards déjà existants et éprouvés: Webfinger, OAuth
|
||
2, <span class="caps">CORS</span> et <span class="caps">REST</span>.</p>
|
||
<p>L&#8217;<span class="caps">API</span> est simple, des <a href="http://blog.cozycloud.cc/news/2014/08/12/when-unhosted-meets-cozy-cloud/">projets prestigieux
|
||
l&#8217;utilisent</a>.
|
||
Il y a plusieurs <a href="https://github.com/jcoglan/restore">implémentations</a>
|
||
du serveur, et il existe <a href="https://www.npmjs.com/package/remotestorage-server">un squelette
|
||
Node</a> pour
|
||
construire un serveur sur&nbsp;mesure.</p>
|
||
<p><img alt="Remote Storage widget" src="%7Bfilename%7D/images/remotestorage-widget.png"></p>
|
||
<p>Le client
|
||
<a href="https://github.com/remotestorage/remotestorage.js/">remoteStorage.js</a>
|
||
permet d&#8217;intégrer la solution dans les applications Web. Il se charge du
|
||
«store local», du cache, de la synchronization, et fournit un widget qui
|
||
permet aux utilisateurs des applications de choisir le serveur qui
|
||
recevra les données (via&nbsp;Webfinger).</p>
|
||
<p><a href="https://github.com/michielbdejong/ludbud">ludbud</a>, la version épurée de
|
||
<em>remoteStorage.js</em>, se limite à l&#8217;abstraction du stockage distant. Cela
|
||
permettrait à terme, d&#8217;avoir une seule bibliothèque pour stocker dans un
|
||
serveur <em>remoteStorage</em>, <em>ownCloud</em> ou chez les méchants comme <em>Google
|
||
Drive</em> ou <em>Dropbox</em>.</p>
|
||
<p>Au premier abord, la spécification correspond à ce que nous voulons&nbsp;accomplir:</p>
|
||
<ul>
|
||
<li>La philosophie du protocole est&nbsp;saine;</li>
|
||
<li>L&#8217;éco-système est bien&nbsp;fichu;</li>
|
||
<li>La vision politique colle: redonner le contrôle des données aux
|
||
utilisateurs (voir <a href="http://unhosted.org/">unhosted</a>);</li>
|
||
<li>Les choix techniques compatibles avec ce qu&#8217;on a commencé (<span class="caps">CORS</span>,
|
||
<span class="caps">REST</span>, OAuth&nbsp;2);</li>
|
||
</ul>
|
||
<p>En revanche, vis à vis de la manipulation des données, il y a plusieurs
|
||
différences avec ce que nous souhaitons&nbsp;faire:</p>
|
||
<ul>
|
||
<li>L&#8217;<span class="caps">API</span> suit globalement une métaphore «fichiers» (dossier/documents),
|
||
plutôt que «données» (collection/enregistrements)&nbsp;;</li>
|
||
<li>Il n&#8217;y a pas de validation des enregistrements selon un schéma (même
|
||
si <a href="https://remotestorage.io/doc/code/files/baseclient/types-js.html">certaines
|
||
implémentations</a>
|
||
du protocole le font)&nbsp;;</li>
|
||
<li>Il n&#8217;y a pas la possibilité de trier/filtrer les enregistrements
|
||
selon des attributs&nbsp;;</li>
|
||
<li>Les permissions <a href="https://groups.google.com/forum/#!topic/unhosted/5_NOGq8BPTo">se limitent à
|
||
privé/public</a>
|
||
(et <a href="https://github.com/remotestorage/spec/issues/58#issue-27249452">l&#8217;auteur envisage plutôt un modèle à la
|
||
Git</a>)[1]&nbsp;;</li>
|
||
</ul>
|
||
<p>En résumé, il semblerait que ce que nous souhaitons faire avec le
|
||
stockage d&#8217;enregistrements validés est complémentaire avec <em>Remote
|
||
Storage</em>.</p>
|
||
<p>Si des besoins de persistence orientés «fichiers» se présentent, a
|
||
priori nous aurions tort de réinventer les solutions apportées par cette
|
||
spécification. Il y a donc de grandes chances que nous l´intégrions à
|
||
terme, et que <em>Remote Storage</em> devienne une facette de notre&nbsp;solution.</p>
|
||
<h2 id="pouchdb">PouchDB</h2>
|
||
<p><a href="http://pouchdb.com/">PouchDB</a> est une bibliothèque JavaScript qui
|
||
permet de manipuler des enregistrements en local et de les synchroniser
|
||
vers une base&nbsp;distante.</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">javascript</span>
|
||
<span class="k">var</span><span class="w"> </span><span class="n">db</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">new</span><span class="w"> </span><span class="n">PouchDB</span><span class="p">(</span><span class="s1">&#39;dbname&#39;</span><span class="p">);</span>
|
||
|
||
<span class="n">db</span><span class="o">.</span><span class="n">put</span><span class="p">({</span>
|
||
<span class="w"> </span><span class="n">_id</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;dave@gmail.com&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;David&#39;</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">age</span><span class="p">:</span><span class="w"> </span><span class="mi">68</span>
|
||
<span class="p">});</span>
|
||
|
||
<span class="n">db</span><span class="o">.</span><span class="n">replicate</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s1">&#39;http://example.com/mydb&#39;</span><span class="p">);</span>
|
||
</code></pre></div>
|
||
|
||
<p>Le projet a le vent en poupe, bénéficie de nombreux contributeurs,
|
||
l&#8217;éco-système est très riche et l&#8217;adoption par des projets <a href="https://github.com/hoodiehq/wip-hoodie-store-on-pouchdb">comme
|
||
Hoodie</a> ne fait
|
||
que confirmer la pertinence de l&#8217;outil pour les développeurs&nbsp;frontend.</p>
|
||
<p><em>PouchDB</em> gère un « store » local, dont la persistence est abstraite et
|
||
<a href="http://pouchdb.com/2014/07/25/pouchdb-levels-up.html">repose sur</a> l&#8217;<span class="caps">API</span>
|
||
<a href="https://github.com/level/levelup#relationship-to-leveldown">LevelDown</a>
|
||
pour persister les données dans <a href="https://github.com/Level/levelup/wiki/Modules#storage-back-ends">n&#8217;importe quel
|
||
backend</a>.</p>
|
||
<p>Même si <em>PouchDB</em> adresse principalement les besoins des applications
|
||
«*offline-first*», il peut être utilisé aussi bien dans le navigateur
|
||
que côté serveur, via&nbsp;Node.</p>
|
||
<h3 id="synchronisation">Synchronisation</h3>
|
||
<p>La synchronisation (ou réplication) des données locales s&#8217;effectue sur
|
||
un <a href="http://couchdb.apache.org/">CouchDB</a>&nbsp;distant.</p>
|
||
<p>Le projet <a href="https://github.com/pouchdb/pouchdb-server">PouchDB Server</a>
|
||
implémente l&#8217;<span class="caps">API</span> de CouchDB en NodeJS. Comme <em>PouchDB</em> est utilisé, on
|
||
obtient un service qui se comporte comme un <em>CouchDB</em> mais qui stocke
|
||
ses données n&#8217;importe où, dans un <em>Redis</em> ou un <em>PostgreSQL</em> par&nbsp;exemple.</p>
|
||
<p>La synchronisation est complète. Autrement dit, tous les enregistrements
|
||
qui sont sur le serveur se retrouvent synchronisés dans le client. Il
|
||
est possible de filtrer les collections synchronisées, mais cela <a href="http://pouchdb.com/2015/04/05/filtered-replication.html">n&#8217;a
|
||
pas pour objectif de sécuriser l&#8217;accès aux
|
||
données</a>.</p>
|
||
<p>L&#8217;approche recommandée pour cloisonner les données par utilisateur
|
||
consiste à créer <a href="https://github.com/nolanlawson/pouchdb-authentication#some-people-can-read-some-docs-some-people-can-write-those-same-docs">une base de données par
|
||
utilisateur</a>.</p>
|
||
<p>Ce n&#8217;est pas forcément un problème, CouchDB <a href="https://mail-archives.apache.org/mod_mbox/couchdb-user/201401.mbox/%3C52CEB873.7080404@ironicdesign.com%3E">supporte des centaines de
|
||
milliers de bases sans
|
||
sourciller</a>.
|
||
Mais selon les cas d&#8217;utilisation, le cloisement n&#8217;est pas toujours
|
||
facile à déterminer (par rôle, par application, par collection,&nbsp;&#8230;).</p>
|
||
<h2 id="le-cas-dutilisation-payments">Le cas d&#8217;utilisation « Payments&nbsp;»</h2>
|
||
<p><img alt="Put Payments Here -- Before the Internet - CC-NC-SA Katy Silberger
|
||
https://www.flickr.com/photos/katysilbs/11163812186" src="%7Bfilename%7D/images/put-payments.jpg"></p>
|
||
<p>Dans les prochaines semaines, nous devrons mettre sur pied un prototype
|
||
pour tracer l&#8217;historique des paiements et abonnements d&#8217;un&nbsp;utilisateur.</p>
|
||
<p>Le besoin est&nbsp;simple:</p>
|
||
<ul>
|
||
<li>l&#8217;application « Payment » enregistre les paiements et abonnements
|
||
d&#8217;un utilisateur pour une application&nbsp;donnée;</li>
|
||
<li>l&#8217;application « Donnée » interroge le service pour vérifier qu&#8217;un
|
||
utilisateur a payé ou est&nbsp;abonné;</li>
|
||
<li>l&#8217;utilisateur interroge le service pour obtenir la liste de tous ses&nbsp;abonnements.</li>
|
||
</ul>
|
||
<p>Seule l&#8217;application « Payment » a le droit de créer/modifier/supprimer
|
||
des enregistrements, les deux autres ne peuvent que consulter en lecture&nbsp;seule.</p>
|
||
<p>Une application donnée ne peut pas accéder aux paiements des autres
|
||
applications, et un utilisateur ne peut pas accéder aux paiements des
|
||
autres&nbsp;utilisateurs.</p>
|
||
<h3 id="avec-remotestorage">Avec&nbsp;RemoteStorage</h3>
|
||
<p><img alt="Remote Love - CC-BY-NC Julie
|
||
https://www.flickr.com/photos/mamajulie2008/2609549461" src="%7Bfilename%7D/images/remote-love.jpg"></p>
|
||
<p>Clairement, l&#8217;idée de <em>RemoteStorage</em> est de dissocier l&#8217;application
|
||
executée, et les données créées par l&#8217;utilisateur avec&nbsp;celle-ci.</p>
|
||
<p>Dans notre cas, c&#8217;est l&#8217;application « Payment » qui manipule des données
|
||
concernant un utilisateur. Mais celles-ci ne lui appartiennent pas
|
||
directement: certes un utilisateur doit pouvoir les supprimer, surtout
|
||
pas en créer ou les&nbsp;modifier!</p>
|
||
<p>La notion de permissions limitée à privé/publique ne suffit pas dans ce
|
||
cas&nbsp;précis.</p>
|
||
<h3 id="avec-pouchdb">Avec&nbsp;PouchDB</h3>
|
||
<p>Il va falloir créer une <em>base de données</em> par utilisateur, afin d&#8217;isoler
|
||
les enregistrements de façon sécurisée. Seule l&#8217;application « Payment »
|
||
aura tous les droits sur les&nbsp;databases.</p>
|
||
<p>Mais cela ne suffit&nbsp;pas.</p>
|
||
<p>Il ne faut pas qu&#8217;une application puisse voir les paiements des autres
|
||
applications, donc il va aussi falloir recloisonner, et créer une <em>base
|
||
de données</em> par&nbsp;application.</p>
|
||
<p>Quand un utilisateur voudra accéder à l&#8217;ensemble de ses paiements, il
|
||
faudra agréger les <em>databases</em> de toutes les applications. Quand
|
||
l&#8217;équipe marketing voudra faire des statistiques sur l&#8217;ensemble des
|
||
applications, il faudra agrégér des centaines de milliers de
|
||
<em>databases</em>.</p>
|
||
<p>Ce qui est fort dommage, puisqu&#8217;il est probable que les paiements ou
|
||
abonnements d&#8217;un utilisateur pour une application se comptent sur les
|
||
doigts d&#8217;une main. Des centaines de milliers de bases contenant moins de
|
||
5 enregistrements&nbsp;?</p>
|
||
<p>De plus, dans le cas de l&#8217;application « Payment », le serveur est
|
||
implémenté en Python. Utiliser un wrapper JavaScript comme le fait
|
||
<a href="https://pythonhosted.org/Python-PouchDB/">python-pouchdb</a> cela ne nous
|
||
fait pas trop&nbsp;rêver.</p>
|
||
<h2 id="un-nouvel-eco-systeme">Un nouvel éco-système&nbsp;?</h2>
|
||
<p><img alt="Wagon wheel - CC-BY-NC-SA arbyreed
|
||
https://www.flickr.com/photos/19779889@N00/16161808220" src="%7Bfilename%7D/images/wagon-wheel.jpg"></p>
|
||
<p>Évidemment, quand on voit la richesse des projets <em>PouchDB</em> et <em>Remote
|
||
Storage</em> et la dynamique de ces communautés, il est légitime d&#8217;hésiter
|
||
avant de développer une solution&nbsp;alternative.</p>
|
||
<p>Quand nous avons créé le serveur <em>Reading List</em>, nous l&#8217;avons construit
|
||
avec <a href="http://cliquet.readthedocs.org/">Cliquet</a>, ce fût l&#8217;occasion de
|
||
mettre au point <a href="http://cliquet.readthedocs.org/en/latest/api/">un protocole très
|
||
simple</a>, fortement
|
||
inspiré de <a href="http://en.wikipedia.org/wiki/Firefox_Sync">Firefox Sync</a>,
|
||
pour faire de la synchronisation&nbsp;d&#8217;enregistrements.</p>
|
||
<p>Et si les clients <em>Reading List</em> ont pu être implémentés en quelques
|
||
semaines, que ce soit en JavaScript, Java (Android) et <span class="caps">ASM</span> (Add-on
|
||
Firefox), c&#8217;est que le principe «*offline first*» du service est&nbsp;trivial.</p>
|
||
<h3 id="les-compromis">Les&nbsp;compromis</h3>
|
||
<p>Évidemment, nous n&#8217;avons pas la prétention de concurrencer <em>CouchDB</em>.
|
||
Nous faisons plusieurs&nbsp;concessions:</p>
|
||
<ul>
|
||
<li>De base, les collections d&#8217;enregistrements sont cloisonnées par&nbsp;utilisateur;</li>
|
||
<li>Pas d&#8217;historique des&nbsp;révisions;</li>
|
||
<li>Pas de diff sur les enregistrements entre&nbsp;révisions;</li>
|
||
<li>De base, pas de résolution de conflit&nbsp;automatique;</li>
|
||
<li>Pas de synchronisation par flux (<em>streams</em>);</li>
|
||
</ul>
|
||
<p>Jusqu&#8217;à preuve du contraire, ces compromis excluent la possibilité
|
||
d&#8217;implémenter un <a href="https://github.com/pouchdb/pouchdb/blob/master/lib/adapters/http/http.js#L721-L946">adapter
|
||
PouchDB</a>
|
||
pour la synchronisation avec le protocole <span class="caps">HTTP</span> de <em>Cliquet</em>.</p>
|
||
<p>Dommage puisque capitaliser sur l&#8217;expérience client de <em>PouchDB</em> au
|
||
niveau synchro client semble être une très bonne&nbsp;idée.</p>
|
||
<p>En revanche, nous avons plusieurs fonctionnalités&nbsp;intéressantes:</p>
|
||
<ul>
|
||
<li>Pas de&nbsp;map-reduce;</li>
|
||
<li>Synchronisation partielle et/ou ordonnée et/ou paginée&nbsp;;</li>
|
||
<li>Le client choisit, via des headers, d&#8217;écraser la donnée ou de
|
||
respecter la version du serveur&nbsp;;</li>
|
||
<li>Un seul serveur à déployer pour N applications&nbsp;;</li>
|
||
<li>Auto-hébergement simplissime&nbsp;;</li>
|
||
<li>Le client peut choisir de ne pas utiliser de « store local » du tout&nbsp;;</li>
|
||
<li>Dans le client <span class="caps">JS</span>, la gestion du « store local » sera externalisée
|
||
(on pense à <a href="https://github.com/mozilla/localForage">LocalForage</a> ou
|
||
<a href="https://github.com/dfahlander/Dexie.js">Dexie.js</a>)&nbsp;;</li>
|
||
</ul>
|
||
<p>Et, on répond au reste des <a href="#storage-specs">specifications mentionnées au début de
|
||
l&#8217;article</a>&nbsp;!</p>
|
||
<h3 id="les-arguments-philosophiques">Les arguments&nbsp;philosophiques</h3>
|
||
<p>Il est <a href="http://en.wikipedia.org/wiki/Law_of_the_instrument">illusoire de penser qu&#8217;on peut tout faire avec un seul
|
||
outil</a>.</p>
|
||
<p>Nous avons d&#8217;autres cas d&#8217;utilisations dans les cartons qui semblent
|
||
correspondre au scope de <em>PouchDB</em> (<em>pas de notion de permissions ou de
|
||
partage, environnement JavaScript, &#8230;</em>). Nous saurons en tirer profit
|
||
quand cela s&#8217;avèrera pertinent&nbsp;!</p>
|
||
<p>L&#8217;éco-système que nous voulons construire tentera de couvrir les cas
|
||
d&#8217;utilisation qui sont mal adressés par <em>PouchDB</em>. Il se&nbsp;voudra:</p>
|
||
<ul>
|
||
<li>Basé sur notre protocole très simple&nbsp;;</li>
|
||
<li>Minimaliste et multi-usages (<em>comme la fameuse <span class="caps">2CV</span></em>)&nbsp;;</li>
|
||
<li>Naïf (<em>pas de rocket science</em>)&nbsp;;</li>
|
||
<li>Sans magie (<em>explicite et facile à réimplémenter from scratch</em>)&nbsp;;</li>
|
||
</ul>
|
||
<p><a href="http://cliquet.readthedocs.org/en/latest/rationale.html">La philosophie et les fonctionnalités du toolkit python
|
||
Cliquet</a> seront
|
||
bien entendu à l&#8217;honneur&nbsp;:)</p>
|
||
<p>Quant à <em>Remote Storage</em>, dès que le besoin se présentera, nous serons
|
||
très fier de rejoindre l&#8217;initiative, mais pour l&#8217;instant cela nous
|
||
paraît risqué de démarrer en tordant la&nbsp;solution.</p>
|
||
<h3 id="les-arguments-pratiques">Les arguments&nbsp;pratiques</h3>
|
||
<p>Avant d&#8217;accepter de déployer une solution à base de <em>CouchDB</em>, les <em>ops</em>
|
||
de Mozilla vont nous demander de leur prouver par A+B que ce n&#8217;est pas
|
||
faisable avec les stacks qui sont déjà rodées en interne (i.e. MySQL,
|
||
Redis,&nbsp;PostgreSQL).</p>
|
||
<p>De plus, on doit s&#8217;engager sur une pérennité d&#8217;au moins 5 ans pour les
|
||
données. Avec <em>Cliquet</em>, en utilisant le backend PostgreSQL, les données
|
||
sont persistées à plat dans un <a href="https://github.com/mozilla-services/cliquet/blob/40aa33/cliquet/storage/postgresql/schema.sql#L14-L28">schéma PostgreSQL tout
|
||
bête</a>.
|
||
Ce qui ne sera pas le cas d&#8217;un adapteur LevelDown qui va manipuler des
|
||
notions de révisions éclatées dans un schéma&nbsp;clé-valeur.</p>
|
||
<p>Si nous basons le service sur <em>Cliquet</em>, comme c&#8217;est le cas avec
|
||
<a href="http://kinto.readthedocs.org">Kinto</a>, tout le travail d&#8217;automatisation
|
||
de la mise en production (<em>monitoring, builds <span class="caps">RPM</span>, Puppet&#8230;</em>) que nous
|
||
avons fait pour <em>Reading List</em> est complètement&nbsp;réutilisable.</p>
|
||
<p>De même, si on repart avec une stack complètement différente, nous
|
||
allons devoir recommencer tout le travail de rodage, de profiling et
|
||
d&#8217;optimisation effectué au premier&nbsp;trimestre.</p>
|
||
<h2 id="les-prochaines-etapes">Les prochaines&nbsp;étapes</h2>
|
||
<p>Et il est encore temps de changer de stratégie :) Nous aimerions avoir
|
||
un maximum de retours ! C&#8217;est toujours une décision difficile à
|
||
prendre&#8230; <code>&lt;/appel à troll&gt;</code></p>
|
||
<ul>
|
||
<li>Tordre un éco-système existant vs. constuire sur mesure&nbsp;;</li>
|
||
<li>Maîtriser l&#8217;ensemble vs. s&#8217;intégrer&nbsp;;</li>
|
||
<li>Contribuer vs. refaire&nbsp;;</li>
|
||
<li>Guider vs.&nbsp;suivre.</li>
|
||
</ul>
|
||
<p>Nous avons vraiment l&#8217;intention de rejoindre l&#8217;initiative
|
||
<a href="https://nobackend.org/">no-backend</a>, et ce premier pas n&#8217;exclue pas que
|
||
nous convergions à terme ! Peut-être que nous allons finir par rendre
|
||
notre service compatible avec <em>Remote Storage</em>, et peut-être que
|
||
<em>PouchDB</em> deviendra plus agnostique quand au protocole de&nbsp;synchronisation&#8230;</p>
|
||
<p><img alt="XKCD — Standards
|
||
https://xkcd.com/927/" src="%7Bfilename%7D/images/standards.png"></p>
|
||
<p>Utiliser ce nouvel écosystème pour le projet « Payments » va nous
|
||
permettre de mettre au point un système de permissions (<em>probablement
|
||
basé sur les scopes OAuth</em>) qui correspond au besoin exprimé. Et nous
|
||
avons bien l&#8217;intention de puiser dans <a href="http://blog.daybed.io/daybed-revival.html">notre expérience avec Daybed sur
|
||
le sujet</a>.</p>
|
||
<p>Nous extrairons aussi le code des clients implémentés pour <em>Reading
|
||
List</em> afin de faire un client JavaScript&nbsp;minimaliste.</p>
|
||
<p>En partant dans notre coin, nous prenons plusieurs&nbsp;risques:</p>
|
||
<ul>
|
||
<li>réinventer une roue dont nous n&#8217;avons pas connaissance&nbsp;;</li>
|
||
<li>échouer à faire de l&#8217;éco-système <em>Cliquet</em> un projet communautaire&nbsp;;</li>
|
||
<li>échouer à positionner <em>Cliquet</em> dans la niche des cas non couverts
|
||
par PouchDB&nbsp;:)</li>
|
||
</ul>
|
||
<p>Comme <a href="http://pouchdb.com/2015/04/05/filtered-replication.html">le dit Giovanni
|
||
Ornaghi</a>:</p>
|
||
<blockquote>
|
||
<p>Rolling out your set of webservices, push notifications, or background
|
||
services might give you more control, but at the same time it will
|
||
force you to engineer, write, test, and maintain a whole new&nbsp;ecosystem.</p>
|
||
</blockquote>
|
||
<p>C&#8217;est justement l&#8217;éco-système dont est responsable l&#8217;équipe <em>Mozilla
|
||
Cloud Services</em>!</p>
|
||
<ol>
|
||
<li>Il existe le <a href="https://sharesome.5apps.com/">projet Sharesome</a> qui
|
||
permet de partager publiquement des ressources de son <em>remote
|
||
Storage</em>.</li>
|
||
</ol></content><category term="code"></category></entry><entry><title>Service de nuages !</title><link href="https://blog.notmyidea.org/service-de-nuages-fr.html" rel="alternate"></link><published>2015-04-01T00:00:00+02:00</published><updated>2015-04-01T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2015-04-01:/service-de-nuages-fr.html</id><summary type="html"><p class="first last">Retour sur le premier trimestre 2015: Readinglist, Kinto,&nbsp;Cliquet.</p>
|
||
</summary><content type="html"><p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
|
||
<p>Pas mal de changements depuis le début de l&#8217;année pour l&#8217;équipe
|
||
«cloud-services»&nbsp;francophone!</p>
|
||
<p>Tout d&#8217;abord, nouvelle importante, l&#8217;équipe s&#8217;étoffe avec des profils assez
|
||
complémentaires: <a class="reference external" href="https://nicolas.perriault.net/">n1k0</a> et <a class="reference external" href="http://mathieu-leplatre.info">Mathieu</a> sont venus prêter main forte à <a class="reference external" href="http://ziade.org/">Tarek</a>, <a class="reference external" href="http://natim.ionyse.com">Rémy</a> et <a class="reference external" href="http://notmyidea.org">Alexis</a>.</p>
|
||
<p>Le début de l&#8217;année a vu le lancement de <a class="reference external" href="https://www.mozilla.org/en-US/firefox/hello/">Firefox Hello</a> ce qui nous a permis de passer
|
||
à l&#8217;échelle <a class="reference external" href="https://github.com/mozilla-services/loop-server">le serveur</a>,
|
||
écrit en Node.js®, pour&nbsp;l&#8217;occasion.</p>
|
||
<div class="section" id="un-serveur-de-listes-de-lecture">
|
||
<h2>Un serveur de listes de&nbsp;lecture</h2>
|
||
<p>En parallèle, un projet de <a class="reference external" href="https://readinglist.readthedocs.org">synchronisation de liste de lecture</a> (<em>Reading List</em>) a vu le jour. L&#8217;idée
|
||
étant de pouvoir marquer des pages &#8220;à lire pour plus tard&#8221; et de continuer la
|
||
lecture sur n&#8217;importe quel périphérique synchronisé (Firefox pour Android ou
|
||
Firefox Desktop). Un équivalent libre à <a class="reference external" href="http://getpocket.com">Pocket</a> en quelque sorte, qu&#8217;il est
|
||
possible d&#8217;héberger&nbsp;soit-même.</p>
|
||
<img alt="Capture d'écran de Firefox nightly avec readinglist." src="https://blog.notmyidea.org/images/readinglist-screenshot.png" />
|
||
<p>Pour le construire, nous aurions pu réutiliser <a class="reference external" href="https://github.com/mozilla-services/server-syncstorage">Firefox Sync</a>, après tout
|
||
c&#8217;est un service de synchronisation de données très robuste, construit avec <a class="reference external" href="http://cornice.readthedocs.org/">Cornice</a>.
|
||
Mais seulement, <em>Sync</em> n&#8217;a pas été pensé pour garantir la pérennité des données,
|
||
et la marche était trop haute pour changer ça en&nbsp;profondeur.</p>
|
||
<p>Nous aurions pu aussi nous contenter de faire une énième application qui expose
|
||
une <span class="caps">API</span> et persiste des données dans une base de&nbsp;données.</p>
|
||
<p>Mais cette nouvelle petite équipe n&#8217;est pas là par hasard&nbsp;:)</p>
|
||
</div>
|
||
<div class="section" id="la-daybed-team">
|
||
<h2>La «Daybed&nbsp;Team»</h2>
|
||
<p>On partage une vision: un service générique de stockage de données ! Peut-être
|
||
que ça vous rappelle <a class="reference external" href="https://daybed.io">un certain projet nommé Daybed</a> ?
|
||
Pour les applications clientes, JavaScript, mobiles ou autres, l&#8217;utilisation de
|
||
ce service doit être un jeu d&#8217;enfant ! L&#8217;application gère ses données
|
||
localement (aka offline-first), et synchronise à la&nbsp;demande.</p>
|
||
<p>Ici, le cœur du serveur <em>Reading List</em> est justement une <span class="caps">API</span> &#8220;<span class="caps">CRUD</span>&#8221; (Create,
|
||
Retrieve, Update, Delete), qui gère de la synchronisation et de
|
||
l&#8217;authentification. Nous avons donc pris le parti de faire une <span class="caps">API</span> &#8220;simple&#8221;,
|
||
avec le moins de spécificités possible, qui poserait les bases d&#8217;un service
|
||
générique. Notamment parce qu&#8217;il y a d&#8217;autres projets dans la même trempe qui vont&nbsp;suivre.</p>
|
||
<p>Pas mal d&#8217;expérience ayant été accumulée au sein de l&#8217;équipe, avec d&#8217;une part la
|
||
création de <em>Firefox Sync</em>, et d&#8217;autre part avec <em>Daybed</em>, notre side-project, nous
|
||
tentons de ne pas reproduire les mêmes erreurs, tout en gardant les concepts
|
||
qui ont fait leurs&nbsp;preuves.</p>
|
||
<p>Par exemple, nous avons conservé le mécanisme de collections d&#8217;enregistrements
|
||
et de <em>timestamp</em> de <em>Sync</em>. Comme ces problématiques sont récurrentes, voire
|
||
incontournables, nous avons décidé de reprendre le protocole de synchronisation,
|
||
de l&#8217;étendre légèrement et surtout de le dissocier du projet de listes de&nbsp;lecture.</p>
|
||
</div>
|
||
<div class="section" id="le-mecanisme-qui-force-a-aller-de-l-avant">
|
||
<h2>Le mécanisme qui force à aller de&nbsp;l&#8217;avant</h2>
|
||
<p>Comme première pierre à l&#8217;édifice, nous avons donné naissance au projet
|
||
<a class="reference external" href="https://cliquet.readthedocs.org">Cliquet</a>, dont l&#8217;idée principale est de
|
||
fournir une implémentation de ce protocole en python, tout en factorisant
|
||
l&#8217;ensemble de nos bonnes pratiques (pour la prod&nbsp;notamment).</p>
|
||
<img alt="Logo du projet Cliquet" class="align-right" src="https://blog.notmyidea.org/images/cliquet/cliquet-logo.png" />
|
||
<p>L&#8217;avantage d&#8217;avoir un protocole plutôt qu&#8217;un monolithe, c&#8217;est que si vous
|
||
préférez Asyncio, io.js ou Go, on vous encouragera à publier votre
|
||
implémentation alternative&nbsp;!</p>
|
||
<p>Avec <em>Cliquet</em>, le code du serveur liste de lecture consiste principalement
|
||
à définir un schéma pour les enregistrements, puis à forcer des valeurs de
|
||
champs sur certains appels. Cela réduit ce projet à quelques dizaines de lignes
|
||
de&nbsp;code.</p>
|
||
<p>Quant au futur service de stockage générique, <a class="reference external" href="http://kinto.readthedocs.org">le projet</a> en est encore à ses balbutiements mais c&#8217;est
|
||
bel et bien en route ! Il permet déjà d&#8217;être branché comme backend de stockage
|
||
dans une application <em>Cliquet</em>, et ça <a class="reference external" href="https://github.com/mozilla-services/kinto/blob/0.2.1/kinto/views/collection.py">implémenté en 20 lignes de code</a>!</p>
|
||
<p>Ah, et cette fois, nous ne construirons les fonctionnalités qu&#8217;à partir des
|
||
besoins concrets qui surviennent. Ça paraît tout bête, mais sur <em>Daybed</em> on
|
||
l&#8217;a pas vu venir&nbsp;:)</p>
|
||
<p>Dans les prochains articles, nous avons prévu de décrire les bonnes pratiques
|
||
rassemblées dans le protocole (ou <em>Cliquet</em>), certains points techniques précis
|
||
et de vous présenter notre vision via des exemples et&nbsp;tutoriaux.</p>
|
||
<p>À bientôt, donc&nbsp;!</p>
|
||
</div>
|
||
</content><category term="code"></category></entry><entry><title>What’s Hawk and how to use it?</title><link href="https://blog.notmyidea.org/whats-hawk-and-how-to-use-it.html" rel="alternate"></link><published>2014-07-31T00:00:00+02:00</published><updated>2014-07-31T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2014-07-31:/whats-hawk-and-how-to-use-it.html</id><summary type="html">
|
||
<p>At Mozilla, we recently had to implement <a href="https://github.com/hueniverse/hawk">the Hawk authentication
|
||
scheme</a> for a number of projects,
|
||
and we came up creating two libraries to ease integration into pyramid
|
||
and node.js&nbsp;apps.</p>
|
||
<p>But maybe you don&#8217;t know&nbsp;Hawk.</p>
|
||
<p>Hawk is a relatively new technology, crafted by one of the …</p></summary><content type="html">
|
||
<p>At Mozilla, we recently had to implement <a href="https://github.com/hueniverse/hawk">the Hawk authentication
|
||
scheme</a> for a number of projects,
|
||
and we came up creating two libraries to ease integration into pyramid
|
||
and node.js&nbsp;apps.</p>
|
||
<p>But maybe you don&#8217;t know&nbsp;Hawk.</p>
|
||
<p>Hawk is a relatively new technology, crafted by one of the original
|
||
<a href="https://en.wikipedia.org/wiki/OAuth">OAuth</a> specification authors, that
|
||
intends to replace the 2-legged OAuth authentication scheme using a
|
||
simpler&nbsp;approach.</p>
|
||
<p>It is an authentication scheme for <span class="caps">HTTP</span>, built around <a href="https://en.wikipedia.org/wiki/Hmac"><span class="caps">HMAC</span>
|
||
digests</a> of requests and&nbsp;responses.</p>
|
||
<p>Every authenticated client request has an Authorization header
|
||
containing a <span class="caps">MAC</span> (Message Authentication Code) and some additional
|
||
metadata, then each server response to authenticated requests contains a
|
||
Server-Authorization header that authenticates the response, so the
|
||
client is sure it comes from the right&nbsp;server.</p>
|
||
<h2 id="exchange-of-the-hawk-id-and-hawk-key">Exchange of the hawk id and hawk&nbsp;key</h2>
|
||
<p>To sign the requests, a client needs to retrieve a token id and a token
|
||
key from the&nbsp;server.</p>
|
||
<p>Hawk itself does not define how these credentials should be exchanged
|
||
between the server and the client. The excellent team behind <a href="http://accounts.firefox.com">Firefox
|
||
Accounts</a> put together a scheme to do that,
|
||
which acts like the&nbsp;following:</p>
|
||
<div class="note">
|
||
|
||
<div class="admonition-title">
|
||
|
||
Note
|
||
|
||
</div>
|
||
|
||
All this derivation crazyness might seem a bit complicated, but don&#8217;t
|
||
worry, we put together some libraries that takes care of that for you
|
||
automatically.
|
||
|
||
If you are not interested into these details, you can directly jump to
|
||
the next section to see how to use the libraries.
|
||
|
||
</div>
|
||
|
||
<p>When your server application needs to send you the credentials, it will
|
||
return it inside a specific Hawk-Session-Token header. This token can be
|
||
derived to split this string in two values (hawk id and hawk key) that
|
||
you will use to sign your next&nbsp;requests.</p>
|
||
<p>In order to get the hawk credentials, you&#8217;ll need&nbsp;to:</p>
|
||
<p>First, do an <a href="http://en.wikipedia.org/wiki/HKDF"><span class="caps">HKDF</span> derivation</a> on the
|
||
given session token. You&#8217;ll need to use the following&nbsp;parameters:</p>
|
||
<div class="highlight"><pre><span></span><code>key_material = HKDF(hawk_session, &quot;&quot;, &#39;identity.mozilla.com/picl/v1/sessionToken&#39;, 32*2)
|
||
</code></pre></div>
|
||
|
||
<div class="note">
|
||
|
||
<div class="admonition-title">
|
||
|
||
Note
|
||
|
||
</div>
|
||
|
||
The `identity.mozilla.com/picl/v1/sessionToken` is a reference to this
|
||
way of deriving the credentials, not an actual <span class="caps">URL</span>.
|
||
|
||
</div>
|
||
|
||
<p>Then, the key material you&#8217;ll get out of the <span class="caps">HKDF</span> need to be separated
|
||
into two parts, the first 32 hex caracters are the hawk id, and the next
|
||
32 ones are the hawk&nbsp;key.</p>
|
||
<p>Credentials:</p>
|
||
<div class="highlight"><pre><span></span><code>javascript
|
||
credentials = {
|
||
&#39;id&#39;: keyMaterial[0:32],
|
||
&#39;key&#39;: keyMaterial[32:64],
|
||
&#39;algorithm&#39;: &#39;sha256&#39;
|
||
}
|
||
</code></pre></div>
|
||
|
||
<h2 id="httpie">Httpie</h2>
|
||
<p>To showcase APIs in the documentation, I like to use
|
||
<a href="https://github.com/jakubroztocil/httpie">httpie</a>, a curl-replacement
|
||
with a nicer <span class="caps">API</span>, built around <a href="http://python-requests.org">the python requests
|
||
library</a>.</p>
|
||
<p>Luckily, HTTPie allows you to plug different authentication schemes for
|
||
it, so <a href="https://github.com/mozilla-services/requests-hawk">I wrote a
|
||
wrapper</a> around
|
||
<a href="https://github.com/kumar303/mohawk">mohawk</a> to add hawk support to the
|
||
requests&nbsp;lib.</p>
|
||
<p>Doing hawk requests in your terminal is now as simple&nbsp;as:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>requests-hawk<span class="w"> </span>httpie
|
||
$<span class="w"> </span>http<span class="w"> </span>GET<span class="w"> </span>localhost:5000/registration<span class="w"> </span>--auth-type<span class="o">=</span>hawk<span class="w"> </span>--auth<span class="o">=</span><span class="s1">&#39;id:key&#39;</span>
|
||
</code></pre></div>
|
||
|
||
<p>In addition, it will help you to craft requests using the requests&nbsp;library:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">requests</span>
|
||
<span class="kn">from</span> <span class="nn">requests_hawk</span> <span class="kn">import</span> <span class="n">HawkAuth</span>
|
||
|
||
<span class="n">hawk_auth</span> <span class="o">=</span> <span class="n">HawkAuth</span><span class="p">(</span>
|
||
<span class="n">credentials</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;id&#39;</span><span class="p">:</span> <span class="nb">id</span><span class="p">,</span> <span class="s1">&#39;key&#39;</span><span class="p">:</span> <span class="n">key</span><span class="p">,</span> <span class="s1">&#39;algorithm&#39;</span><span class="p">:</span> <span class="s1">&#39;sha256&#39;</span><span class="p">})</span>
|
||
|
||
<span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/url&quot;</span><span class="p">,</span> <span class="n">auth</span><span class="o">=</span><span class="n">hawk_auth</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<p>Alternatively, if you don&#8217;t have the token id and key, you can pass the
|
||
hawk session token I talked about earlier and the lib will take care of
|
||
the derivation for&nbsp;you:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">hawk_auth</span> <span class="o">=</span> <span class="n">HawkAuth</span><span class="p">(</span>
|
||
<span class="n">hawk_session</span><span class="o">=</span><span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s1">&#39;hawk-session-token&#39;</span><span class="p">],</span>
|
||
<span class="n">server_url</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">server_url</span>
|
||
<span class="p">)</span>
|
||
<span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/url&quot;</span><span class="p">,</span> <span class="n">auth</span><span class="o">=</span><span class="n">hawk_auth</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<h2 id="integrate-with-python-pyramid-apps">Integrate with python pyramid&nbsp;apps</h2>
|
||
<p>If you&#8217;re writing pyramid applications, you&#8217;ll be happy to learn that
|
||
<a href="https://www.rfk.id.au/blog/">Ryan Kelly</a> put together a library that
|
||
makes Hawk work as an Authentication provider for them. I&#8217;m chocked how
|
||
simple it is to use&nbsp;it.</p>
|
||
<p>Here is a demo of how we implemented it for&nbsp;Daybed:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">pyramid_hawkauth</span> <span class="kn">import</span> <span class="n">HawkAuthenticationPolicy</span>
|
||
|
||
<span class="n">policy</span> <span class="o">=</span> <span class="n">HawkAuthenticationPolicy</span><span class="p">(</span><span class="n">decode_hawk_id</span><span class="o">=</span><span class="n">get_hawk_id</span><span class="p">)</span>
|
||
<span class="n">config</span><span class="o">.</span><span class="n">set_authentication_policy</span><span class="p">(</span><span class="n">authn_policy</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<p>The get_hawk_id function is a function that takes a request and a
|
||
tokenid and returns a tuple of (token_id,&nbsp;token_key).</p>
|
||
<p>How you want to store the tokens and retrieve them is up to you. The
|
||
default implementation (e.g. if you don&#8217;t pass a decode_hawk_id
|
||
function) decodes the key from the token itself, using a master secret
|
||
on the server (so you don&#8217;t need to store&nbsp;anything).</p>
|
||
<h2 id="integrate-with-nodejs-express-apps">Integrate with node.js Express&nbsp;apps</h2>
|
||
<p>We had to implement Hawk authentication for two node.js projects and
|
||
finally came up factorizing everything in a library for express, named
|
||
<a href="https://github.com/mozilla-services/express-hawkauth">express-hawkauth</a>.</p>
|
||
<p>In order to plug it in your application, you&#8217;ll need to use it as a&nbsp;middleware:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">javascript</span>
|
||
<span class="k">var</span><span class="w"> </span><span class="n">express</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">require</span><span class="p">(</span><span class="s2">&quot;express&quot;</span><span class="p">);</span>
|
||
<span class="k">var</span><span class="w"> </span><span class="n">hawk</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">require</span><span class="p">(</span><span class="s2">&quot;express-hawkauth&quot;</span><span class="p">);</span>
|
||
<span class="n">app</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">express</span><span class="p">();</span>
|
||
|
||
<span class="k">var</span><span class="w"> </span><span class="n">hawkMiddleware</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">hawk</span><span class="o">.</span><span class="n">getMiddleware</span><span class="p">({</span>
|
||
<span class="w"> </span><span class="n">hawkOptions</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span>
|
||
<span class="w"> </span><span class="n">getSession</span><span class="p">:</span><span class="w"> </span><span class="n">function</span><span class="p">(</span><span class="n">tokenId</span><span class="p">,</span><span class="w"> </span><span class="n">cb</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">A</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">which</span><span class="w"> </span><span class="k">pass</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">cb</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">key</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">algorithm</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">the</span>
|
||
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">given</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="n">id</span><span class="o">.</span><span class="w"> </span><span class="n">First</span><span class="w"> </span><span class="n">argument</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">callback</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">potential</span>
|
||
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">error</span><span class="o">.</span>
|
||
<span class="w"> </span><span class="n">cb</span><span class="p">(</span><span class="nb nb-Type">null</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="n">key</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;key&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">algorithm</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;sha256&quot;</span><span class="p">});</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="n">createSession</span><span class="p">:</span><span class="w"> </span><span class="n">function</span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">key</span><span class="p">,</span><span class="w"> </span><span class="n">cb</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">A</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">which</span><span class="w"> </span><span class="n">stores</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">session</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">given</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">key</span><span class="o">.</span>
|
||
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Argument</span><span class="w"> </span><span class="n">returned</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">potential</span><span class="w"> </span><span class="n">error</span><span class="o">.</span>
|
||
<span class="w"> </span><span class="n">cb</span><span class="p">(</span><span class="nb nb-Type">null</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="n">setUser</span><span class="p">:</span><span class="w"> </span><span class="n">function</span><span class="p">(</span><span class="n">req</span><span class="p">,</span><span class="w"> </span><span class="n">res</span><span class="p">,</span><span class="w"> </span><span class="n">tokenId</span><span class="p">,</span><span class="w"> </span><span class="n">cb</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">A</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">uses</span><span class="w"> </span><span class="n">req</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">res</span><span class="p">,</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">hawkId</span><span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="n">they</span><span class="s1">&#39;re known so</span>
|
||
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">tweak</span><span class="w"> </span><span class="n">it</span><span class="o">.</span><span class="w"> </span><span class="n">For</span><span class="w"> </span><span class="n">instance</span><span class="p">,</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">store</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">tokenId</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">the</span>
|
||
<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">user</span><span class="o">.</span>
|
||
<span class="w"> </span><span class="n">req</span><span class="o">.</span><span class="n">user</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">tokenId</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">});</span>
|
||
|
||
<span class="n">app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/hawk-enabled-endpoint&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">hawkMiddleware</span><span class="p">);</span>
|
||
</code></pre></div>
|
||
|
||
<p>If you pass the createSession parameter, all non-authenticated requests
|
||
will create a new hawk session and return it with the response, in the
|
||
Hawk-Session-Token&nbsp;header.</p>
|
||
<p>If you want to only check a valid hawk session exists (without creating
|
||
a new one), just create a middleware which doesn&#8217;t have any
|
||
createSession parameter&nbsp;defined.</p>
|
||
<h2 id="some-reference-implementations">Some reference&nbsp;implementations</h2>
|
||
<p>As a reference, here is how we&#8217;re using the libraries I&#8217;m talking about,
|
||
in case that helps you to integrate with your&nbsp;projects.</p>
|
||
<ul>
|
||
<li>The Mozilla Loop server <a href="https://github.com/mozilla-services/loop-server/blob/master/loop/index.js#L70-L133">uses hawk as authentication once you&#8217;re
|
||
logged in with a valid BrowserID
|
||
assertion</a>;
|
||
request, to keep a session between client and&nbsp;server;</li>
|
||
<li><a href="https://github.com/spiral-project/daybed/commit/f178b4e43015fa077430798dcd3d0886c7611caf">I recently added hawk support on the Daybed
|
||
project</a>
|
||
(that&#8217;s a pyramid / cornice)&nbsp;app.</li>
|
||
<li>It&#8217;s also interesting to note that Kumar put together <a href="http://hawkrest.readthedocs.org/en/latest/">hawkrest, for
|
||
the django rest&nbsp;framework</a></li>
|
||
</ul></content><category term="code"></category></entry><entry><title>Implementing CORS in Cornice</title><link href="https://blog.notmyidea.org/implementing-cors-in-cornice.html" rel="alternate"></link><published>2013-02-04T00:00:00+01:00</published><updated>2013-02-04T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2013-02-04:/implementing-cors-in-cornice.html</id><summary type="html">
|
||
<div class="note">
|
||
|
||
<div class="admonition-title">
|
||
|
||
Note
|
||
|
||
</div>
|
||
|
||
I&#8217;m cross-posting [on the mozilla services
|
||
weblog](https://blog.mozilla.org/services/). Since this is the first
|
||
time we&#8217;re doing that, I though it could be useful to point you there.
|
||
Check it out and expect more technical articles there in the future.
|
||
|
||
</div>
|
||
|
||
<p>For security reasons, it …</p></summary><content type="html">
|
||
<div class="note">
|
||
|
||
<div class="admonition-title">
|
||
|
||
Note
|
||
|
||
</div>
|
||
|
||
I&#8217;m cross-posting [on the mozilla services
|
||
weblog](https://blog.mozilla.org/services/). Since this is the first
|
||
time we&#8217;re doing that, I though it could be useful to point you there.
|
||
Check it out and expect more technical articles there in the future.
|
||
|
||
</div>
|
||
|
||
<p>For security reasons, it&#8217;s not possible to do cross-domain requests. In
|
||
other words, if you have a page served from the domain lolnet.org, it
|
||
will not be possible for it to get data from&nbsp;notmyidea.org.</p>
|
||
<p>Well, it&#8217;s possible, using tricks and techniques like
|
||
<a href="http://en.wikipedia.org/wiki/JSONP"><span class="caps">JSONP</span></a>, but that doesn&#8217;t work all
|
||
the time (see <a href="#how-this-is-different-from-jsonp">the section below</a>). I
|
||
remember myself doing some simple proxies on my domain server to be able
|
||
to query other&#8217;s <span class="caps">API</span>.</p>
|
||
<p>Thankfully, there is a nicer way to do this, namely, &#8220;Cross Origin
|
||
Resource-Sharing&#8221;, or <a href="http://www.w3.org/TR/cors/"><span class="caps">CORS</span></a>.</p>
|
||
<h2 id="you-want-an-icecream-go-ask-your-dad-first">You want an icecream? Go ask your dad&nbsp;first.</h2>
|
||
<p>If you want to use <span class="caps">CORS</span>, you need the <span class="caps">API</span> you&#8217;re querying to support it;
|
||
on the server&nbsp;side.</p>
|
||
<p>The <span class="caps">HTTP</span> server need to answer to the <span class="caps">OPTIONS</span> verb, and with the
|
||
appropriate response&nbsp;headers.</p>
|
||
<p><span class="caps">OPTIONS</span> is sent as what the authors of the spec call a &#8220;preflight
|
||
request&#8221;; just before doing a request to the <span class="caps">API</span>, the <em>User-Agent</em> (the
|
||
browser most of the time) asks the permission to the resource, with an
|
||
<span class="caps">OPTIONS</span>&nbsp;call.</p>
|
||
<p>The server answers, and tell what is available and what&nbsp;isn&#8217;t:</p>
|
||
<p><img alt="The CORS flow (from the HTML5 CORS tutorial)" src="/images/cors_flow.png"></p>
|
||
<ul>
|
||
<li>
|
||
<p>1a. The User-Agent, rather than doing the call directly, asks the
|
||
server, the <span class="caps">API</span>, the permission to do the request. It does so with
|
||
the following&nbsp;headers:</p>
|
||
<ul>
|
||
<li><strong>Access-Control-Request-Headers</strong>, contains the headers the
|
||
User-Agent want to&nbsp;access.</li>
|
||
<li><strong>Access-Control-Request-Method</strong> contains the method the
|
||
User-Agent want to&nbsp;access.</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<p>1b. The <span class="caps">API</span> answers what is&nbsp;authorized:</p>
|
||
<ul>
|
||
<li><strong>Access-Control-Allow-Origin</strong> the origin that&#8217;s accepted. Can
|
||
be * or the domain&nbsp;name.</li>
|
||
<li><strong>Access-Control-Allow-Methods</strong> a <em>list</em> of allowed methods.
|
||
This can be cached. Note than the request asks permission for
|
||
one method and the server should return a list of accepted&nbsp;methods.</li>
|
||
<li><strong>Access-Allow-Headers</strong> a list of allowed headers, for all of
|
||
the methods, since this can be cached as&nbsp;well.</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<ol>
|
||
<li>The User-Agent can do the &#8220;normal&#8221;&nbsp;request.</li>
|
||
</ol>
|
||
</li>
|
||
</ul>
|
||
<p>So, if you want to access the /icecream resource, and do a <span class="caps">PUT</span> there,
|
||
you&#8217;ll have the following&nbsp;flow:</p>
|
||
<div class="highlight"><pre><span></span><code>&gt; OPTIONS /icecream
|
||
&gt; Access-Control-Request-Methods = PUT
|
||
&gt; Origin: notmyidea.org
|
||
&lt; Access-Control-Allow-Origin = notmyidea.org
|
||
&lt; Access-Control-Allow-Methods = PUT,GET,DELETE
|
||
200 OK
|
||
</code></pre></div>
|
||
|
||
<p>You can see that we have an Origin Header in the request, as well as a
|
||
Access-Control-Request-Methods. We&#8217;re here asking if we have the right,
|
||
as notmyidea.org, to do a <span class="caps">PUT</span> request on&nbsp;/icecream.</p>
|
||
<p>And the server tells us that we can do that, as well as <span class="caps">GET</span> and <span class="caps">DELETE</span>.</p>
|
||
<p>I&#8217;ll not cover all the details of the <span class="caps">CORS</span> specification here, but bear
|
||
in mind than with <span class="caps">CORS</span>, you can control what are the authorized methods,
|
||
headers, origins, and if the client is allowed to send authentication
|
||
information or&nbsp;not.</p>
|
||
<h2 id="a-word-about-security">A word about&nbsp;security</h2>
|
||
<p><span class="caps">CORS</span> is not an answer for every cross-domain call you want to do,
|
||
because you need to control the service you want to call. For instance,
|
||
if you want to build a feed reader and access the feeds on different
|
||
domains, you can be pretty much sure that the servers will not implement
|
||
<span class="caps">CORS</span>, so you&#8217;ll need to write a proxy yourself, to provide&nbsp;this.</p>
|
||
<p>Secondly, if misunderstood, <span class="caps">CORS</span> can be insecure, and cause problems.
|
||
Because the rules apply when a client wants to do a request to a server,
|
||
you need to be extra careful about who you&#8217;re&nbsp;authorizing.</p>
|
||
<p>An incorrectly secured <span class="caps">CORS</span> server can be accessed by a malicious client
|
||
very easily, bypassing network security. For instance, if you host a
|
||
server on an intranet that is only available from behind a <span class="caps">VPN</span> but
|
||
accepts every cross-origin call. A bad guy can inject javascript into
|
||
the browser of a user who has access to your protected server and make
|
||
calls to your service, which is probably not what you&nbsp;want.</p>
|
||
<h2 id="how-this-is-different-from-jsonp">How this is different from <span class="caps">JSONP</span>?</h2>
|
||
<p>You may know the <a href="http://en.wikipedia.org/wiki/JSONP"><span class="caps">JSONP</span></a> protocol.
|
||
<span class="caps">JSONP</span> allows cross origin, but for a particular use case, and does have
|
||
some drawbacks (for instance, it&#8217;s not possible to do DELETEs or PUTs
|
||
with <span class="caps">JSONP</span>).</p>
|
||
<p><span class="caps">JSONP</span> exploits the fact that it is possible to get information from
|
||
another domain when you are asking for javascript code, using the
|
||
\&lt;script&gt;&nbsp;element.</p>
|
||
<blockquote>
|
||
<p>Exploiting the open policy for \&lt;script&gt; elements, some pages use
|
||
them to retrieve JavaScript code that operates on dynamically
|
||
generated <span class="caps">JSON</span>-formatted data from other origins. This usage pattern
|
||
is known as <span class="caps">JSONP</span>. Requests for <span class="caps">JSONP</span> retrieve not <span class="caps">JSON</span>, but arbitrary
|
||
JavaScript code. They are evaluated by the JavaScript interpreter, not
|
||
parsed by a <span class="caps">JSON</span>&nbsp;parser.</p>
|
||
</blockquote>
|
||
<h2 id="using-cors-in-cornice">Using <span class="caps">CORS</span> in&nbsp;Cornice</h2>
|
||
<p>Okay, things are hopefully clearer about <span class="caps">CORS</span>, let&#8217;s see how we
|
||
implemented it on the&nbsp;server-side.</p>
|
||
<p>Cornice is a toolkit that lets you define resources in python and takes
|
||
care of the heavy lifting for you, so I wanted it to take care of the
|
||
<span class="caps">CORS</span> support as&nbsp;well.</p>
|
||
<p>In Cornice, you define a service like&nbsp;this:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">cornice</span> <span class="kn">import</span> <span class="n">Service</span>
|
||
|
||
<span class="n">foobar</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;foobar&quot;</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s2">&quot;/foobar&quot;</span><span class="p">)</span>
|
||
|
||
<span class="c1"># and then you do something with it</span>
|
||
<span class="nd">@foobar</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
|
||
<span class="k">def</span> <span class="nf">get_foobar</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="c1"># do something with the request.</span>
|
||
</code></pre></div>
|
||
|
||
<p>To add <span class="caps">CORS</span> support to this resource, you can go this way, with the
|
||
cors_origins&nbsp;parameter:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">foobar</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">&#39;foobar&#39;</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s1">&#39;/foobar&#39;</span><span class="p">,</span> <span class="n">cors_origins</span><span class="o">=</span><span class="p">(</span><span class="s1">&#39;*&#39;</span><span class="p">,))</span>
|
||
</code></pre></div>
|
||
|
||
<p>Ta-da! You have enabled <span class="caps">CORS</span> for your service. <strong>Be aware that you&#8217;re
|
||
authorizing anyone to query your server, that may not be what you&nbsp;want.</strong></p>
|
||
<p>Of course, you can specify a list of origins you trust, and you don&#8217;t
|
||
need to stick with *, which means &#8220;authorize&nbsp;everyone&#8221;.</p>
|
||
<h3 id="headers">Headers</h3>
|
||
<p>You can define the headers you want to expose for the&nbsp;service:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">foobar</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">&#39;foobar&#39;</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s1">&#39;/foobar&#39;</span><span class="p">,</span> <span class="n">cors_origins</span><span class="o">=</span><span class="p">(</span><span class="s1">&#39;*&#39;</span><span class="p">,))</span>
|
||
|
||
<span class="nd">@foobar</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">cors_headers</span><span class="o">=</span><span class="p">(</span><span class="s1">&#39;X-My-Header&#39;</span><span class="p">,</span> <span class="s1">&#39;Content-Type&#39;</span><span class="p">))</span>
|
||
<span class="k">def</span> <span class="nf">get_foobars_please</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="s2">&quot;some foobar for you&quot;</span>
|
||
</code></pre></div>
|
||
|
||
<p>I&#8217;ve done some testing and it wasn&#8217;t working on Chrome because I wasn&#8217;t
|
||
handling the headers the right way (The missing one was Content-Type,
|
||
that Chrome was asking for). With my first version of the
|
||
implementation, I needed the service implementers to explicitely list
|
||
all the headers that should be exposed. While this improves security, it
|
||
can be frustrating while&nbsp;developing.</p>
|
||
<p>So I introduced an expose_all_headers flag, which is set to True by
|
||
default, if the service supports <span class="caps">CORS</span>.</p>
|
||
<h3 id="cookies-credentials">Cookies /&nbsp;Credentials</h3>
|
||
<p>By default, the requests you do to your <span class="caps">API</span> endpoint don&#8217;t include the
|
||
credential information for security reasons. If you really want to do
|
||
that, you need to enable it using the cors_credentials parameter. You
|
||
can activate this one on a per-service basis or on a per-method&nbsp;basis.</p>
|
||
<h3 id="caching">Caching</h3>
|
||
<p>When you do a preflight request, the information returned by the server
|
||
can be cached by the User-Agent so that it&#8217;s not redone before each
|
||
actual&nbsp;call.</p>
|
||
<p>The caching period is defined by the server, using the
|
||
Access-Control-Max-Age header. You can configure this timing using the
|
||
cors_max_age&nbsp;parameter.</p>
|
||
<h3 id="simplifying-the-api">Simplifying the <span class="caps">API</span></h3>
|
||
<p>We have cors_headers, cors_enabled, cors_origins, cors_credentials,
|
||
cors_max_age, cors_expose_all_headers … a fair number of
|
||
parameters. If you want to have a specific <span class="caps">CORS</span>-policy for your
|
||
services, that can be a bit tedious to pass these to your services all
|
||
the&nbsp;time.</p>
|
||
<p>I introduced another way to pass the <span class="caps">CORS</span> policy, so you can do
|
||
something like&nbsp;that:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">policy</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">enabled</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||
<span class="n">headers</span><span class="o">=</span><span class="p">(</span><span class="s1">&#39;X-My-Header&#39;</span><span class="p">,</span> <span class="s1">&#39;Content-Type&#39;</span><span class="p">),</span>
|
||
<span class="n">origins</span><span class="o">=</span><span class="p">(</span><span class="s1">&#39;*.notmyidea.org&#39;</span><span class="p">),</span>
|
||
<span class="n">credentials</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||
<span class="n">max_age</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
|
||
|
||
<span class="n">foobar</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s1">&#39;foobar&#39;</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s1">&#39;/foobar&#39;</span><span class="p">,</span> <span class="n">cors_policy</span><span class="o">=</span><span class="n">policy</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<h2 id="comparison-with-other-implementations">Comparison with other&nbsp;implementations</h2>
|
||
<p>I was curious to have a look at other implementations of <span class="caps">CORS</span>, in django
|
||
for instance, and I found <a href="https://gist.github.com/426829.js">a gist about
|
||
it</a>.</p>
|
||
<p>Basically, this adds a middleware that adds the &#8220;rights&#8221; headers to the
|
||
answer, depending on the&nbsp;request.</p>
|
||
<p>While this approach works, it&#8217;s not implementing the specification
|
||
completely. You need to add support for all the resources at&nbsp;once.</p>
|
||
<p>We can think about a nice way to implement this specifying a definition
|
||
of what&#8217;s supposed to be exposed via <span class="caps">CORS</span> and what shouldn&#8217;t directly in
|
||
your settings. In my opinion, <span class="caps">CORS</span> support should be handled at the
|
||
service definition level, except for the list of authorized hosts.
|
||
Otherwise, you don&#8217;t know exactly what&#8217;s going on when you look at the
|
||
definition of the&nbsp;service.</p>
|
||
<h2 id="resources">Resources</h2>
|
||
<p>There are a number of good resources that can be useful to you if you
|
||
want to either understand how <span class="caps">CORS</span> works, or if you want to implement it&nbsp;yourself.</p>
|
||
<ul>
|
||
<li><a href="http://enable-cors.org/">http://enable-cors.org/</a> is useful to get started when you don&#8217;t
|
||
know anything about <span class="caps">CORS</span>.</li>
|
||
<li>There is a <span class="caps">W3C</span> wiki page containing information that may be useful
|
||
about clients, common pitfalls etc:
|
||
<a href="http://www.w3.org/wiki/CORS_Enabled">http://www.w3.org/wiki/CORS_Enabled</a></li>
|
||
<li><em><span class="caps">HTML5</span> rocks</em> has a tutorial explaining how to implement <span class="caps">CORS</span>, with
|
||
<a href="http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server">a nice section about the
|
||
server-side</a>.</li>
|
||
<li>Be sure to have a look at the <a href="http://caniuse.com/#search=cors">clients support-matrix for this
|
||
feature</a>.</li>
|
||
<li>About security, <a href="https://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity">check out this&nbsp;page</a></li>
|
||
<li>If you want to have a look at the implementation code, check <a href="https://github.com/mozilla-services/cornice/pull/98/files">on&nbsp;github</a></li>
|
||
</ul>
|
||
<p>Of course, the <span class="caps">W3C</span> specification is the best resource to rely on. This
|
||
specification isn&#8217;t hard to read, so you may want to go through it.
|
||
Especially the <a href="http://www.w3.org/TR/cors/#resource-processing-model">&#8220;resource processing model&#8221;&nbsp;section</a></p></content><category term="code"></category></entry><entry><title>Status board</title><link href="https://blog.notmyidea.org/status-board.html" rel="alternate"></link><published>2012-12-29T00:00:00+01:00</published><updated>2012-12-29T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2012-12-29:/status-board.html</id><summary type="html">
|
||
<p>À force de démarrer des services web pour un oui et pour un non, de
|
||
proposer à des copains d&#8217;héberger leurs sites, de faire pareil pour
|
||
quelques assos etc, je me suis retrouvé avec, comme dirait l&#8217;autre, <em>une
|
||
bonne platrée</em> de sites et de services à gérer sur …</p></summary><content type="html">
|
||
<p>À force de démarrer des services web pour un oui et pour un non, de
|
||
proposer à des copains d&#8217;héberger leurs sites, de faire pareil pour
|
||
quelques assos etc, je me suis retrouvé avec, comme dirait l&#8217;autre, <em>une
|
||
bonne platrée</em> de sites et de services à gérer sur lolnet.org, mon&nbsp;serveur.</p>
|
||
<p>Jusqu&#8217;à très récemment, rien de tout ça n&#8217;était sauvegardé, et non plus
|
||
monitoré. Après quelques recherches, je suis tombé sur
|
||
<a href="http://www.stashboard.org/">stashboard</a>, un &#8220;status board&#8221; qu&#8217;il est
|
||
bien fait. Le seul problème, c&#8217;est écrit pour se lancer sur <span class="caps">GAE</span>, <em>Google
|
||
App Engine</em>. Heureusement, c&#8217;est open-source, et ça a été forké pour
|
||
donner naissance à
|
||
<a href="https://github.com/bfirsh/whiskerboard">whiskerboard</a> (la planche
|
||
moustachue, pour les non&nbsp;anglophones).</p>
|
||
<p><img alt="Capture d'écran du site." src="/images/status_board.png"></p>
|
||
<h2 id="verifier-le-statut-des-services">Vérifier le statut des&nbsp;services</h2>
|
||
<p>Donc, c&#8217;est chouette, c&#8217;est facile à installer, tout ça, mais… mais ça ne fait en fait pas ce que je veux: ça ne fait que m&#8217;afficher le statut des services, mais ça ne vérifie pas que tout est bien&nbsp;&#8220;up&#8221;.</p>
|
||
<p>Bon, un peu embêtant pour moi, parce que c&#8217;est vraiment ça que je voulais. Pas grave, je sais un peu coder, autant que ça serve. J&#8217;ai ajouté quelques fonctionnalités au soft, qui sont disponibles sur mon fork, sur github:: <a href="https://github.com/almet/whiskerboard">https://github.com/almet/whiskerboard</a>&nbsp;.</p>
|
||
<p>Entres autres, il est désormais possible de lancer
|
||
<a href="http://celeryproject.org/">celery</a> en tache de fond et de vérifier périodiquement que les services sont toujours bien vivants, en utilisant une tache&nbsp;spécifique.</p>
|
||
<p>C&#8217;était un bonheur de développer ça (on a fait ça à deux, avec guillaume, avec un mumble + tmux en pair prog, en une petite soirée, ça&nbsp;dépote).</p>
|
||
<p>Les modifications sont assez simples, vous pouvez aller jeter un œil aux changements ici:
|
||
<a href="https://github.com/almet/whiskerboard/compare/b539337416...master">https://github.com/almet/whiskerboard/compare/b539337416&#8230;master</a></p>
|
||
<p>En&nbsp;gros:</p>
|
||
<ul>
|
||
<li>ajout d&#8217;une connection_string aux services (de la forme&nbsp;protocol://host:port)</li>
|
||
<li>ajout d&#8217;une commande check_status qui s&#8217;occupe d&#8217;itérer sur les
|
||
services et de lancer des taches celery qui vont bien, en fonction
|
||
du&nbsp;protocole</li>
|
||
<li>ajout des taches en&nbsp;question</li>
|
||
</ul>
|
||
<h2 id="deploiement">Déploiement</h2>
|
||
<p>Le plus long a été de le déployer en fin de compte, parce que je ne
|
||
voulais pas déployer mon service de supervision sur mon serveur,&nbsp;forcément.</p>
|
||
<p>Après un essai (plutôt rapide en fait) sur <a href="http://heroku.com">heroku</a>,
|
||
je me suis rendu compte qu&#8217;il me fallait payer pas loin de 35$ par mois
|
||
pour avoir un process celeryd qui tourne, donc j&#8217;ai un peu cherché
|
||
ailleurs, pour finalement déployer la chose chez
|
||
<a href="https://www.alwaysdata.com/">alwaysdata</a></p>
|
||
<p>Après quelques péripéties, j&#8217;ai réussi à faire tourner le tout, ça à été
|
||
un peu la bataille au départ pour installer virtualenv (j&#8217;ai du faire
|
||
des changements dans mon <span class="caps">PATH</span> pour que ça puisse marcher), voici mon&nbsp;`.bash_profile`:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="k">export</span><span class="w"> </span><span class="n">PYTHONPATH</span><span class="o">=~/</span><span class="n">modules</span><span class="o">/</span>
|
||
<span class="k">export</span><span class="w"> </span><span class="n">PATH</span><span class="o">=$</span><span class="n">HOME</span><span class="o">/</span><span class="n">modules</span><span class="o">/</span><span class="n">bin</span><span class="p">:</span><span class="o">$</span><span class="n">HOME</span><span class="o">/</span><span class="n">modules</span><span class="o">/</span><span class="p">:</span><span class="o">$</span><span class="n">PATH</span>
|
||
</code></pre></div>
|
||
|
||
<p>Et après y&#8217;a plus qu&#8217;à installer avec&nbsp;`easy_install`:</p>
|
||
<div class="highlight"><pre><span></span><code>easy_install --install-dir ~/modules -U pip
|
||
easy_install --install-dir ~/modules -U virtualenv
|
||
</code></pre></div>
|
||
|
||
<p>Et à créer le&nbsp;virtualenv:</p>
|
||
<div class="highlight"><pre><span></span><code>virtualenv venv
|
||
venv/bin/pip install -r requirements.txt
|
||
</code></pre></div>
|
||
|
||
<p>Dernière étape, la création d&#8217;un fichier application.wsgi qui s&#8217;occupe
|
||
de rendre l&#8217;application disponible, avec le bon&nbsp;venv:</p>
|
||
<h2 id="ssl-et-requests"><span class="caps">SSL</span> et&nbsp;Requests</h2>
|
||
<p>Quelques tours de manivelle plus loin, j&#8217;ai un celeryd qui tourne et qui
|
||
consomme les taches qui lui sont envoyées (pour des questions de
|
||
simplicité, j&#8217;ai utilisé le backend django de celery, donc pas besoin
|
||
d&#8217;<span class="caps">AMQP</span>, par&nbsp;exemple).</p>
|
||
<p>Problème, les ressources que je vérifie en <span class="caps">SSL</span> (<span class="caps">HTTPS</span>) me jettent. Je
|
||
sais pas exactement pourquoi à l&#8217;heure qu&#8217;il est, mais il semble que
|
||
lorsque je fais une requête avec
|
||
<a href="http://docs.python-requests.org/en/latest/">Requests</a> je me récupère
|
||
des <em>Connection Refused</em>. Peut être une sombre histoire de proxy ? En
|
||
attendant, les appels avec <span class="caps">CURL</span> fonctionnent, donc j&#8217;ai fait <a href="https://github.com/ametaireau/whiskerboard/blob/master/board/tasks.py#L17">un
|
||
fallback vers <span class="caps">CURL</span> lorsque les autres méthodes
|
||
échouent</a>.
|
||
Pas super propre, mais ça&nbsp;fonctionne.</p>
|
||
<p><strong><span class="caps">EDIT</span></strong> Finalement, il se trouve que mon serveur était mal configuré.
|
||
J&#8217;utilisais haproxy + stunnel, et la négiciation <span class="caps">SSL</span> se passait mal. Une
|
||
fois <span class="caps">SSL</span> et <span class="caps">TLS</span> activés, et SSLv2 désactivé, tout fonctionne&nbsp;mieux.</p>
|
||
<h2 id="et-voila">Et&nbsp;voilà</h2>
|
||
<p>Finalement, j&#8217;ai mon joli status-board qui tourne à merveille sur
|
||
<a href="http://status.lolnet.org">http://status.lolnet.org</a>&nbsp;:-)</p></content><category term="code"></category></entry><entry><title>Astuces SSH</title><link href="https://blog.notmyidea.org/astuces-ssh.html" rel="alternate"></link><published>2012-12-27T00:00:00+01:00</published><updated>2012-12-27T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2012-12-27:/astuces-ssh.html</id><summary type="html">
|
||
<h2 id="tunelling">Tunelling</h2>
|
||
<p>Parce que je m&#8217;en rapelle jamais (tête de&nbsp;linote):</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>ssh<span class="w"> </span>-f<span class="w"> </span>hote<span class="w"> </span>-L<span class="w"> </span>local:lolnet.org:destination<span class="w"> </span>-N
|
||
</code></pre></div>
|
||
|
||
<h2 id="sshconfig">.ssh/config</h2>
|
||
<p>(merci <a href="http://majerti.fr">gaston</a>&nbsp;!)</p>
|
||
<p>La directive suivante dans .ssh/config permet de sauter d&#8217;hôte en hôte
|
||
séparés par des &#8220;+&#8221;&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>Host <span class="gs">*+*</span>
|
||
ProxyCommand ssh $(echo %h | sed
|
||
&#39;s/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/\2 -l …</code></pre></div></summary><content type="html">
|
||
<h2 id="tunelling">Tunelling</h2>
|
||
<p>Parce que je m&#8217;en rapelle jamais (tête de&nbsp;linote):</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>ssh<span class="w"> </span>-f<span class="w"> </span>hote<span class="w"> </span>-L<span class="w"> </span>local:lolnet.org:destination<span class="w"> </span>-N
|
||
</code></pre></div>
|
||
|
||
<h2 id="sshconfig">.ssh/config</h2>
|
||
<p>(merci <a href="http://majerti.fr">gaston</a>&nbsp;!)</p>
|
||
<p>La directive suivante dans .ssh/config permet de sauter d&#8217;hôte en hôte
|
||
séparés par des &#8220;+&#8221;&nbsp;:</p>
|
||
<div class="highlight"><pre><span></span><code>Host <span class="gs">*+*</span>
|
||
ProxyCommand ssh $(echo %h | sed
|
||
&#39;s/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/\2 -l \1/;s/:/ -p /&#39;)
|
||
PATH=.:\$PATH nc -w1 $(echo %h | sed &#39;s/^.*+//;/:/!s/$/ %p/;s/:/ /&#39;)
|
||
</code></pre></div>
|
||
|
||
<p>On peut donc spécifier des &#8220;sauts&#8221; ssh du&nbsp;style:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">ssh</span><span class="w"> </span><span class="n">root</span><span class="mf">@91.25.25.25</span><span class="o">+</span><span class="mf">192.168.1.1</span>
|
||
</code></pre></div>
|
||
|
||
<p>Ensuite on peut essayer de&nbsp;rajouter:</p>
|
||
<div class="highlight"><pre><span></span><code>Host &lt;label_pour_mon_serveur_privé&gt;
|
||
user &lt;monuser(root)&gt;
|
||
IdentityFile &lt;chemin vers ma clé ssh pour le serveur publique&gt;
|
||
hostname ip_serveur_publique+ip_serveur_privé
|
||
</code></pre></div></content><category term="code"></category></entry><entry><title>Gnome 3, extensions</title><link href="https://blog.notmyidea.org/gnome-3-extensions.html" rel="alternate"></link><published>2012-12-27T00:00:00+01:00</published><updated>2012-12-27T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2012-12-27:/gnome-3-extensions.html</id><summary type="html">
|
||
<p>Après avoir tenté pendant un bout de temps unity, le bureau par defaut
|
||
de ubuntu, j&#8217;ai eu envie de changements, et j&#8217;ai donc essayé un peu de
|
||
regarder du coté de gnome 3, à&nbsp;nouveau.</p>
|
||
<p>Et finalement, j&#8217;ai trouvé quelques extensions qui sont vraiment utiles,
|
||
que je …</p></summary><content type="html">
|
||
<p>Après avoir tenté pendant un bout de temps unity, le bureau par defaut
|
||
de ubuntu, j&#8217;ai eu envie de changements, et j&#8217;ai donc essayé un peu de
|
||
regarder du coté de gnome 3, à&nbsp;nouveau.</p>
|
||
<p>Et finalement, j&#8217;ai trouvé quelques extensions qui sont vraiment utiles,
|
||
que je liste&nbsp;ici.</p>
|
||
<ul>
|
||
<li><a href="https://extensions.gnome.org/extension/547/antisocial-menu/">Antisocial
|
||
Menu</a>
|
||
vire les boutons et textes en rapport avec le web social. J&#8217;en avais
|
||
pas besoin puisque je suis connecté à mon instant messenger dans un
|
||
terminal, en utilisant&nbsp;weechat.</li>
|
||
<li><a href="https://extensions.gnome.org/extension/97/coverflow-alt-tab/">Coverflow
|
||
Alt-Tab</a>
|
||
change le switcher d&#8217;applications par defaut. Je le trouve bien plus
|
||
pratique que celui par defaut puisqu&#8217;il me permet de voir &#8220;en grand&#8221;
|
||
quelle est la fenêtre que je vais&nbsp;afficher.</li>
|
||
<li><a href="https://extensions.gnome.org/extension/55/media-player-indicator/">Media player
|
||
indicator</a>
|
||
me permet de voir en temps réel ce qui se passe dans mon lecteur
|
||
audio. Ça semble ne pas être grand chose, mais ça me manquait. Ça
|
||
s&#8217;intègre niquel avec Spotify, et ça c&#8217;est&nbsp;chouette.</li>
|
||
<li><a href="https://extensions.gnome.org/extension/149/search-firefox-bookmarks-provider/">Rechercher dans les bookmarks
|
||
firefox</a>
|
||
permet de… à votre avis&nbsp;?</li>
|
||
</ul>
|
||
<p>Un peu moins utile mais sait on&nbsp;jamais:</p>
|
||
<ul>
|
||
<li>“<a href="https://extensions.gnome.org/extension/130/advanced-settings-in-usermenu/">Advanced Settings in
|
||
UserMenu</a>”
|
||
permet d&#8217;avoir un raccourci vers les paramètres avancés dans le menu
|
||
utilisateur (en haut à&nbsp;droite)</li>
|
||
<li>Une <a href="https://extensions.gnome.org/extension/409/gtg-integration/">intégration à Getting things
|
||
Gnome</a>
|
||
(un truc de <span class="caps">GTD</span>). Je suis en train d&#8217;expérimenter avec cet outil,
|
||
donc je ne sais pas encore si ça va rester, mais pourquoi&nbsp;pas.</li>
|
||
</ul>
|
||
<p>Vous pouvez aller faire un tour sur <a href="https://extensions.gnome.org/">https://extensions.gnome.org/</a> pour
|
||
en trouver d&#8217;autres à votre&nbsp;gout.</p></content><category term="code"></category></entry><entry><title>Cheese & code - Wrap-up</title><link href="https://blog.notmyidea.org/cheese-code-wrap-up.html" rel="alternate"></link><published>2012-10-22T00:00:00+02:00</published><updated>2012-10-22T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2012-10-22:/cheese-code-wrap-up.html</id><summary type="html">
|
||
<p>This week-end I hosted a <em>cheese <span class="amp">&amp;</span> code</em> session in the country-side of
|
||
Angers,&nbsp;France.</p>
|
||
<p>We were a bunch of python hackers and it rained a lot, wich forced us to
|
||
stay inside and to code.&nbsp;Bad.</p>
|
||
<p>We were not enough to get rid of all the cheese and the awesome …</p></summary><content type="html">
|
||
<p>This week-end I hosted a <em>cheese <span class="amp">&amp;</span> code</em> session in the country-side of
|
||
Angers,&nbsp;France.</p>
|
||
<p>We were a bunch of python hackers and it rained a lot, wich forced us to
|
||
stay inside and to code.&nbsp;Bad.</p>
|
||
<p>We were not enough to get rid of all the cheese and the awesome meals,
|
||
but well, we finally managed it pretty&nbsp;well.</p>
|
||
<p>Here is a summary of what we worked&nbsp;on:</p>
|
||
<h2 id="daybed">Daybed</h2>
|
||
<p>Daybed started some time ago, and intend to be a replacement to google
|
||
forms, in term of features, but backed as a <span class="caps">REST</span> web service, in python,
|
||
and open&nbsp;source.</p>
|
||
<p>In case you wonder, daybed is effectively the name of a couch. We chose
|
||
this name because of the similarities (in the sound) with <strong>db</strong>, and
|
||
because we&#8217;re using <strong>CouchDB</strong> as a&nbsp;backend.</p>
|
||
<p><img alt="Daybed is a big couch!" src="/images/daybed.jpg"></p>
|
||
<p>We mainly hacked on daybed and are pretty close to the release of the
|
||
first version, meaning that we have something&nbsp;working.</p>
|
||
<p><a href="http://github.com/spiral-project/daybed">The code</a> is available on
|
||
github, and we also wrote <a href="http://daybed.rtfd.org">a small
|
||
documentation</a> for&nbsp;it.</p>
|
||
<p>Mainly, we did a lot of cleanup, rewrote a bunch of tests so that it
|
||
would be easier to continue to work on the project, and implemented some
|
||
minor features. I&#8217;m pretty confidend that we now have really good basis
|
||
for this&nbsp;project.</p>
|
||
<p>Also, we will have a nice todolist application, with the backend <strong>and</strong>
|
||
the frontend, in javascript / html / css, you&#8217;ll know more when it&#8217;ll be
|
||
ready&nbsp;:-)</p>
|
||
<p>Once we have something good enough, we&#8217;ll release the first version and
|
||
I&#8217;ll host it somewhere so that people can play with&nbsp;it.</p>
|
||
<h2 id="cornice">Cornice</h2>
|
||
<p>Daybed is built on top of <a href="http://cornice.rtfd.org">Cornice</a>, a
|
||
framework to ease the creation of&nbsp;web-services.</p>
|
||
<p>At Pycon France, we had the opportunity to attend a good presentation
|
||
about <a href="https://github.com/SPORE/specifications"><span class="caps">SPORE</span></a>. <span class="caps">SPORE</span> is a way
|
||
to describe your <span class="caps">REST</span> web services, as <span class="caps">WSDL</span> is for <span class="caps">WS</span>-* services. This
|
||
allows to ease the creation of generic <span class="caps">SPORE</span> clients, which are able to
|
||
consume any <span class="caps">REST</span> <span class="caps">API</span> with a <span class="caps">SPORE</span>&nbsp;endpoint.</p>
|
||
<p>Here is how you can let cornice describe your web service for&nbsp;you</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">cornice.ext.spore</span> <span class="kn">import</span> <span class="n">generate_spore_description</span>
|
||
<span class="kn">from</span> <span class="nn">cornice.service</span> <span class="kn">import</span> <span class="n">Service</span><span class="p">,</span> <span class="n">get_services</span>
|
||
|
||
<span class="n">spore</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="s1">&#39;spore&#39;</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s1">&#39;/spore&#39;</span><span class="p">,</span> <span class="n">renderer</span><span class="o">=</span><span class="s1">&#39;jsonp&#39;</span><span class="p">)</span>
|
||
<span class="nd">@spore</span><span class="o">.</span><span class="n">get</span>
|
||
<span class="k">def</span> <span class="nf">get_spore</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="n">services</span> <span class="o">=</span> <span class="n">get_services</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">generate_spore_description</span><span class="p">(</span><span class="n">services</span><span class="p">,</span> <span class="s1">&#39;Service name&#39;</span><span class="p">,</span>
|
||
<span class="n">request</span><span class="o">.</span><span class="n">application_url</span><span class="p">,</span> <span class="s1">&#39;1.0&#39;</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<p>And you&#8217;ll get a definition of your service, in <span class="caps">SPORE</span>, available at&nbsp;/spore.</p>
|
||
<p>Of course, you can use it to do other things, like generating the file
|
||
locally and exporting it wherever it makes sense to you,&nbsp;etc.</p>
|
||
<p>I released today <a href="http://crate.io/packages/cornice/">Cornice 0.11</a>,
|
||
which adds into other things the support for <span class="caps">SPORE</span>, plus some other
|
||
fixes we found on our&nbsp;way.</p>
|
||
<h2 id="respire">Respire</h2>
|
||
<p>Once you have the description of the service, you can do generic clients
|
||
consuming&nbsp;them!</p>
|
||
<p>We first wanted to contribute to <a href="https://github.com/bl0b/spyre">spyre</a>
|
||
but it was written in a way that wasn&#8217;t supporting to <span class="caps">POST</span> data, and
|
||
they were using their own stack to handle <span class="caps">HTTP</span>. A lot of code that
|
||
already exists in other&nbsp;libraries.</p>
|
||
<p>While waiting the train with <a href="http://natim.ionyse.com/">Rémy</a>, we hacked
|
||
something together, named &#8220;Respire&#8221;, a thin layer on top of the awesome
|
||
<a href="http://python-requests.org">Requests</a>&nbsp;library.</p>
|
||
<p>We have a first version, feel free to have a look at it and provide
|
||
enhancements if you feel like it. We&#8217;re still hacking on it so it may
|
||
break (for the better), but that had been working pretty well for us so&nbsp;far.</p>
|
||
<p>You can <a href="http://github.com/spiral-project/respire">find the project on
|
||
github</a>, but here is how to
|
||
use it, really quickly (these examples are how to interact with&nbsp;daybed)</p>
|
||
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">respire</span> <span class="kn">import</span> <span class="n">client_from_url</span>
|
||
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="c1"># create the client from the SPORE definition</span>
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="n">cl</span> <span class="o">=</span> <span class="n">client_from_url</span><span class="p">(</span><span class="s1">&#39;http://localhost:8000/spore&#39;</span><span class="p">)</span>
|
||
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="c1"># in daybed, create a new definition</span>
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="n">todo_def</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="o">...</span> <span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="s2">&quot;todo&quot;</span><span class="p">,</span>
|
||
<span class="o">...</span> <span class="s2">&quot;description&quot;</span><span class="p">:</span> <span class="s2">&quot;A list of my stuff to do&quot;</span><span class="p">,</span>
|
||
<span class="o">...</span> <span class="s2">&quot;fields&quot;</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="o">...</span> <span class="p">{</span>
|
||
<span class="o">...</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;item&quot;</span><span class="p">,</span>
|
||
<span class="o">...</span> <span class="s2">&quot;type&quot;</span><span class="p">:</span> <span class="s2">&quot;string&quot;</span><span class="p">,</span>
|
||
<span class="o">...</span> <span class="s2">&quot;description&quot;</span><span class="p">:</span> <span class="s2">&quot;The item&quot;</span>
|
||
<span class="o">...</span> <span class="p">},</span>
|
||
<span class="o">...</span> <span class="p">{</span>
|
||
<span class="o">...</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;status&quot;</span><span class="p">,</span>
|
||
<span class="o">...</span> <span class="s2">&quot;type&quot;</span><span class="p">:</span> <span class="s2">&quot;enum&quot;</span><span class="p">,</span>
|
||
<span class="o">...</span> <span class="s2">&quot;choices&quot;</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="o">...</span> <span class="s2">&quot;done&quot;</span><span class="p">,</span>
|
||
<span class="o">...</span> <span class="s2">&quot;todo&quot;</span>
|
||
<span class="o">...</span> <span class="p">],</span>
|
||
<span class="o">...</span> <span class="s2">&quot;description&quot;</span><span class="p">:</span> <span class="s2">&quot;is it done or not&quot;</span>
|
||
<span class="o">...</span> <span class="p">}</span>
|
||
<span class="o">...</span> <span class="p">]}</span>
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="n">cl</span><span class="o">.</span><span class="n">put_definition</span><span class="p">(</span><span class="n">model_name</span><span class="o">=</span><span class="s1">&#39;todo&#39;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">todo_def</span><span class="p">)</span>
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="n">cl</span><span class="o">.</span><span class="n">post_data</span><span class="p">(</span><span class="n">model_name</span><span class="o">=</span><span class="s1">&#39;todo&#39;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">item</span><span class="o">=</span><span class="s1">&#39;make it work&#39;</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="s1">&#39;todo&#39;</span><span class="p">))</span>
|
||
<span class="p">{</span><span class="sa">u</span><span class="s1">&#39;id&#39;</span><span class="p">:</span> <span class="sa">u</span><span class="s1">&#39;9f2c90c0529a442cfdc03c191b022cf7&#39;</span><span class="p">}</span>
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="n">cl</span><span class="o">.</span><span class="n">get_data</span><span class="p">(</span><span class="n">model_name</span><span class="o">=</span><span class="s1">&#39;todo&#39;</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<p>Finally, we were out of cheese so everyone headed back to their
|
||
respective houses and&nbsp;cities.</p>
|
||
<p>Until next&nbsp;time?</p></content><category term="code"></category></entry><entry><title>Circus sprint at PyconFR</title><link href="https://blog.notmyidea.org/circus-sprint-at-pyconfr.html" rel="alternate"></link><published>2012-09-17T00:00:00+02:00</published><updated>2012-09-17T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2012-09-17:/circus-sprint-at-pyconfr.html</id><summary type="html">
|
||
<p>Last Thursday to Sunday, <a href="http://pycon.fr">Pycon France</a> took place, in
|
||
Paris. It was the opportunity to meet a lot of people and to talk about
|
||
python awesomness in&nbsp;general.</p>
|
||
<p>We had three tracks this year, plus sprints the two first days. We
|
||
sprinted on <a href="http://circus.io">Circus</a>, the process and socket manager
|
||
we …</p></summary><content type="html">
|
||
<p>Last Thursday to Sunday, <a href="http://pycon.fr">Pycon France</a> took place, in
|
||
Paris. It was the opportunity to meet a lot of people and to talk about
|
||
python awesomness in&nbsp;general.</p>
|
||
<p>We had three tracks this year, plus sprints the two first days. We
|
||
sprinted on <a href="http://circus.io">Circus</a>, the process and socket manager
|
||
we&#8217;re using at Mozilla for some of our&nbsp;setups.</p>
|
||
<p>The project gathered some interest, and we ended up with 5 persons
|
||
working on it. Of course, we spent some time explaining what is Circus,
|
||
how it had been built, a lot of time talking about use-cases and
|
||
possible improvements, but we also managed to add new&nbsp;features.</p>
|
||
<p>Having people wanting to sprint on our projects is exciting because
|
||
that&#8217;s when making things in the open unleashes its full potential. You
|
||
can&#8217;t imagine how happy I was to have some friends come and work on this
|
||
with us&nbsp;:)</p>
|
||
<p>Here is a wrap-up of the&nbsp;sprint:</p>
|
||
<h2 id="autocompletion-on-the-command-line">Autocompletion on the&nbsp;command-line</h2>
|
||
<p><a href="http://natim.ionyse.com">Remy Hubscher</a> worked on the command-line
|
||
autocompletion. Now we have a fancy command-line interface which is able
|
||
to aucomplete if you&#8217;re using bash. It seems that not that much work is
|
||
needed to make it happen on zsh as well&nbsp;:)</p>
|
||
<p><a href="https://github.com/mozilla-services/circus/blob/master/extras/circusctl_bash_completion">Have a look at the&nbsp;feature</a></p>
|
||
<p>On the same topic, we now have a cool shell for Circus. If you start the
|
||
circusctl command without any option, you&#8217;ll end-up with a cool shell.
|
||
Thanks <a href="https://github.com/jojax">Jonathan Dorival</a> for the work on
|
||
this! You can have a look at <a href="https://github.com/mozilla-services/circus/pull/268">the pull
|
||
request</a>.</p>
|
||
<h2 id="future-changes-to-the-web-ui">Future changes to the web&nbsp;ui</h2>
|
||
<p><a href="https://twitter.com/rachbelaid">Rachid Belaid</a> had a deep look at the
|
||
source code and is much more familiarized to it now than before. We
|
||
discussed the possibility to change the implementation of the web ui,
|
||
and I&#8217;m glad of this. Currently, it&#8217;s done with bottle.py and we want to
|
||
switch to&nbsp;pyramid.</p>
|
||
<p>He fixed some issues that were in the tracker, so we now can have the
|
||
age of watchers in the webui, for&nbsp;instance.</p>
|
||
<h2 id="bug-and-doc-fixing">Bug and doc&nbsp;fixing</h2>
|
||
<p>While reading the source code, we found some inconsistencies and fixed
|
||
them, with <a href="http://mathieu.agopian.info/">Mathieu Agopian</a>. We also
|
||
tried to improve the documentation at different&nbsp;levels.</p>
|
||
<p>Documentation still needs a lot of love, and I&#8217;m planning to spend some
|
||
time on this shortly. I&#8217;ve gathered a bunch of feedback on&nbsp;this</p>
|
||
<h2 id="circus-clustering-capabilities">Circus clustering&nbsp;capabilities</h2>
|
||
<p>One feature I wanted to work on during this sprint was the clustering
|
||
abilities of Circus. Nick Pellegrino made an internship on this topic at
|
||
Mozilla so we spent some time to review his pull&nbsp;requests.</p>
|
||
<p>A lot of code was written for this so we discussed a bunch of things
|
||
regarding all of this. It took us more time than expected (and I still
|
||
need to spend more time on this to provide appropriate feedback), but it
|
||
allowed us to have a starting-point about what this clustering thing
|
||
could&nbsp;be.</p>
|
||
<p>Remy wrote <a href="http://tech.novapost.fr/circus-clustering-management-en.html">a good summary about our
|
||
brainstorming</a>
|
||
so I&#8217;ll not do it again here, but feel free to contact us if you have
|
||
ideas on this, they&#8217;re very&nbsp;welcome!</p>
|
||
<h2 id="project-management">Project&nbsp;management</h2>
|
||
<p>We&#8217;ve had some inquiries telling us that&#8217;s not as easy as it should to
|
||
get started with the Circus project. Some of the reasons are that we
|
||
don&#8217;t have any release schedule, and that the documentation is hairy
|
||
enough to lost people, at some point&nbsp;:)</p>
|
||
<p>That&#8217;s something we&#8217;ll try to fix soon&nbsp;:)</p>
|
||
<p>PyconFR was a very enjoyable event. I&#8217;m looking forward to meet the
|
||
community again and discuss how Circus can evolve in ways that are
|
||
interesting to&nbsp;everyone.</p>
|
||
<p>Tarek and me are going to <a href="http://python.ie/pycon/2012/">Pycon ireland</a>,
|
||
feel free to reach us if you&#8217;re going there, we&#8217;ll be happy to meet and
|
||
enjoy&nbsp;beers!</p></content><category term="code"></category></entry><entry><title>Refactoring Cornice</title><link href="https://blog.notmyidea.org/refactoring-cornice.html" rel="alternate"></link><published>2012-05-01T00:00:00+02:00</published><updated>2012-05-01T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2012-05-01:/refactoring-cornice.html</id><summary type="html">
|
||
<p>After working for a while with <a href="http://cornice.readthedocs.com">Cornice</a>
|
||
to define our APIs at <a href="http://docs.services.mozilla.com">Services</a>, it
|
||
turned out that the current implementation wasn&#8217;t flexible enough to
|
||
allow us to do what we wanted to&nbsp;do.</p>
|
||
<p>Cornice started as a toolkit on top of the
|
||
<a href="http://docs.pylonsproject.org/en/latest/docs/pyramid.html">pyramid</a>
|
||
routing system, allowing to register services …</p></summary><content type="html">
|
||
<p>After working for a while with <a href="http://cornice.readthedocs.com">Cornice</a>
|
||
to define our APIs at <a href="http://docs.services.mozilla.com">Services</a>, it
|
||
turned out that the current implementation wasn&#8217;t flexible enough to
|
||
allow us to do what we wanted to&nbsp;do.</p>
|
||
<p>Cornice started as a toolkit on top of the
|
||
<a href="http://docs.pylonsproject.org/en/latest/docs/pyramid.html">pyramid</a>
|
||
routing system, allowing to register services in a simpler way. Then we
|
||
added some niceties such as the ability to automatically generate the
|
||
services documentation or returning the correct <span class="caps">HTTP</span> headers <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">as defined
|
||
by the <span class="caps">HTTP</span>
|
||
specification</a>
|
||
without the need from the developer to deal with them nor to know&nbsp;them.</p>
|
||
<p>If you&#8217;re not familiar with Cornice, here is how you define a simple
|
||
service with&nbsp;it:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">cornice.service</span> <span class="kn">import</span> <span class="n">Service</span>
|
||
<span class="n">bar</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">path</span><span class="o">=</span><span class="s2">&quot;/bar&quot;</span><span class="p">)</span>
|
||
|
||
<span class="nd">@bar</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">validators</span><span class="o">=</span><span class="n">validators</span><span class="p">,</span> <span class="n">accept</span><span class="o">=</span><span class="s1">&#39;application/json&#39;</span><span class="p">)</span>
|
||
<span class="k">def</span> <span class="nf">get_drink</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="c1"># do something with the request (with moderation).</span>
|
||
</code></pre></div>
|
||
|
||
<p>This external <span class="caps">API</span> is quite cool, as it allows to do a bunch of things
|
||
quite easily. For instance, we&#8217;ve written our
|
||
<a href="https://github.com/mozilla-services/tokenserver">token-server</a> code on
|
||
top of this in a&nbsp;blast.</p>
|
||
<h2 id="the-burden">The&nbsp;burden</h2>
|
||
<p>The problem with this was that we were mixing internally the service
|
||
description logic with the route registration one. The way we were doing
|
||
this was via an extensive use of decorators&nbsp;internally.</p>
|
||
<p>The <span class="caps">API</span> of the cornice.service.Service class was as following
|
||
(simplified so you can get the gist of&nbsp;it).</p>
|
||
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Service</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">service_kwargs</span><span class="p">):</span>
|
||
<span class="c1"># some information, such as the colander schemas (for validation),</span>
|
||
<span class="c1"># the defined methods that had been registered for this service and</span>
|
||
<span class="c1"># some other things were registered as instance variables.</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">schemas</span> <span class="o">=</span> <span class="n">service_kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">schema</span><span class="s1">&#39;, None)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">defined_methods</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">definitions</span> <span class="o">=</span> <span class="p">[]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">api</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">view_kwargs</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">&quot;&quot;&quot;This method is a decorator that is being used by some alias</span>
|
||
<span class="sd"> methods.</span>
|
||
<span class="sd"> &quot;&quot;&quot;</span>
|
||
<span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">view</span><span class="p">):</span>
|
||
<span class="c1"># all the logic goes here. And when I mean all the logic, I</span>
|
||
<span class="c1"># mean it.</span>
|
||
<span class="c1"># 1. we are registering a callback to the pyramid routing</span>
|
||
<span class="c1"># system so it gets called whenever the module using the</span>
|
||
<span class="c1"># decorator is used.</span>
|
||
<span class="c1"># 2. we are transforming the passed arguments so they conform</span>
|
||
<span class="c1"># to what is expected by the pyramid routing system.</span>
|
||
<span class="c1"># 3. We are storing some of the passed arguments into the</span>
|
||
<span class="c1"># object so we can retrieve them later on.</span>
|
||
<span class="c1"># 4. Also, we are transforming the passed view before</span>
|
||
<span class="c1"># registering it in the pyramid routing system so that it</span>
|
||
<span class="c1"># can do what Cornice wants it to do (checking some rules,</span>
|
||
<span class="c1"># applying validators and filters etc.</span>
|
||
<span class="k">return</span> <span class="n">wrapper</span>
|
||
|
||
<span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A shortcut of the api decorator&quot;&quot;&quot;</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">api</span><span class="p">(</span><span class="n">request_method</span><span class="o">=</span><span class="s2">&quot;GET&quot;</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<p>I encourage you to go read <a href="https://github.com/mozilla-services/cornice/blob/4e0392a2ae137b6a11690459bcafd7325e86fa9e/cornice/service.py#L44">the entire
|
||
file</a>.
|
||
on github so you can get a better opinion on how all of this was&nbsp;done.</p>
|
||
<p>A bunch of things are&nbsp;wrong:</p>
|
||
<ul>
|
||
<li>first, we are not separating the description logic from the
|
||
registration one. This causes problems when we need to access the
|
||
parameters passed to the service, because the parameters you get are
|
||
not exactly the ones you passed but the ones that the pyramid
|
||
routing system is expecting. For instance, if you want to get the
|
||
view get_drink, you will instead get a decorator which contains
|
||
this&nbsp;view.</li>
|
||
<li>second, we are using decorators as APIs we expose. Even if
|
||
decorators are good as shortcuts, they shouldn&#8217;t be the default way
|
||
to deal with an <span class="caps">API</span>. A good example of this is <a href="https://github.com/mozilla-services/cornice/blob/4e0392a2ae137b6a11690459bcafd7325e86fa9e/cornice/resource.py#L56">how the resource
|
||
module consumes this
|
||
<span class="caps">API</span></a>.
|
||
This is quite hard to&nbsp;follow.</li>
|
||
<li>Third, in the api method, a bunch of things are done regarding
|
||
inheritance of parameters that are passed to the service or to its
|
||
decorator methods. This leaves you with a really hard to follow path
|
||
when it comes to add new parameters to your <span class="caps">API</span>.</li>
|
||
</ul>
|
||
<h2 id="how-do-we-improve-this">How do we improve&nbsp;this?</h2>
|
||
<p>Python is great because it allows you to refactor things in an easy way.
|
||
What I did isn&#8217;t breaking our APIs, but make things way simpler to
|
||
hack-on. One example is that it allowed me to add features that we
|
||
wanted to bring to Cornice really quickly (a matter of minutes), without
|
||
touching the <span class="caps">API</span> that&nbsp;much.</p>
|
||
<p>Here is the gist of the new&nbsp;architecture:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Service</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
||
<span class="c1"># we define class-level variables that will be the default values for</span>
|
||
<span class="c1"># this service. This makes things more extensible than it was before.</span>
|
||
<span class="n">renderer</span> <span class="o">=</span> <span class="s1">&#39;simplejson&#39;</span>
|
||
<span class="n">default_validators</span> <span class="o">=</span> <span class="n">DEFAULT_VALIDATORS</span>
|
||
<span class="n">default_filters</span> <span class="o">=</span> <span class="n">DEFAULT_FILTERS</span>
|
||
|
||
<span class="c1"># we also have some class-level parameters that are useful to know</span>
|
||
<span class="c1"># which parameters are supposed to be lists (and so converted as such)</span>
|
||
<span class="c1"># or which are mandatory.</span>
|
||
<span class="n">mandatory_arguments</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;renderer&#39;</span><span class="p">,)</span>
|
||
<span class="n">list_arguments</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;validators&#39;</span><span class="p">,</span> <span class="s1">&#39;filters&#39;</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
|
||
<span class="c1"># setup name, path and description as instance variables</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">path</span> <span class="o">=</span> <span class="n">path</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">description</span> <span class="o">=</span> <span class="n">description</span>
|
||
|
||
<span class="c1"># convert the arguments passed to something we want to store</span>
|
||
<span class="c1"># and then store them as attributes of the instance (because they</span>
|
||
<span class="c1"># were passed to the constructor</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">arguments</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_arguments</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span>
|
||
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">arguments</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
|
||
|
||
<span class="c1"># we keep having the defined_methods tuple and the list of</span>
|
||
<span class="c1"># definitions that are done for this service</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">defined_methods</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">definitions</span> <span class="o">=</span> <span class="p">[]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">get_arguments</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">conf</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Returns a dict of arguments. It does all the conversions for</span>
|
||
<span class="sd"> you, and uses the information that were defined at the instance</span>
|
||
<span class="sd"> level as fallbacks.</span>
|
||
<span class="sd"> &quot;&quot;&quot;</span>
|
||
|
||
<span class="k">def</span> <span class="nf">add_view</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="n">view</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Add a view to this service.&quot;&quot;&quot;</span>
|
||
<span class="c1"># this is really simple and looks a lot like this</span>
|
||
<span class="n">method</span> <span class="o">=</span> <span class="n">method</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">definitions</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">method</span><span class="p">,</span> <span class="n">view</span><span class="p">,</span> <span class="n">args</span><span class="p">))</span>
|
||
<span class="k">if</span> <span class="n">method</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">defined_methods</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">defined_methods</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">method</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">&quot;&quot;&quot;This is only another interface to the add_view method, exposing a</span>
|
||
<span class="sd"> decorator interface&quot;&quot;&quot;</span>
|
||
<span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">view</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">add_view</span><span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="n">view</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">view</span>
|
||
<span class="k">return</span> <span class="n">wrapper</span>
|
||
</code></pre></div>
|
||
|
||
<p>So, the service is now only storing the information that&#8217;s passed to it
|
||
and nothing more. No more route registration logic goes here. Instead, I
|
||
added this as another feature, even in a different module. The function
|
||
is named register_service_views and has the following&nbsp;signature:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">register_service_views</span><span class="p">(</span><span class="n">config</span><span class="p">,</span> <span class="n">service</span><span class="p">)</span>
|
||
</code></pre></div>
|
||
|
||
<p>To sum up, here are the changes I&nbsp;made:</p>
|
||
<ol>
|
||
<li>Service description is now separated from the route&nbsp;registration.</li>
|
||
<li>cornice.service.Service now provides a hook_view method, which is
|
||
not a decorator. decorators are still present but they are optional
|
||
(you don&#8217;t need to use them if you don&#8217;t want&nbsp;to).</li>
|
||
<li>Everything has been decoupled as much as possible, meaning that you
|
||
really can use the Service class as a container of information about
|
||
the services you are describing. This is especially useful when
|
||
generating&nbsp;documentation.</li>
|
||
</ol>
|
||
<p>As a result, it is now possible to use Cornice with other frameworks. It
|
||
means that you can stick with the service description but plug any other
|
||
framework on top of it. cornice.services.Service is now only a
|
||
description tool. To register routes, one would need to read the
|
||
information contained into this service and inject the right parameters
|
||
into their preferred routing&nbsp;system.</p>
|
||
<p>However, no integration with other frameworks is done at the moment even
|
||
if the design allows&nbsp;it.</p>
|
||
<p>The same way, the sphinx description layer is now only a consumer of
|
||
this service description tool: it looks at what&#8217;s described and build-up
|
||
the documentation from&nbsp;it.</p>
|
||
<p>The resulting branch is not merged yet. Still, you can <a href="https://github.com/mozilla-services/cornice/tree/refactor-the-world">have a look at
|
||
it</a>.</p>
|
||
<p>Any suggestions are of course welcome&nbsp;:-)</p></content><category term="code"></category></entry><entry><title>Djangocong 2012</title><link href="https://blog.notmyidea.org/djangocong-2012.html" rel="alternate"></link><published>2012-04-16T00:00:00+02:00</published><updated>2012-04-16T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2012-04-16:/djangocong-2012.html</id><summary type="html">
|
||
<p>Ce week-end, c&#8217;était <a href="http://rencontres.django-fr.org">djangocong</a>, une
|
||
conférence autour de <a href="http://djangoproject.org">django</a>, de
|
||
<a href="http://python.org">python</a> et du web, qui avait lieu dans le sud, à
|
||
Carnon-plage, à quelques kilomètres de Montpellier la&nbsp;belle.</p>
|
||
<p>J&#8217;ai vraiment apprécié les trois jours passés avec cette bande de geeks.
|
||
Je m&#8217;attendais à des <em>nerds</em>, j …</p></summary><content type="html">
|
||
<p>Ce week-end, c&#8217;était <a href="http://rencontres.django-fr.org">djangocong</a>, une
|
||
conférence autour de <a href="http://djangoproject.org">django</a>, de
|
||
<a href="http://python.org">python</a> et du web, qui avait lieu dans le sud, à
|
||
Carnon-plage, à quelques kilomètres de Montpellier la&nbsp;belle.</p>
|
||
<p>J&#8217;ai vraiment apprécié les trois jours passés avec cette bande de geeks.
|
||
Je m&#8217;attendais à des <em>nerds</em>, j&#8217;y ai trouvé une qualité d&#8217;écoute, des
|
||
personnes qui partagent des valeurs qui leur sont chères, mais qui ne
|
||
limitent pas leurs discussions à du technique. Eeeh ouais, encore un
|
||
préjugé qui tombe, tiens&nbsp;:)</p>
|
||
<p>En tant que <em>hackers</em>, on a le moyen de créer des outils qui sont utiles
|
||
à tous, et qui peuvent être utiles pour favoriser la collaboration et la
|
||
mise en commun des données. J&#8217;ai eu l&#8217;occasion de discuter de projets
|
||
tournant autour de l&#8217;entraide, que ça soit pour mettre en lien des
|
||
associations d&#8217;économie sociale et solidaire (<span class="caps">ESS</span>) ou simplement pour
|
||
que les populations <em>non tech</em> <a href="http://blog.notmyidea.org/quels-usages-pour-linformatique-fr.html">puissent utiliser toute la puissance de
|
||
l&#8217;outil qu&#8217;est le
|
||
web</a>.</p>
|
||
<p>Au niveau du format des conférences, je ne savais pas trop à quoi
|
||
m&#8217;attendre, au vu des échos de l&#8217;an dernier, mais c&#8217;était adapté: des
|
||
mini-confs de 12mn le samedi matin + début d&#8217;aprem, en mode no-wifi pour
|
||
récupérer une qualité d&#8217;écoute. Et contrairement à mes attentes, ce
|
||
n&#8217;est pas trop court. Pas mal de retours d&#8217;expérience pour le coup, et
|
||
une matinée pas vraiment techniques, mais ça pose le décor et permet de
|
||
savoir qui fait&nbsp;quoi.</p>
|
||
<p>Parmi l&#8217;ensemble des conférences du matin, je retiens principalement
|
||
celle de Mathieu Leplatre, &#8220;des cartes d&#8217;un autre monde&#8221;, qui m&#8217;a
|
||
réellement bluffée quand à la facilité de créer des cartes avec
|
||
<a href="http://mapbox.com/tilemill/">TileMill</a>, et qui me pousse à reconsidérer
|
||
le fait que &#8220;la carto, c&#8217;est compliqué&#8221;. <a href="https://www.youtube.com/watch?v=7NPQo54NbJ8">La vidéo est (déja !)
|
||
disponible en ligne</a>, je
|
||
vous invite à la regarder (c&#8217;est une 15aine de minutes) pour vous faire
|
||
un avis&nbsp;;)</p>
|
||
<p>Une fois les conf passées, ça reste très intéressant, voire plus: il
|
||
reste un jour et demi pour discuter avec les autres présents. On a pu se
|
||
retrouver avec Mathieu pour discuter de &#8220;notre&#8221; projet <a href="http://blog.notmyidea.org/carto-forms-fr.html">&#8220;carto
|
||
forms&#8221;</a>, qui à finalement
|
||
pu se redéfinir un peu plus et donner naissance à un
|
||
<a href="https://github.com/spiral-project/daybed/blob/master/README.rst"><span class="caps">README</span></a>.
|
||
On en à profité pour lui choisir un nouveau nom: &#8220;daybed&#8221;, en référence
|
||
à&nbsp;couchdb.</p>
|
||
<p>Ça devrait se transformer en code d&#8217;ici peu. La curiosité aidant, on a
|
||
pu discuter du projet avec d&#8217;autres personnes et affiner les attentes de
|
||
chacun pour finalement arriver à quelque chose d&#8217;assez&nbsp;sympathique.</p>
|
||
<p>J&#8217;ai aussi pu me rendre compte que pas mal de monde utilise
|
||
<a href="http://pelican.notmyidea.org">pelican</a>, le bout de code que j&#8217;ai codé
|
||
pour générer ce blog, et avoir des retours utiles ! Probablement des
|
||
réflexions à venir sur comment éviter qu&#8217;un projet open-source ne
|
||
devienne chronophage, et sur comment réussir à garder une qualité dans
|
||
le code source tout en ne froissant pas les&nbsp;contributeurs.</p>
|
||
<p>Bien évidemment, c&#8217;était aussi l&#8217;occaz de rencontrer des gens qu&#8217;on ne
|
||
voir que sur les inter-nets, et de discuter un brin de tout ce qui fait
|
||
que notre monde est chouette et moins&nbsp;chouette.</p>
|
||
<p>Entres autres faits notoires, JMad a perdu au baby-foot face à Exirel,
|
||
même en m&#8217;ayant à ses cotés pour le déconcentrer (et je suis un joueur
|
||
d&#8217;un autre monde - en d&#8217;autres termes, je suis nul), David`bgk ne s&#8217;est
|
||
pas levé pour aller courir le dimanche matin (il avait dit 5 heures!),
|
||
Les suisses ont essayé de me convertir à coup d&#8217;abricotine, j&#8217;ai perdu
|
||
au skulls-n-roses en quelques tours et on a allumé un feu chez Stéphane
|
||
le dimanche soir (oui oui, à montpellier, mi avril, je vous le dis
|
||
qu&#8217;ils mentent avec leur soit disant&nbsp;soleil).</p>
|
||
<p>Et c&#8217;est sans parler de <a href="http://jehaisleprintemps.net/blog/fr/2012/04/15/j-ecris-ton-nom/">la
|
||
brasucade</a>&nbsp;…</p>
|
||
<p>Bref, vivement la prochaine (et allez, cette fois ci je ferais une
|
||
présentation&nbsp;!)</p></content><category term="code"></category></entry><entry><title>Génération de formulaires, geolocalisés ?</title><link href="https://blog.notmyidea.org/generation-de-formulaires-geolocalises.html" rel="alternate"></link><published>2012-04-02T00:00:00+02:00</published><updated>2012-04-02T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2012-04-02:/generation-de-formulaires-geolocalises.html</id><summary type="html">
|
||
<p>On a un plan. Un &#8220;truc de&nbsp;ouf&#8221;.</p>
|
||
<p>À plusieurs reprises, des amis m&#8217;ont demandé de leur coder la même
|
||
chose, à quelques détails près: une page web avec un formulaire qui
|
||
permettrait de soumettre des informations géographiques, lié à une carte
|
||
et des manières de filtrer&nbsp;l&#8217;information …</p></summary><content type="html">
|
||
<p>On a un plan. Un &#8220;truc de&nbsp;ouf&#8221;.</p>
|
||
<p>À plusieurs reprises, des amis m&#8217;ont demandé de leur coder la même
|
||
chose, à quelques détails près: une page web avec un formulaire qui
|
||
permettrait de soumettre des informations géographiques, lié à une carte
|
||
et des manières de filtrer&nbsp;l&#8217;information.</p>
|
||
<p>L&#8217;idée fait son bout de chemin, et je commence à penser qu&#8217;on peut même
|
||
avoir quelque chose de vraiment flexible et utile. J&#8217;ai nommé le projet
|
||
<em>carto-forms</em> pour l&#8217;instant (mais c&#8217;est uniquement un nom de&nbsp;code).</p>
|
||
<p>Pour résumer: et si on avait un moyen de construire des formulaires, un
|
||
peu comme Google forms, mais avec des informations géographiques en&nbsp;plus?</p>
|
||
<p>Si vous ne connaissez pas Google forms, il s&#8217;agit d&#8217;une interface simple
|
||
d&#8217;utilisation pour générer des formulaires et récupérer des informations
|
||
depuis ces&nbsp;derniers.</p>
|
||
<p>Google forms est un super outil mais à mon avis manque deux choses
|
||
importantes: premièrement, il s&#8217;agit d&#8217;un outil propriétaire (oui, on
|
||
peut aussi dire privateur) et il n&#8217;est donc pas possible de le hacker un
|
||
peu pour le faire devenir ce qu&#8217;on souhaite, ni l&#8217;installer sur notre
|
||
propre serveur. Deuxièmement, il ne sait pas vraiment fonctionner avec
|
||
des informations géographiques, et il n&#8217;y à pas d&#8217;autre moyen de filtrer
|
||
les informations que l&#8217;utilisation de leur système de feuilles de&nbsp;calcul.</p>
|
||
<p>Après avoir réfléchi un petit peu à ça, j&#8217;ai contacté
|
||
<a href="http://blog.mathieu-leplatre.info/">Mathieu</a> et les anciens collègues
|
||
de chez <a href="http://makina-corpus.com">Makina Corpus</a>, puisque les projets
|
||
libres à base de carto sont à même de les&nbsp;intéresser.</p>
|
||
<p>Imaginez le cas&nbsp;suivant:</p>
|
||
<ol>
|
||
<li>Dans une &#8220;mapping party&#8221;, on choisit un sujet particulier à
|
||
cartographier et on design un formulaire (liste des champs (tags) a
|
||
remplir + description + le type d&#8217;information)&nbsp;;</li>
|
||
<li>Sur place, les utilisateurs remplissent les champs du formulaire
|
||
avec ce qu&#8217;ils voient. Les champs géolocalisés peuvent être remplis
|
||
automatiquement avec la géolocalisation du téléphone&nbsp;;</li>
|
||
<li>À la fin de la journée, il est possible de voir une carte des
|
||
contributions, avec le formulaire choisi&nbsp;;</li>
|
||
<li>Un script peut importer les résultats et les publier vers&nbsp;OpenStreetMap.</li>
|
||
</ol>
|
||
<h2 id="quelques-cas-dutilisation">Quelques cas&nbsp;d&#8217;utilisation</h2>
|
||
<p>J&#8217;arrive à imaginer différents cas d&#8217;utilisation pour cet outil. Le
|
||
premier est celui que j&#8217;ai approximativement décrit plus haut: la
|
||
génération de cartes de manière collaborative, avec des filtres à
|
||
facettes. Voici un flux d&#8217;utilisation&nbsp;général:</p>
|
||
<ul>
|
||
<li>
|
||
<p>Un &#8220;administrateur&#8221; se rend sur le site web et crée un nouveau
|
||
formulaire pour l&#8217;ensemble des évènements alternatifs. Il crée les
|
||
champs&nbsp;suivants:</p>
|
||
<ul>
|
||
<li>Nom: le champ qui contient le nom de&nbsp;l&#8217;évènement.</li>
|
||
<li>Catégorie: la catégorie de l&#8217;évènement (marche, concert,
|
||
manifestation…). Il peut s&#8217;agir d&#8217;un champ à multiples&nbsp;occurrences.</li>
|
||
<li>Le lieu de l&#8217;évènement. Celui-ci peut être donné soit par une
|
||
adresse soit en sélectionnant un point sur une&nbsp;carte.</li>
|
||
<li>Date: la date de l&#8217;évènement (un &#8220;date picker&#8221; peut permettre
|
||
cela&nbsp;facilement)</li>
|
||
</ul>
|
||
<p>Chaque champ dans le formulaire a des informations sémantiques
|
||
associées (oui/non, multiple sélection, date, heure, champ géocodé,
|
||
sélection carto,&nbsp;etc.)</p>
|
||
</li>
|
||
<li>
|
||
<p>Une fois terminé, le formulaire est généré et une <span class="caps">URL</span> permet d&#8217;y
|
||
accéder. (par exemple <a href="http://forms.notmyidea.org/alternatives">http://forms.notmyidea.org/alternatives</a>).</p>
|
||
</li>
|
||
<li>
|
||
<p>Une <span class="caps">API</span> <span class="caps">REST</span> permet à d&#8217;autres applications d&#8217;accéder aux
|
||
informations et d&#8217;en ajouter / modifier de&nbsp;nouvelles.</p>
|
||
</li>
|
||
<li>
|
||
<p>Il est maintenant possible de donner l&#8217;<span class="caps">URL</span> à qui voudra en faire bon
|
||
usage. N&#8217;importe qui peut ajouter des informations. On peut
|
||
également imaginer une manière de modérer les modifications si
|
||
besoin&nbsp;est.</p>
|
||
</li>
|
||
<li>
|
||
<p>Bien sur, la dernière phase est la plus intéressante: il est
|
||
possible de filtrer les informations par lieu, catégorie ou date, le
|
||
tout soit via une <span class="caps">API</span> <span class="caps">REST</span>, soit via une jolie carte et quelques
|
||
contrôles bien placés, dans le&nbsp;navigateur.</p>
|
||
</li>
|
||
</ul>
|
||
<p>Vous avez dû remarquer que le processus de création d&#8217;un formulaire est
|
||
volontairement très simple. L&#8217;idée est que n&#8217;importe qui puisse créer
|
||
des cartes facilement, en quelques clics. Si une <span class="caps">API</span> bien pensée suit,
|
||
on peut imaginer faire de la validation coté serveur et même faire des
|
||
applications pour téléphone assez&nbsp;simplement.</p>
|
||
<p>Pour aller un peu plus loin, si on arrive à penser un format de
|
||
description pour le formulaire, il sera possible de construire les
|
||
formulaires de manière automatisée sur différentes plateformes et
|
||
également sur des clients&nbsp;génériques.</p>
|
||
<p>On imagine pas mal d&#8217;exemples pour ce projet: des points de recyclage,
|
||
les endroits accessibles (pour fauteuils roulants etc.), identification
|
||
des arbres, bons coins à champignons, recensement des espèces en voie de
|
||
disparition (l&#8217;aigle de Bonelli est actuellement suivi en utilisant une
|
||
feuille de calcul partagée !), suivi des espèces dangereuses (le frelon
|
||
asiatique par exemple), cartographier les points d&#8217;affichage
|
||
publicitaires, participation citoyenne (graffitis, nids de poule, voir
|
||
<a href="http://fixmystreet.ca">http://fixmystreet.ca</a>), geocaching, trajectoires (randonnées,
|
||
coureurs,&nbsp;cyclistes)…</p>
|
||
<p>Voici quelques exemples où ce projet pourrait être utile (la liste n&#8217;est
|
||
pas&nbsp;exhaustive):</p>
|
||
<h3 id="un-backend-sig-simple-a-utiliser">Un backend <span class="caps">SIG</span> simple à&nbsp;utiliser</h3>
|
||
<p>Disons que vous êtes développeur mobile. Vous ne voulez pas vous
|
||
encombrer avec PostGIS ou écrire du code spécifique pour récupérer et
|
||
insérer des données <span class="caps">SIG</span>! Vous avez besoin de <em>Carto-Forms</em>! Une <span class="caps">API</span>
|
||
simple vous aide à penser vos modèles et vos formulaires, et cette même
|
||
<span class="caps">API</span> vous permet d&#8217;insérer et de récupérer des données. Vous pouvez vous
|
||
concentrer sur votre application et non pas sur la manière dont les
|
||
données géographiques sont stockées et&nbsp;gérées.</p>
|
||
<p>En d&#8217;autres termes, vous faites une distinction entre le stockage des
|
||
informations et leur&nbsp;affichage.</p>
|
||
<p>Si vous êtes un développeur django, plomino, drupal etc. vous pouvez
|
||
développer un module pour &#8220;plugger&#8221; vos modèles et votre interface
|
||
utilisateur avec celle de <em>Carto-Forms</em>. De cette manière, il est
|
||
possible d&#8217;exposer les formulaires aux utilisateurs de vos backoffices.
|
||
De la même manière, il est possible d&#8217;écrire des widgets qui consomment
|
||
des données et les affichent (en utilisant par exemple une bibliothèque
|
||
javascript de&nbsp;webmapping).</p>
|
||
<h3 id="un-outil-de-visualisation">Un outil de&nbsp;visualisation</h3>
|
||
<p>Puisque les données peuvent être proposées de manière automatisée en
|
||
utilisant l&#8217;<span class="caps">API</span>, vous pouvez utiliser la page de résultat de Carto-forms
|
||
comme un outil de&nbsp;visualisation.</p>
|
||
<p>Il est possible d&#8217;explorer mon jeu de données en utilisant des filtres
|
||
sur chacun des champs. La recherche à facettes peut être une idée pour
|
||
faciliter ce filtrage. Une carte affiche le résultat. Vous avez
|
||
l&#8217;impressoin d&#8217;être en face d&#8217;un système d&#8217;aide à la décision&nbsp;!</p>
|
||
<p>Évidemment, il est possible de télécharger les données brutes (geojson,
|
||
xml). Idéalement, le mieux serait d&#8217;obtenir ces données filtrées
|
||
directement depuis une <span class="caps">API</span> Web, et un lien permet de partager la page
|
||
avec l&#8217;état des filtres et le niveau de zoom / la localisation de la&nbsp;carte.</p>
|
||
<h3 id="un-service-generique-pour-gerer-les-formulaires">Un service générique pour gérer les&nbsp;formulaires</h3>
|
||
<p>Si vous souhaitez générer un fichier de configuration (ou ce que vous
|
||
voulez, messages emails, …) vous aurez besoin d&#8217;un formulaire et d&#8217;un
|
||
template pour injecter les données proposées par les utilisateurs et
|
||
récupérer un&nbsp;résultat.</p>
|
||
<p>Un service de gestion des formulaires pourrait être utile pour créer des
|
||
formulaires de manière automatique et récupérer les données &#8220;nettoyées&#8221;
|
||
et&nbsp;&#8220;validées&#8221;.</p>
|
||
<p>On peut imaginer par exemple l&#8217;utilisation d&#8217;un système de templates
|
||
externe reposant sur <em>carto-forms</em>. Celui-ci &#8220;parserait&#8221; le contenu des
|
||
templates et pourrait le lier aux informations ajoutées par les
|
||
utilisateurs via un&nbsp;formulaire.</p>
|
||
<p>Pour ce cas particulier, il n&#8217;y a pas besoin d&#8217;informations
|
||
géographiques (<span class="caps">SIG</span>). Il s&#8217;agit quasiment du service proposé
|
||
actuellement par Google&nbsp;forms.</p>
|
||
<h2 id="ca-nexiste-pas-deja-tout-ca">Ça n&#8217;existe pas déjà tout ça&nbsp;?</h2>
|
||
<p>Bien sur, il y a Google forms, qui vous permet de faire ce genre de
|
||
choses, mais comme je l&#8217;ai précisé plus haut, il ne s&#8217;agit pas
|
||
exactement de la même&nbsp;chose.</p>
|
||
<p>Nous avons découvert <a href="https://webform.com">https://webform.com</a> qui permet de créer des
|
||
formulaires avec un système de drag&#8217;n&#8217;drop. J&#8217;adorerais reproduire
|
||
quelque chose de similaire pour l&#8217;interface utilisateur. Par contre ce
|
||
projet ne gère pas les appels via <span class="caps">API</span> et les informations de
|
||
géolocalisation&nbsp;…</p>
|
||
<p>L&#8217;idée de <a href="http://thoth.io">http://thoth.io</a> est également assez sympathique: une api
|
||
très simple pour stocker et récupérer des données. En plus de ça,
|
||
<em>carto-forms</em> proposerait de la validation de données et proposerait un
|
||
support des points <span class="caps">SIG</span> (point, ligne,&nbsp;polygone).</p>
|
||
<p><a href="http://mapbox.com">http://mapbox.com</a> fait également un superbe travail autour de la
|
||
cartographie, mais ne prends pas en compte le coté auto-génération de&nbsp;formulaires…</p>
|
||
<h2 id="on-est-parti">On est parti&nbsp;?!</h2>
|
||
<p>Comme vous avez pu vous en rendre compte, il ne s&#8217;agit pas d&#8217;un problème
|
||
outrageusement complexe. On a pas mal discuté avec Mathieu, à propos de
|
||
ce qu&#8217;on souhaite faire et du comment. Il se trouve qu&#8217;on peut sûrement
|
||
s&#8217;en sortir avec une solution élégante sans trop de problèmes. Mathieu
|
||
est habitué à travailler autour des projets de <span class="caps">SIG</span> (ce qui est parfait
|
||
parce que ce n&#8217;est pas mon cas) et connaît son sujet. Une bonne
|
||
opportunité&nbsp;d&#8217;apprendre!</p>
|
||
<p>On sera tous les deux à <a href="http://rencontres.django-fr.org">Djangocong</a> le
|
||
14 et 15 Avril, et on prévoit une session de <em>tempête de cerveau</em> et un
|
||
sprint sur ce projet. Si vous êtes dans le coin et que vous souhaitez
|
||
discuter ou nous filer un coup de patte, n&#8217;hésitez&nbsp;pas!</p>
|
||
<p>On ne sait pas encore si on utilisera django ou quelque chose d&#8217;autre.
|
||
On a pensé un peu à CouchDB, son système de couchapps et geocouch, mais
|
||
rien n&#8217;est encore gravé dans le marbre ! N&#8217;hésitez pas à proposer vos
|
||
solutions ou&nbsp;suggestions.</p>
|
||
<p>Voici le document etherpad sur lequel on a travaillé jusqu&#8217;à maintenant:
|
||
<a href="http://framapad.org/carto-forms">http://framapad.org/carto-forms</a>. N&#8217;hésitez pas à l&#8217;éditer et à ajouter
|
||
vos commentaires, c&#8217;est son&nbsp;objectif!</p>
|
||
<p>Merci à <a href="http://sneakernet.fr/">Arnaud</a> pour la relecture et la
|
||
correction de quelques typos dans le texte&nbsp;:)</p></content><category term="code"></category></entry><entry><title>Thoughts about a form generation service, GIS enabled</title><link href="https://blog.notmyidea.org/thoughts-about-a-form-generation-service-gis-enabled.html" rel="alternate"></link><published>2012-04-02T00:00:00+02:00</published><updated>2012-04-02T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2012-04-02:/thoughts-about-a-form-generation-service-gis-enabled.html</id><summary type="html">
|
||
<p><em>Written by Alexis Métaireau <span class="amp">&amp;</span> Mathieu&nbsp;Leplatre</em></p>
|
||
<p>We have a plan. A &#8220;fucking good&#8221;&nbsp;one.</p>
|
||
<p>A bunch of friends asked me twice for quite the same thing: a webpage
|
||
with a form, tied to a map generation with some information filtering.
|
||
They didn&#8217;t explicitly ask that but that&#8217;s the …</p></summary><content type="html">
|
||
<p><em>Written by Alexis Métaireau <span class="amp">&amp;</span> Mathieu&nbsp;Leplatre</em></p>
|
||
<p>We have a plan. A &#8220;fucking good&#8221;&nbsp;one.</p>
|
||
<p>A bunch of friends asked me twice for quite the same thing: a webpage
|
||
with a form, tied to a map generation with some information filtering.
|
||
They didn&#8217;t explicitly ask that but that&#8217;s the gist of&nbsp;it.</p>
|
||
<p>This idea has been stuck in my head since then and I even think that we
|
||
can come out with something a little bit more flexible and useful. I&#8217;ve
|
||
named it <em>carto-forms</em> for now, but that&#8217;s only the&nbsp;&#8220;codename&#8221;.</p>
|
||
<p>To put it shortly: what if we had a way to build forms, ala Google
|
||
forms, but with geographic information in&nbsp;them?</p>
|
||
<p>If you don&#8217;t know Google forms, it means having an user-friendly way to
|
||
build forms and to use them to gather information from different&nbsp;users.</p>
|
||
<p>In my opinion, Google forms is missing two important things: first, it&#8217;s
|
||
not open-source, so it&#8217;s not possible to hack it or even to run it on
|
||
your own server. Second, it doesn&#8217;t really know how to deal with
|
||
geographic data, and there is no way to filter the information more than
|
||
in a&nbsp;spreadsheet.</p>
|
||
<p>I knew that <a href="http://blog.mathieu-leplatre.info/">Mathieu</a> and some folks
|
||
at <a href="http://makina-corpus.com">Makina Corpus</a> would be interested in
|
||
this, so I started a discussion with him on <span class="caps">IRC</span> and we refined the
|
||
details of the project and its&nbsp;objectives.</p>
|
||
<p>Imagine the&nbsp;following:</p>
|
||
<ol>
|
||
<li>For a mapping party, we choose a specific topic to map and design
|
||
the form (list of fields (i.e. tags) to be filled + description +
|
||
type of the information)&nbsp;;</li>
|
||
<li>In situ, users fill the form fields with what they see. Geo fields
|
||
can be pre-populated using device geolocation&nbsp;;</li>
|
||
<li>At the end of the day, we can see a map with all user contributions
|
||
seized through this particular form&nbsp;;</li>
|
||
<li>If relevant, a script could eventually import the resulting dataset
|
||
and publish/merge with&nbsp;OpenStreetMap.</li>
|
||
</ol>
|
||
<h2 id="some-use-cases">Some use&nbsp;cases</h2>
|
||
<p>I can see some use cases for this. The first one is a collaborative map,
|
||
with facet filtering. Let&#8217;s draw a potential user&nbsp;flow:</p>
|
||
<ul>
|
||
<li>
|
||
<p>An &#8220;administrator&#8221; goes to the website and creates a form to list
|
||
all the alternative-related events. He creates the following&nbsp;fields:</p>
|
||
<ul>
|
||
<li>Name: a plain text field containing the name of the&nbsp;event.</li>
|
||
<li>Category: the category of the event. Can be a finite&nbsp;list.</li>
|
||
<li>Location: The location of the event. It could be provided by
|
||
selecting a point on a map or by typing an&nbsp;address.</li>
|
||
<li>Date: the date of the event (a datepicker could do the&nbsp;trick)</li>
|
||
</ul>
|
||
<p>Each field in the form has semantic information associated with it
|
||
(yes/no, multiple selection, date-time, geocoding carto, carto
|
||
selection&nbsp;etc)</p>
|
||
</li>
|
||
<li>
|
||
<p>Once finished, the form is generated and the user gets an url (say
|
||
<a href="http://forms.notmyidea.org/alternatives">http://forms.notmyidea.org/alternatives</a>) for&nbsp;it.</p>
|
||
</li>
|
||
<li>
|
||
<p><span class="caps">REST</span> APIs allow third parties to get the form description and to
|
||
push/edit/get information from&nbsp;there.</p>
|
||
</li>
|
||
<li>
|
||
<p>He can communicate the address in any way he wants to his community
|
||
so they can go to the page and add information to&nbsp;it.</p>
|
||
</li>
|
||
<li>
|
||
<p>Then, it is possible to filter the results per location / date or
|
||
category. This can be done via <span class="caps">API</span> calls (useful for third parties)
|
||
or via a nice interface in the&nbsp;browser.</p>
|
||
</li>
|
||
</ul>
|
||
<p>So, as you may have noticed, this would allow us to create interactive
|
||
maps really easily. It&#8217;s almost just a matter of some clicks to the
|
||
users. If we also come up with a nice Web <span class="caps">API</span> for this, we could do
|
||
server-side validation and build even phone applications&nbsp;easily.</p>
|
||
<p>To push the cursor a bit further, if we can come with a cool description
|
||
format for the forms, we could even build the forms dynamically on
|
||
different platforms, with generic&nbsp;clients.</p>
|
||
<p>As mentioned before, the idea of a simple tool to support collaborative
|
||
mapping fullfils a recurring necessity&nbsp;!</p>
|
||
<p>We envision a lot of example uses for this : recycling spots, accessible
|
||
spots (wheelchairs, etc.), trees identification, mushrooms picking
|
||
areas, tracking of endangered species (e.g. Bonelli&#8217;s Eagle is currently
|
||
tracked by sharing a spreadsheet), spotting of dangerous species (e.g.
|
||
asian predatory wasps), map advertisement boards (most cities do not
|
||
track them!), citizen reporting (e.g. graffiti, potholes, garbage,
|
||
lightning like <a href="http://fixmystreet.ca">http://fixmystreet.ca</a>), geocaching, trajectories (e.g
|
||
hiking, runners,&nbsp;cyclists)&#8230;</p>
|
||
<p>Here are some other examples of where <em>carto-forms</em> could be&nbsp;useful:</p>
|
||
<h3 id="simple-gis-storage-backend">Simple <span class="caps">GIS</span> storage&nbsp;backend</h3>
|
||
<p>Let&#8217;s say you are a mobile developer, you don&#8217;t want to bother with
|
||
PostGIS nor write a custom and insecure code to insert and retrieve your
|
||
<span class="caps">GIS</span> data! You need carto-forms! A simple <span class="caps">API</span> helps you design your
|
||
models/forms and the same <span class="caps">API</span> allows you to <span class="caps">CRUD</span> and query your data.
|
||
Thus, you only need to focus on your application, not on how <span class="caps">GIS</span> data
|
||
will be&nbsp;handled.</p>
|
||
<p>We make a distinction between storage and&nbsp;widgets.</p>
|
||
<p>Besides, if you are a django / drupal / plomino&#8230; maintainer : you can
|
||
develop a module to &#8220;plug&#8221; your models (content types) and <span class="caps">UI</span> to
|
||
carto-forms! Carto forms are then exposed to your backoffice users (ex:
|
||
drupal admin <span class="caps">UI</span>, django adminsite), and likewise you can write your own
|
||
<span class="caps">HTML</span> widgets that consume datasets in frontend views (facets in
|
||
<span class="caps">JSON</span>/<span class="caps">XML</span>, and map data in&nbsp;GeoJSON).</p>
|
||
<h3 id="visualization-tool">Visualization&nbsp;tool</h3>
|
||
<p>Since data submission can be done programmatically using the <span class="caps">API</span>, you
|
||
could use Carto-forms results page as a visualization&nbsp;tool.</p>
|
||
<p>You can explore your dataset content using filters related to each form
|
||
field. Facets filtering is a great advantage, and a map shows the
|
||
resulting features set. You feel like you&#8217;re in front of a decision
|
||
support&nbsp;system!</p>
|
||
<p>Of course, filtered raw data can be downloaded (GeoJSON, <span class="caps">XML</span>) and a
|
||
permalink allows to share the page with the state of the filters and the
|
||
zoom/location of the&nbsp;map.</p>
|
||
<h3 id="generic-forms-service">Generic forms&nbsp;service</h3>
|
||
<p>If you want to generate a configuration file (or whatever, email
|
||
messages, &#8230;), you will need a form and a template to inlay user
|
||
submitted values and get the&nbsp;result.</p>
|
||
<p>A form service would be really useful to create forms programmatically
|
||
and retrieve cleaned and validated input&nbsp;values.</p>
|
||
<p>You could run a dedicated template service based on <em>carto-forms</em>!
|
||
Parsing a template content, this external service could create a form
|
||
dynamically and bind them together. The output of the form service
|
||
(fields =&gt; values) would be bound to the input of a template engine
|
||
(variables =&gt; final&nbsp;result).</p>
|
||
<p>Note that for this use-case, there is no specific need of <span class="caps">GIS</span> data nor
|
||
storage of records for further&nbsp;retrieval.</p>
|
||
<h2 id="whats-out-in-the-wild-already">What&#8217;s out in the wild&nbsp;already?</h2>
|
||
<p>Of course, there is Google forms, which allows you to do these kind of
|
||
things, but it&#8217;s closed and not exactly what we are describing&nbsp;here.</p>
|
||
<p>We&#8217;ve discovered the interesting <a href="https://webform.com/">https://webform.com/</a> which allows one
|
||
to create forms with a nice drag-n-drop flow. I would love to reproduce
|
||
something similar for the user experience. However, the project doesn&#8217;t
|
||
handle APIs and geolocation&nbsp;information.</p>
|
||
<p>The idea of <a href="http://thoth.io">http://thoth.io</a> is very attractive : an extremely simple
|
||
web <span class="caps">API</span> to store and retrieve data. In addition, <em>carto-forms</em> would do
|
||
datatype validation and have basic <span class="caps">GIS</span> fields (point, line,&nbsp;polygon).</p>
|
||
<p><a href="http://mapbox.com">http://mapbox.com</a> also did an awesome work on cartography, but didn&#8217;t
|
||
take into account the form aspect we&#8217;re leveraging&nbsp;here.</p>
|
||
<h2 id="so-lets-get-it-real">So… Let&#8217;s get it&nbsp;real!</h2>
|
||
<p>As you may have understood, this isn&#8217;t a really complicated problem. We
|
||
have been sometimes chatting about that with Mathieu about what we would
|
||
need and how we could achieve&nbsp;this.</p>
|
||
<p>We can probably come with an elegant solution without too much pain.
|
||
Mathieu is used to work with <span class="caps">GIS</span> systems (which is really cool because
|
||
I&#8217;m not at all) and knows his subject, so that&#8217;s an opportunity to learn&nbsp;;-)</p>
|
||
<p>We will be at <a href="http://rencontres.django-fr.org">Djangocong</a> on April 14
|
||
and 15 and will probably have a brainstorming session and a sprint on
|
||
this, so if you are around and want to help us, or just to discuss, feel
|
||
free to&nbsp;join!</p>
|
||
<p>We don&#8217;t know yet if we will be using django for this or something else.
|
||
We have been thinking about couchdb, couchapps and geocouch but nothing
|
||
is written in stone yet. Comments and proposals are&nbsp;welcome!</p>
|
||
<p>Here is the etherpad document we worked on so far:
|
||
<a href="http://framapad.org/carto-forms">http://framapad.org/carto-forms</a>. Don&#8217;t hesitate to add your thoughts
|
||
and edit it, that&#8217;s what it&#8217;s made&nbsp;for!</p>
|
||
<p>Thanks to <a href="http://sneakernet.fr/">Arnaud</a> and
|
||
<a href="http://qwerty.fuzz.me.uk/">Fuzzmz</a> for proof-reading and typo&nbsp;fixing.</p></content><category term="code"></category></entry><entry><title>Introducing Cornice</title><link href="https://blog.notmyidea.org/introducing-cornice.html" rel="alternate"></link><published>2011-12-07T00:00:00+01:00</published><updated>2011-12-07T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-12-07:/introducing-cornice.html</id><summary type="html">
|
||
<p>Wow, already my third working day at Mozilla. Since Monday, I&#8217;ve been
|
||
working with <a href="http://ziade.org">Tarek Ziadé</a>, on a pyramid <span class="caps">REST</span>-ish
|
||
toolkit named <a href="https://github.com/mozilla-services/cornice">Cornice</a>.</p>
|
||
<p>Its goal is to take care for you of what you&#8217;re usually missing so you
|
||
can focus on what&#8217;s important. Cornice provides you …</p></summary><content type="html">
|
||
<p>Wow, already my third working day at Mozilla. Since Monday, I&#8217;ve been
|
||
working with <a href="http://ziade.org">Tarek Ziadé</a>, on a pyramid <span class="caps">REST</span>-ish
|
||
toolkit named <a href="https://github.com/mozilla-services/cornice">Cornice</a>.</p>
|
||
<p>Its goal is to take care for you of what you&#8217;re usually missing so you
|
||
can focus on what&#8217;s important. Cornice provides you facilities for
|
||
validation of any&nbsp;kind.</p>
|
||
<p>The goal is to simplify your work, but we don&#8217;t want to reinvent the
|
||
wheel, so it is easily pluggable with validations frameworks, such as
|
||
<a href="http://docs.pylonsproject.org/projects/colander/en/latest/">Colander</a>.</p>
|
||
<h2 id="handling-errors-and-validation">Handling errors and&nbsp;validation</h2>
|
||
<p>Here is how it&nbsp;works:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">service</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;service&quot;</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s2">&quot;/service&quot;</span><span class="p">)</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">is_awesome</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="s1">&#39;awesome&#39;</span> <span class="ow">in</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="p">:</span>
|
||
<span class="n">request</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s1">&#39;query&#39;</span><span class="p">,</span> <span class="s1">&#39;awesome&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;the awesome parameter is required&#39;</span><span class="p">)</span>
|
||
|
||
|
||
<span class="nd">@service</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">validator</span><span class="o">=</span><span class="n">is_awesome</span><span class="p">)</span>
|
||
<span class="k">def</span> <span class="nf">get1</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;test&quot;</span><span class="p">:</span> <span class="s2">&quot;yay!&quot;</span><span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<p>All the errors collected during the validation process, or after, are
|
||
collected before returning the request. If any, a error 400 is fired up,
|
||
with the list of problems encountered returned as a nice json list
|
||
response (we plan to support multiple formats in the&nbsp;future)</p>
|
||
<p>As you might have seen, request.errors.add takes three parameters:
|
||
<strong>location</strong>, <strong>name</strong> and <strong>description</strong>.</p>
|
||
<p><strong>location</strong> is where the error is located in the request. It can either
|
||
be &#8220;body&#8221;, &#8220;query&#8221;, &#8220;headers&#8221; or &#8220;path&#8221;. <strong>name</strong> is the name of the
|
||
variable causing problem, if any, and <strong>description</strong> contains a more
|
||
detailed&nbsp;message.</p>
|
||
<p>Let&#8217;s run this simple service and send some queries to&nbsp;it:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>curl<span class="w"> </span>-v<span class="w"> </span>http://127.0.0.1:5000/service
|
||
&gt;<span class="w"> </span>GET<span class="w"> </span>/service<span class="w"> </span>HTTP/1.1
|
||
&gt;<span class="w"> </span>Host:<span class="w"> </span><span class="m">127</span>.0.0.1:5000
|
||
&gt;<span class="w"> </span>Accept:<span class="w"> </span>*/*
|
||
&gt;
|
||
*<span class="w"> </span>HTTP<span class="w"> </span><span class="m">1</span>.0,<span class="w"> </span>assume<span class="w"> </span>close<span class="w"> </span>after<span class="w"> </span>body
|
||
&lt;<span class="w"> </span>HTTP/1.0<span class="w"> </span><span class="m">400</span><span class="w"> </span>Bad<span class="w"> </span>Request
|
||
&lt;<span class="w"> </span>Content-Type:<span class="w"> </span>application/json<span class="p">;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>UTF-8
|
||
<span class="o">[{</span><span class="s2">&quot;location&quot;</span>:<span class="w"> </span><span class="s2">&quot;query&quot;</span>,<span class="w"> </span><span class="s2">&quot;name&quot;</span>:<span class="w"> </span><span class="s2">&quot;awesome&quot;</span>,<span class="w"> </span><span class="s2">&quot;description&quot;</span>:<span class="w"> </span><span class="s2">&quot;You lack awesomeness!&quot;</span><span class="o">}</span>
|
||
</code></pre></div>
|
||
|
||
<p>I&#8217;ve removed the extra clutter from the curl&#8217;s output, but you got the
|
||
general&nbsp;idea.</p>
|
||
<p>The content returned is in <span class="caps">JSON</span>, and I know exactly what I have to do:
|
||
add an &#8220;awesome&#8221; parameter in my query. Let&#8217;s do it&nbsp;again:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>curl<span class="w"> </span>http://127.0.0.1:5000/service?awesome<span class="o">=</span>yeah
|
||
<span class="o">{</span><span class="s2">&quot;test&quot;</span>:<span class="w"> </span><span class="s2">&quot;yay!&quot;</span><span class="o">}</span>
|
||
</code></pre></div>
|
||
|
||
<p>Validators can also convert parts of the request and store the converted
|
||
value in request.validated. It is a standard dict automatically attached
|
||
to the&nbsp;requests.</p>
|
||
<p>For instance, in our validator, we can chose to validate the parameter
|
||
passed and use it in the body of the&nbsp;webservice:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">service</span> <span class="o">=</span> <span class="n">Service</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;service&quot;</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s2">&quot;/service&quot;</span><span class="p">)</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">is_awesome</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="s1">&#39;awesome&#39;</span> <span class="ow">in</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="p">:</span>
|
||
<span class="n">request</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s1">&#39;query&#39;</span><span class="p">,</span> <span class="s1">&#39;awesome&#39;</span><span class="p">,</span>
|
||
<span class="s1">&#39;the awesome parameter is required&#39;</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">request</span><span class="o">.</span><span class="n">validated</span><span class="p">[</span><span class="s1">&#39;awesome&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;awesome &#39;</span> <span class="o">+</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="p">[</span><span class="s1">&#39;awesome&#39;</span><span class="p">]</span>
|
||
|
||
|
||
<span class="nd">@service</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">validator</span><span class="o">=</span><span class="n">is_awesome</span><span class="p">)</span>
|
||
<span class="k">def</span> <span class="nf">get1</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;test&quot;</span><span class="p">:</span> <span class="n">request</span><span class="o">.</span><span class="n">validated</span><span class="p">[</span><span class="s1">&#39;awesome&#39;</span><span class="p">]}</span>
|
||
</code></pre></div>
|
||
|
||
<p>The output would look like&nbsp;this:</p>
|
||
<div class="highlight"><pre><span></span><code>curl http://127.0.0.1:5000/service?awesome=yeah
|
||
{&quot;test&quot;: &quot;awesome yeah&quot;}
|
||
</code></pre></div>
|
||
|
||
<h2 id="dealing-with-accept-headers">Dealing with &#8220;Accept&#8221;&nbsp;headers</h2>
|
||
<p>The <span class="caps">HTTP</span> spec defines a <strong>Accept</strong> header the client can send so the
|
||
response is encoded the right way. A resource, available at an <span class="caps">URL</span>, can
|
||
be available in different formats. This is especially true for web&nbsp;services.</p>
|
||
<p>Cornice can help you dealing with this. The services you define can tell
|
||
which Content-Type values they can deal with and this will be checked
|
||
against the <strong>Accept</strong> headers sent by the&nbsp;client.</p>
|
||
<p>Let&#8217;s refine a bit our previous example, by specifying which
|
||
content-types are supported, using the accept&nbsp;parameter:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="nd">@service</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">validator</span><span class="o">=</span><span class="n">is_awesome</span><span class="p">,</span> <span class="n">accept</span><span class="o">=</span><span class="p">(</span><span class="s2">&quot;application/json&quot;</span><span class="p">,</span> <span class="s2">&quot;text/json&quot;</span><span class="p">))</span>
|
||
<span class="k">def</span> <span class="nf">get1</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;test&quot;</span><span class="p">:</span> <span class="s2">&quot;yay!&quot;</span><span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<p>Now, if you specifically ask for <span class="caps">XML</span>, Cornice will throw a 406 with the
|
||
list of accepted Content-Type&nbsp;values:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>curl<span class="w"> </span>-vH<span class="w"> </span><span class="s2">&quot;Accept: application/xml&quot;</span><span class="w"> </span>http://127.0.0.1:5000/service
|
||
&gt;<span class="w"> </span>GET<span class="w"> </span>/service<span class="w"> </span>HTTP/1.1
|
||
&gt;<span class="w"> </span>Host:<span class="w"> </span><span class="m">127</span>.0.0.1:5000
|
||
&gt;<span class="w"> </span>Accept:<span class="w"> </span>application/xml
|
||
&gt;
|
||
&lt;<span class="w"> </span>HTTP/1.0<span class="w"> </span><span class="m">406</span><span class="w"> </span>Not<span class="w"> </span>Acceptable
|
||
&lt;<span class="w"> </span>Content-Type:<span class="w"> </span>application/json<span class="p">;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>UTF-8
|
||
&lt;<span class="w"> </span>Content-Length:<span class="w"> </span><span class="m">33</span>
|
||
&lt;
|
||
<span class="o">[</span><span class="s2">&quot;application/json&quot;</span>,<span class="w"> </span><span class="s2">&quot;text/json&quot;</span><span class="o">]</span>
|
||
</code></pre></div>
|
||
|
||
<h2 id="building-your-documentation-automatically">Building your documentation&nbsp;automatically</h2>
|
||
<p>writing documentation for web services can be painful, especially when
|
||
your services evolve. Cornice provides a sphinx directive to
|
||
automatically document your <span class="caps">API</span> in your&nbsp;docs.</p>
|
||
<div class="highlight"><pre><span></span><code><span class="nx">rst</span>
|
||
<span class="p">..</span><span class="w"> </span><span class="nx">services</span><span class="o">::</span>
|
||
<span class="w"> </span><span class="p">:</span><span class="kn">package</span><span class="p">:</span><span class="w"> </span><span class="nx">coolapp</span>
|
||
<span class="w"> </span><span class="p">:</span><span class="nx">service</span><span class="p">:</span><span class="w"> </span><span class="nx">quote</span>
|
||
</code></pre></div>
|
||
|
||
<p>Here is an example of what a generated page looks like:
|
||
<a href="http://packages.python.org/cornice/exampledoc.html">http://packages.python.org/cornice/exampledoc.html</a></p>
|
||
<h2 id="yay-how-can-i-get-it">Yay! How can I get&nbsp;it?</h2>
|
||
<p>We just cut a 0.4 release, so it&#8217;s available at
|
||
<a href="http://pypi.python.org/pypi/cornice">http://pypi.python.org/pypi/cornice</a> You can install it easily using
|
||
pip, for&nbsp;instance:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>cornice
|
||
</code></pre></div>
|
||
|
||
<p>You can also have a look at the documentation at
|
||
<a href="http://packages.python.org/cornice/">http://packages.python.org/cornice/</a></p>
|
||
<h2 id="whats-next">What&#8217;s&nbsp;next?</h2>
|
||
<p>We try to make our best to find how Cornice can help you build better
|
||
web services. Cool features we want for the future include the automatic
|
||
publication of a static definition of the services, so it can be used by
|
||
clients to discover services in a nice&nbsp;way.</p>
|
||
<p>Of course, we are open to all your ideas and patches! If you feel
|
||
haskish and want to see the sources, <a href="https://github.com/mozilla-services/cornice">go grab them on
|
||
github</a> , commit and send
|
||
us a pull&nbsp;request!</p></content><category term="code"></category></entry><entry><title>How are you handling your shared expenses?</title><link href="https://blog.notmyidea.org/how-are-you-handling-your-shared-expenses.html" rel="alternate"></link><published>2011-10-15T00:00:00+02:00</published><updated>2011-10-15T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-10-15:/how-are-you-handling-your-shared-expenses.html</id><summary type="html">
|
||
<p><strong><span class="caps">TL</span>;<span class="caps">DR</span>:</strong> We&#8217;re kick-starting a new application to manage your shared
|
||
expenses. Have a look at <a href="http://ihatemoney.notmyidea.org">http://ihatemoney.notmyidea.org</a></p>
|
||
<p>As a student, I lived in a lot of different locations, and the majority
|
||
of them had something in common: I lived with others. It usually was a
|
||
great …</p></summary><content type="html">
|
||
<p><strong><span class="caps">TL</span>;<span class="caps">DR</span>:</strong> We&#8217;re kick-starting a new application to manage your shared
|
||
expenses. Have a look at <a href="http://ihatemoney.notmyidea.org">http://ihatemoney.notmyidea.org</a></p>
|
||
<p>As a student, I lived in a lot of different locations, and the majority
|
||
of them had something in common: I lived with others. It usually was a
|
||
great experience (and I think I will continue to live with others). Most
|
||
of the time, we had to spend some time each month to compute who had to
|
||
pay what to the&nbsp;others.</p>
|
||
<p>I wanted to create a pet project using flask, so I wrote a little (\~150
|
||
lines) flask application to handle this. It worked out pretty well for
|
||
my housemates and me, and as we had to move into different locations,
|
||
one of them asked me if he could continue to use it for the year to&nbsp;come.</p>
|
||
<p>I said yes and gave it some more thoughts: We probably aren&#8217;t the only
|
||
ones interested by such kind of software. I decided to extend a bit more
|
||
the software to have a concept of projects and persons (the list of
|
||
persons was hard-coded in the first time,&nbsp;boooh!).</p>
|
||
<p>I then discussed with a friend of mine, who was excited about it and
|
||
wanted to learn python. Great! That&#8217;s a really nice way to get started.
|
||
Some more friends were also interested in it and contributed some
|
||
features and provided feedback (thanks
|
||
<a href="http://www.sneakernet.fr/">Arnaud</a> and&nbsp;Quentin!)</p>
|
||
<p>Since that, the project now support multiple languages and provides a
|
||
<span class="caps">REST</span> <span class="caps">API</span> (android and iphone apps in the tubes!), into other things.
|
||
There is no need to register for an account or whatnot, just enter a
|
||
project name, a secret code and a contact email, invite friends and
|
||
that&#8217;s it (this was inspired by&nbsp;doodle)!</p>
|
||
<p><img alt="Capture d'écran du site." src="/images/ihatemoney.png"></p>
|
||
<p>You can try the project at <a href="http://ihatemoney.notmyidea.org">http://ihatemoney.notmyidea.org</a> for now,
|
||
and the code lives at <a href="https://github.com/spiral-project/ihatemoney/">https://github.com/spiral-project/ihatemoney/</a>.</p>
|
||
<h2 id="features">Features</h2>
|
||
<p>In the wild, currently, there already are some implementations of this
|
||
shared budget manager thing. The fact is that most of them are either
|
||
hard to use, with a too much fancy design or simply trying to do too
|
||
much things at&nbsp;once.</p>
|
||
<p>No, I don&#8217;t want my budget manager to make my shopping list, or to run a
|
||
blog for me, thanks. I want it to let me focus on something else. Keep
|
||
out of my&nbsp;way.</p>
|
||
<h3 id="no-user-registration">No user&nbsp;registration</h3>
|
||
<p>You don&#8217;t need to register an account on the website to start using it.
|
||
You just have to create a project, set a secret code for it, and give
|
||
both the url and the code to the people you want to share it with (or
|
||
the website can poke them for&nbsp;you).</p>
|
||
<h3 id="keeping-things-simple">Keeping things&nbsp;simple</h3>
|
||
<p><span class="dquo">&#8220;</span>Keep It Simple, Stupid&#8221; really matches our philosophy here: you want to
|
||
add a bill? Okay. Just do it. You just have to enter who paid, for who,
|
||
how much, and a description, like you would have done when you&#8217;re back
|
||
from the farmer&#8217;s market on raw&nbsp;paper.</p>
|
||
<h3 id="no-categories">No&nbsp;categories</h3>
|
||
<p>Some people like to organise their stuff into different &#8220;categories&#8221;:
|
||
leisure, work, eating, etc. That&#8217;s not something I want (at least to
|
||
begin&nbsp;with).</p>
|
||
<p>I want things to be simple. Got that? Great. Just add your&nbsp;bills!</p>
|
||
<h3 id="balance">Balance</h3>
|
||
<p>One of the most useful thing is to know what&#8217;s your &#8220;balance&#8221; compared
|
||
to others. In other words, if you&#8217;re negative, you owe money, if you&#8217;re
|
||
positive, you have to receive money. This allows you to dispatch who has
|
||
to pay for the next thing, in order to re-equilibrate the&nbsp;balance.</p>
|
||
<p>Additionally, the system is able to compute for you who has to give how
|
||
much to who, in order to reduce the number of transactions needed to
|
||
restore the&nbsp;balance.</p>
|
||
<h3 id="api"><span class="caps">API</span></h3>
|
||
<p>All of what&#8217;s possible to do with the standard web interface is also
|
||
available through a <span class="caps">REST</span> <span class="caps">API</span>. I developed a simple <span class="caps">REST</span> toolkit for
|
||
flask for this (and I should release&nbsp;it!).</p>
|
||
<h2 id="interested">Interested?</h2>
|
||
<p>This project is open source. All of us like to share what we are doing
|
||
and would be happy to work with new people and implement new ideas. If
|
||
you have a nice idea about this, if you want to tweak it or to fill
|
||
bugs. Don&#8217;t hesitate a second! The project lives at
|
||
<a href="http://github.com/spiral-project/ihatemoney/">http://github.com/spiral-project/ihatemoney/</a></p></content><category term="code"></category></entry><entry><title>Using dbpedia to get languages influences</title><link href="https://blog.notmyidea.org/using-dbpedia-to-get-languages-influences.html" rel="alternate"></link><published>2011-08-16T00:00:00+02:00</published><updated>2011-08-16T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-08-16:/using-dbpedia-to-get-languages-influences.html</id><summary type="html">
|
||
<p>While browsing the Python&#8217;s wikipedia page, I found information about
|
||
the languages influenced by python, and the languages that influenced
|
||
python&nbsp;itself.</p>
|
||
<p>Well, that&#8217;s kind of interesting to know which languages influenced
|
||
others, it could even be more interesting to have an overview of the
|
||
connexion between them …</p></summary><content type="html">
|
||
<p>While browsing the Python&#8217;s wikipedia page, I found information about
|
||
the languages influenced by python, and the languages that influenced
|
||
python&nbsp;itself.</p>
|
||
<p>Well, that&#8217;s kind of interesting to know which languages influenced
|
||
others, it could even be more interesting to have an overview of the
|
||
connexion between them, keeping python as the main&nbsp;focus.</p>
|
||
<p>This information is available on the wikipedia page, but not in a really
|
||
exploitable format. Hopefully, this information is provided into the
|
||
information box present on the majority of wikipedia pages. And… guess
|
||
what? there is project with the goal to scrap and index all this
|
||
information in a more queriable way, using the semantic web&nbsp;technologies.</p>
|
||
<p>Well, you may have guessed it, the project in question in dbpedia, and
|
||
exposes information in the form of <span class="caps">RDF</span> triples, which are way more easy
|
||
to work with than simple <span class="caps">HTML</span>.</p>
|
||
<p>For instance, let&#8217;s take the page about python:
|
||
<a href="http://dbpedia.org/page/Python_%28programming_language%29">http://dbpedia.org/page/Python_%28programming_language%29</a></p>
|
||
<p>The interesting properties here are &#8220;Influenced&#8221; and &#8220;InfluencedBy&#8221;,
|
||
which allows us to get a list of languages. Unfortunately, they are not
|
||
really using all the power of the Semantic Web here, and the list is
|
||
actually a string with coma separated values in&nbsp;it.</p>
|
||
<p>Anyway, we can use a simple rule: All wikipedia pages of programming
|
||
languages are either named after the name of the language itself, or
|
||
suffixed with &#8220;( programming language)&#8221;, which is the case for&nbsp;python.</p>
|
||
<p>So I&#8217;ve built <a href="https://github.com/ametaireau/experiments/blob/master/influences/get_influences.py">a tiny script to extract the information from
|
||
dbpedia</a>
|
||
and transform them into a shiny graph using&nbsp;graphviz.</p>
|
||
<p>After a&nbsp;nice:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>python<span class="w"> </span>get_influences.py<span class="w"> </span>python<span class="w"> </span>dot<span class="w"> </span><span class="p">|</span><span class="w"> </span>dot<span class="w"> </span>-Tpng<span class="w"> </span>&gt;<span class="w"> </span>influences.png
|
||
</code></pre></div>
|
||
|
||
<p>The result is the following graph (<a href="http://files.lolnet.org/alexis/influences.png">see it directly
|
||
here</a>)</p>
|
||
<p><img alt="Graph des influances des langages les uns sur les
|
||
autres." src="http://files.lolnet.org/alexis/influences.png"></p>
|
||
<p>While reading this diagram, keep in mind that it is a) not listing all
|
||
the languages and b) keeping a python&nbsp;perspective.</p>
|
||
<p>This means that you can trust the scheme by following the arrows from
|
||
python to something and from something to python, it is not trying to
|
||
get the matching between all the languages at the same time to keep
|
||
stuff&nbsp;readable.</p>
|
||
<p>It would certainly be possible to have all the connections between all
|
||
languages (and the resulting script would be easier) to do so, but the
|
||
resulting graph would probably be way less&nbsp;readable.</p>
|
||
<p>You can find the script <a href="https://github.com/ametaireau/experiments">on my github
|
||
account</a>. Feel free to adapt
|
||
it for whatever you want if you feel&nbsp;hackish.</p></content><category term="code"></category></entry><entry><title>Pelican, 9 months later</title><link href="https://blog.notmyidea.org/pelican-9-months-later.html" rel="alternate"></link><published>2011-07-25T00:00:00+02:00</published><updated>2011-07-25T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-07-25:/pelican-9-months-later.html</id><summary type="html">
|
||
<p>Back in October, I released
|
||
<a href="http://docs.notmyidea.org/alexis/pelican">pelican</a>, a little piece of
|
||
code I wrote to power this weblog. I had simple needs: I wanted to be
|
||
able to use my text editor of choice (vim), a vcs (mercurial) and
|
||
restructured text. I started to write a really simple blog engine in …</p></summary><content type="html">
|
||
<p>Back in October, I released
|
||
<a href="http://docs.notmyidea.org/alexis/pelican">pelican</a>, a little piece of
|
||
code I wrote to power this weblog. I had simple needs: I wanted to be
|
||
able to use my text editor of choice (vim), a vcs (mercurial) and
|
||
restructured text. I started to write a really simple blog engine in
|
||
something like a hundred python lines and released it on&nbsp;github.</p>
|
||
<p>And people started contributing. I wasn&#8217;t at all expecting to see people
|
||
interested in such a little piece of code, but it turned out that they
|
||
were. I refactored the code to make it evolve a bit more by two times
|
||
and eventually, in 9 months, got 49 forks, 139 issues and 73 pull&nbsp;requests.</p>
|
||
<p><strong>Which is clearly&nbsp;awesome.</strong></p>
|
||
<p>I pulled features such as translations, tag clouds, integration with
|
||
different services such as twitter or piwik, import from dotclear and
|
||
rss, fixed a number of mistakes and improved a lot the codebase. This
|
||
was a proof that there is a bunch of people that are willing to make
|
||
better softwares just for the sake of&nbsp;fun.</p>
|
||
<p>Thank you, guys, you&#8217;re why I like open source so&nbsp;much.</p></content><category term="code"></category></entry><entry><title>Using JPype to bridge python and Java</title><link href="https://blog.notmyidea.org/using-jpype-to-bridge-python-and-java.html" rel="alternate"></link><published>2011-06-11T00:00:00+02:00</published><updated>2011-06-11T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-06-11:/using-jpype-to-bridge-python-and-java.html</id><summary type="html">
|
||
<p>Java provides some interesting libraries that have no exact equivalent
|
||
in python. In my case, the awesome boilerpipe library allows me to
|
||
remove uninteresting parts of <span class="caps">HTML</span> pages, like menus, footers and other
|
||
&#8220;boilerplate&#8221;&nbsp;contents.</p>
|
||
<p>Boilerpipe is written in Java. Two solutions then: using java from
|
||
python or reimplement boilerpipe …</p></summary><content type="html">
|
||
<p>Java provides some interesting libraries that have no exact equivalent
|
||
in python. In my case, the awesome boilerpipe library allows me to
|
||
remove uninteresting parts of <span class="caps">HTML</span> pages, like menus, footers and other
|
||
&#8220;boilerplate&#8221;&nbsp;contents.</p>
|
||
<p>Boilerpipe is written in Java. Two solutions then: using java from
|
||
python or reimplement boilerpipe in python. I will let you guess which
|
||
one I chosen,&nbsp;meh.</p>
|
||
<p>JPype allows to bridge python project with java libraries. It takes
|
||
another point of view than Jython: rather than reimplementing python in
|
||
Java, both languages are interfacing at the <span class="caps">VM</span> level. This means you
|
||
need to start a <span class="caps">VM</span> from your python script, but it does the job and stay
|
||
fully compatible with Cpython and its C&nbsp;extensions.</p>
|
||
<h2 id="first-steps-with-jpype">First steps with&nbsp;JPype</h2>
|
||
<p>Once JPype installed (you&#8217;ll have to hack a bit some files to integrate
|
||
seamlessly with your system) you can access java classes by doing
|
||
something like&nbsp;that:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">jpype</span>
|
||
<span class="n">jpype</span><span class="o">.</span><span class="n">startJVM</span><span class="p">(</span><span class="n">jpype</span><span class="o">.</span><span class="n">getDefaultJVMPath</span><span class="p">())</span>
|
||
|
||
<span class="c1"># you can then access to the basic java functions</span>
|
||
<span class="n">jpype</span><span class="o">.</span><span class="n">java</span><span class="o">.</span><span class="n">lang</span><span class="o">.</span><span class="n">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="s2">&quot;hello world&quot;</span><span class="p">)</span>
|
||
|
||
<span class="c1"># and you have to shutdown the VM at the end</span>
|
||
<span class="n">jpype</span><span class="o">.</span><span class="n">shutdownJVM</span><span class="p">()</span>
|
||
</code></pre></div>
|
||
|
||
<p>Okay, now we have a hello world, but what we want seems somehow more
|
||
complex. We want to interact with java classes, so we will have to load&nbsp;them.</p>
|
||
<h2 id="interfacing-with-boilerpipe">Interfacing with&nbsp;Boilerpipe</h2>
|
||
<p>To install boilerpipe, you just have to run an ant&nbsp;script:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="nb">cd</span><span class="w"> </span>boilerpipe
|
||
$<span class="w"> </span>ant
|
||
</code></pre></div>
|
||
|
||
<p>Here is a simple example of how to use boilerpipe in Java, from their&nbsp;sources</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nn">de.l3s.boilerpipe.demo</span><span class="p">;</span>
|
||
<span class="kn">import</span><span class="w"> </span><span class="nn">java.net.URL</span><span class="p">;</span>
|
||
<span class="kn">import</span><span class="w"> </span><span class="nn">de.l3s.boilerpipe.extractors.ArticleExtractor</span><span class="p">;</span>
|
||
|
||
<span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Oneliner</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">URL</span><span class="w"> </span><span class="n">url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">URL</span><span class="p">(</span><span class="s">&quot;http://notmyidea.org&quot;</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">ArticleExtractor</span><span class="p">.</span><span class="na">INSTANCE</span><span class="p">.</span><span class="na">getText</span><span class="p">(</span><span class="n">url</span><span class="p">));</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<p>To run&nbsp;it:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>javac<span class="w"> </span>-cp<span class="w"> </span>dist/boilerpipe-1.1-dev.jar:lib/nekohtml-1.9.13.jar:lib/xerces-2.9.1.jar<span class="w"> </span>src/demo/de/l3s/boilerpipe/demo/Oneliner.java
|
||
$<span class="w"> </span>java<span class="w"> </span>-cp<span class="w"> </span>src/demo:dist/boilerpipe-1.1-dev.jar:lib/nekohtml-1.9.13.jar:lib/xerces-2.9.1.jar<span class="w"> </span>de.l3s.boilerpipe.demo.Oneliner
|
||
</code></pre></div>
|
||
|
||
<p>Yes, this is kind of ugly, sorry for your eyes. Let&#8217;s try something
|
||
similar, but from&nbsp;python</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">jpype</span>
|
||
|
||
<span class="c1"># start the JVM with the good classpaths</span>
|
||
<span class="n">classpath</span> <span class="o">=</span> <span class="s2">&quot;dist/boilerpipe-1.1-dev.jar:lib/nekohtml-1.9.13.jar:lib/xerces-2.9.1.jar&quot;</span>
|
||
<span class="n">jpype</span><span class="o">.</span><span class="n">startJVM</span><span class="p">(</span><span class="n">jpype</span><span class="o">.</span><span class="n">getDefaultJVMPath</span><span class="p">(),</span> <span class="s2">&quot;-Djava.class.path=</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">classpath</span><span class="p">)</span>
|
||
|
||
<span class="c1"># get the Java classes we want to use</span>
|
||
<span class="n">DefaultExtractor</span> <span class="o">=</span> <span class="n">jpype</span><span class="o">.</span><span class="n">JPackage</span><span class="p">(</span><span class="s2">&quot;de&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">l3s</span><span class="o">.</span><span class="n">boilerpipe</span><span class="o">.</span><span class="n">extractors</span><span class="o">.</span><span class="n">DefaultExtractor</span>
|
||
|
||
<span class="c1"># call them !</span>
|
||
<span class="nb">print</span> <span class="n">DefaultExtractor</span><span class="o">.</span><span class="n">INSTANCE</span><span class="o">.</span><span class="n">getText</span><span class="p">(</span><span class="n">jpype</span><span class="o">.</span><span class="n">java</span><span class="o">.</span><span class="n">net</span><span class="o">.</span><span class="n">URL</span><span class="p">(</span><span class="s2">&quot;http://blog.notmyidea.org&quot;</span><span class="p">))</span>
|
||
</code></pre></div>
|
||
|
||
<p>And you get what you&nbsp;want.</p>
|
||
<p>I must say I didn&#8217;t thought it could work so easily. This will allow me
|
||
to extract text content from URLs and remove the <em>boilerplate</em> text
|
||
easily for infuse (my master thesis project), without having to write
|
||
java code,&nbsp;nice!</p></content><category term="code"></category></entry><entry><title>Un coup de main pour mon mémoire !</title><link href="https://blog.notmyidea.org/un-coup-de-main-pour-mon-memoire.html" rel="alternate"></link><published>2011-05-25T00:00:00+02:00</published><updated>2011-05-25T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-05-25:/un-coup-de-main-pour-mon-memoire.html</id><summary type="html">
|
||
<p>Ça y est, bientôt la fin. <span class="caps">LA</span> <span class="caps">FIN</span>. La fin des études, et le début du
|
||
reste. En attendant je bosse sur mon mémoire de fin d&#8217;études et j&#8217;aurais
|
||
besoin d&#8217;un petit coup de&nbsp;main.</p>
|
||
<p>Mon mémoire porte sur les systèmes de recommandation. Pour ceux qui
|
||
connaissent …</p></summary><content type="html">
|
||
<p>Ça y est, bientôt la fin. <span class="caps">LA</span> <span class="caps">FIN</span>. La fin des études, et le début du
|
||
reste. En attendant je bosse sur mon mémoire de fin d&#8217;études et j&#8217;aurais
|
||
besoin d&#8217;un petit coup de&nbsp;main.</p>
|
||
<p>Mon mémoire porte sur les systèmes de recommandation. Pour ceux qui
|
||
connaissent last.fm, je fais quelque chose de similaire mais pour les
|
||
sites internet: en me basant sur ce que vous visitez quotidiennement et
|
||
comment vous le visitez (quelles horaires, quelle emplacement
|
||
géographique, etc.) je souhaites proposer des liens qui vous
|
||
intéresseront potentiellement, en me basant sur l&#8217;avis des personnes
|
||
qui ont des profils similaires au&nbsp;votre.</p>
|
||
<p>Le projet est loin d&#8217;être terminé, mais la première étape est de
|
||
récupérer des données de navigation, idéalement beaucoup de données de
|
||
navigation. Donc si vous pouvez me filer un coup de main je vous en
|
||
serais éternellement reconnaissant (pour ceux qui font semblant de pas
|
||
comprendre, entendez &#8220;tournée&nbsp;générale&#8221;).</p>
|
||
<p>J&#8217;ai créé un petit site web (en anglais) qui résume un peu le concept,
|
||
qui vous propose de vous inscrire et de télécharger un plugin firefox
|
||
qui m&#8217;enverra des information sur les sites que vous visitez (si vous
|
||
avez l&#8217;habitude d&#8217;utiliser chrome vous pouvez considérer de switcher à
|
||
firefox4 pour les deux prochains mois pour me filer un coup de main). Il
|
||
est possible de désactiver le plugin d&#8217;un simple clic si vous souhaitez
|
||
garder votre vie privée privée&nbsp;;-)</p>
|
||
<p>Le site est par là: <a href="http://infuse.notmyidea.org">http://infuse.notmyidea.org</a>. Une fois le plugin
|
||
téléchargé et le compte créé il faut renseigner vos identifiants dans
|
||
le plugin en question, et c&#8217;est&nbsp;tout!</p>
|
||
<p>A votre bon cœur ! Je récupérerais probablement des données durant les
|
||
2 prochains mois pour ensuite les analyser&nbsp;correctement.</p>
|
||
<p>Merci pour votre aide&nbsp;!</p></content><category term="code"></category></entry><entry><title>Analyse users’ browsing context to build up a web recommender</title><link href="https://blog.notmyidea.org/analyse-users-browsing-context-to-build-up-a-web-recommender.html" rel="alternate"></link><published>2011-04-01T00:00:00+02:00</published><updated>2011-04-01T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-04-01:/analyse-users-browsing-context-to-build-up-a-web-recommender.html</id><summary type="html">
|
||
<p>No, this is not an april&#8217;s fool&nbsp;;)</p>
|
||
<p>Wow, it&#8217;s been a long time. My year in Oxford is going really well. I
|
||
realized few days ago that the end of the year is approaching really
|
||
quickly. Exams are coming in one month or such and then I&#8217;ll …</p></summary><content type="html">
|
||
<p>No, this is not an april&#8217;s fool&nbsp;;)</p>
|
||
<p>Wow, it&#8217;s been a long time. My year in Oxford is going really well. I
|
||
realized few days ago that the end of the year is approaching really
|
||
quickly. Exams are coming in one month or such and then I&#8217;ll be working
|
||
full time on my dissertation&nbsp;topic.</p>
|
||
<p>When I learned we&#8217;ll have about 6 month to work on something, I first
|
||
thought about doing a packaging related stuff, but finally decided to
|
||
start something new. After all, that&#8217;s the good time to&nbsp;learn.</p>
|
||
<p>Since a long time, I&#8217;m being impressed by the <a href="http://last.fm">last.fm</a>
|
||
recommender system. They&#8217;re <em>scrobbling</em> the music I listen to since
|
||
something like 5 years now and the recommendations they&#8217;re doing are
|
||
really nice and accurate (I discovered <strong>a lot</strong> of great artists
|
||
listening to the &#8220;neighbour radio&#8221;.) (by the way, <a href="http://lastfm.com/user/akounet/">here
|
||
is</a> my lastfm&nbsp;account)</p>
|
||
<p>So I decided to work on recommender systems, to better understand what
|
||
is it&nbsp;about.</p>
|
||
<p>Recommender systems are usually used to increase the sales of products
|
||
(like Amazon.com does) which is not really what I&#8217;m looking for (The one
|
||
who know me a bit know I&#8217;m kind of sick about all this consumerism going&nbsp;on).</p>
|
||
<p>Actually, the most simple thing I thought of was the web: I&#8217;m browsing
|
||
it quite every day and each time new content appears. I&#8217;ve stopped to
|
||
follow <a href="https://bitbucket.org/bruno/aspirator/">my feed reader</a> because
|
||
of the information overload, and reduced drastically the number of
|
||
people I follow <a href="http://twitter.com/ametaireau/">on twitter</a>.</p>
|
||
<p>Too much information kills the&nbsp;information.</p>
|
||
<p>You shall got what will be my dissertation topic: a recommender system
|
||
for the web. Well, such recommender systems already exists, so I will
|
||
try to add contextual information to them: you&#8217;re probably not
|
||
interested by the same topics at different times of the day, or
|
||
depending on the computer you&#8217;re using. We can also probably make good
|
||
use of the way you browse to create groups into the content you&#8217;re
|
||
browsing (or even use the great firefox4 tab group&nbsp;feature).</p>
|
||
<p>There is a large part of concerns to have about user&#8217;s privacy as&nbsp;well.</p>
|
||
<p>Here is my proposal (copy/pasted from the one I had to do for my&nbsp;master)</p>
|
||
<h2 id="introduction-and-rationale">Introduction and&nbsp;rationale</h2>
|
||
<p>Nowadays, people surf the web more and more often. New web pages are
|
||
created each day so the amount of information to retrieve is more
|
||
important as the time passes. These users uses the web in different
|
||
contexts, from finding cooking recipes to technical&nbsp;articles.</p>
|
||
<p>A lot of people share the same interest to various topics, and the
|
||
quantity of information is such than it&#8217;s really hard to triage them
|
||
efficiently without spending hours doing it. Firstly because of the huge
|
||
quantity of information but also because the triage is something
|
||
relative to each person. Although, this triage can be facilitated by
|
||
fetching the browsing information of all particular individuals and put
|
||
the in&nbsp;perspective.</p>
|
||
<p>Machine learning is a branch of Artificial Intelligence (<span class="caps">AI</span>) which deals
|
||
with how a program can learn from data. Recommendation systems are a
|
||
particular application area of machine learning which is able to
|
||
recommend things (links in our case) to the users, given a particular
|
||
database containing the previous choices users have&nbsp;made.</p>
|
||
<p>This browsing information is currently available in browsers. Even if it
|
||
is not in a very usable format, it is possible to transform it to
|
||
something useful. This information gold mine just wait to be used.
|
||
Although, it is not as simple as it can seems at the first approach: It
|
||
is important to take care of the context the user is in while browsing
|
||
links. For instance, It&#8217;s more likely that during the day, a computer
|
||
scientist will browse computing related links, and that during the
|
||
evening, he browse cooking recipes or something&nbsp;else.</p>
|
||
<p>Page contents are also interesting to analyse, because that&#8217;s what
|
||
people browse and what actually contain the most interesting part of the
|
||
information. The raw data extracted from the browsing can then be
|
||
translated into something more useful (namely tags, type of resource,
|
||
visit frequency, navigation context&nbsp;etc.)</p>
|
||
<p>The goal of this dissertation is to create a recommender system for web
|
||
links, including this context&nbsp;information.</p>
|
||
<p>At the end of the dissertation, different pieces of software will be
|
||
provided, from raw data collection from the browser to a recommendation&nbsp;system.</p>
|
||
<h2 id="background-review">Background&nbsp;Review</h2>
|
||
<p>This dissertation is mainly about data extraction, analysis and
|
||
recommendation systems. Two different research area can be isolated:
|
||
Data preprocessing and Information&nbsp;filtering.</p>
|
||
<p>The first step in order to make recommendations is to gather some data.
|
||
The more data we have available, the better it is (T. Segaran, 2007).
|
||
This data can be retrieved in various ways, one of them is to get it
|
||
directly from user&#8217;s&nbsp;browsers.</p>
|
||
<h3 id="data-preparation-and-extraction">Data preparation and&nbsp;extraction</h3>
|
||
<p>The data gathered from browsers is basically URLs and additional
|
||
information about the context of the navigation. There is clearly a need
|
||
to extract more information about the meaning of the data the user is
|
||
browsing, starting by the content of the web&nbsp;pages.</p>
|
||
<p>Because the information provided on the current Web is not meant to be
|
||
read by machines (T. Berners Lee, 2001) there is a need of tools to
|
||
extract meaning from web pages. The information needs to be preprocessed
|
||
before stored in a machine readable format, allowing to make
|
||
recommendations (Choochart et Al,&nbsp;2004).</p>
|
||
<p>Data preparation is composed of two steps: cleaning and structuring (
|
||
Castellano et Al, 2007). Because raw data can contain a lot of un-needed
|
||
text (such as menus, headers etc.) and need to be cleaned prior to be
|
||
stored. Multiple techniques can be used here and belongs to boilerplate
|
||
removal and full text extraction (Kohlschütter et Al,&nbsp;2010).</p>
|
||
<p>Then, structuring the information: category, type of content (news,
|
||
blog, wiki) can be extracted from raw data. This kind of information is
|
||
not clearly defined by <span class="caps">HTML</span> pages so there is a need of tools to
|
||
recognise&nbsp;them.</p>
|
||
<p>Some context-related information can also be inferred from each
|
||
resource. It can go from the visit frequency to the navigation group the
|
||
user was in while browsing. It is also possible to determine if the user
|
||
&#8220;liked&#8221; a resource, and determine a mark for it, which can be used by
|
||
information filtering a later step (T. Segaran,&nbsp;2007).</p>
|
||
<p>At this stage, structuring the data is required. Storing this kind of
|
||
information in <span class="caps">RDBMS</span> can be a bit tedious and require complex queries to
|
||
get back the data in an usable format. Graph databases can play a major
|
||
role in the simplification of information storage and&nbsp;querying.</p>
|
||
<h3 id="information-filtering">Information&nbsp;filtering</h3>
|
||
<p>To filter the information, three techniques can be used (Balabanovic et
|
||
Al,&nbsp;1997):</p>
|
||
<ul>
|
||
<li>The content-based approach states that if an user have liked
|
||
something in the past, he is more likely to like similar things in
|
||
the future. So it&#8217;s about establishing a profile for the user and
|
||
compare new items against&nbsp;it.</li>
|
||
<li>The collaborative approach will rather recommend items that other
|
||
similar users have liked. This approach consider only the
|
||
relationship between users, and not the profile of the user we are
|
||
making recommendations&nbsp;to.</li>
|
||
<li>the hybrid approach, which appeared recently combine both of the
|
||
previous approaches, giving recommendations when items score high
|
||
regarding user&#8217;s profile, or if a similar user already liked&nbsp;it.</li>
|
||
</ul>
|
||
<p>Grouping is also something to consider at this stage (G. Myatt, 2007).
|
||
Because we are dealing with huge amount of data, it can be useful to
|
||
detect group of data that can fit together. Data clustering is able to
|
||
find such groups (T. Segaran,&nbsp;2007).</p>
|
||
<p>References:</p>
|
||
<ul>
|
||
<li>Balabanović, M., <span class="amp">&amp;</span> Shoham, Y. (1997). Fab: content-based,
|
||
collaborative recommendation. Communications of the <span class="caps">ACM</span>, 40(3),
|
||
66–72. <span class="caps">ACM</span>. Retrieved March 1, 2011, from
|
||
<a href="http://portal.acm.org/citation.cfm?id=245108.245124&amp;">http://portal.acm.org/citation.cfm?id=245108.245124&amp;</a>;.</li>
|
||
<li>Berners-Lee, T., Hendler, J., <span class="amp">&amp;</span> Lassila, O. (2001). The semantic
|
||
web: Scientific american. Scientific American, 284(5), 34–43.
|
||
Retrieved November 21, 2010, from
|
||
<a href="http://www.citeulike.org/group/222/article/1176986">http://www.citeulike.org/group/222/article/1176986</a>.</li>
|
||
<li>Castellano, G., Fanelli, A., <span class="amp">&amp;</span> Torsello, M. (2007). <span class="caps">LODAP</span>: a LOg
|
||
DAta Preprocessor for mining Web browsing patterns. Proceedings of
|
||
the 6th Conference on 6th <span class="caps">WSEAS</span> Int. Conf. on Artificial
|
||
Intelligence, Knowledge Engineering and Data Bases-Volume 6 (p.
|
||
12–17). World Scientific and Engineering Academy and Society
|
||
(<span class="caps">WSEAS</span>). Retrieved March 8, 2011, from
|
||
<a href="http://portal.acm.org/citation.cfm?id=1348485.1348488">http://portal.acm.org/citation.cfm?id=1348485.1348488</a>.</li>
|
||
<li>Kohlschutter, C., Fankhauser, P., <span class="amp">&amp;</span> Nejdl, W. (2010). Boilerplate
|
||
detection using shallow text features. Proceedings of the third <span class="caps">ACM</span>
|
||
international conference on Web search and data mining (p. 441–450).
|
||
<span class="caps">ACM</span>. Retrieved March 8, 2011, from
|
||
<a href="http://portal.acm.org/citation.cfm?id=1718542">http://portal.acm.org/citation.cfm?id=1718542</a>.</li>
|
||
<li>Myatt, <span class="caps">G. J.</span>(2007). Making Sense of Data: A Practical Guide to
|
||
Exploratory Data Analysis and Data&nbsp;Mining.</li>
|
||
<li>Segaran, T. (2007). Collective&nbsp;Intelligence.</li>
|
||
</ul>
|
||
<h2 id="privacy">Privacy</h2>
|
||
<p>The first thing that&#8217;s come to people minds when it comes to process
|
||
their browsing data is privacy. People don&#8217;t want to be stalked. That&#8217;s
|
||
perfectly right, and I don&#8217;t&nbsp;either.</p>
|
||
<p>But such a system don&#8217;t have to deal with people identities. It&#8217;s
|
||
completely possible to process completely anonymous data, and that&#8217;s
|
||
probably what I&#8217;m gonna&nbsp;do.</p>
|
||
<p>By the way, if you have interesting thoughts about that, if you do know
|
||
projects that do seems related, fire the comments&nbsp;!</p>
|
||
<h2 id="whats-the-plan">What&#8217;s the plan&nbsp;?</h2>
|
||
<p>There is a lot of different things to explore, especially because I&#8217;m a
|
||
complete novice in that&nbsp;field.</p>
|
||
<ul>
|
||
<li>I want to develop a firefox plugin, to extract the browsing
|
||
informations ( still, I need to know exactly which kind of
|
||
informations to retrieve). The idea is to provide some <em>raw</em>
|
||
browsing data, and then to transform it and to store it in the
|
||
better possible&nbsp;way.</li>
|
||
<li>Analyse how to store the informations in a graph database. What can
|
||
be the different methods to store this data and to visualize the
|
||
relationship between different pieces of data? How can I define the
|
||
different contexts, and add those informations in the&nbsp;db?</li>
|
||
<li>Process the data using well known recommendation algorithms. Compare
|
||
the results and criticize their&nbsp;value.</li>
|
||
</ul>
|
||
<p>There is plenty of stuff I want to try during this&nbsp;experimentation:</p>
|
||
<ul>
|
||
<li>I want to try using Geshi to visualize the connexion between the
|
||
links, and the&nbsp;contexts</li>
|
||
<li>Try using graph databases such as&nbsp;Neo4j</li>
|
||
<li>Having a deeper look at tools such as scikit.learn (a machine
|
||
learning toolkit in&nbsp;python)</li>
|
||
<li>Analyse web pages in order to categorize them. Processing their
|
||
contents as well, to do some keyword based classification will be&nbsp;done.</li>
|
||
</ul>
|
||
<p>Lot of work on its way, yay&nbsp;!</p></content><category term="code"></category></entry><entry><title>Working directly on your server? How to backup and sync your dev environment with unison</title><link href="https://blog.notmyidea.org/working-directly-on-your-server-how-to-backup-and-sync-your-dev-environment-with-unison.html" rel="alternate"></link><published>2011-03-16T00:00:00+01:00</published><updated>2011-03-16T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-03-16:/working-directly-on-your-server-how-to-backup-and-sync-your-dev-environment-with-unison.html</id><summary type="html">
|
||
<p>I have a server running freebsd since some time now, and was wondering
|
||
about the possibility to directly have a development environment ready
|
||
to use when I get a internet connexion, even if I&#8217;m not on my&nbsp;computer.</p>
|
||
<p>Since I use vim to code, and spend most of my …</p></summary><content type="html">
|
||
<p>I have a server running freebsd since some time now, and was wondering
|
||
about the possibility to directly have a development environment ready
|
||
to use when I get a internet connexion, even if I&#8217;m not on my&nbsp;computer.</p>
|
||
<p>Since I use vim to code, and spend most of my time in a console while
|
||
developing, it&#8217;s possible to work via ssh, from&nbsp;everywhere.</p>
|
||
<p>The only problem is the synchronisation of the source code, config files
|
||
etc. from my machine to the&nbsp;server.</p>
|
||
<p>Unison provides an interesting way to synchronise two folders, even over
|
||
a network. So let&#8217;s do it&nbsp;!</p>
|
||
<h2 id="creating-the-jail">Creating the&nbsp;jail</h2>
|
||
<p>In case you don&#8217;t use FreeBSD, you can skip this&nbsp;section.</p>
|
||
<div class="highlight"><pre><span></span><code># I have a flavour jail named default
|
||
$ ezjail-admin -f default workspace.notmyidea.org 172.19.1.6
|
||
$ ezjail-admin start workspace.notmyidea.org
|
||
</code></pre></div>
|
||
|
||
<p>In my case, because the &#8220;default&#8221; flavour contains already a lot of
|
||
interesting things, my jail come already setup with ssh, bash and vim
|
||
for instance, but maybe you&#8217;ll need it in your&nbsp;case.</p>
|
||
<p>I want to be redirected to the ssh of the jail when I connect to the
|
||
host with the 20006 port. Add lines in <code>/etc/pf.conf</code>:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nv">workspace_jail</span><span class="o">=</span><span class="s2">&quot;172.19.1.6&quot;</span>
|
||
<span class="w"> </span>rdr<span class="w"> </span>on<span class="w"> </span><span class="nv">$ext_if</span><span class="w"> </span>proto<span class="w"> </span>tcp<span class="w"> </span>from<span class="w"> </span>any<span class="w"> </span>to<span class="w"> </span><span class="nv">$ext_ip</span><span class="w"> </span>port<span class="w"> </span><span class="m">20006</span><span class="w"> </span>-&gt;<span class="w"> </span><span class="nv">$workspace_jail</span><span class="w"> </span>port<span class="w"> </span><span class="m">22</span>
|
||
</code></pre></div>
|
||
|
||
<p>Reload packet filter&nbsp;rules</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>/etc/rc.d/pf<span class="w"> </span>reload
|
||
</code></pre></div>
|
||
|
||
<h2 id="working-with-unison">Working with&nbsp;unison</h2>
|
||
<p>Now that we&#8217;ve set up the jail. Set up unison on the server and on your
|
||
client. Unison is available on the freebsd ports so just install&nbsp;it</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>ssh<span class="w"> </span>notmyidea.org<span class="w"> </span>-p<span class="w"> </span><span class="m">20006</span>
|
||
$<span class="w"> </span>make<span class="w"> </span>-C<span class="w"> </span>/usr/ports/net/unison-nox11<span class="w"> </span>config-recursive
|
||
$<span class="w"> </span>make<span class="w"> </span>-C<span class="w"> </span>/usr/ports/net/unison-nox11<span class="w"> </span>package-recursive
|
||
</code></pre></div>
|
||
|
||
<p>Install as well unison on your local machine. Double check to install
|
||
the same version on the client and on the server. Ubuntu contains the
|
||
2.27.57 as well as the&nbsp;2.32.52.</p>
|
||
<p>Check that unison is installed and reachable via ssh from your&nbsp;machine</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>ssh<span class="w"> </span>notmyidea.org<span class="w"> </span>-p<span class="w"> </span><span class="m">20006</span><span class="w"> </span>unison<span class="w"> </span>-version
|
||
unison<span class="w"> </span>version<span class="w"> </span><span class="m">2</span>.27.157
|
||
$<span class="w"> </span>unison<span class="w"> </span>-version
|
||
unison<span class="w"> </span>version<span class="w"> </span><span class="m">2</span>.27.57
|
||
</code></pre></div>
|
||
|
||
<h2 id="let-sync-our-folders">Let sync our&nbsp;folders</h2>
|
||
<p>The first thing I want to sync is my vim configuration. Well, it&#8217;s
|
||
already <a href="http://github.com/ametaireau/dotfiles/">in a git repository</a>
|
||
but let&#8217;s try to use unison for it right&nbsp;now.</p>
|
||
<p>I have two machines then: workspace, the jail, and ecureuil my&nbsp;laptop.</p>
|
||
<div class="highlight"><pre><span></span><code>unison<span class="w"> </span>.vim<span class="w"> </span>ssh://notmyidea.org:20006/.vim
|
||
unison<span class="w"> </span>.vimrc<span class="w"> </span>ssh://notmyidea.org:20006/.vimrc
|
||
</code></pre></div>
|
||
|
||
<p>It is also possible to put all the informations in a config file, and
|
||
then to only run unison. (fire up vim&nbsp;\~/.unison/default.prf.</p>
|
||
<p>Here is my&nbsp;config:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="na">root</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">/home/alexis</span>
|
||
<span class="w"> </span><span class="na">root</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">ssh://notmyidea.org:20006</span>
|
||
|
||
<span class="w"> </span><span class="na">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">.vimrc</span>
|
||
<span class="w"> </span><span class="na">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">dotfiles</span>
|
||
<span class="w"> </span><span class="na">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">dev</span>
|
||
|
||
<span class="w"> </span><span class="na">follow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Name *</span>
|
||
</code></pre></div>
|
||
|
||
<p>My vimrc is in fact a symbolic link on my laptop, but I don&#8217;t want to
|
||
specify each of the links to unison. That&#8217;s why the follow = Name * is&nbsp;for.</p>
|
||
<p>The folders you want to synchronize are maybe a bit large. If so,
|
||
considering others options such as rsync for the first import may be a
|
||
good idea (I enjoyed my university huge upload bandwith to upload <span class="caps">2GB</span> in
|
||
20mn&nbsp;;)</p>
|
||
<h2 id="run-the-script-frequently">Run the script&nbsp;frequently</h2>
|
||
<p>Once that done, you just need to run the unison command line some times
|
||
when you want to sync your two machines. I&#8217;ve wrote a tiny script to get
|
||
some feedback from the&nbsp;sync:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">os</span>
|
||
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
|
||
|
||
<span class="n">DEFAULT_LOGFILE</span> <span class="o">=</span> <span class="s2">&quot;~/unison.log&quot;</span>
|
||
<span class="n">PROGRAM_NAME</span> <span class="o">=</span> <span class="s2">&quot;Unison syncer&quot;</span>
|
||
|
||
<span class="k">def</span> <span class="nf">sync</span><span class="p">(</span><span class="n">logfile</span><span class="o">=</span><span class="n">DEFAULT_LOGFILE</span><span class="p">,</span> <span class="n">program_name</span><span class="o">=</span><span class="n">PROGRAM_NAME</span><span class="p">):</span>
|
||
<span class="c1"># init</span>
|
||
<span class="n">display_message</span> <span class="o">=</span> <span class="kc">True</span>
|
||
<span class="n">error</span> <span class="o">=</span> <span class="kc">False</span>
|
||
|
||
<span class="n">before</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
|
||
<span class="c1"># call unison to make the sync</span>
|
||
<span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s1">&#39;unison -batch &gt; </span><span class="si">{0}</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">logfile</span><span class="p">))</span>
|
||
|
||
<span class="c1"># get the duration of the operation</span>
|
||
<span class="n">td</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">before</span>
|
||
<span class="n">delta</span> <span class="o">=</span> <span class="p">(</span><span class="n">td</span><span class="o">.</span><span class="n">microseconds</span> <span class="o">+</span> <span class="p">(</span><span class="n">td</span><span class="o">.</span><span class="n">seconds</span> <span class="o">+</span> <span class="n">td</span><span class="o">.</span><span class="n">days</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">3600</span><span class="p">)</span> <span class="o">*</span> <span class="mi">10</span><span class="o">**</span><span class="mi">6</span><span class="p">)</span> <span class="o">/</span> <span class="mi">10</span><span class="o">**</span><span class="mi">6</span>
|
||
|
||
<span class="c1"># check what was the last entry in the log</span>
|
||
<span class="n">log</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">expanduser</span><span class="p">(</span><span class="n">logfile</span><span class="p">))</span>
|
||
<span class="n">lines</span> <span class="o">=</span> <span class="n">log</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="s1">&#39;No updates to propagate&#39;</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
|
||
<span class="n">display_message</span> <span class="o">=</span> <span class="kc">False</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">output</span> <span class="o">=</span> <span class="p">[</span><span class="n">l</span> <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">lines</span> <span class="k">if</span> <span class="s2">&quot;Synchronization&quot;</span> <span class="ow">in</span> <span class="n">l</span><span class="p">]</span>
|
||
|
||
<span class="n">message</span> <span class="o">=</span> <span class="n">output</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
|
||
<span class="n">message</span> <span class="o">+=</span> <span class="s2">&quot; It took </span><span class="si">{0}</span><span class="s2">s.&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">delta</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="n">display_message</span><span class="p">:</span>
|
||
<span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s1">&#39;notify-send -i </span><span class="si">{2}</span><span class="s1"> &quot;</span><span class="si">{0}</span><span class="s1">&quot; &quot;</span><span class="si">{1}</span><span class="s1">&quot;&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">program_name</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span>
|
||
<span class="s1">&#39;error&#39;</span> <span class="k">if</span> <span class="n">error</span> <span class="k">else</span> <span class="s1">&#39;info&#39;</span><span class="p">))</span>
|
||
|
||
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
|
||
<span class="n">sync</span><span class="p">()</span>
|
||
</code></pre></div>
|
||
|
||
<p>This is probably perfectible, but that does the&nbsp;job.</p>
|
||
<p>Last step is to tell you machine to run that frequently. That&#8217;s what
|
||
crontab is made for, so let&#8217;s <code>crontab -e</code>:</p>
|
||
<div class="highlight"><pre><span></span><code> $ <span class="gs">* *</span>/3 <span class="gs">* *</span> * . ~/.Xdbus; /usr/bin/python /home/alexis/dev/python/unison-syncer/sync.py
|
||
</code></pre></div>
|
||
|
||
<p>The \~/.Xdbus allows cron to communicate with your X11 session. Here is
|
||
its&nbsp;content.</p>
|
||
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/bash</span>
|
||
|
||
<span class="c1"># Get the pid of nautilus</span>
|
||
<span class="nv">nautilus_pid</span><span class="o">=</span><span class="k">$(</span>pgrep<span class="w"> </span>-u<span class="w"> </span><span class="nv">$LOGNAME</span><span class="w"> </span>-n<span class="w"> </span>nautilus<span class="k">)</span>
|
||
|
||
<span class="c1"># If nautilus isn&#39;t running, just exit silently</span>
|
||
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$nautilus_pid</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
|
||
<span class="nb">exit</span><span class="w"> </span><span class="m">0</span>
|
||
<span class="k">fi</span>
|
||
|
||
<span class="c1"># Grab the DBUS_SESSION_BUS_ADDRESS variable from nautilus&#39;s environment</span>
|
||
<span class="nb">eval</span><span class="w"> </span><span class="k">$(</span>tr<span class="w"> </span><span class="s1">&#39;\0&#39;</span><span class="w"> </span><span class="s1">&#39;\n&#39;</span><span class="w"> </span>&lt;<span class="w"> </span>/proc/<span class="nv">$nautilus_pid</span>/environ<span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span><span class="s1">&#39;^DBUS_SESSION_BUS_ADDRESS=&#39;</span><span class="k">)</span>
|
||
|
||
<span class="c1"># Check that we actually found it</span>
|
||
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$DBUS_SESSION_BUS_ADDRESS</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
|
||
<span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;Failed to find bus address&quot;</span><span class="w"> </span>&gt;<span class="p">&amp;</span><span class="m">2</span>
|
||
<span class="nb">exit</span><span class="w"> </span><span class="m">1</span>
|
||
<span class="k">fi</span>
|
||
|
||
<span class="c1"># export it so that child processes will inherit it</span>
|
||
<span class="nb">export</span><span class="w"> </span>DBUS_SESSION_BUS_ADDRESS
|
||
</code></pre></div>
|
||
|
||
<p>And it comes from
|
||
<a href="http://ubuntuforums.org/showthread.php?p=10148738#post10148738">here</a>.</p>
|
||
<p>A sync takes about 20s + the upload time on my machine, which stay
|
||
acceptable for all of my&nbsp;developments.</p></content><category term="code"></category></entry><entry><title>Wrap up of the distutils2 paris’ sprint</title><link href="https://blog.notmyidea.org/wrap-up-of-the-distutils2-paris-sprint.html" rel="alternate"></link><published>2011-02-08T00:00:00+01:00</published><updated>2011-02-08T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-02-08:/wrap-up-of-the-distutils2-paris-sprint.html</id><summary type="html">
|
||
<p>Finally, thanks to a bunch of people that helped me to pay my train and
|
||
bus tickets, I&#8217;ve made it to paris for the distutils2&nbsp;sprint.</p>
|
||
<p>They have been a bit more than 10 people to come during the sprint, and
|
||
it was very productive. Here&#8217;s a taste …</p></summary><content type="html">
|
||
<p>Finally, thanks to a bunch of people that helped me to pay my train and
|
||
bus tickets, I&#8217;ve made it to paris for the distutils2&nbsp;sprint.</p>
|
||
<p>They have been a bit more than 10 people to come during the sprint, and
|
||
it was very productive. Here&#8217;s a taste of what we&#8217;ve been working&nbsp;on:</p>
|
||
<ul>
|
||
<li>the datafiles, a way to specify and to handle the installation of
|
||
files which are not python-related (pictures, manpages and so&nbsp;on).</li>
|
||
<li>mkgcfg, a tool to help you to create a setup.cfg in minutes (and
|
||
with funny&nbsp;examples)</li>
|
||
<li>converters from setup.py scripts. We do now have a piece of code
|
||
which reads your current setup.py file and fill in some fields in
|
||
the setup.cfg for&nbsp;you.</li>
|
||
<li>a compatibility layer for distutils1, so it can read the setup.cfg
|
||
you will wrote for distutils2&nbsp;:-)</li>
|
||
<li>the uninstaller, so it&#8217;s now possible to uninstall what have been
|
||
installed by distutils2 (see <span class="caps">PEP</span>&nbsp;376)</li>
|
||
<li>the installer, and the setuptools compatibility layer, which will
|
||
allow you to rely on setuptools&#8217; based distributions (and there are
|
||
plenty of&nbsp;them!)</li>
|
||
<li>The compilers, so they are more flexible than they were. Since
|
||
that&#8217;s an obscure part of the code for distutils2 commiters (it
|
||
comes directly from the distutils1 ages), having some guys who
|
||
understood the problematics here was a&nbsp;must.</li>
|
||
</ul>
|
||
<p>Some people have also tried to port their packaging from distutils1 to
|
||
distutils2. They have spotted a number of bugs and made some
|
||
improvements to the code, to make it more friendly to&nbsp;use.</p>
|
||
<p>I&#8217;m really pleased to see how newcomers went trough the code, and
|
||
started hacking so fast. I must say it wasn&#8217;t the case when we started
|
||
to work on distutils1 so that&#8217;s a very good point: people now can hack
|
||
the code quicker than they could&nbsp;before.</p>
|
||
<p>Some of the features here are not <em>completely</em> finished yet, but are on
|
||
the tubes, and will be ready for a release (hopefully) at the end of the&nbsp;week.</p>
|
||
<p>Big thanks to logilab for hosting (and sponsoring my train ticket) and
|
||
providing us food, and to bearstech for providing some money for
|
||
breakfast and&nbsp;bears^Wbeers.</p>
|
||
<p>Again, a big thanks to all the people who gave me money to pay the
|
||
transport, I really wasn&#8217;t expecting such thing to happen&nbsp;:-)</p></content><category term="code"></category></entry><entry><title>PyPI on CouchDB</title><link href="https://blog.notmyidea.org/pypi-on-couchdb.html" rel="alternate"></link><published>2011-01-20T00:00:00+01:00</published><updated>2011-01-20T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-01-20:/pypi-on-couchdb.html</id><summary type="html">
|
||
<p>By now, there are two ways to retrieve data from PyPI (the Python
|
||
Package Index). You can both rely on xml/rpc or on the &#8220;simple&#8221; <span class="caps">API</span>. The
|
||
simple <span class="caps">API</span> is not so simple to use as the name suggest, and have several
|
||
existing&nbsp;drawbacks.</p>
|
||
<p>Basically, if you want to …</p></summary><content type="html">
|
||
<p>By now, there are two ways to retrieve data from PyPI (the Python
|
||
Package Index). You can both rely on xml/rpc or on the &#8220;simple&#8221; <span class="caps">API</span>. The
|
||
simple <span class="caps">API</span> is not so simple to use as the name suggest, and have several
|
||
existing&nbsp;drawbacks.</p>
|
||
<p>Basically, if you want to use informations coming from the simple <span class="caps">API</span>,
|
||
you will have to parse web pages manually, to extract informations using
|
||
some black vodoo magic. Badly, magic have a price, and it&#8217;s sometimes
|
||
impossible to get exactly the informations you want to get from this
|
||
index. That&#8217;s the technique currently being used by distutils2,
|
||
setuptools and&nbsp;pip.</p>
|
||
<p>On the other side, while <span class="caps">XML</span>/<span class="caps">RPC</span> is working fine, it&#8217;s requiring extra
|
||
work to the python servers each time you request something, which can
|
||
lead to some outages from time to time. Also, it&#8217;s important to point
|
||
out that, even if PyPI have a mirroring infrastructure, it&#8217;s only for
|
||
the so-called <em>simple</em> <span class="caps">API</span>, and not for the <span class="caps">XML</span>/<span class="caps">RPC</span>.</p>
|
||
<h2 id="couchdb">CouchDB</h2>
|
||
<p>Here comes CouchDB. CouchDB is a document oriented database, that knows
|
||
how to speak <span class="caps">REST</span> and <span class="caps">JSON</span>. It&#8217;s easy to use, and provides out of the
|
||
box a replication&nbsp;mechanism.</p>
|
||
<h2 id="so-what">So, what&nbsp;?</h2>
|
||
<p>Hmm, I&#8217;m sure you got it. I&#8217;ve wrote a piece of software to link
|
||
informations from PyPI to a CouchDB instance. Then you can replicate all
|
||
the PyPI index with only one <span class="caps">HTTP</span> request on the CouchDB server. You can
|
||
also access the informations from the index directly using a <span class="caps">REST</span> <span class="caps">API</span>,
|
||
speaking json.&nbsp;Handy.</p>
|
||
<p>So PyPIonCouch is using the PyPI <span class="caps">XML</span>/<span class="caps">RPC</span> <span class="caps">API</span> to get data from PyPI, and
|
||
generate records in the CouchDB&nbsp;instance.</p>
|
||
<p>The final goal is to avoid to rely on this &#8220;simple&#8221; <span class="caps">API</span>, and rely on a
|
||
<span class="caps">REST</span> insterface instead. I have set up a couchdb server on my server,
|
||
which is available at
|
||
<a href="http://couchdb.notmyidea.org/_utils/database.html?pypi">http://couchdb.notmyidea.org/_utils/database.html?pypi</a>.</p>
|
||
<p>There is not a lot to see there for now, but I&#8217;ve done the first import
|
||
from PyPI yesterday and all went fine: it&#8217;s possible to access the
|
||
metadata of all PyPI projects via a <span class="caps">REST</span> interface. Next step is to
|
||
write a client for this <span class="caps">REST</span> interface in&nbsp;distutils2.</p>
|
||
<h2 id="example">Example</h2>
|
||
<p>For now, you can use pypioncouch via the command line, or via the python
|
||
<span class="caps">API</span>.</p>
|
||
<h3 id="using-the-command-line">Using the command&nbsp;line</h3>
|
||
<p>You can do something like that for a full import. This <strong>will</strong> take
|
||
long, because it&#8217;s fetching all the projects at pypi and importing their&nbsp;metadata:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">pypioncouch</span> <span class="o">--</span><span class="n">fullimport</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">your</span><span class="o">.</span><span class="n">couchdb</span><span class="o">.</span><span class="n">instance</span><span class="o">/</span>
|
||
</code></pre></div>
|
||
|
||
<p>If you already have the data on your couchdb instance, you can just
|
||
update it with the last informations from pypi. <strong>However, I recommend
|
||
to just replicate the principal node, hosted at
|
||
<a href="http://couchdb.notmyidea.org/pypi/">http://couchdb.notmyidea.org/pypi/</a></strong>, to avoid the duplication of&nbsp;nodes:</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pypioncouch<span class="w"> </span>--update<span class="w"> </span>http://your.couchdb.instance/
|
||
</code></pre></div>
|
||
|
||
<p>The principal node is updated once a day by now, I&#8217;ll try to see if it&#8217;s
|
||
enough, and ajust with the&nbsp;time.</p>
|
||
<h3 id="using-the-python-api">Using the python <span class="caps">API</span></h3>
|
||
<p>You can also use the python <span class="caps">API</span> to interact with&nbsp;pypioncouch:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">pypioncouch</span> <span class="kn">import</span> <span class="n">XmlRpcImporter</span><span class="p">,</span> <span class="n">import_all</span><span class="p">,</span> <span class="n">update</span>
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="n">full_import</span><span class="p">()</span>
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="n">update</span><span class="p">()</span>
|
||
</code></pre></div>
|
||
|
||
<h2 id="whats-next">What&#8217;s next&nbsp;?</h2>
|
||
<p>I want to make a couchapp, in order to navigate PyPI easily. Here are
|
||
some of the features I want to&nbsp;propose:</p>
|
||
<ul>
|
||
<li>List all the available&nbsp;projects</li>
|
||
<li>List all the projects, filtered by&nbsp;specifiers</li>
|
||
<li>List all the projects by&nbsp;author/maintainer</li>
|
||
<li>List all the projects by&nbsp;keywords</li>
|
||
<li>Page for each&nbsp;project.</li>
|
||
<li>Provide a PyPI &#8220;Simple&#8221; <span class="caps">API</span> equivalent, even if I want to replace
|
||
it, I do think it will be really easy to setup mirrors that way,
|
||
with the out of the box couchdb&nbsp;replication</li>
|
||
</ul>
|
||
<p>I also still need to polish the import mechanism, so I can directly
|
||
store in&nbsp;couchdb:</p>
|
||
<ul>
|
||
<li>The <span class="caps">OPML</span> files for each&nbsp;project</li>
|
||
<li>The upload_time as couchdb friendly format (list of&nbsp;int)</li>
|
||
<li>The tags as lists (currently it&#8217;s only a string separated by&nbsp;spaces</li>
|
||
</ul>
|
||
<p>The work I&#8217;ve done by now is available on
|
||
<a href="https://bitbucket.org/ametaireau/pypioncouch/">https://bitbucket.org/ametaireau/pypioncouch/</a>. Keep in mind that it&#8217;s
|
||
still a work in progress, and everything can break at any time. However,
|
||
any feedback will be appreciated&nbsp;!</p></content><category term="code"></category></entry><entry><title>Help me to go to the distutils2 paris’ sprint</title><link href="https://blog.notmyidea.org/help-me-to-go-to-the-distutils2-paris-sprint.html" rel="alternate"></link><published>2011-01-15T00:00:00+01:00</published><updated>2011-01-15T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2011-01-15:/help-me-to-go-to-the-distutils2-paris-sprint.html</id><summary type="html">
|
||
<p><strong>Edit: Thanks to logilab and some amazing people, I can make it to
|
||
paris for the sprint. Many thanks to them for the&nbsp;support!</strong></p>
|
||
<p>There will be a distutils2 sprint from the 27th to the 30th of january,
|
||
thanks to logilab which will host the&nbsp;event.</p>
|
||
<p>You can find more …</p></summary><content type="html">
|
||
<p><strong>Edit: Thanks to logilab and some amazing people, I can make it to
|
||
paris for the sprint. Many thanks to them for the&nbsp;support!</strong></p>
|
||
<p>There will be a distutils2 sprint from the 27th to the 30th of january,
|
||
thanks to logilab which will host the&nbsp;event.</p>
|
||
<p>You can find more informations about the sprint on the wiki page of the
|
||
event (<a href="http://wiki.python.org/moin/Distutils/SprintParis">http://wiki.python.org/moin/Distutils/SprintParis</a>).</p>
|
||
<p>I really want to go there but I&#8217;m unfortunately blocked in <span class="caps">UK</span> for money
|
||
reasons. The cheapest two ways I&#8217;ve found is about £80, which I can&#8217;t
|
||
afford. Following some advices on #distutils, I&#8217;ve set up a ChipIn
|
||
account for that, so if some people want to help me making it to go
|
||
there, they can give me some money that&nbsp;way.</p>
|
||
<p>I&#8217;ll probably work on the installer (to support old distutils and
|
||
setuptools distributions) and on the uninstaller (depending on the first
|
||
task). If I can&#8217;t make it to paris, I&#8217;ll hang around on <span class="caps">IRC</span> to give some
|
||
help while&nbsp;needed.</p>
|
||
<p>If you want to contribute some money to help me go there, feel free to
|
||
use this chipin page:
|
||
<a href="http://ametaireau.chipin.com/distutils2-sprint-in-paris">http://ametaireau.chipin.com/distutils2-sprint-in-paris</a></p>
|
||
<p>Thanks for your support&nbsp;!</p></content><category term="code"></category></entry><entry><title>How to reboot your bebox using the CLI</title><link href="https://blog.notmyidea.org/how-to-reboot-your-bebox-using-the-cli.html" rel="alternate"></link><published>2010-10-21T00:00:00+02:00</published><updated>2010-10-21T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-10-21:/how-to-reboot-your-bebox-using-the-cli.html</id><summary type="html">
|
||
<p>I&#8217;ve an internet connection which, for some obscure reasons, tend to be
|
||
very slow from time to time. After rebooting the box (yes, that&#8217;s a hard
|
||
solution), all the things seems to go fine&nbsp;again.</p>
|
||
<h2 id="edit-using-grep"><span class="caps">EDIT</span> : Using&nbsp;grep</h2>
|
||
<p>After a bit of reflexion, that&#8217;s also really easy …</p></summary><content type="html">
|
||
<p>I&#8217;ve an internet connection which, for some obscure reasons, tend to be
|
||
very slow from time to time. After rebooting the box (yes, that&#8217;s a hard
|
||
solution), all the things seems to go fine&nbsp;again.</p>
|
||
<h2 id="edit-using-grep"><span class="caps">EDIT</span> : Using&nbsp;grep</h2>
|
||
<p>After a bit of reflexion, that&#8217;s also really easy to do using directly
|
||
the command line tools curl, grep and tail (but really harder to&nbsp;read).</p>
|
||
<div class="highlight"><pre><span></span><code>curl<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span>-u<span class="w"> </span>joel:joel<span class="w"> </span>http://bebox.config/cgi/b/info/restart/<span class="se">\?</span>be<span class="se">\=</span><span class="m">0</span><span class="se">\&amp;</span>l0<span class="se">\=</span><span class="m">1</span><span class="se">\&amp;</span>l1<span class="se">\=</span><span class="m">0</span><span class="se">\&amp;</span>tid<span class="se">\=</span>RESTART<span class="w"> </span>-d<span class="w"> </span><span class="s2">&quot;0=17&amp;2=`curl -u joel:joel http://bebox.config/cgi/b/info/restart/\?be\=0\&amp;l0\=1\&amp;l1\=0\&amp;tid\=RESTART | grep -o &quot;</span><span class="nv">name</span><span class="o">=</span><span class="s1">&#39;2&#39;</span><span class="w"> </span><span class="nv">value</span><span class="o">=</span><span class="err">&#39;</span><span class="o">[</span><span class="m">0</span>-9<span class="o">]</span><span class="se">\+</span><span class="s2">&quot; | grep -o &quot;</span><span class="o">[</span><span class="m">0</span>-9<span class="o">]</span><span class="se">\+</span><span class="s2">&quot; | tail -n 1`&amp;1&quot;</span>
|
||
</code></pre></div>
|
||
|
||
<h2 id="the-python-version">The Python&nbsp;version</h2>
|
||
<p>Well, that&#8217;s not the optimal solution, that&#8217;s a bit &#8220;gruik&#8221;, but it&nbsp;works.</p>
|
||
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">urllib2</span>
|
||
<span class="kn">import</span> <span class="nn">urlparse</span>
|
||
<span class="kn">import</span> <span class="nn">re</span>
|
||
<span class="kn">import</span> <span class="nn">argparse</span>
|
||
|
||
<span class="n">REBOOT_URL</span> <span class="o">=</span> <span class="s1">&#39;/b/info/restart/?be=0&amp;l0=1&amp;l1=0&amp;tid=RESTART&#39;</span>
|
||
<span class="n">BOX_URL</span> <span class="o">=</span> <span class="s1">&#39;http://bebox.config/cgi&#39;</span>
|
||
|
||
<span class="k">def</span> <span class="nf">open_url</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">):</span>
|
||
<span class="n">passman</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">HTTPPasswordMgrWithDefaultRealm</span><span class="p">()</span>
|
||
<span class="n">passman</span><span class="o">.</span><span class="n">add_password</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">)</span>
|
||
<span class="n">authhandler</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">HTTPBasicAuthHandler</span><span class="p">(</span><span class="n">passman</span><span class="p">)</span>
|
||
|
||
<span class="n">opener</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">build_opener</span><span class="p">(</span><span class="n">authhandler</span><span class="p">)</span>
|
||
|
||
<span class="n">urllib2</span><span class="o">.</span><span class="n">install_opener</span><span class="p">(</span><span class="n">opener</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">url</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
|
||
|
||
<span class="k">def</span> <span class="nf">reboot</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">):</span>
|
||
<span class="n">data</span> <span class="o">=</span> <span class="n">open_url</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">)</span>
|
||
<span class="n">token</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s2">&quot;name\=</span><span class="se">\\</span><span class="s2">&#39;2</span><span class="se">\\</span><span class="s2">&#39; value=</span><span class="se">\\</span><span class="s2">&#39;([0-9]+)</span><span class="se">\\</span><span class="s2">&#39;&quot;</span><span class="p">,</span> <span class="n">data</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
|
||
<span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">urllib2</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="n">url</span><span class="o">=</span><span class="n">url</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="s1">&#39;0=17&amp;2=</span><span class="si">%s</span><span class="s1">&amp;1&#39;</span> <span class="o">%</span> <span class="n">token</span><span class="p">))</span>
|
||
|
||
<span class="k">if</span> <span class="vm">__file__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
|
||
<span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="o">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&quot;&quot;&quot;Reboot your bebox !&quot;&quot;&quot;</span><span class="p">)</span>
|
||
|
||
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="n">dest</span><span class="o">=</span><span class="s1">&#39;user&#39;</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s1">&#39;username&#39;</span><span class="p">)</span>
|
||
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="n">dest</span><span class="o">=</span><span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s1">&#39;password&#39;</span><span class="p">)</span>
|
||
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="n">boxurl</span><span class="o">=</span><span class="s1">&#39;boxurl&#39;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">BOX_URL</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s1">&#39;Base box url. Default is </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">BOX_URL</span><span class="p">)</span>
|
||
|
||
<span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span>
|
||
<span class="n">url</span> <span class="o">=</span> <span class="n">urlparse</span><span class="o">.</span><span class="n">urljoin</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">boxurl</span><span class="p">,</span> <span class="n">REBOOT_URL</span><span class="p">)</span>
|
||
<span class="n">reboot</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">args</span><span class="o">.</span><span class="n">username</span><span class="p">,</span> <span class="n">args</span><span class="o">.</span><span class="n">password</span><span class="p">)</span>
|
||
</code></pre></div></content><category term="code"></category></entry><entry><title>Dynamically change your gnome desktop wallpaper</title><link href="https://blog.notmyidea.org/dynamically-change-your-gnome-desktop-wallpaper.html" rel="alternate"></link><published>2010-10-11T00:00:00+02:00</published><updated>2010-10-11T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-10-11:/dynamically-change-your-gnome-desktop-wallpaper.html</id><summary type="html">
|
||
<p>In gnome, you can can use a <span class="caps">XML</span> file to have a dynamic wallpaper. It&#8217;s
|
||
not so easy, and you can&#8217;t just tell: use the pictures in this folder to
|
||
do&nbsp;so.</p>
|
||
<p>You can have a look to the git repository if you want:
|
||
<a href="http://github.com/ametaireau/gnome-background-generator">http://github.com/ametaireau …</a></p></summary><content type="html">
|
||
<p>In gnome, you can can use a <span class="caps">XML</span> file to have a dynamic wallpaper. It&#8217;s
|
||
not so easy, and you can&#8217;t just tell: use the pictures in this folder to
|
||
do&nbsp;so.</p>
|
||
<p>You can have a look to the git repository if you want:
|
||
<a href="http://github.com/ametaireau/gnome-background-generator">http://github.com/ametaireau/gnome-background-generator</a></p>
|
||
<p>Some time ago, I&#8217;ve made a little python script to ease that, and you
|
||
can now use it too. It&#8217;s named &#8220;gnome-background-generator&#8221;, and you can
|
||
install it via pip for&nbsp;instance.</p>
|
||
<div class="highlight"><pre><span></span><code>shell
|
||
$ pip install gnome-background-generator
|
||
</code></pre></div>
|
||
|
||
<p>Then, you have just to use it this&nbsp;way:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">shell</span>
|
||
<span class="n">$ gnome-background-generator -p ~/Images/walls -s</span>
|
||
<span class="n">/home/alexis/Images/walls/dynamic-wallpaper.xml generated</span>
|
||
</code></pre></div>
|
||
|
||
<p>Here is a extract of the&nbsp;`&#8212;help`:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="n">shell</span>
|
||
<span class="o">$</span><span class="w"> </span><span class="n">gnome</span><span class="o">-</span><span class="n">background</span><span class="o">-</span><span class="n">generator</span><span class="w"> </span><span class="o">--</span><span class="n">help</span>
|
||
<span class="n">usage</span><span class="p">:</span><span class="w"> </span><span class="n">gnome</span><span class="o">-</span><span class="n">background</span><span class="o">-</span><span class="n">generator</span><span class="w"> </span><span class="p">[</span><span class="o">-</span><span class="n">h</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="o">-</span><span class="n">p</span><span class="w"> </span><span class="n">PATH</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="o">-</span><span class="n">o</span><span class="w"> </span><span class="n">OUTPUT</span><span class="p">]</span>
|
||
<span class="w"> </span><span class="p">[</span><span class="o">-</span><span class="n">t</span><span class="w"> </span><span class="n">TRANSITION_TIME</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="o">-</span><span class="n">d</span><span class="w"> </span><span class="n">DISPLAY_TIME</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="o">-</span><span class="n">s</span><span class="p">]</span>
|
||
<span class="w"> </span><span class="p">[</span><span class="o">-</span><span class="n">b</span><span class="p">]</span>
|
||
|
||
<span class="n">A</span><span class="w"> </span><span class="n">simple</span><span class="w"> </span><span class="n">command</span><span class="w"> </span><span class="n">line</span><span class="w"> </span><span class="k">tool</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">generate</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">XML</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">use</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">gnome</span>
|
||
<span class="n">wallpapers</span><span class="p">,</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">have</span><span class="w"> </span><span class="n">dynamic</span><span class="w"> </span><span class="n">walls</span>
|
||
|
||
<span class="n">optional</span><span class="w"> </span><span class="n">arguments</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="o">-</span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="o">--</span><span class="n">help</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="n">help</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">exit</span>
|
||
<span class="w"> </span><span class="o">-</span><span class="n">p</span><span class="w"> </span><span class="n">PATH</span><span class="p">,</span><span class="w"> </span><span class="o">--</span><span class="n">path</span><span class="w"> </span><span class="n">PATH</span><span class="w"> </span><span class="n">Path</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">look</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">pictures</span><span class="o">.</span><span class="w"> </span><span class="n">If</span><span class="w"> </span><span class="n">no</span><span class="w"> </span><span class="n">output</span><span class="w"> </span><span class="k">is</span>
|
||
<span class="w"> </span><span class="n">specified</span><span class="p">,</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">used</span><span class="w"> </span><span class="n">too</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">outputing</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">dynamic</span><span class="o">-</span>
|
||
<span class="w"> </span><span class="n">wallpaper</span><span class="o">.</span><span class="n">xml</span><span class="w"> </span><span class="n">file</span><span class="o">.</span><span class="w"> </span><span class="n">Default</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">current</span>
|
||
<span class="w"> </span><span class="n">directory</span><span class="w"> </span><span class="p">(</span><span class="o">.</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="o">-</span><span class="n">o</span><span class="w"> </span><span class="n">OUTPUT</span><span class="p">,</span><span class="w"> </span><span class="o">--</span><span class="n">output</span><span class="w"> </span><span class="n">OUTPUT</span>
|
||
<span class="w"> </span><span class="n">Output</span><span class="w"> </span><span class="n">filename</span><span class="o">.</span><span class="w"> </span><span class="n">If</span><span class="w"> </span><span class="n">no</span><span class="w"> </span><span class="n">filename</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">specified</span><span class="p">,</span><span class="w"> </span><span class="n">a</span>
|
||
<span class="w"> </span><span class="n">dynamic</span><span class="o">-</span><span class="n">wallpaper</span><span class="o">.</span><span class="n">xml</span><span class="w"> </span><span class="n">file</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">generated</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">the</span>
|
||
<span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">containing</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">pictures</span><span class="o">.</span><span class="w"> </span><span class="n">You</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">also</span><span class="w"> </span><span class="n">use</span><span class="w"> </span><span class="s2">&quot;-&quot;</span><span class="w"> </span><span class="n">to</span>
|
||
<span class="w"> </span><span class="n">display</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">xml</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">stdout</span><span class="o">.</span>
|
||
<span class="w"> </span><span class="o">-</span><span class="n">t</span><span class="w"> </span><span class="n">TRANSITION_TIME</span><span class="p">,</span><span class="w"> </span><span class="o">--</span><span class="n">transition</span><span class="o">-</span><span class="n">time</span><span class="w"> </span><span class="n">TRANSITION_TIME</span>
|
||
<span class="w"> </span><span class="n">Time</span><span class="w"> </span><span class="p">(</span><span class="ow">in</span><span class="w"> </span><span class="n">seconds</span><span class="p">)</span><span class="w"> </span><span class="n">transitions</span><span class="w"> </span><span class="n">must</span><span class="w"> </span><span class="n">last</span><span class="w"> </span><span class="p">(</span><span class="n">default</span><span class="w"> </span><span class="n">value</span>
|
||
<span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">seconds</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="o">-</span><span class="n">d</span><span class="w"> </span><span class="n">DISPLAY_TIME</span><span class="p">,</span><span class="w"> </span><span class="o">--</span><span class="n">display</span><span class="o">-</span><span class="n">time</span><span class="w"> </span><span class="n">DISPLAY_TIME</span>
|
||
<span class="w"> </span><span class="n">Time</span><span class="w"> </span><span class="p">(</span><span class="ow">in</span><span class="w"> </span><span class="n">seconds</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">picture</span><span class="w"> </span><span class="n">must</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">displayed</span><span class="o">.</span><span class="w"> </span><span class="n">Default</span>
|
||
<span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="mi">900</span><span class="w"> </span><span class="p">(</span><span class="mi">15</span><span class="n">mn</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="o">-</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="o">--</span><span class="n">set</span><span class="o">-</span><span class="n">background</span><span class="w"> </span><span class="s1">&#39;&#39;&#39;try to set the background using gnome-appearance-</span>
|
||
<span class="s1"> properties</span>
|
||
<span class="s1"> -b, --debug</span>
|
||
</code></pre></div></content><category term="code"></category></entry><entry><title>How to install NGINX + PHP 5.3 on FreeBSD.</title><link href="https://blog.notmyidea.org/how-to-install-nginx-php-53-on-freebsd.html" rel="alternate"></link><published>2010-10-10T00:00:00+02:00</published><updated>2010-10-10T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-10-10:/how-to-install-nginx-php-53-on-freebsd.html</id><summary type="html">
|
||
<ul>
|
||
<li>
|
||
<p>date<br>&nbsp;2010-10-10</p>
|
||
</li>
|
||
<li>
|
||
<p>category<br>&nbsp;tech</p>
|
||
</li>
|
||
</ul>
|
||
<p>I&#8217;ve not managed so far to get completely rid of php, so here&#8217;s a simple
|
||
reminder about how to install php on <span class="caps">NGINX</span>, for FreeBSD. Nothing hard,
|
||
but that&#8217;s worse to have the piece of configuration somewhere&nbsp;!</p>
|
||
<div class="highlight"><pre><span></span><code><span class="err">#</span><span class="w"> </span><span class="nx">update</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">ports</span>
|
||
<span class="err">$</span><span class="w"> </span><span class="nx">portsnap</span><span class="w"> </span><span class="nx">fetch</span><span class="w"> </span><span class="nx">update …</span></code></pre></div></summary><content type="html">
|
||
<ul>
|
||
<li>
|
||
<p>date<br>&nbsp;2010-10-10</p>
|
||
</li>
|
||
<li>
|
||
<p>category<br>&nbsp;tech</p>
|
||
</li>
|
||
</ul>
|
||
<p>I&#8217;ve not managed so far to get completely rid of php, so here&#8217;s a simple
|
||
reminder about how to install php on <span class="caps">NGINX</span>, for FreeBSD. Nothing hard,
|
||
but that&#8217;s worse to have the piece of configuration somewhere&nbsp;!</p>
|
||
<div class="highlight"><pre><span></span><code><span class="err">#</span><span class="w"> </span><span class="nx">update</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">ports</span>
|
||
<span class="err">$</span><span class="w"> </span><span class="nx">portsnap</span><span class="w"> </span><span class="nx">fetch</span><span class="w"> </span><span class="nx">update</span>
|
||
|
||
<span class="err">#</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nx">php5</span><span class="w"> </span><span class="nx">port</span>
|
||
<span class="err">$</span><span class="w"> </span><span class="nx">make</span><span class="w"> </span><span class="nx">config</span><span class="o">-</span><span class="nx">recursive</span><span class="w"> </span><span class="o">-</span><span class="nx">C</span><span class="w"> </span><span class="o">/</span><span class="nx">usr</span><span class="o">/</span><span class="nx">ports</span><span class="o">/</span><span class="nx">lang</span><span class="o">/</span><span class="nx">php5</span><span class="o">-</span><span class="nx">extensions</span>
|
||
<span class="err">$</span><span class="w"> </span><span class="nx">make</span><span class="w"> </span><span class="kn">package</span><span class="o">-</span><span class="nx">recursive</span><span class="w"> </span><span class="o">-</span><span class="nx">C</span><span class="w"> </span><span class="o">/</span><span class="nx">usr</span><span class="o">/</span><span class="nx">ports</span><span class="o">/</span><span class="nx">lang</span><span class="o">/</span><span class="nx">php5</span><span class="o">-</span><span class="nx">extensions</span>
|
||
|
||
<span class="err">#</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nx">nginx</span>
|
||
<span class="err">$</span><span class="w"> </span><span class="nx">make</span><span class="w"> </span><span class="nx">config</span><span class="o">-</span><span class="nx">recursive</span><span class="w"> </span><span class="o">-</span><span class="nx">C</span><span class="w"> </span><span class="o">/</span><span class="nx">usr</span><span class="o">/</span><span class="nx">ports</span><span class="o">/</span><span class="nx">www</span><span class="o">/</span><span class="nx">nginx</span><span class="o">-</span><span class="nx">devel</span>
|
||
<span class="err">$</span><span class="w"> </span><span class="nx">make</span><span class="w"> </span><span class="kn">package</span><span class="o">-</span><span class="nx">recursive</span><span class="w"> </span><span class="o">-</span><span class="nx">C</span><span class="w"> </span><span class="o">/</span><span class="nx">usr</span><span class="o">/</span><span class="nx">ports</span><span class="o">/</span><span class="nx">www</span><span class="o">/</span><span class="nx">nginx</span><span class="o">-</span><span class="nx">devel</span>
|
||
</code></pre></div>
|
||
|
||
<p>Now we have all the dependencies installed, we need to configure a bit
|
||
the&nbsp;server.</p>
|
||
<p>That&#8217;s a simple thing in fact, but it could be good to have something
|
||
that will work without effort over&nbsp;time.</p>
|
||
<p>Here&#8217;s a sample of my&nbsp;configuration:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="nt">server</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="err">server_name</span><span class="w"> </span><span class="err">ndd</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="err">set</span><span class="w"> </span><span class="err">$path</span><span class="w"> </span><span class="err">/path/to/your/files</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="err">root</span><span class="w"> </span><span class="err">$path</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="err">location</span><span class="w"> </span><span class="err">/</span><span class="w"> </span><span class="err">{</span>
|
||
<span class="w"> </span><span class="err">index</span><span class="w"> </span><span class="err">index.php</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
|
||
<span class="w"> </span><span class="nt">location</span><span class="w"> </span><span class="o">~*</span><span class="w"> </span><span class="o">^.+.(</span><span class="nt">jpg</span><span class="o">|</span><span class="nt">jpeg</span><span class="o">|</span><span class="nt">gif</span><span class="o">|</span><span class="nt">css</span><span class="o">|</span><span class="nt">png</span><span class="o">|</span><span class="nt">js</span><span class="o">|</span><span class="nt">ico</span><span class="o">|</span><span class="nt">xml</span><span class="o">)$</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="err">access_log</span><span class="w"> </span><span class="err">off</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="err">expires</span><span class="w"> </span><span class="err">30d</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
|
||
<span class="w"> </span><span class="nt">location</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">.</span><span class="nc">php</span><span class="o">$</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="err">fastcgi_param</span><span class="w"> </span><span class="err">SCRIPT_FILENAME</span><span class="w"> </span><span class="err">$path$fastcgi_script_name</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="err">fastcgi_pass</span><span class="w"> </span><span class="err">backend</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="err">include</span><span class="w"> </span><span class="err">fastcgi_params</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="err">}</span>
|
||
|
||
<span class="nt">upstream</span><span class="w"> </span><span class="nt">backend</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="err">server</span><span class="w"> </span><span class="err">127.0.0.1:9000</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<p>And that&#8217;s it&nbsp;!</p></content><category term="code"></category></entry><entry><title>Pelican, a simple static blog generator in python</title><link href="https://blog.notmyidea.org/pelican-a-simple-static-blog-generator-in-python.html" rel="alternate"></link><published>2010-10-06T00:00:00+02:00</published><updated>2010-10-06T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-10-06:/pelican-a-simple-static-blog-generator-in-python.html</id><summary type="html">
|
||
<p>Those days, I&#8217;ve wrote a little python application to fit my blogging
|
||
needs. I&#8217;m an occasional blogger, a vim lover, I like restructured text
|
||
and DVCSes, so I&#8217;ve made a little tool that makes good use of all&nbsp;that.</p>
|
||
<p><a href="http://docs.getpelican.com">Pelican</a> (for calepin) is just a simple
|
||
tool …</p></summary><content type="html">
|
||
<p>Those days, I&#8217;ve wrote a little python application to fit my blogging
|
||
needs. I&#8217;m an occasional blogger, a vim lover, I like restructured text
|
||
and DVCSes, so I&#8217;ve made a little tool that makes good use of all&nbsp;that.</p>
|
||
<p><a href="http://docs.getpelican.com">Pelican</a> (for calepin) is just a simple
|
||
tool to generate your blog as static files, letting you using your
|
||
editor of choice (vim!). It&#8217;s easy to extend, and has a template
|
||
support (via&nbsp;jinja2).</p>
|
||
<p>I&#8217;ve made it to fit <em>my</em> needs. I hope it will fit yours, but maybe it
|
||
wont, and it have not be designed to feet everyone&#8217;s&nbsp;needs.</p>
|
||
<p>Need an example ? You&#8217;re looking at it ! This weblog is using pelican
|
||
to be generated, also for the atom&nbsp;feeds.</p>
|
||
<p>I&#8217;ve released it under <span class="caps">AGPL</span>, since I want all the modifications to be
|
||
profitable to all the&nbsp;users.</p>
|
||
<p>You can find a repository to fork at
|
||
<a href="https://github.com/getpelican/pelican/">https://github.com/getpelican/pelican/</a>. feel free to hack it&nbsp;!</p>
|
||
<p>If you just want to get started, use your installer of choice (pip,
|
||
easy_install, …) And then have a look to the help (pelican&nbsp;&#8212;help)</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>pelican
|
||
</code></pre></div>
|
||
|
||
<h2 id="usage">Usage</h2>
|
||
<p>Here&#8217;s a sample usage of&nbsp;pelican</p>
|
||
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pelican<span class="w"> </span>.
|
||
writing<span class="w"> </span>/home/alexis/projets/notmyidea.org/output/index.html
|
||
writing<span class="w"> </span>/home/alexis/projets/notmyidea.org/output/tags.html
|
||
writing<span class="w"> </span>/home/alexis/projets/notmyidea.org/output/categories.html
|
||
writing<span class="w"> </span>/home/alexis/projets/notmyidea.org/output/archives.html
|
||
writing<span class="w"> </span>/home/alexis/projets/notmyidea.org/output/category/python.html
|
||
writing
|
||
/home/alexis/projets/notmyidea.org/output/pelican-a-simple-static-blog-generator-in-python.html
|
||
Done<span class="w"> </span>!
|
||
</code></pre></div>
|
||
|
||
<p>You also can use the &#8212;help option for the command line to get more&nbsp;informations</p>
|
||
<div class="highlight"><pre><span></span><code><span class="nv">$pelican</span><span class="w"> </span>--help
|
||
usage:<span class="w"> </span>pelican<span class="w"> </span><span class="o">[</span>-h<span class="o">]</span><span class="w"> </span><span class="o">[</span>-t<span class="w"> </span>TEMPLATES<span class="o">]</span><span class="w"> </span><span class="o">[</span>-o<span class="w"> </span>OUTPUT<span class="o">]</span><span class="w"> </span><span class="o">[</span>-m<span class="w"> </span>MARKUP<span class="o">]</span><span class="w"> </span><span class="o">[</span>-s<span class="w"> </span>SETTINGS<span class="o">]</span><span class="w"> </span><span class="o">[</span>-b<span class="o">]</span>
|
||
<span class="w"> </span>path
|
||
|
||
A<span class="w"> </span>tool<span class="w"> </span>to<span class="w"> </span>generate<span class="w"> </span>a<span class="w"> </span>static<span class="w"> </span>blog,<span class="w"> </span>with<span class="w"> </span>restructured<span class="w"> </span>text<span class="w"> </span>input<span class="w"> </span>files.
|
||
|
||
positional<span class="w"> </span>arguments:
|
||
<span class="w"> </span>path<span class="w"> </span>Path<span class="w"> </span>where<span class="w"> </span>to<span class="w"> </span>find<span class="w"> </span>the<span class="w"> </span>content<span class="w"> </span>files<span class="w"> </span><span class="o">(</span>default<span class="w"> </span>is
|
||
<span class="w"> </span><span class="s2">&quot;content&quot;</span><span class="o">)</span>.
|
||
|
||
optional<span class="w"> </span>arguments:
|
||
<span class="w"> </span>-h,<span class="w"> </span>--help<span class="w"> </span>show<span class="w"> </span>this<span class="w"> </span><span class="nb">help</span><span class="w"> </span>message<span class="w"> </span>and<span class="w"> </span><span class="nb">exit</span>
|
||
<span class="w"> </span>-t<span class="w"> </span>TEMPLATES,<span class="w"> </span>--templates-path<span class="w"> </span>TEMPLATES
|
||
<span class="w"> </span>Path<span class="w"> </span>where<span class="w"> </span>to<span class="w"> </span>find<span class="w"> </span>the<span class="w"> </span>templates.<span class="w"> </span>If<span class="w"> </span>not<span class="w"> </span>specified,
|
||
<span class="w"> </span>will<span class="w"> </span>uses<span class="w"> </span>the<span class="w"> </span>ones<span class="w"> </span>included<span class="w"> </span>with<span class="w"> </span>pelican.
|
||
<span class="w"> </span>-o<span class="w"> </span>OUTPUT,<span class="w"> </span>--output<span class="w"> </span>OUTPUT
|
||
<span class="w"> </span>Where<span class="w"> </span>to<span class="w"> </span>output<span class="w"> </span>the<span class="w"> </span>generated<span class="w"> </span>files.<span class="w"> </span>If<span class="w"> </span>not<span class="w"> </span>specified,
|
||
<span class="w"> </span>a<span class="w"> </span>directory<span class="w"> </span>will<span class="w"> </span>be<span class="w"> </span>created,<span class="w"> </span>named<span class="w"> </span><span class="s2">&quot;output&quot;</span><span class="w"> </span><span class="k">in</span><span class="w"> </span>the
|
||
<span class="w"> </span>current<span class="w"> </span>path.
|
||
<span class="w"> </span>-m<span class="w"> </span>MARKUP,<span class="w"> </span>--markup<span class="w"> </span>MARKUP
|
||
<span class="w"> </span>the<span class="w"> </span>markup<span class="w"> </span>language<span class="w"> </span>to<span class="w"> </span>use.<span class="w"> </span>Currently<span class="w"> </span>only
|
||
<span class="w"> </span>ReSTreucturedtext<span class="w"> </span>is<span class="w"> </span>available.
|
||
<span class="w"> </span>-s<span class="w"> </span>SETTINGS,<span class="w"> </span>--settings<span class="w"> </span>SETTINGS
|
||
<span class="w"> </span>the<span class="w"> </span>settings<span class="w"> </span>of<span class="w"> </span>the<span class="w"> </span>application.<span class="w"> </span>Default<span class="w"> </span>to<span class="w"> </span>None.
|
||
<span class="w"> </span>-b,<span class="w"> </span>--debug
|
||
</code></pre></div>
|
||
|
||
<p>Enjoy&nbsp;:)</p></content><category term="code"></category></entry><entry><title>An amazing summer of code working on distutils2</title><link href="https://blog.notmyidea.org/an-amazing-summer-of-code-working-on-distutils2.html" rel="alternate"></link><published>2010-08-16T00:00:00+02:00</published><updated>2010-08-16T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-08-16:/an-amazing-summer-of-code-working-on-distutils2.html</id><summary type="html">
|
||
<p>The <a href="http://code.google.com/soc/">Google Summer of Code</a> I&#8217;ve spent
|
||
working on <a href="http://hg.python.org/distutils2/">distutils2</a> is over. It
|
||
was a really amazing experience, for many&nbsp;reasons.</p>
|
||
<p>First of all, we had a very good team, we were 5 students working on
|
||
distutils2: <a href="http://zubin71.wordpress.com">Zubin</a>,
|
||
<a href="http://wokslog.wordpress.com/">Éric</a>,
|
||
<a href="http://gsoc.djolonga.com/">Josip</a>,
|
||
<a href="http://konryd.blogspot.com/">Konrad</a> and me. In addition,
|
||
<a href="http://mouadino.blogspot.com/">Mouad</a> have worked on …</p></summary><content type="html">
|
||
<p>The <a href="http://code.google.com/soc/">Google Summer of Code</a> I&#8217;ve spent
|
||
working on <a href="http://hg.python.org/distutils2/">distutils2</a> is over. It
|
||
was a really amazing experience, for many&nbsp;reasons.</p>
|
||
<p>First of all, we had a very good team, we were 5 students working on
|
||
distutils2: <a href="http://zubin71.wordpress.com">Zubin</a>,
|
||
<a href="http://wokslog.wordpress.com/">Éric</a>,
|
||
<a href="http://gsoc.djolonga.com/">Josip</a>,
|
||
<a href="http://konryd.blogspot.com/">Konrad</a> and me. In addition,
|
||
<a href="http://mouadino.blogspot.com/">Mouad</a> have worked on the PyPI testing
|
||
infrastructure. You could find what each person have done on <a href="http://bitbucket.org/tarek/distutils2/wiki/GSoC_2010_teams">the wiki
|
||
page of
|
||
distutils2</a>.</p>
|
||
<p>We were in contact with each others really often, helping us when
|
||
possible (in #distutils), and were continuously aware of the state of
|
||
the work of each participant. This, in my opinion, have bring us in a
|
||
good&nbsp;shape.</p>
|
||
<p>Then, I&#8217;ve learned a lot. Python packaging was completely new to me at
|
||
the time of the GSoC start, and I was pretty unfamiliar with python good
|
||
practices too, as I&#8217;ve been introducing myself to python in the late&nbsp;2009.</p>
|
||
<p>I&#8217;ve recently looked at some python code I wrote just three months ago,
|
||
and I was amazed to think about many improvements to made on it. I guess
|
||
this is a good indicator of the path I&#8217;ve traveled since I wrote&nbsp;it.</p>
|
||
<p>This summer was awesome because I&#8217;ve learned about python good
|
||
practices, now having some strong
|
||
<a href="http://mercurial.selenic.com/">mercurial</a> knowledge, and I&#8217;ve seen a
|
||
little how the python community&nbsp;works.</p>
|
||
<p>Then, I would like to say a big thanks to all the mentors that have
|
||
hanged around while needed, on <span class="caps">IRC</span> or via mail, and especially my mentor
|
||
for this summer, <a href="http://tarek.ziade.org">Tarek Ziadé</a>.</p>
|
||
<p>Thanks a lot for your motivation, your leadership and your cheerfulness,
|
||
even with a new-born and a new&nbsp;work!</p>
|
||
<h2 id="why">Why&nbsp;?</h2>
|
||
<p>I wanted to work on python packaging because, as the time pass, we were
|
||
having a sort of complex tools in this field. Each one wanted to add
|
||
features to distutils, but not in a standard&nbsp;way.</p>
|
||
<p>Now, we have PEPs that describes some format we agreed on (see <span class="caps">PEP</span> 345),
|
||
and we wanted to have a tool on which users can base their code on,
|
||
that&#8217;s <a href="http://hg.python.org/distutils2/">distutils2</a>.</p>
|
||
<h2 id="my-job">My&nbsp;job</h2>
|
||
<p>I had to provide a way to crawl the PyPI indexes in a simple way, and do
|
||
some installation / uninstallation&nbsp;scripts.</p>
|
||
<p>All the work done is available in <a href="http://bitbucket.org/ametaireau/distutils2/">my bitbucket
|
||
repository</a>.</p>
|
||
<h3 id="crawling-the-pypi-indexes">Crawling the PyPI&nbsp;indexes</h3>
|
||
<p>There are two ways of requesting informations from the indexes: using
|
||
the &#8220;simple&#8221; index, that is a kind of <span class="caps">REST</span> index, and using <span class="caps">XML</span>-<span class="caps">RPC</span>.</p>
|
||
<p>I&#8217;ve done the two implementations, and a high level <span class="caps">API</span> to query those
|
||
twos. Basically, this supports the mirroring infrastructure defined in
|
||
<span class="caps">PEP</span> 381. So far, the work I&#8217;ve done is gonna be used in pip (they&#8217;ve
|
||
basically copy/paste the code, but this will change as soon as we get
|
||
something completely stable for distutils2), and that&#8217;s a good news, as
|
||
it was the main reason for what I&#8217;ve done&nbsp;that.</p>
|
||
<p>I&#8217;ve tried to have an unified <span class="caps">API</span> for the clients, to switch from one to
|
||
another implementation easily. I&#8217;m already thinking of adding others
|
||
crawlers to this stuff, and it was made to be&nbsp;extensible.</p>
|
||
<p>If you want to get more informations about the crawlers/PyPI clients,
|
||
please refer to the distutils2 documentation, especially <a href="http://distutils2.notmyidea.org/library/distutils2.index.html">the pages
|
||
about
|
||
indexes</a>.</p>
|
||
<p>You can find the changes I made about this in the
|
||
<a href="http://hg.python.org/distutils2/">distutils2</a> source code&nbsp;.</p>
|
||
<h3 id="installation-uninstallation-scripts">Installation / Uninstallation&nbsp;scripts</h3>
|
||
<p>Next step was to think about an installation script, and an uninstaller.
|
||
I&#8217;ve not done the uninstaller part, and it&#8217;s a smart part, as it&#8217;s
|
||
basically removing some files from the system, so I&#8217;ll probably do it in
|
||
a near&nbsp;future.</p>
|
||
<p><a href="http://hg.python.org/distutils2/">distutils2</a> provides a way to install
|
||
distributions, and to handle dependencies between releases. For now,
|
||
this support is only about the last version of the <span class="caps">METADATA</span> (1.2) (See,
|
||
the <span class="caps">PEP</span> 345), but I&#8217;m working on a compatibility layer for the old
|
||
metadata, and for the informations provided via <span class="caps">PIP</span> requires.txt, for&nbsp;instance.</p>
|
||
<h3 id="extra-work">Extra&nbsp;work</h3>
|
||
<p>Also, I&#8217;ve done some extra work. this&nbsp;includes:</p>
|
||
<ul>
|
||
<li>working on the <span class="caps">PEP</span> 345, and having some discussion about it (about
|
||
the names of some&nbsp;fields).</li>
|
||
<li>writing a PyPI server mock, useful for tests. you can find more
|
||
information about it on the
|
||
<a href="http://distutils.notmyidea.org">documentation</a>.</li>
|
||
</ul>
|
||
<h2 id="futures-plans">Futures&nbsp;plans</h2>
|
||
<p>As I said, I&#8217;ve enjoyed working on distutils2, and the people I&#8217;ve met
|
||
here are really pleasant to work with. So I <em>want</em> to continue
|
||
contributing on python, and especially on python packaging, because
|
||
there is still a lot of things to do in this scope, to get something
|
||
really&nbsp;usable.</p>
|
||
<p>I&#8217;m not plainly satisfied by the work I&#8217;ve done, so I&#8217;ll probably tweak
|
||
it a bit: the installer part is not yet completely finished, and I want
|
||
to add support for a real
|
||
<a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"><span class="caps">REST</span></a>
|
||
index in the&nbsp;future.</p>
|
||
<p>We&#8217;ll talk again of this in the next months, probably, but we definitely
|
||
need a real
|
||
<a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"><span class="caps">REST</span></a> <span class="caps">API</span>
|
||
for <a href="http://pypi.python.org">PyPI</a>, as the &#8220;simple&#8221; index <em>is</em> an ugly
|
||
hack, in my opinion. I&#8217;ll work on a serious proposition about this,
|
||
maybe involving <a href="http://couchdb.org">CouchDB</a>, as it seems to be a good
|
||
option for what we want&nbsp;here.</p>
|
||
<h2 id="issues">Issues</h2>
|
||
<p>I&#8217;ve encountered some issues during this summer. The main one is that&#8217;s
|
||
hard to work remotely, especially being in the same room that we live,
|
||
with others. I like to just think about a project with other people, a
|
||
paper and a pencil, no computers. This have been not so possible at the
|
||
start of the project, as I needed to read a lot of code to understand
|
||
the codebase, and then to read/write&nbsp;emails.</p>
|
||
<p>I&#8217;ve finally managed to work in an office, so good point for home/office&nbsp;separation.</p>
|
||
<p>I&#8217;d not planned there will be so a high number of emails to read, in
|
||
order to follow what&#8217;s up in the python world, and be a part of the
|
||
community seems to takes some times to read/write emails, especially for
|
||
those (like me) that arent so confortable with english (but this had
|
||
brought me some english fu&nbsp;!).</p>
|
||
<h2 id="thanks">Thanks&nbsp;!</h2>
|
||
<p>A big thanks to <a href="http://www.graine-libre.fr/">Graine Libre</a> and <a href="http://www.makina-corpus.com/">Makina
|
||
Corpus</a>, which has offered me to come
|
||
into their offices from time to time, to share they cheerfulness ! Many
|
||
thanks too to the Google Summer of Code program for setting up such an
|
||
initiative. If you&#8217;re a student, if you&#8217;re interested about <span class="caps">FOSS</span>, dont
|
||
hesitate any second, it&#8217;s a really good opportunity to work on
|
||
interesting&nbsp;projects!</p></content><category term="code"></category></entry><entry><title>Sprinting on distutils2 in Tours</title><link href="https://blog.notmyidea.org/sprinting-on-distutils2-in-tours.html" rel="alternate"></link><published>2010-07-10T00:00:00+02:00</published><updated>2010-07-10T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-07-10:/sprinting-on-distutils2-in-tours.html</id><summary type="html">
|
||
<ul>
|
||
<li>
|
||
<p>date<br>&nbsp;2010-07-06</p>
|
||
</li>
|
||
<li>
|
||
<p>category<br>&nbsp;tech</p>
|
||
</li>
|
||
</ul>
|
||
<p>Yesterday, as I was traveling to Tours, I&#8217;ve took some time to visit
|
||
Éric, another student who&#8217;s working on distutils2 this summer, as a
|
||
part of the GSoC. Basically, it was to take a drink, discuss a bit about
|
||
distutils2, our respective tasks and …</p></summary><content type="html">
|
||
<ul>
|
||
<li>
|
||
<p>date<br>&nbsp;2010-07-06</p>
|
||
</li>
|
||
<li>
|
||
<p>category<br>&nbsp;tech</p>
|
||
</li>
|
||
</ul>
|
||
<p>Yesterday, as I was traveling to Tours, I&#8217;ve took some time to visit
|
||
Éric, another student who&#8217;s working on distutils2 this summer, as a
|
||
part of the GSoC. Basically, it was to take a drink, discuss a bit about
|
||
distutils2, our respective tasks and general feelings, and to put a face
|
||
on a pseudonym. I&#8217;d really enjoyed this time, because Éric knows a lot
|
||
of things about mercurial and python good practices, and I&#8217;m eager to
|
||
learn about those. So, we have discussed about things, have not wrote so
|
||
much code, but have some things to propose so far, about documentation,
|
||
and I also provides here some bribes of conversations we&nbsp;had.</p>
|
||
<h2 id="documentation">Documentation</h2>
|
||
<p>While writing the PyPI simple index crawler documentation, I realized
|
||
that we miss some structure, or how-to about the documentation. Yep, you
|
||
read well. We lack documentation on how to make documentation. Heh.
|
||
We&#8217;re missing some rules to follow, and this lead to a not-so-structured
|
||
final documentation. We probably target three type of publics, and we
|
||
can split the documentation regarding&nbsp;those:</p>
|
||
<ul>
|
||
<li><strong>Packagers</strong> who want to distribute their&nbsp;softwares.</li>
|
||
<li><strong>End users</strong> who need to understand how to use end user commands,
|
||
like the&nbsp;installer/uninstaller</li>
|
||
<li><strong>packaging coders</strong> who <em>use</em> distutils2, as a base for building a
|
||
package&nbsp;manager.</li>
|
||
</ul>
|
||
<p>We also need to discuss about a pattern to follow while writing
|
||
documentation. How many parts do we need ? Where to put the <span class="caps">API</span>
|
||
description ? etc. That&#8217;s maybe seems to be not so important, but I
|
||
guess the readers would appreciate to have the same structure all along
|
||
distutils2&nbsp;documentation.</p>
|
||
<h2 id="mercurial">Mercurial</h2>
|
||
<p>I&#8217;m really <em>not</em> a mercurial power user. I use it on daily basis, but I
|
||
lack of basic knowledge about it. Big thanks Éric for sharing yours with
|
||
me, you&#8217;re of a great help. We have talked about some mercurial
|
||
extensions that seems to make the life simpler, while used the right
|
||
way. I&#8217;ve not used them so far, so consider this as a personal&nbsp;note.</p>
|
||
<ul>
|
||
<li>hg histedit, to edit the&nbsp;history</li>
|
||
<li>hg crecord, to select the changes to&nbsp;commit</li>
|
||
</ul>
|
||
<p>We have spent some time to review a merge I made sunday, to re-merge it,
|
||
and commit the changes as a new changeset. Awesome. These things make me
|
||
say I <strong>need</strong> to read <a href="http://hgbook.red-bean.com/read/">the hg book</a>,
|
||
and will do as soon as I got some spare time: mercurial seems to be
|
||
simply great. So &#8230; Great. I&#8217;m a powerful merger now&nbsp;!</p>
|
||
<h2 id="on-using-tools">On using&nbsp;tools</h2>
|
||
<p>Because we <em>also</em> are <em>hackers</em>, we have shared a bit our ways to code,
|
||
the tools we use, etc. Both of us were using vim, and I&#8217;ve discovered
|
||
vimdiff and hgtk, which will completely change the way I navigate into
|
||
the mercurial history. We aren&#8217;t &#8220;power users&#8221;, so we have learned from
|
||
each other about vim tips. You can find <a href="http://github.com/ametaireau/dotfiles">my dotfiles on
|
||
github</a>, if it could help.
|
||
They&#8217;re not perfect, and not intended to be, because changing all the
|
||
time, as I learn. Don&#8217;t hesitate to have a look, and to propose
|
||
enhancements if you have&nbsp;!</p>
|
||
<h2 id="on-being-pythonic">On being&nbsp;pythonic</h2>
|
||
<p>My background as an old Java user disserves me so far, as the paradigms
|
||
are not the same while coding in python. Hard to find the more pythonic
|
||
way to do, and sometimes hard to unlearn my way to think about software
|
||
engineering. Well, it seems that the only solution is to read code, and
|
||
to re-read import this from times to times ! <a href="http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html">Coding like a
|
||
pythonista</a>
|
||
seems to be a must-read, so, I know what to&nbsp;do.</p>
|
||
<h2 id="conclusion">Conclusion</h2>
|
||
<p>It was really great. Next time, we&#8217;ll need to focus a bit more on
|
||
distutils2, and to have a bullet list of things to do, but days like
|
||
this one are opportunities to catch ! We&#8217;ll probably do another sprint
|
||
in a few weeks, stay tuned&nbsp;!</p></content><category term="code"></category></entry><entry><title>Introducing the distutils2 index crawlers</title><link href="https://blog.notmyidea.org/introducing-the-distutils2-index-crawlers.html" rel="alternate"></link><published>2010-07-06T00:00:00+02:00</published><updated>2010-07-06T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-07-06:/introducing-the-distutils2-index-crawlers.html</id><summary type="html">
|
||
<p>I&#8217;m working for about a month for distutils2, even if I was being a bit
|
||
busy (as I had some class courses and exams to work&nbsp;on)</p>
|
||
<p>I&#8217;ll try do sum-up my general feelings here, and the work I&#8217;ve made so
|
||
far. You can also find, if …</p></summary><content type="html">
|
||
<p>I&#8217;m working for about a month for distutils2, even if I was being a bit
|
||
busy (as I had some class courses and exams to work&nbsp;on)</p>
|
||
<p>I&#8217;ll try do sum-up my general feelings here, and the work I&#8217;ve made so
|
||
far. You can also find, if you&#8217;re interested, my weekly summaries in <a href="http://wiki.notmyidea.org/distutils2_schedule">a
|
||
dedicated wiki page</a>.</p>
|
||
<h2 id="general-feelings">General&nbsp;feelings</h2>
|
||
<p>First, and it&#8217;s a really important point, the GSoC is going very well,
|
||
for me as for other students, at least from my perspective. It&#8217;s a
|
||
pleasure to work with such enthusiast people, as this make the global
|
||
atmosphere very pleasant to&nbsp;live.</p>
|
||
<p>First of all, I&#8217;ve spent time to read the existing codebase, and to
|
||
understand what we&#8217;re going to do, and what&#8217;s the rationale to do&nbsp;so.</p>
|
||
<p>It&#8217;s really clear for me now: what we&#8217;re building is the foundations of
|
||
a packaging infrastructure in python. The fact is that many projects
|
||
co-exists, and comes all with their good concepts. Distutils2 tries to
|
||
take the interesting parts of all, and to provide it in the python
|
||
standard libs, respecting the recently written <span class="caps">PEP</span> about&nbsp;packaging.</p>
|
||
<p>With distutils2, it will be simpler to make &#8220;things&#8221; compatible. So if
|
||
you think about a new way to deal with distributions and packaging in
|
||
python, you can use the Distutils2 APIs to do&nbsp;so.</p>
|
||
<h2 id="tasks">Tasks</h2>
|
||
<p>My main task while working on distutils2 is to provide an installation
|
||
and an un-installation command, as described in <span class="caps">PEP</span> 376. For this, I
|
||
first need to get informations about the existing distributions (what&#8217;s
|
||
their version, name, metadata, dependencies,&nbsp;etc.)</p>
|
||
<p>The main index, you probably know and use, is PyPI. You can access it at
|
||
<a href="http://pypi.python.org">http://pypi.python.org</a>.</p>
|
||
<h2 id="pypi-index-crawling">PyPI index&nbsp;crawling</h2>
|
||
<p>There is two ways to get these informations from PyPI: using the simple
|
||
<span class="caps">API</span>, or via xml-rpc&nbsp;calls.</p>
|
||
<p>A goal was to use the version specifiers defined
|
||
in<a href="http://www.python.org/dev/peps/pep-0345/"><span class="caps">PEP</span> 345</a> and to provides a
|
||
way to sort the grabbed distributions depending our needs, to pick the
|
||
version we&nbsp;want/need.</p>
|
||
<h3 id="using-the-simple-api">Using the simple <span class="caps">API</span></h3>
|
||
<p>The simple <span class="caps">API</span> is composed of <span class="caps">HTML</span> pages you can access at
|
||
<a href="http://pypi.python.org/simple/">http://pypi.python.org/simple/</a>.</p>
|
||
<p>Distribute and Setuptools already provides a crawler for that, but it
|
||
deals with their internal mechanisms, and I found that the code was not
|
||
so clear as I want, that&#8217;s why I&#8217;ve preferred to pick up the good ideas,
|
||
and some implementation details, plus re-thinking the global&nbsp;architecture.</p>
|
||
<p>The rules are simple: each project have a dedicated page, which allows
|
||
us to get informations&nbsp;about:</p>
|
||
<ul>
|
||
<li>the distribution download locations (for some&nbsp;versions)</li>
|
||
<li>homepage&nbsp;links</li>
|
||
<li>some other useful informations, as the bugtracker address, for&nbsp;instance.</li>
|
||
</ul>
|
||
<p>If you want to find all the distributions of the &#8220;EggsAndSpam&#8221; project,
|
||
you could do the following (do not take so attention to the names here,
|
||
as the <span class="caps">API</span> will probably change a&nbsp;bit):</p>
|
||
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">index</span> <span class="o">=</span> <span class="n">SimpleIndex</span><span class="p">()</span>
|
||
<span class="o">&gt;&gt;&gt;</span> <span class="n">index</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s2">&quot;EggsAndSpam&quot;</span><span class="p">)</span>
|
||
<span class="p">[</span><span class="n">EggsAndSpam</span> <span class="mf">1.1</span><span class="p">,</span> <span class="n">EggsAndSpam</span> <span class="mf">1.2</span><span class="p">,</span> <span class="n">EggsAndSpam</span> <span class="mf">1.3</span><span class="p">]</span>
|
||
</code></pre></div>
|
||
|
||
<p>We also could use version&nbsp;specifiers:</p>
|
||
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">index</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s2">&quot;EggsAndSpam (&lt; =1.2)&quot;</span><span class="p">)</span>
|
||
<span class="p">[</span><span class="n">EggsAndSpam</span> <span class="mf">1.1</span><span class="p">,</span> <span class="n">EggsAndSpam</span> <span class="mf">1.2</span><span class="p">]</span>
|
||
</code></pre></div>
|
||
|
||
<p>Internally, what&#8217;s done here is the&nbsp;following:</p>
|
||
<ul>
|
||
<li>it process the <a href="http://pypi.python.org/simple/FooBar/">http://pypi.python.org/simple/FooBar/</a> page,
|
||
searching for download&nbsp;URLs.</li>
|
||
<li>for each found distribution download <span class="caps">URL</span>, it creates an object,
|
||
containing informations about the project name, the version and the
|
||
<span class="caps">URL</span> where the archive&nbsp;remains.</li>
|
||
<li>it sort the found distributions, using version numbers. The default
|
||
behavior here is to prefer source distributions (over binary ones),
|
||
and to rely on the last &#8220;final&#8221; distribution (rather than beta,
|
||
alpha etc.&nbsp;ones)</li>
|
||
</ul>
|
||
<p>So, nothing hard or difficult&nbsp;here.</p>
|
||
<p>We provides a bunch of other features, like relying on the new PyPI
|
||
mirroring infrastructure or filter the found distributions by some
|
||
criterias. If you&#8217;re curious, please browse the <a href="http://distutils2.notmyidea.org/">distutils2
|
||
documentation</a>.</p>
|
||
<h3 id="using-xml-rpc">Using&nbsp;xml-rpc</h3>
|
||
<p>We also can make some xmlrpc calls to retreive informations from PyPI.
|
||
It&#8217;s a really more reliable way to get informations from from the index
|
||
(as it&#8217;s just the index that provides the informations), but cost
|
||
processes on the PyPI distant&nbsp;server.</p>
|
||
<p>For now, this way of querying the xmlrpc client is not available on
|
||
Distutils2, as I&#8217;m working on it. The main pieces are already present
|
||
(I&#8217;ll reuse some work I&#8217;ve made from the SimpleIndex querying, and <a href="http://github.com/ametaireau/pypiclient">some
|
||
code already set up</a>), what I
|
||
need to do is to provide a xml-rpc PyPI mock server, and that&#8217;s on what
|
||
I&#8217;m actually working&nbsp;on.</p>
|
||
<h2 id="processes">Processes</h2>
|
||
<p>For now, I&#8217;m trying to follow the &#8220;documentation, then test, then code&#8221;
|
||
path, and that seems to be really needed while working with a community.
|
||
Code is hard to read/understand, compared to documentation, and it&#8217;s
|
||
easier to&nbsp;change.</p>
|
||
<p>While writing the simple index crawling work, I must have done this to
|
||
avoid some changes on the <span class="caps">API</span>, and some loss of&nbsp;time.</p>
|
||
<p>Also, I&#8217;ve set up <a href="http://wiki.notmyidea.org/distutils2_schedule">a
|
||
schedule</a>, and the goal
|
||
is to be sure everything will be ready in time, for the end of the
|
||
summer. (And now, I need to learn to follow schedules&nbsp;&#8230;)</p></content><category term="code"></category></entry><entry><title>Use Restructured Text (ReST) to power your presentations</title><link href="https://blog.notmyidea.org/use-restructured-text-rest-to-power-your-presentations.html" rel="alternate"></link><published>2010-06-25T00:00:00+02:00</published><updated>2010-06-25T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-06-25:/use-restructured-text-rest-to-power-your-presentations.html</id><summary type="html">
|
||
<ul>
|
||
<li>
|
||
<p>date<br>&nbsp;2010-06-25</p>
|
||
</li>
|
||
<li>
|
||
<p>category<br>&nbsp;tech</p>
|
||
</li>
|
||
</ul>
|
||
<p>Wednesday, we give a presentation, with some friends, about the CouchDB
|
||
Database, to <a href="http://www.toulibre.org">the Toulouse local <span class="caps">LUG</span></a>. Thanks a
|
||
lot to all the presents for being there, it was a pleasure to talk about
|
||
this topic with you. Too bad the season is over now an …</p></summary><content type="html">
|
||
<ul>
|
||
<li>
|
||
<p>date<br>&nbsp;2010-06-25</p>
|
||
</li>
|
||
<li>
|
||
<p>category<br>&nbsp;tech</p>
|
||
</li>
|
||
</ul>
|
||
<p>Wednesday, we give a presentation, with some friends, about the CouchDB
|
||
Database, to <a href="http://www.toulibre.org">the Toulouse local <span class="caps">LUG</span></a>. Thanks a
|
||
lot to all the presents for being there, it was a pleasure to talk about
|
||
this topic with you. Too bad the season is over now an I quit Toulouse
|
||
next&nbsp;year.</p>
|
||
<p>During our brainstorming about the topic, we used some paper, and we
|
||
wanted to make a presentation the simpler way. First thing that come to
|
||
my mind was using <a href="http://docutils.sourceforge.net/rst.html">restructured
|
||
text</a>, so I&#8217;ve wrote a simple
|
||
file containing our different bullet points. In fact, there is quite
|
||
nothing to do then, to have a working&nbsp;presentation.</p>
|
||
<p>So far, I&#8217;ve used <a href="http://code.google.com/p/rst2pdf/">the rst2pdf
|
||
program</a>, and a simple template, to
|
||
generate output. It&#8217;s probably simple to have similar results using
|
||
latex + beamer, I&#8217;ll try this next time, but as I&#8217;m not familiar with
|
||
latex syntax, restructured text was a great&nbsp;option.</p>
|
||
<p>Here are <a href="http://files.lolnet.org/alexis/rst-presentations/couchdb/couchdb.pdf">the final <span class="caps">PDF</span>
|
||
output</a>,
|
||
<a href="http://files.lolnet.org/alexis/rst-presentations/couchdb/couchdb.rst">Rhe ReST
|
||
source</a>,
|
||
<a href="http://files.lolnet.org/alexis/rst-presentations/slides.style">the theme
|
||
used</a>,
|
||
and the command line to generate the <span class="caps">PDF</span>:</p>
|
||
<div class="highlight"><pre><span></span><code>rst2pdf couchdb.rst -b1 -s ../slides.style
|
||
</code></pre></div></content><category term="code"></category></entry><entry><title>first week working on distutils2</title><link href="https://blog.notmyidea.org/first-week-working-on-distutils2.html" rel="alternate"></link><published>2010-06-04T00:00:00+02:00</published><updated>2010-06-04T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-06-04:/first-week-working-on-distutils2.html</id><summary type="html">
|
||
<p>As I&#8217;ve been working on <a href="http://hg.python.org/distutils2/">Distutils2</a>
|
||
during the past week, taking part of the
|
||
<a href="http://code.google.com/intl/fr/soc/"><span class="caps">GSOC</span></a> program, here is a short
|
||
summary of what I&#8217;ve done so&nbsp;far.</p>
|
||
<p>As my courses are not over yet, I&#8217;ve not worked as much as I wanted, and
|
||
this will continues until …</p></summary><content type="html">
|
||
<p>As I&#8217;ve been working on <a href="http://hg.python.org/distutils2/">Distutils2</a>
|
||
during the past week, taking part of the
|
||
<a href="http://code.google.com/intl/fr/soc/"><span class="caps">GSOC</span></a> program, here is a short
|
||
summary of what I&#8217;ve done so&nbsp;far.</p>
|
||
<p>As my courses are not over yet, I&#8217;ve not worked as much as I wanted, and
|
||
this will continues until the end of June. My main tasks are about
|
||
making installation and uninstallation commands, to have a simple way to
|
||
install distributions via
|
||
<a href="http://hg.python.org/distutils2/">Distutils2</a>.</p>
|
||
<p>To do this, we need to rely on informations provided by the Python
|
||
Package Index (<a href="http://pypi.python.org/">PyPI</a>), and there is at least
|
||
two ways to retreive informations from here: <span class="caps">XML</span>-<span class="caps">RPC</span> and the &#8220;simple&#8221;
|
||
<span class="caps">API</span>.</p>
|
||
<p>So, I&#8217;ve been working on porting some
|
||
<a href="http://bitbucket.org/tarek/distribute/">Distribute</a> related stuff to
|
||
<a href="http://hg.python.org/distutils2/">Distutils2</a>, cutting off all non
|
||
distutils&#8217; things, as we do not want to depend from Distribute&#8217;s
|
||
internals. My main work has been about reading the whole code, writing
|
||
tests about this and making those tests&nbsp;possible.</p>
|
||
<p>In fact, there was a need of a pypi mocked server, and, after reading
|
||
and introducing myself to the distutils behaviors and code, I&#8217;ve taken
|
||
some time to improve the work <a href="http://bitbucket.org/konrad">Konrad</a>
|
||
makes about this&nbsp;mock.</p>
|
||
<h2 id="a-pypi-server-mock">A PyPI Server&nbsp;mock</h2>
|
||
<p>The mock is embeded in a thread, to make it available during the tests,
|
||
in a non blocking way. We first used <a href="http://wsgi.org"><span class="caps">WSGI</span></a> and
|
||
<a href="http://docs.python.org/library/wsgiref.html">wsgiref</a> in order control
|
||
what to serve, and to log the requests made to the server, but finally
|
||
realised that <a href="http://docs.python.org/library/wsgiref.html">wsgiref</a> is
|
||
not python 2.4 compatible (and we <em>need</em> to be python 2.4 compatible in&nbsp;Distutils2).</p>
|
||
<p>So, we switched to
|
||
<a href="http://docs.python.org/library/basehttpserver.html">BaseHTTPServer</a> and
|
||
<a href="http://docs.python.org/library/simplehttpserver.html">SimpleHTTPServer</a>,
|
||
and updated our tests accordingly. It&#8217;s been an opportunity to realize
|
||
that <a href="http://wsgi.org"><span class="caps">WSGI</span></a> has been a great step forward for making
|
||
<span class="caps">HTTP</span> servers, and expose a really simplest way to discuss with <span class="caps">HTTP</span>&nbsp;!</p>
|
||
<p>You can find <a href="http://bitbucket.org/ametaireau/distutils2/changesets">the modifications I
|
||
made</a>, and the
|
||
<a href="http://bitbucket.org/ametaireau/distutils2/src/tip/docs/source/test_framework.rst">related
|
||
docs</a>
|
||
about this on <a href="http://bitbucket.org/ametaireau/distutils2/">my bitbucket distutils2
|
||
clone</a>.</p>
|
||
<h2 id="the-pypi-simple-api">The PyPI Simple <span class="caps">API</span></h2>
|
||
<p>So, back to the main problematic: make a python library to access and
|
||
request information stored on PyPI, via the simple <span class="caps">API</span>. As I said, I&#8217;ve
|
||
just grabbed the work made from
|
||
<a href="http://bitbucket.org/tarek/distribute/">Distribute</a>, and played a bit
|
||
with, in order to view what are the different use cases, and started to
|
||
write the related&nbsp;tests.</p>
|
||
<h2 id="the-work-to-come">The work to&nbsp;come</h2>
|
||
<p>So, once all use cases covered with tests, I&#8217;ll rewrite a bit the
|
||
grabbed code, and do some software design work (to not expose all things
|
||
as privates methods, have a clear <span class="caps">API</span>, and other things like this), then
|
||
update the tests accordingly and write a documentation to make this&nbsp;clear.</p>
|
||
<p>Next step is to a little client, as I&#8217;ve <a href="http://github.com/ametaireau/pypiclient">already started
|
||
here</a> I&#8217;ll take you updated&nbsp;!</p></content><category term="code"></category></entry><entry><title>A Distutils2 GSoC</title><link href="https://blog.notmyidea.org/a-distutils2-gsoc.html" rel="alternate"></link><published>2010-05-01T00:00:00+02:00</published><updated>2010-05-01T00:00:00+02:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2010-05-01:/a-distutils2-gsoc.html</id><summary type="html">
|
||
<p><span class="caps">WOW</span>. I&#8217;ve been accepted to be a part of the <a href="http://code.google.com/intl/fr/soc/">Google Summer Of
|
||
Code</a> program, and will work on
|
||
<a href="http://python.org/">python</a> <a href="http://hg.python.org/distutils2/">distutils2</a>, with <a href="http://pygsoc.wordpress.com/">a</a> <a href="http://konryd.blogspot.com/">lot</a> <a href="http://ziade.org/">of</a> (intersting !) <a href="http://zubin71.wordpress.com/">people</a>.</p>
|
||
<blockquote>
|
||
<p>So, it&#8217;s about building the successor of Distutils2, ie. &#8220;the python
|
||
package manager&#8221;. Today, there is too many ways to package …</p></blockquote></summary><content type="html">
|
||
<p><span class="caps">WOW</span>. I&#8217;ve been accepted to be a part of the <a href="http://code.google.com/intl/fr/soc/">Google Summer Of
|
||
Code</a> program, and will work on
|
||
<a href="http://python.org/">python</a> <a href="http://hg.python.org/distutils2/">distutils2</a>, with <a href="http://pygsoc.wordpress.com/">a</a> <a href="http://konryd.blogspot.com/">lot</a> <a href="http://ziade.org/">of</a> (intersting !) <a href="http://zubin71.wordpress.com/">people</a>.</p>
|
||
<blockquote>
|
||
<p>So, it&#8217;s about building the successor of Distutils2, ie. &#8220;the python
|
||
package manager&#8221;. Today, there is too many ways to package a python
|
||
application (pip, setuptools, distribute, distutils, etc.) so there is
|
||
a huge effort to make in order to make all this packaging stuff
|
||
interoperable, as pointed out by
|
||
the <a href="http://www.python.org/dev/peps/pep-0376/"><span class="caps">PEP</span> 376</a>.</p>
|
||
</blockquote>
|
||
<p>In more details, I&#8217;m going to work on the Installer / Uninstaller features of Distutils2, and on a PyPI <span class="caps">XML</span>-<span class="caps">RPC</span> client for distutils2. Here are the already defined&nbsp;tasks:</p>
|
||
<ul>
|
||
<li>Implement Distutils2 APIs described in <span class="caps">PEP</span>&nbsp;376.</li>
|
||
<li>Add the uninstall&nbsp;command.</li>
|
||
<li>think about a basic installer / uninstaller script. (with deps) &#8212;
|
||
similar to&nbsp;pip/easy_install</li>
|
||
<li>in a pypi&nbsp;subpackage;</li>
|
||
<li>Integrate a module similar to setuptools&#8217;&nbsp;package_index&#8217;</li>
|
||
<li>PyPI <span class="caps">XML</span>-<span class="caps">RPC</span> client for distutils 2:
|
||
<a href="http://bugs.python.org/issue8190">http://bugs.python.org/issue8190</a></li>
|
||
</ul>
|
||
<p>As I&#8217;m relatively new to python, I&#8217;ll need some extra work in order to apply all good practice, among other things that can make a developper-life joyful. I&#8217;ll post here, each week, my advancement, and my tought about python and especialy python packaging&nbsp;world.</p></content><category term="code"></category></entry><entry><title>Python ? go !</title><link href="https://blog.notmyidea.org/python-go.html" rel="alternate"></link><published>2009-12-17T00:00:00+01:00</published><updated>2009-12-17T00:00:00+01:00</updated><author><name></name></author><id>tag:blog.notmyidea.org,2009-12-17:/python-go.html</id><summary type="html">
|
||
<p>Cela fait maintenant un peu plus d&#8217;un mois que je travaille sur un
|
||
projet en <a href="http://www.djangoproject.org">django</a>, et que,
|
||
nécessairement, je me forme à <a href="http://python.org/">Python</a>. Je prends
|
||
un plaisir non dissimulé à découvrir ce langage (et à l&#8217;utiliser), qui
|
||
ne cesse de me surprendre. Les premiers mots qui me …</p></summary><content type="html">
|
||
<p>Cela fait maintenant un peu plus d&#8217;un mois que je travaille sur un
|
||
projet en <a href="http://www.djangoproject.org">django</a>, et que,
|
||
nécessairement, je me forme à <a href="http://python.org/">Python</a>. Je prends
|
||
un plaisir non dissimulé à découvrir ce langage (et à l&#8217;utiliser), qui
|
||
ne cesse de me surprendre. Les premiers mots qui me viennent à l&#8217;esprit
|
||
à propos de Python, sont &#8220;logique&#8221; et &#8220;simple&#8221;. Et pourtant puissant
|
||
pour autant. Je ne manque d&#8217;ailleurs pas une occasion pour faire un peu
|
||
d&#8217;<em>évangélisation</em> auprès des quelques personnes qui veulent bien&nbsp;m&#8217;écouter.</p>
|
||
<h2 id="the-zen-of-python">The Zen of&nbsp;Python</h2>
|
||
<p>Avant toute autre chose, je pense utile de citer Tim Peters, et <a href="http://www.python.org/dev/peps/pep-0020/">le
|
||
<span class="caps">PEP20</span></a>, qui constituent une
|
||
très bonne introduction au langage, qui prends la forme d&#8217;un <em>easter
|
||
egg</em> présent dans&nbsp;python</p>
|
||
<div class="highlight"><pre><span></span><code>&gt;&gt;&gt;<span class="w"> </span>import<span class="w"> </span>this
|
||
The<span class="w"> </span>Zen<span class="w"> </span>of<span class="w"> </span>Python,<span class="w"> </span>by<span class="w"> </span>Tim<span class="w"> </span>Peters
|
||
|
||
Beautiful<span class="w"> </span>is<span class="w"> </span>better<span class="w"> </span>than<span class="w"> </span>ugly.
|
||
Explicit<span class="w"> </span>is<span class="w"> </span>better<span class="w"> </span>than<span class="w"> </span>implicit.
|
||
Simple<span class="w"> </span>is<span class="w"> </span>better<span class="w"> </span>than<span class="w"> </span>complex.
|
||
Complex<span class="w"> </span>is<span class="w"> </span>better<span class="w"> </span>than<span class="w"> </span>complicated.
|
||
Flat<span class="w"> </span>is<span class="w"> </span>better<span class="w"> </span>than<span class="w"> </span>nested.
|
||
Sparse<span class="w"> </span>is<span class="w"> </span>better<span class="w"> </span>than<span class="w"> </span>dense.
|
||
Readability<span class="w"> </span>counts.
|
||
Special<span class="w"> </span>cases<span class="w"> </span>aren<span class="s1">&#39;t special enough to break the rules.</span>
|
||
<span class="s1">Although practicality beats purity.</span>
|
||
<span class="s1">Errors should never pass silently.</span>
|
||
<span class="s1">Unless explicitly silenced.</span>
|
||
<span class="s1">In the face of ambiguity, refuse the temptation to guess.</span>
|
||
<span class="s1">There should be one-- and preferably only one --obvious way to do it.</span>
|
||
<span class="s1">Although that way may not be obvious at first unless you&#39;</span>re<span class="w"> </span>Dutch.
|
||
Now<span class="w"> </span>is<span class="w"> </span>better<span class="w"> </span>than<span class="w"> </span>never.
|
||
Although<span class="w"> </span>never<span class="w"> </span>is<span class="w"> </span>often<span class="w"> </span>better<span class="w"> </span>than<span class="w"> </span>*right*<span class="w"> </span>now.
|
||
If<span class="w"> </span>the<span class="w"> </span>implementation<span class="w"> </span>is<span class="w"> </span>hard<span class="w"> </span>to<span class="w"> </span>explain,<span class="w"> </span>it<span class="s1">&#39;s a bad idea.</span>
|
||
<span class="s1">If the implementation is easy to explain, it may be a good idea.</span>
|
||
<span class="s1">Namespaces are one honking great idea -- let&#39;</span>s<span class="w"> </span><span class="k">do</span><span class="w"> </span>more<span class="w"> </span>of<span class="w"> </span>those!
|
||
</code></pre></div>
|
||
|
||
<p>J&#8217;ai la vague impression que c&#8217;est ce que j&#8217;ai toujours cherché à faire
|
||
en <span class="caps">PHP</span>, et particulièrement dans <a href="http://www.spiral-project.org">le framework
|
||
Spiral</a>, mais en ajoutant ces concepts
|
||
dans une sur-couche au langage. Ici, c&#8217;est directement de <em>l&#8217;esprit</em> de
|
||
python qu&#8217;il s&#8217;agit, ce qui signifie que la plupart des bibliothèques
|
||
python suivent ces concepts. Elle est pas belle la vie&nbsp;?</p>
|
||
<h2 id="comment-commencer-et-par-ou">Comment commencer, et par ou&nbsp;?</h2>
|
||
<p>Pour ma part, j&#8217;ai commencé par la lecture de quelques livres et
|
||
articles intéressants, qui constituent une bonne entrée en matière sur
|
||
le sujet (La liste n&#8217;est bien évidemment pas exhaustive et vos
|
||
commentaires sont les bienvenus)&nbsp;:</p>
|
||
<ul>
|
||
<li><a href="http://diveintopython.adrahon.org/">Dive into&nbsp;python</a></li>
|
||
<li><a href="http://www.swaroopch.com/notes/Python_fr:Table_des_Matières">A byte of&nbsp;python</a></li>
|
||
<li><a href="http://www.amazon.fr/Python-Petit-guide-lusage-développeur/dp/2100508830">Python: petit guide à l&#8217;usage du développeur
|
||
agile</a>
|
||
de <a href="http://tarekziade.wordpress.com/">Tarek&nbsp;Ziadé</a></li>
|
||
<li><a href="http://docs.python.org/index.html">La documentation officielle
|
||
python</a>, bien sûr&nbsp;!</li>
|
||
<li><a href="http://video.pycon.fr/videos/pycon-fr-2009/">Les vidéos du
|
||
pyconfr 2009</a>!</li>
|
||
<li>Un peu de temps, et une console python ouverte&nbsp;:)</li>
|
||
</ul>
|
||
<p>J&#8217;essaye par ailleurs de partager au maximum les ressources que je
|
||
trouve de temps à autres, que ce soit <a href="http://www.twitter.com/ametaireau">via
|
||
twitter</a> ou <a href="http://delicious.com/ametaireau">via mon compte
|
||
delicious</a>. Allez jeter un œil <a href="http://delicious.com/ametaireau/python">au tag
|
||
python</a> sur mon profil, peut
|
||
être que vous trouverez des choses intéressantes, qui&nbsp;sait!</p>
|
||
<h2 id="un-python-sexy">Un python&nbsp;sexy</h2>
|
||
<p>Quelques fonctionnalités qui devraient vous mettre l&#8217;eau à la&nbsp;bouche:</p>
|
||
<ul>
|
||
<li><a href="http://docs.python.org/library/stdtypes.html#comparisons">Le chaînage des opérateurs de
|
||
comparaison</a>
|
||
est possible (a\&lt;b \&lt;c dans une&nbsp;condition)</li>
|
||
<li>Assignation de valeurs multiples (il est possible de faire a,b,c =
|
||
1,2,3 par&nbsp;exemple)</li>
|
||
<li><a href="http://docs.python.org/tutorial/datastructures.html">Les listes</a>
|
||
sont simples à manipuler&nbsp;!</li>
|
||
<li>Les <a href="http://docs.python.org/tutorial/datastructures.html#list-comprehensions">list
|
||
comprehension</a>,
|
||
ou comment faire des opérations complexes sur les listes, de manière&nbsp;simple.</li>
|
||
<li>Les
|
||
<a href="http://docs.python.org/library/doctest.html?highlight=doctest">doctests</a>:
|
||
ou comment faire des tests directement dans la documentation de vos
|
||
classes, tout en la documentant avec de vrais&nbsp;exemples.</li>
|
||
<li>Les
|
||
<a href="http://www.python.org/doc/essays/metaclasses/meta-vladimir.txt">métaclasses</a>,
|
||
ou comment contrôler la manière dont les classes se&nbsp;construisent</li>
|
||
<li>Python est <a href="http://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic%20language%20and%20also%20a%20strongly%20typed%20language">un langage à typage fort
|
||
dynamique</a>:
|
||
c&#8217;est ce qui m&#8217;agaçait avec <span class="caps">PHP</span> qui est un langage à typage faible&nbsp;dynamique.</li>
|
||
</ul>
|
||
<p>Cous pouvez également aller regarder <a href="http://video.pycon.fr/videos/free/53/">l&#8217;atelier donné par Victor Stinner
|
||
durant le Pyconfr 09</a>. Have fun&nbsp;!</p></content><category term="code"></category></entry></feed> |