{"action":"created","comment":{"url":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/comments/61959842","id":61959842,"diff_hunk":"@@ -0,0 +1,240 @@\n+# Guide to creating a basic Openstack API service charm\n+\n+This guide will walk the creation of a basic charm for the Openstack\n+Congress service. [Congress Info](https://wiki.openstack.org/wiki/Congress).\n+The charm will use prewritten Openstack [layers and interfaces](https://github.com/openstack-charmers)\n+Once the charm is written it will be compose using [charm tools](https://github.com/juju/charm-tools/).\n+\n+The Congress service needs to register endpoints with Keystone. It needs a service username and password and it also needs a MySQL backend to store its schema.\n+\n+# Create the skeleton charm\n+\n+## Create charm directories\n+\n+Firstly create a directory for the new charm and manage the charm with git.\n+\n+```bash\n+mkdir -p congress/charm\n+cd congress\n+git init\n+```\n+The top layer of this charm is the Congress specific code this code will live in the charm subdirectory.\n+\n+```mkdir -p charm/{reactive,lib}```\n+\n+## Describe the Service and required layer(s)\n+\n+The new charm needs a basic metadata.yaml to describe what service the charm provides. Edit charm/metadata.yaml\n+\n+```yaml\n+name: congress\n+summary: Policy as a service\n+description: |\n+ Congress is an open policy framework for the cloud. With Congress, a cloud\n+ operator can declare, monitor, enforce, and audit \"policy\" in a heterogeneous\n+ cloud environment.\n+```\n+\n+The [openstack-api layer](https://github.com/openstack-charmers/charm-layer-openstack-api) defines a series of config options and interfaces which are mostly common accross Openstack API services e.g. including the openstack-api-layer will pull in the Keystone and Mysql interfaces (among others) as well as the charm layers the new Congress charm can leverage. To instruct \"charm build\" to pull in the openstack-api layer edit layer.yaml:\n+\n+```yaml\n+includes: ['layer:openstack-api']\n+```\n+\n+# Add Congress configuration\n+\n+## Define Congress attributes\n+\n+To define the attributes of Congress, inherit the OpenStackCharm class and set Congress specific variables. This is done on the charm/lib/charm/openstack/congress.py file.\n+\n+```\n+from charm.openstack.ip import PUBLIC, INTERNAL, ADMIN\n+from charm.openstack.charm import OpenStackCharmFactory, OpenStackCharm\n+from charm.openstack.adapters import OpenStackRelationAdapters\n+\n+class CongressCharm(OpenStackCharm):\n+    # Packages the service needs installed\n+    packages = ['congress-server', 'congress-common', 'python-antlr3', 'python-pymysql']\n+\n+    # Init services the charm manages\n+    services = ['congress-server']\n+\n+    # Standard interface adapters class to use.\n+    adapters_class = OpenStackRelationAdapters\n+\n+    # Ports that need exposing.\n+    default_service = 'congress-api'\n+    api_ports = {\n+        'congress-api': {\n+            PUBLIC: 1789,\n+            ADMIN: 1789,\n+            INTERNAL: 1789,\n+        }\n+    }\n+\n+    # Database sync command used to initalise the schema.\n+    sync_cmd = ['congress-db-manage', '--config-file', '/etc/congress/congress.conf', 'upgrade', 'head']\n+\n+    # The restart map defines which services should be restarted when a given file changes\n+    restart_map = {\n+        '/etc/congress/congress.conf': ['congress-server'],\n+        '/etc/congress/api-paste.ini': ['congress-server'],\n+        '/etc/congress/policy.json': ['congress-server'],\n+    }\n+```\n+\n+Finally create a charm factory, this is used to allow a different Charm class to be used for different Openstack releases. In this case the same charm class will be used for all Liberty and above releases.\n+\n+```\n+class CongressCharmFactory(OpenStackCharmFactory):\n+\n+    releases = {\n+        'liberty': CongressCharm\n+    }\n+\n+    first_release = 'liberty'\n+```\n+\n+## Add Congress code to react to events\n+\n+### Install Congress Packages\n+\n+The reactive framework is going to emit events that the Congress charm can react to. the charm needs to define how its going to react to these events and also raise new events as needed.\n+\n+The first action a charm needs to do is to install the Congress code. This is by done running the install method from CongressCharm created earlier. Edit charm/reactive/handlers.py ...\n+\n+```\n+import charms.reactive as reactive\n+import charm.openstack.congress as congress\n+import charmhelpers.contrib.openstack.utils as ch_utils\n+\n+charm = None\n+\n+def get_charm():\n+    # Retrieve a charm instance from the charm factory.\n+    global charm\n+    if charm is None:\n+        charm = congress.CongressCharmFactory.charm(\n+            release=ch_utils.os_release('congress-common', base='liberty'))\n+    return charm\n+\n+@reactive.hook('install')\n+def install_packages():\n+    # When install hook fires install charm packages\n+    get_charm().install()\n+```\n+\n+### Configure Congress Relation\n+\n+At this point the charm could be built and deployed and it would deploy a unit, and install congress. However there is no code to specify how this charm should interact with the services to depend on. For example when joining the database the charm needs to specify the user and database it requires. The following code configures the relations with the dependant services.\n+\n+```\n+@reactive.when('amqp.connected')\n+def setup_amqp_req(amqp):\n+    # Request username and vhost from rabbit charm\n+    amqp.request_access(username='congress',\n+                        vhost='openstack')\n+\n+@reactive.when('shared-db.connected')\n+def setup_database(database):\n+    # Request username and database from database charm\n+    database.configure('congress', 'congress', hookenv.unit_private_ip())\n+\n+@reactive.when('identity-service.connected')\n+def setup_endpoint(keystone):\n+    # Regester endpoints with keystone and receive service credentials\n+    charm = get_charm()\n+    keystone.register_endpoints(charm.service_type,\n+                                charm.region,\n+                                charm.public_url,\n+                                charm.internal_url,\n+                                charm.admin_url)\n+```\n+\n+## Configure Congress\n+\n+Now that the charm has the relations defined that it needs the Congress charm is in a postion to generate its configuration files.\n+\n+### Create templates\n+\n+The charm code searches through the templates directories looking for a directory cooresponding to the Openstack release being installed\n+or earlier. Since Liberty is the earliest release the charm is supporting a directory called liberty will house the templates.\n+\n+```mkdir -p templates/liberty\n+cp <from pkg>/{api-paste.init,policy.json,congress.conf} templates/liberty```","path":"openstack-api-charm-creation-guide.md","position":null,"original_position":164,"commit_id":"d4383fdfcb2e1d1c48914d54080b1ac68b7c53d7","original_commit_id":"0fd34c7f521509e657e6c04be79932898f42a6ed","user":{"login":"coreycb","id":1678881,"avatar_url":"https://avatars.githubusercontent.com/u/1678881?v=3","gravatar_id":"","url":"https://api.github.com/users/coreycb","html_url":"https://github.com/coreycb","followers_url":"https://api.github.com/users/coreycb/followers","following_url":"https://api.github.com/users/coreycb/following{/other_user}","gists_url":"https://api.github.com/users/coreycb/gists{/gist_id}","starred_url":"https://api.github.com/users/coreycb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coreycb/subscriptions","organizations_url":"https://api.github.com/users/coreycb/orgs","repos_url":"https://api.github.com/users/coreycb/repos","events_url":"https://api.github.com/users/coreycb/events{/privacy}","received_events_url":"https://api.github.com/users/coreycb/received_events","type":"User","site_admin":false},"body":"Looks like congress is only in mitaka.","created_at":"2016-05-03T21:31:34Z","updated_at":"2016-05-03T21:31:34Z","html_url":"https://github.com/openstack-charmers/openstack-community/pull/3#discussion_r61959842","pull_request_url":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/3","_links":{"self":{"href":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/comments/61959842"},"html":{"href":"https://github.com/openstack-charmers/openstack-community/pull/3#discussion_r61959842"},"pull_request":{"href":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/3"}}},"pull_request":{"url":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/3","id":68751884,"html_url":"https://github.com/openstack-charmers/openstack-community/pull/3","diff_url":"https://github.com/openstack-charmers/openstack-community/pull/3.diff","patch_url":"https://github.com/openstack-charmers/openstack-community/pull/3.patch","issue_url":"https://api.github.com/repos/openstack-charmers/openstack-community/issues/3","number":3,"state":"open","locked":false,"title":"Added guide for writing an Openstack API charm","user":{"login":"gnuoy","id":6472064,"avatar_url":"https://avatars.githubusercontent.com/u/6472064?v=3","gravatar_id":"","url":"https://api.github.com/users/gnuoy","html_url":"https://github.com/gnuoy","followers_url":"https://api.github.com/users/gnuoy/followers","following_url":"https://api.github.com/users/gnuoy/following{/other_user}","gists_url":"https://api.github.com/users/gnuoy/gists{/gist_id}","starred_url":"https://api.github.com/users/gnuoy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gnuoy/subscriptions","organizations_url":"https://api.github.com/users/gnuoy/orgs","repos_url":"https://api.github.com/users/gnuoy/repos","events_url":"https://api.github.com/users/gnuoy/events{/privacy}","received_events_url":"https://api.github.com/users/gnuoy/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-05-03T18:25:42Z","updated_at":"2016-05-03T21:31:34Z","closed_at":null,"merged_at":null,"merge_commit_sha":"20faf45fcc808efb16ed547dc33f1d2308274a96","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/3/commits","review_comments_url":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/3/comments","review_comment_url":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/comments{/number}","comments_url":"https://api.github.com/repos/openstack-charmers/openstack-community/issues/3/comments","statuses_url":"https://api.github.com/repos/openstack-charmers/openstack-community/statuses/d4383fdfcb2e1d1c48914d54080b1ac68b7c53d7","head":{"label":"gnuoy:api-charm-guide","ref":"api-charm-guide","sha":"d4383fdfcb2e1d1c48914d54080b1ac68b7c53d7","user":{"login":"gnuoy","id":6472064,"avatar_url":"https://avatars.githubusercontent.com/u/6472064?v=3","gravatar_id":"","url":"https://api.github.com/users/gnuoy","html_url":"https://github.com/gnuoy","followers_url":"https://api.github.com/users/gnuoy/followers","following_url":"https://api.github.com/users/gnuoy/following{/other_user}","gists_url":"https://api.github.com/users/gnuoy/gists{/gist_id}","starred_url":"https://api.github.com/users/gnuoy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gnuoy/subscriptions","organizations_url":"https://api.github.com/users/gnuoy/orgs","repos_url":"https://api.github.com/users/gnuoy/repos","events_url":"https://api.github.com/users/gnuoy/events{/privacy}","received_events_url":"https://api.github.com/users/gnuoy/received_events","type":"User","site_admin":false},"repo":{"id":57991430,"name":"openstack-community","full_name":"gnuoy/openstack-community","owner":{"login":"gnuoy","id":6472064,"avatar_url":"https://avatars.githubusercontent.com/u/6472064?v=3","gravatar_id":"","url":"https://api.github.com/users/gnuoy","html_url":"https://github.com/gnuoy","followers_url":"https://api.github.com/users/gnuoy/followers","following_url":"https://api.github.com/users/gnuoy/following{/other_user}","gists_url":"https://api.github.com/users/gnuoy/gists{/gist_id}","starred_url":"https://api.github.com/users/gnuoy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gnuoy/subscriptions","organizations_url":"https://api.github.com/users/gnuoy/orgs","repos_url":"https://api.github.com/users/gnuoy/repos","events_url":"https://api.github.com/users/gnuoy/events{/privacy}","received_events_url":"https://api.github.com/users/gnuoy/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gnuoy/openstack-community","description":"","fork":true,"url":"https://api.github.com/repos/gnuoy/openstack-community","forks_url":"https://api.github.com/repos/gnuoy/openstack-community/forks","keys_url":"https://api.github.com/repos/gnuoy/openstack-community/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gnuoy/openstack-community/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gnuoy/openstack-community/teams","hooks_url":"https://api.github.com/repos/gnuoy/openstack-community/hooks","issue_events_url":"https://api.github.com/repos/gnuoy/openstack-community/issues/events{/number}","events_url":"https://api.github.com/repos/gnuoy/openstack-community/events","assignees_url":"https://api.github.com/repos/gnuoy/openstack-community/assignees{/user}","branches_url":"https://api.github.com/repos/gnuoy/openstack-community/branches{/branch}","tags_url":"https://api.github.com/repos/gnuoy/openstack-community/tags","blobs_url":"https://api.github.com/repos/gnuoy/openstack-community/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gnuoy/openstack-community/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gnuoy/openstack-community/git/refs{/sha}","trees_url":"https://api.github.com/repos/gnuoy/openstack-community/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gnuoy/openstack-community/statuses/{sha}","languages_url":"https://api.github.com/repos/gnuoy/openstack-community/languages","stargazers_url":"https://api.github.com/repos/gnuoy/openstack-community/stargazers","contributors_url":"https://api.github.com/repos/gnuoy/openstack-community/contributors","subscribers_url":"https://api.github.com/repos/gnuoy/openstack-community/subscribers","subscription_url":"https://api.github.com/repos/gnuoy/openstack-community/subscription","commits_url":"https://api.github.com/repos/gnuoy/openstack-community/commits{/sha}","git_commits_url":"https://api.github.com/repos/gnuoy/openstack-community/git/commits{/sha}","comments_url":"https://api.github.com/repos/gnuoy/openstack-community/comments{/number}","issue_comment_url":"https://api.github.com/repos/gnuoy/openstack-community/issues/comments{/number}","contents_url":"https://api.github.com/repos/gnuoy/openstack-community/contents/{+path}","compare_url":"https://api.github.com/repos/gnuoy/openstack-community/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gnuoy/openstack-community/merges","archive_url":"https://api.github.com/repos/gnuoy/openstack-community/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gnuoy/openstack-community/downloads","issues_url":"https://api.github.com/repos/gnuoy/openstack-community/issues{/number}","pulls_url":"https://api.github.com/repos/gnuoy/openstack-community/pulls{/number}","milestones_url":"https://api.github.com/repos/gnuoy/openstack-community/milestones{/number}","notifications_url":"https://api.github.com/repos/gnuoy/openstack-community/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gnuoy/openstack-community/labels{/name}","releases_url":"https://api.github.com/repos/gnuoy/openstack-community/releases{/id}","deployments_url":"https://api.github.com/repos/gnuoy/openstack-community/deployments","created_at":"2016-05-03T18:24:49Z","updated_at":"2016-02-17T16:12:42Z","pushed_at":"2016-05-03T18:30:28Z","git_url":"git://github.com/gnuoy/openstack-community.git","ssh_url":"git@github.com:gnuoy/openstack-community.git","clone_url":"https://github.com/gnuoy/openstack-community.git","svn_url":"https://github.com/gnuoy/openstack-community","homepage":null,"size":24,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"openstack-charmers:master","ref":"master","sha":"7b824d510c983b8c6dc8e7329d22372cd661c919","user":{"login":"openstack-charmers","id":12201962,"avatar_url":"https://avatars.githubusercontent.com/u/12201962?v=3","gravatar_id":"","url":"https://api.github.com/users/openstack-charmers","html_url":"https://github.com/openstack-charmers","followers_url":"https://api.github.com/users/openstack-charmers/followers","following_url":"https://api.github.com/users/openstack-charmers/following{/other_user}","gists_url":"https://api.github.com/users/openstack-charmers/gists{/gist_id}","starred_url":"https://api.github.com/users/openstack-charmers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openstack-charmers/subscriptions","organizations_url":"https://api.github.com/users/openstack-charmers/orgs","repos_url":"https://api.github.com/users/openstack-charmers/repos","events_url":"https://api.github.com/users/openstack-charmers/events{/privacy}","received_events_url":"https://api.github.com/users/openstack-charmers/received_events","type":"Organization","site_admin":false},"repo":{"id":51991592,"name":"openstack-community","full_name":"openstack-charmers/openstack-community","owner":{"login":"openstack-charmers","id":12201962,"avatar_url":"https://avatars.githubusercontent.com/u/12201962?v=3","gravatar_id":"","url":"https://api.github.com/users/openstack-charmers","html_url":"https://github.com/openstack-charmers","followers_url":"https://api.github.com/users/openstack-charmers/followers","following_url":"https://api.github.com/users/openstack-charmers/following{/other_user}","gists_url":"https://api.github.com/users/openstack-charmers/gists{/gist_id}","starred_url":"https://api.github.com/users/openstack-charmers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openstack-charmers/subscriptions","organizations_url":"https://api.github.com/users/openstack-charmers/orgs","repos_url":"https://api.github.com/users/openstack-charmers/repos","events_url":"https://api.github.com/users/openstack-charmers/events{/privacy}","received_events_url":"https://api.github.com/users/openstack-charmers/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/openstack-charmers/openstack-community","description":"","fork":true,"url":"https://api.github.com/repos/openstack-charmers/openstack-community","forks_url":"https://api.github.com/repos/openstack-charmers/openstack-community/forks","keys_url":"https://api.github.com/repos/openstack-charmers/openstack-community/keys{/key_id}","collaborators_url":"https://api.github.com/repos/openstack-charmers/openstack-community/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/openstack-charmers/openstack-community/teams","hooks_url":"https://api.github.com/repos/openstack-charmers/openstack-community/hooks","issue_events_url":"https://api.github.com/repos/openstack-charmers/openstack-community/issues/events{/number}","events_url":"https://api.github.com/repos/openstack-charmers/openstack-community/events","assignees_url":"https://api.github.com/repos/openstack-charmers/openstack-community/assignees{/user}","branches_url":"https://api.github.com/repos/openstack-charmers/openstack-community/branches{/branch}","tags_url":"https://api.github.com/repos/openstack-charmers/openstack-community/tags","blobs_url":"https://api.github.com/repos/openstack-charmers/openstack-community/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/openstack-charmers/openstack-community/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/openstack-charmers/openstack-community/git/refs{/sha}","trees_url":"https://api.github.com/repos/openstack-charmers/openstack-community/git/trees{/sha}","statuses_url":"https://api.github.com/repos/openstack-charmers/openstack-community/statuses/{sha}","languages_url":"https://api.github.com/repos/openstack-charmers/openstack-community/languages","stargazers_url":"https://api.github.com/repos/openstack-charmers/openstack-community/stargazers","contributors_url":"https://api.github.com/repos/openstack-charmers/openstack-community/contributors","subscribers_url":"https://api.github.com/repos/openstack-charmers/openstack-community/subscribers","subscription_url":"https://api.github.com/repos/openstack-charmers/openstack-community/subscription","commits_url":"https://api.github.com/repos/openstack-charmers/openstack-community/commits{/sha}","git_commits_url":"https://api.github.com/repos/openstack-charmers/openstack-community/git/commits{/sha}","comments_url":"https://api.github.com/repos/openstack-charmers/openstack-community/comments{/number}","issue_comment_url":"https://api.github.com/repos/openstack-charmers/openstack-community/issues/comments{/number}","contents_url":"https://api.github.com/repos/openstack-charmers/openstack-community/contents/{+path}","compare_url":"https://api.github.com/repos/openstack-charmers/openstack-community/compare/{base}...{head}","merges_url":"https://api.github.com/repos/openstack-charmers/openstack-community/merges","archive_url":"https://api.github.com/repos/openstack-charmers/openstack-community/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/openstack-charmers/openstack-community/downloads","issues_url":"https://api.github.com/repos/openstack-charmers/openstack-community/issues{/number}","pulls_url":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls{/number}","milestones_url":"https://api.github.com/repos/openstack-charmers/openstack-community/milestones{/number}","notifications_url":"https://api.github.com/repos/openstack-charmers/openstack-community/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/openstack-charmers/openstack-community/labels{/name}","releases_url":"https://api.github.com/repos/openstack-charmers/openstack-community/releases{/id}","deployments_url":"https://api.github.com/repos/openstack-charmers/openstack-community/deployments","created_at":"2016-02-18T08:19:01Z","updated_at":"2016-02-17T16:12:42Z","pushed_at":"2016-05-03T18:36:39Z","git_url":"git://github.com/openstack-charmers/openstack-community.git","ssh_url":"git@github.com:openstack-charmers/openstack-community.git","clone_url":"https://github.com/openstack-charmers/openstack-community.git","svn_url":"https://github.com/openstack-charmers/openstack-community","homepage":null,"size":26,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":1,"forks":3,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/3"},"html":{"href":"https://github.com/openstack-charmers/openstack-community/pull/3"},"issue":{"href":"https://api.github.com/repos/openstack-charmers/openstack-community/issues/3"},"comments":{"href":"https://api.github.com/repos/openstack-charmers/openstack-community/issues/3/comments"},"review_comments":{"href":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/3/comments"},"review_comment":{"href":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/openstack-charmers/openstack-community/pulls/3/commits"},"statuses":{"href":"https://api.github.com/repos/openstack-charmers/openstack-community/statuses/d4383fdfcb2e1d1c48914d54080b1ac68b7c53d7"}}}}