#8 Create a new Flask app that answers the GET /v2/ Docker API call.
Merged 7 years ago by bowlofeggs. Opened 7 years ago by bowlofeggs.
bowlofeggs/fegistry 2  into  master

file added
+10
@@ -0,0 +1,10 @@ 

+ *__pycache__

+ .coverage

+ .dnf-cache

+ .eggs

+ .vagrant

+ coverage.xml

+ devel/ansible/playbook.retry

+ fegistry.egg-info/

+ nosetests.xml

+ Vagrantfile

file modified
+72 -1
@@ -1,3 +1,74 @@ 

  # fegistry

  

- The Fedora registry endpoint. 

\ No newline at end of file

+ The Fedora registry endpoint.

+ 

+ 

+ ## Development

+ 

+ ### Contribution guidelines

+ 

+ Before you submit a pull request to fegistry, please ensure that it meets these criteria:

+ 

+ * All tests must pass.

+ * Code should have 100% test coverage. This one is particularly important, as we don't want to

+   deploy any broken code into production.

+ * Functions, methods, and classes should have docblocks that explain what the code block is, and

+   describing any parameters it accepts and what it returns (if anything).

+ * Code should follow [PEP-8](https://www.python.org/dev/peps/pep-0008). You can use the

+   ```flake8``` utility to automatically check your code. There is a

+   ```fegistry.tests.test_style.TestStyle.test_code_with_flake8``` test, which enforced PEP-8 on the

+   codebase.

+ 

+ 

+ ### Development environment

+ 

+ [Vagrant](https://www.vagrantup.com) allows contributors to get quickly up and running with a

+ development environment by automatically configuring a virtual machine. Before you get

+ started, ensure that your host machine has virtualization extensions enabled in its BIOS so the

+ guest doesn't go slower than molasses. To get started, simply

+ use these commands:

+ 

+ ```

+     $ sudo dnf install ansible libvirt vagrant-libvirt vagrant-sshfs

+     $ sudo systemctl enable libvirtd

+     $ sudo systemctl start libvirtd

+     $ cp Vagrantfile.example Vagrantfile

+     # Make sure your fegistry checkout is your shell's cwd

+     $ vagrant up

+ ```

+ 

+ fegistry is now running in the guest, and port 5000 has been forwarded on your host:

+ 

+ ```

+     $ curl -i http://localhost:5000/v2/

+ 	HTTP/1.0 200 OK

+ 	Content-Type: application/json

+ 	Content-Length: 2

+ 	Docker-Distribution-API-Version: registry/2.0

+ 	Server: Werkzeug/0.11.10 Python/3.5.2

+ 	Date: Thu, 15 Dec 2016 22:04:33 GMT

+ 

+ 	{}

+ ```

+ 

+ You can use ```vagrant ssh``` to ssh into the guest if you like. Inside the guest environment, you

+ will find the code shared at ```/home/vagrant/fegistry```. There are some convenient bash aliases:

+ 

+ ```flog```:     Display the development server's log. You can pass a ```-f``` flag to continuously

+                 display the log.

+ ```frestart```: Restart the development server. The development server does automatically pick up

+ 				code changes, so you shouldn't need this much.

+ ```fstart```:   Start the development server.

+ ```fstop```:    Stop the development server.

+ ```ftest```:    Run the test suite.

+ 

+ 

+ ```vagrant ssh``` also accepts a ```-c``` flag that allows you to run a command in the guest. For

+ example, you can run the tests wtih ```vagrant ssh -c ftest```.

+ 

+ When you are done with your Vagrant guest, you can destroy it permanently by running this command on

+ the host:

+ 

+ ```

+     $ vagrant destroy

+ ```

file added
+61
@@ -0,0 +1,61 @@ 

+ # -*- mode: ruby -*-

+ # vi: set ft=ruby :

+ 

+ # To use Vagrant to develop fegistry, on your host:

+ # git clone https://pagure.io/fegistry.git

+ # cd fegistry

+ # cp Vagrantfile.example Vagrantfile

+ # vagrant up

+ # vagrant ssh  # Now you're in the fegistry development environment, have fun!

+ 

+ Vagrant.configure(2) do |config|

+   config.vm.box_url = "https://download.fedoraproject.org/pub/fedora/linux/releases/25/CloudImages"\

+                       "/x86_64/images/Fedora-Cloud-Base-Vagrant-25-1.3.x86_64.vagrant-libvirt.box"

+   config.vm.box = "f25-cloud-libvirt"

+   config.vm.network "forwarded_port", guest: 5000, host: 5000

+ 

+   # This is an optional plugin that, if installed, updates the host's /etc/hosts

+   # file with the hostname of the guest VM. In Fedora it is packaged as

+   # ``vagrant-hostmanager``

+   if Vagrant.has_plugin?("vagrant-hostmanager")

+       config.hostmanager.enabled = true

+       config.hostmanager.manage_host = true

+   end

+ 

+   config.vm.synced_folder ".", "/vagrant", disabled: true

+   config.vm.synced_folder ".", "/home/vagrant/fegistry", type: "sshfs"

+ 

+   # To cache update packages (which is helpful if frequently doing `vagrant destroy && vagrant up`)

+   # you can create a local directory and share it to the guest's DNF cache. Uncomment the lines

+   # below to create and use a dnf cache directory

+   #

+   # Dir.mkdir('.dnf-cache') unless File.exists?('.dnf-cache')

+   # config.vm.synced_folder ".dnf-cache", "/var/cache/dnf", type: "sshfs", sshfs_opts_append: "-o nonempty"

+ 

+   # Ansible needs the guest to have these

+   config.vm.provision "shell", inline: "sudo dnf install -y libselinux-python python2-dnf"

+   config.vm.provision "ansible" do |ansible|

+       ansible.playbook = "devel/ansible/playbook.yml"

+   end

+ 

+   # Create a fegistry guest

+   config.vm.define "fegistry" do |fegistry|

+      fegistry.vm.host_name = "fegistry.example.com"

+ 

+      fegistry.vm.provider :libvirt do |domain|

+          # Season to taste

+          domain.cpus = 4

+          domain.graphics_type = "spice"

+          domain.memory = 512

+          domain.video_type = "qxl"

+ 

+          # Uncomment the following line if you would like to enable libvirt's unsafe cache

+          # mode. It is called unsafe for a reason, as it causes the virtual host to ignore all

+          # fsync() calls from the guest. Only do this if you are comfortable with the possibility of

+          # your development guest becoming corrupted (in which case you should only need to do a

+          # vagrant destroy and vagrant up to get a new one).

+          #

+          # domain.volume_cache = "unsafe"

+      end

+   end

+ end

@@ -0,0 +1,8 @@ 

+ ---

+ - hosts: all

+   become: true

+   become_method: sudo

+   vars:

+   roles:

+     - core

+     - dev

@@ -0,0 +1,12 @@ 

+ ---

+ - name: Install basic packages

+   dnf:

+       name: "{{ item }}"

+       state: present

+   with_items:

+       - bash-completion

+       - dstat

+       - fedora-easy-karma

+       - htop

+       - tmux

+       - tree

@@ -0,0 +1,16 @@ 

+ # .bashrc

+ 

+ # Source global definitions

+ if [ -f /etc/bashrc ]; then

+         . /etc/bashrc

+ fi

+ 

+ # Uncomment the following line if you don't like systemctl's auto-paging feature:

+ # export SYSTEMD_PAGER=

+ 

+ shopt -s expand_aliases

+ alias flog="sudo journalctl -u fegistry"

+ alias frestart="sudo systemctl restart fegistry"

+ alias fstart="sudo systemctl start fegistry"

+ alias fstop="sudo systemctl stop fegistry"

+ alias ftest="pushd /home/vagrant/fegistry && python3 setup.py nosetests; popd"

@@ -0,0 +1,13 @@ 

+ [Unit]

+ Description=fegistry

+ After=network-online.target

+ Wants=network-online.target

+ 

+ [Service]

+ Environment=FLASK_APP=/home/vagrant/fegistry/fegistry/views.py

+ Environment=FLASK_DEBUG=1

+ User=vagrant

+ ExecStart=/usr/bin/flask run -h 0.0.0.0

+ 

+ [Install]

+ WantedBy=multi-user.target

@@ -0,0 +1,38 @@ 

+ ---

+ - name: Install dev packages

+   dnf:

+       name: "{{ item }}"

+       state: present

+   with_items:

+       - git

+       - python3-flake8

+       - python3-flask

+       - python3-mock

+       - python3-nose

+       - python3-nose-cov

+ 

+ - name: Install the .bashrc

+   copy:

+       src: .bashrc

+       dest: /home/vagrant/.bashrc

+       mode: 0644

+       owner: vagrant

+       group: vagrant

+ 

+ - name: Install fegistry in developer mode

+   command: python3 setup.py develop

+   args:

+       chdir: /home/vagrant/fegistry

+       creates: /usr/lib/python3.*/site-packages/fegistry.egg-link

+ 

+ - name: Install the systemd unit

+   copy:

+       src: fegistry.service

+       dest: /etc/systemd/system/fegistry.service

+       mode: 0644

+ 

+ - name: Start and enable the fegistry service

+   systemd:

+       name: fegistry

+       state: started

+       enabled: yes

file added
+16
@@ -0,0 +1,16 @@ 

+ # Copyright â“’ 2016 Red Hat, Inc.

+ # This file is part of fegistry.

+ #

+ # fegistry is free software: you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation, either version 3 of the License, or

+ # (at your option) any later version.

+ #

+ # fegistry is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with Foobar.  If not, see <http://www.gnu.org/licenses/>.

+ __version__ = '0.0.0'

empty or binary file added
@@ -0,0 +1,36 @@ 

+ # Copyright â“’ 2016 Red Hat, Inc.

+ # This file is part of fegistry.

+ #

+ # fegistry is free software: you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation, either version 3 of the License, or

+ # (at your option) any later version.

+ #

+ # fegistry is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with Foobar.  If not, see <http://www.gnu.org/licenses/>.

+ """This test suite enforces code style automatically."""

+ 

+ import os

+ import subprocess

+ import unittest

+ 

+ 

+ REPO_PATH = os.path.abspath(

+     os.path.dirname(os.path.join(os.path.dirname(__file__), '..', '..', '..')))

+ 

+ 

+ class TestStyle(unittest.TestCase):

+     """This test class contains tests pertaining to code style."""

+     def test_code_with_flake8(self):

+         """Enforce PEP-8 compliance on the codebase.

+ 

+         This test runs flake8 on the code.

+         """

+         flake8_command = ['python3-flake8', '--max-line-length', '100', REPO_PATH]

+ 

+         self.assertEqual(subprocess.call(flake8_command), 0)

@@ -0,0 +1,53 @@ 

+ # Copyright â“’ 2016 Red Hat, Inc.

+ # This file is part of fegistry.

+ #

+ # fegistry is free software: you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation, either version 3 of the License, or

+ # (at your option) any later version.

+ #

+ # fegistry is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with Foobar.  If not, see <http://www.gnu.org/licenses/>.

+ """This test suite contains tests on fegistry.views."""

+ 

+ import json

+ import unittest

+ 

+ import flask

+ 

+ from fegistry import views

+ 

+ 

+ class ViewsTestCase(unittest.TestCase):

+     """A superclass for testing the views module."""

+     def setUp(self):

+         views.app.config['TESTING'] = True

+         self.app = views.app.test_client()

+ 

+ 

+ class TestAddDockerHeaders(unittest.TestCase):

+     """This test class contains tests on the add_docker_headers() function."""

+     def test_add_docker_headers(self):

+         """Assert correct behavior when the status code is 200."""

+         response = flask.Response(status="200")

+ 

+         response = views.add_docker_headers(response)

+ 

+         self.assertEqual(response.headers['Docker-Distribution-API-Version'], 'registry/2.0')

+ 

+ 

+ class Testv2(ViewsTestCase):

+     """This test class tests the v2() function."""

+     def test_v2(self):

+         """Check the /v2/ handler."""

+         response = self.app.get('/v2/')

+ 

+         self.assertEqual(response.status_code, 200)

+         self.assertEqual(json.loads(response.get_data().decode('utf-8')), {})

+         self.assertEqual(response.headers['Docker-Distribution-API-Version'], 'registry/2.0')

+         self.assertEqual(response.headers['Content-Type'], 'application/json')

file added
+36
@@ -0,0 +1,36 @@ 

+ # Copyright â“’ 2016 Red Hat, Inc.

+ # This file is part of fegistry.

+ #

+ # fegistry is free software: you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation, either version 3 of the License, or

+ # (at your option) any later version.

+ #

+ # fegistry is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with Foobar.  If not, see <http://www.gnu.org/licenses/>.

+ import flask

+ 

+ 

+ app = flask.Flask(__name__)

+ 

+ 

+ @app.after_request

+ def add_docker_headers(response):

+     """

+     Add the necssary Docker headers to every response.

+     """

+     response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'

+     return response

+ 

+ 

+ @app.route('/v2/')

+ def v2():

+     """

+     Answer the GET /v2/ API call with {}.

+     """

+     return flask.json.jsonify({})

file added
+8
@@ -0,0 +1,8 @@ 

+ [nosetests]

+ cover-erase=TRUE

+ cover-inclusive=TRUE

+ cover-min-percentage=100

+ cover-package=fegistry

+ cover-xml=TRUE

+ with-coverage=TRUE

+ with-xunit=TRUE

file added
+51
@@ -0,0 +1,51 @@ 

+ #!/usr/bin/env python3

+ # Copyright â“’ 2016 Red Hat, Inc.

+ # This file is part of fegistry.

+ #

+ # fegistry is free software: you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation, either version 3 of the License, or

+ # (at your option) any later version.

+ #

+ # fegistry is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with Foobar.  If not, see <http://www.gnu.org/licenses/>.

+ import os

+ 

+ from setuptools import setup, find_packages

+ 

+ import fegistry

+ 

+ 

+ here = os.path.abspath(os.path.dirname(__file__))

+ README = open(os.path.join(here, 'README.md')).read()

+ VERSION = fegistry.__version__

+ 

+ # Possible options are at https://pypi.python.org/pypi?%3Aaction=list_classifiers

+ CLASSIFIERS = [

+     'Development Status :: 2 - Pre-Alpha',

+     'Framework :: Flask',

+     'Intended Audience :: Developers',

+     'Intended Audience :: System Administrators',

+     'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',

+     'Operating System :: POSIX :: Linux',

+     'Programming Language :: Python :: 3 :: Only',

+     'Topic :: System :: Software Distribution']

+ LICENSE = 'GPLv3'

+ MAINTAINER = 'Fedora Infrastructure Team'

+ MAINTAINER_EMAIL = 'infrastructure@lists.fedoraproject.org'

+ PLATFORMS = ['Fedora', 'GNU/Linux']

+ URL = 'https://pagure.io/fegistry'

+ 

+ 

+ setup(

+     name='fegistry', version=VERSION, description='The Fedora registry endpoint.',

+     long_description=README, classifiers=CLASSIFIERS, license=LICENSE, maintainer=MAINTAINER,

+     maintainer_email=MAINTAINER_EMAIL, platforms=PLATFORMS, url=URL, keywords='fedora',

+     packages=find_packages(exclude=('fegistry.tests', 'fegistry.tests.*')),

+     include_package_data=True, zip_safe=False, install_requires=['flask'],

+     tests_require=['flake8', 'mock', 'nose', 'nose-cov'], test_suite="nose.collector")

This commit creates all the boilerplate code to make a new Flask
app, a Vagrant development environment, developer instructions,
tests, and a handler that responds to the Docker v2 GET /v2/ API
endpoint.

fixes #2

If you want, you can set version in fegistry/__init__.py and use this to pull the version out of that file. Then you can use that version in the docs as well, and anywhere else you want.

@jcline whoah that comment just took the views.py (which is currently the entire project) from 11 lines of code to 7 lines. You just eliminated 36% of this project's entire codebase in a single comment!

rebased

7 years ago

I added __version__ to the __init__.py, but I was able to just have the setup.py import the version rather than using the regex. The downside is that it depends on the setup.py being in the current working directory, but I'm OK with that, at least for now. We could probably do it facier later if we want it to work from arbitrary working directories.

rebased

7 years ago

Pull-Request has been merged by bowlofeggs

7 years ago