{"action":"created","comment":{"url":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/comments/69671187","id":69671187,"diff_hunk":"@@ -0,0 +1,354 @@\n+#!/usr/bin/python\n+# -*- coding: utf-8 -*-\n+\n+\"\"\"\n+Generate Minecraft ACLs.\n+\"\"\"\n+\n+import grp\n+import json\n+import os\n+import pwd\n+import stat\n+import tempfile\n+import uuid\n+\n+from datetime import datetime as dt\n+\n+MINECRAFT_API_URL = 'https://api.mojang.com/profiles/minecraft'\n+SERVER_FILE_CHOICES = ['ops', 'whitelist', 'banned-players', 'banned-ips',\n+                       'server-properties']\n+DEFAULT_BAN_EXPIRES = 'forever'\n+DEFAULT_BAN_REASON = 'Banned by an operator.'\n+MINECRAFT_OP_CODE = 4\n+\n+\n+class MissingArgsException(Exception):\n+\n+    def __init__(self, missing_args):\n+        super(MissingArgsException, self).__init__(\n+            message='missing required arguments: {}'.format(\n+                ', '.join(missing_args)))\n+\n+\n+class BadValueException(Exception):\n+\n+    def __init__(self, bad_value):\n+        super(BadValueException, self).__init__(\n+            message='bad value: {}'.format(bad_value))\n+\n+\n+class FileStats(object):\n+    \"\"\"\n+    Compare and update file stats.\n+    \"\"\"\n+    REQUIRED_ARGS = ['owner', 'group']\n+\n+    def __init__(self, module):\n+        self.module = module\n+        missing_args = [x for x in self.REQUIRED_ARGS\n+                        if not self.module.params[x]]\n+        if missing_args:\n+            raise MissingArgsException(missing_args)\n+\n+        self.path = os.path.expanduser(module.params['path'])\n+        self.owner = module.params['owner']\n+        self.group = module.params['group']\n+        self.mode = module.params['mode'] if module.params['mode'] else 0644\n+\n+    def file_stats_changed(self):\n+        try:\n+            st = os.stat(self.path)\n+        except OSError:\n+            # If the file doesn't exist or isn't read, this is the same as\n+            # the stat values being wrong.\n+            return True\n+\n+        current_mode = st.st_mode & stat.S_IMODE(st.st_mode)\n+        # We cannot look up non-existant uids and gids when we compare the\n+        # current state to desired state. Instead, get the usernames and\n+        # groups now to make comparison easier.\n+        current_uid, current_gid = self.module.user_and_group(self.path)\n+        current_user = pwd.getpwuid(current_uid).pw_name\n+        current_group = grp.getgrgid(current_gid).gr_name\n+\n+        return any([\n+            self.owner != current_user,\n+            self.group != current_group,\n+            self.mode != current_mode,\n+        ])\n+\n+    def correct_file_stats(self):\n+        self.module.set_owner_if_different(self.path, self.owner, False)\n+        self.module.set_group_if_different(self.path, self.group, False)\n+        self.module.set_mode_if_different(self.path, self.mode, False)\n+\n+\n+class ServerFile(object):\n+    \"\"\"\n+    Abstract class representing any server file.\n+    \"\"\"\n+    def __init__(self, module):\n+        self.module = module\n+        self.values = self.module.params['values']\n+        self.stats = FileStats(module)\n+\n+    def content(self):\n+        \"\"\"\n+        Return content in its native format (e.g. dict).\n+        \"\"\"\n+        raise NotImplementedError\n+\n+    def content_str(self):\n+        \"\"\"\n+        Return content as a string, for saving to a file.\n+        \"\"\"\n+        return self.content()\n+\n+    def content_changed(self):\n+        \"\"\"\n+        Indicates whether the content changed or not.\n+        \"\"\"\n+        raise NotImplementedError\n+\n+\n+class ServerProperties(ServerFile):\n+    \"\"\"\n+    The Minecraft server.properties file.\n+\n+    The module \"values\" argument should be a list of strings in the format\n+    \"<name>=<value>\".\n+    \"\"\"\n+    def __init__(self, module):\n+        super(ServerProperties, self).__init__(module)\n+        self._content_changed = False\n+        self.newlines = []\n+        properties = {}\n+\n+        for a in self.values:","path":"library/minecraft_server_file.py","position":128,"original_position":128,"commit_id":"65bf0fa0e37908d38feb8f7dc3290730c69fd96b","original_commit_id":"65bf0fa0e37908d38feb8f7dc3290730c69fd96b","user":{"login":"benwebber","id":358027,"avatar_url":"https://avatars.githubusercontent.com/u/358027?v=3","gravatar_id":"","url":"https://api.github.com/users/benwebber","html_url":"https://github.com/benwebber","followers_url":"https://api.github.com/users/benwebber/followers","following_url":"https://api.github.com/users/benwebber/following{/other_user}","gists_url":"https://api.github.com/users/benwebber/gists{/gist_id}","starred_url":"https://api.github.com/users/benwebber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benwebber/subscriptions","organizations_url":"https://api.github.com/users/benwebber/orgs","repos_url":"https://api.github.com/users/benwebber/repos","events_url":"https://api.github.com/users/benwebber/events{/privacy}","received_events_url":"https://api.github.com/users/benwebber/received_events","type":"User","site_admin":false},"body":"Switching to a dictionary means you can completely ignore this validation.","created_at":"2016-07-06T03:31:01Z","updated_at":"2016-07-06T03:31:01Z","html_url":"https://github.com/benwebber/ansible-minecraft/pull/5#discussion_r69671187","pull_request_url":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/5","_links":{"self":{"href":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/comments/69671187"},"html":{"href":"https://github.com/benwebber/ansible-minecraft/pull/5#discussion_r69671187"},"pull_request":{"href":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/5"}}},"pull_request":{"url":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/5","id":76112624,"html_url":"https://github.com/benwebber/ansible-minecraft/pull/5","diff_url":"https://github.com/benwebber/ansible-minecraft/pull/5.diff","patch_url":"https://github.com/benwebber/ansible-minecraft/pull/5.patch","issue_url":"https://api.github.com/repos/benwebber/ansible-minecraft/issues/5","number":5,"state":"open","locked":false,"title":"Support for setting server.properties","user":{"login":"markrcote","id":131299,"avatar_url":"https://avatars.githubusercontent.com/u/131299?v=3","gravatar_id":"","url":"https://api.github.com/users/markrcote","html_url":"https://github.com/markrcote","followers_url":"https://api.github.com/users/markrcote/followers","following_url":"https://api.github.com/users/markrcote/following{/other_user}","gists_url":"https://api.github.com/users/markrcote/gists{/gist_id}","starred_url":"https://api.github.com/users/markrcote/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markrcote/subscriptions","organizations_url":"https://api.github.com/users/markrcote/orgs","repos_url":"https://api.github.com/users/markrcote/repos","events_url":"https://api.github.com/users/markrcote/events{/privacy}","received_events_url":"https://api.github.com/users/markrcote/received_events","type":"User","site_admin":false},"body":"This PR adds support for setting server.properties entries. The bulk of the work is in the first commit, which refactors `library/minecraft_acl.py` (which is renamed to `minecraft_server_file.py` in a later commit) to allow support for any file type (not just the JSON ACL files). It also puts the file stats comparison and update procedures into their own class to make the main function a bit easier to read. I'm not super happy that I had to have the module object passed in, but it seemed necessary because that's where the `set_*_if_different()` methods live. I am of course open to changing it if you have any other ideas.\r\n\r\nHope this is useful!","created_at":"2016-07-04T05:00:12Z","updated_at":"2016-07-06T03:31:03Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ab4682a8ebcfd07729386a7bed7f62533d7ec656","assignee":{"login":"benwebber","id":358027,"avatar_url":"https://avatars.githubusercontent.com/u/358027?v=3","gravatar_id":"","url":"https://api.github.com/users/benwebber","html_url":"https://github.com/benwebber","followers_url":"https://api.github.com/users/benwebber/followers","following_url":"https://api.github.com/users/benwebber/following{/other_user}","gists_url":"https://api.github.com/users/benwebber/gists{/gist_id}","starred_url":"https://api.github.com/users/benwebber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benwebber/subscriptions","organizations_url":"https://api.github.com/users/benwebber/orgs","repos_url":"https://api.github.com/users/benwebber/repos","events_url":"https://api.github.com/users/benwebber/events{/privacy}","received_events_url":"https://api.github.com/users/benwebber/received_events","type":"User","site_admin":false},"assignees":[{"login":"benwebber","id":358027,"avatar_url":"https://avatars.githubusercontent.com/u/358027?v=3","gravatar_id":"","url":"https://api.github.com/users/benwebber","html_url":"https://github.com/benwebber","followers_url":"https://api.github.com/users/benwebber/followers","following_url":"https://api.github.com/users/benwebber/following{/other_user}","gists_url":"https://api.github.com/users/benwebber/gists{/gist_id}","starred_url":"https://api.github.com/users/benwebber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benwebber/subscriptions","organizations_url":"https://api.github.com/users/benwebber/orgs","repos_url":"https://api.github.com/users/benwebber/repos","events_url":"https://api.github.com/users/benwebber/events{/privacy}","received_events_url":"https://api.github.com/users/benwebber/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/5/commits","review_comments_url":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/5/comments","review_comment_url":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/comments{/number}","comments_url":"https://api.github.com/repos/benwebber/ansible-minecraft/issues/5/comments","statuses_url":"https://api.github.com/repos/benwebber/ansible-minecraft/statuses/65bf0fa0e37908d38feb8f7dc3290730c69fd96b","head":{"label":"markrcote:server-properties","ref":"server-properties","sha":"65bf0fa0e37908d38feb8f7dc3290730c69fd96b","user":{"login":"markrcote","id":131299,"avatar_url":"https://avatars.githubusercontent.com/u/131299?v=3","gravatar_id":"","url":"https://api.github.com/users/markrcote","html_url":"https://github.com/markrcote","followers_url":"https://api.github.com/users/markrcote/followers","following_url":"https://api.github.com/users/markrcote/following{/other_user}","gists_url":"https://api.github.com/users/markrcote/gists{/gist_id}","starred_url":"https://api.github.com/users/markrcote/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markrcote/subscriptions","organizations_url":"https://api.github.com/users/markrcote/orgs","repos_url":"https://api.github.com/users/markrcote/repos","events_url":"https://api.github.com/users/markrcote/events{/privacy}","received_events_url":"https://api.github.com/users/markrcote/received_events","type":"User","site_admin":false},"repo":{"id":50219799,"name":"ansible-minecraft","full_name":"markrcote/ansible-minecraft","owner":{"login":"markrcote","id":131299,"avatar_url":"https://avatars.githubusercontent.com/u/131299?v=3","gravatar_id":"","url":"https://api.github.com/users/markrcote","html_url":"https://github.com/markrcote","followers_url":"https://api.github.com/users/markrcote/followers","following_url":"https://api.github.com/users/markrcote/following{/other_user}","gists_url":"https://api.github.com/users/markrcote/gists{/gist_id}","starred_url":"https://api.github.com/users/markrcote/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markrcote/subscriptions","organizations_url":"https://api.github.com/users/markrcote/orgs","repos_url":"https://api.github.com/users/markrcote/repos","events_url":"https://api.github.com/users/markrcote/events{/privacy}","received_events_url":"https://api.github.com/users/markrcote/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/markrcote/ansible-minecraft","description":"Ansible role for provisioning a vanilla Minecraft server","fork":true,"url":"https://api.github.com/repos/markrcote/ansible-minecraft","forks_url":"https://api.github.com/repos/markrcote/ansible-minecraft/forks","keys_url":"https://api.github.com/repos/markrcote/ansible-minecraft/keys{/key_id}","collaborators_url":"https://api.github.com/repos/markrcote/ansible-minecraft/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/markrcote/ansible-minecraft/teams","hooks_url":"https://api.github.com/repos/markrcote/ansible-minecraft/hooks","issue_events_url":"https://api.github.com/repos/markrcote/ansible-minecraft/issues/events{/number}","events_url":"https://api.github.com/repos/markrcote/ansible-minecraft/events","assignees_url":"https://api.github.com/repos/markrcote/ansible-minecraft/assignees{/user}","branches_url":"https://api.github.com/repos/markrcote/ansible-minecraft/branches{/branch}","tags_url":"https://api.github.com/repos/markrcote/ansible-minecraft/tags","blobs_url":"https://api.github.com/repos/markrcote/ansible-minecraft/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/markrcote/ansible-minecraft/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/markrcote/ansible-minecraft/git/refs{/sha}","trees_url":"https://api.github.com/repos/markrcote/ansible-minecraft/git/trees{/sha}","statuses_url":"https://api.github.com/repos/markrcote/ansible-minecraft/statuses/{sha}","languages_url":"https://api.github.com/repos/markrcote/ansible-minecraft/languages","stargazers_url":"https://api.github.com/repos/markrcote/ansible-minecraft/stargazers","contributors_url":"https://api.github.com/repos/markrcote/ansible-minecraft/contributors","subscribers_url":"https://api.github.com/repos/markrcote/ansible-minecraft/subscribers","subscription_url":"https://api.github.com/repos/markrcote/ansible-minecraft/subscription","commits_url":"https://api.github.com/repos/markrcote/ansible-minecraft/commits{/sha}","git_commits_url":"https://api.github.com/repos/markrcote/ansible-minecraft/git/commits{/sha}","comments_url":"https://api.github.com/repos/markrcote/ansible-minecraft/comments{/number}","issue_comment_url":"https://api.github.com/repos/markrcote/ansible-minecraft/issues/comments{/number}","contents_url":"https://api.github.com/repos/markrcote/ansible-minecraft/contents/{+path}","compare_url":"https://api.github.com/repos/markrcote/ansible-minecraft/compare/{base}...{head}","merges_url":"https://api.github.com/repos/markrcote/ansible-minecraft/merges","archive_url":"https://api.github.com/repos/markrcote/ansible-minecraft/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/markrcote/ansible-minecraft/downloads","issues_url":"https://api.github.com/repos/markrcote/ansible-minecraft/issues{/number}","pulls_url":"https://api.github.com/repos/markrcote/ansible-minecraft/pulls{/number}","milestones_url":"https://api.github.com/repos/markrcote/ansible-minecraft/milestones{/number}","notifications_url":"https://api.github.com/repos/markrcote/ansible-minecraft/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/markrcote/ansible-minecraft/labels{/name}","releases_url":"https://api.github.com/repos/markrcote/ansible-minecraft/releases{/id}","deployments_url":"https://api.github.com/repos/markrcote/ansible-minecraft/deployments","created_at":"2016-01-23T02:33:38Z","updated_at":"2016-01-23T02:33:39Z","pushed_at":"2016-07-04T04:53:09Z","git_url":"git://github.com/markrcote/ansible-minecraft.git","ssh_url":"git@github.com:markrcote/ansible-minecraft.git","clone_url":"https://github.com/markrcote/ansible-minecraft.git","svn_url":"https://github.com/markrcote/ansible-minecraft","homepage":null,"size":66,"stargazers_count":0,"watchers_count":0,"language":"Python","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":"develop"}},"base":{"label":"benwebber:master","ref":"master","sha":"3993549a968d22e8cab7803716efd3ebeecb6ad2","user":{"login":"benwebber","id":358027,"avatar_url":"https://avatars.githubusercontent.com/u/358027?v=3","gravatar_id":"","url":"https://api.github.com/users/benwebber","html_url":"https://github.com/benwebber","followers_url":"https://api.github.com/users/benwebber/followers","following_url":"https://api.github.com/users/benwebber/following{/other_user}","gists_url":"https://api.github.com/users/benwebber/gists{/gist_id}","starred_url":"https://api.github.com/users/benwebber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benwebber/subscriptions","organizations_url":"https://api.github.com/users/benwebber/orgs","repos_url":"https://api.github.com/users/benwebber/repos","events_url":"https://api.github.com/users/benwebber/events{/privacy}","received_events_url":"https://api.github.com/users/benwebber/received_events","type":"User","site_admin":false},"repo":{"id":26407408,"name":"ansible-minecraft","full_name":"benwebber/ansible-minecraft","owner":{"login":"benwebber","id":358027,"avatar_url":"https://avatars.githubusercontent.com/u/358027?v=3","gravatar_id":"","url":"https://api.github.com/users/benwebber","html_url":"https://github.com/benwebber","followers_url":"https://api.github.com/users/benwebber/followers","following_url":"https://api.github.com/users/benwebber/following{/other_user}","gists_url":"https://api.github.com/users/benwebber/gists{/gist_id}","starred_url":"https://api.github.com/users/benwebber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benwebber/subscriptions","organizations_url":"https://api.github.com/users/benwebber/orgs","repos_url":"https://api.github.com/users/benwebber/repos","events_url":"https://api.github.com/users/benwebber/events{/privacy}","received_events_url":"https://api.github.com/users/benwebber/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/benwebber/ansible-minecraft","description":"Ansible role for provisioning a vanilla Minecraft server","fork":false,"url":"https://api.github.com/repos/benwebber/ansible-minecraft","forks_url":"https://api.github.com/repos/benwebber/ansible-minecraft/forks","keys_url":"https://api.github.com/repos/benwebber/ansible-minecraft/keys{/key_id}","collaborators_url":"https://api.github.com/repos/benwebber/ansible-minecraft/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/benwebber/ansible-minecraft/teams","hooks_url":"https://api.github.com/repos/benwebber/ansible-minecraft/hooks","issue_events_url":"https://api.github.com/repos/benwebber/ansible-minecraft/issues/events{/number}","events_url":"https://api.github.com/repos/benwebber/ansible-minecraft/events","assignees_url":"https://api.github.com/repos/benwebber/ansible-minecraft/assignees{/user}","branches_url":"https://api.github.com/repos/benwebber/ansible-minecraft/branches{/branch}","tags_url":"https://api.github.com/repos/benwebber/ansible-minecraft/tags","blobs_url":"https://api.github.com/repos/benwebber/ansible-minecraft/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/benwebber/ansible-minecraft/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/benwebber/ansible-minecraft/git/refs{/sha}","trees_url":"https://api.github.com/repos/benwebber/ansible-minecraft/git/trees{/sha}","statuses_url":"https://api.github.com/repos/benwebber/ansible-minecraft/statuses/{sha}","languages_url":"https://api.github.com/repos/benwebber/ansible-minecraft/languages","stargazers_url":"https://api.github.com/repos/benwebber/ansible-minecraft/stargazers","contributors_url":"https://api.github.com/repos/benwebber/ansible-minecraft/contributors","subscribers_url":"https://api.github.com/repos/benwebber/ansible-minecraft/subscribers","subscription_url":"https://api.github.com/repos/benwebber/ansible-minecraft/subscription","commits_url":"https://api.github.com/repos/benwebber/ansible-minecraft/commits{/sha}","git_commits_url":"https://api.github.com/repos/benwebber/ansible-minecraft/git/commits{/sha}","comments_url":"https://api.github.com/repos/benwebber/ansible-minecraft/comments{/number}","issue_comment_url":"https://api.github.com/repos/benwebber/ansible-minecraft/issues/comments{/number}","contents_url":"https://api.github.com/repos/benwebber/ansible-minecraft/contents/{+path}","compare_url":"https://api.github.com/repos/benwebber/ansible-minecraft/compare/{base}...{head}","merges_url":"https://api.github.com/repos/benwebber/ansible-minecraft/merges","archive_url":"https://api.github.com/repos/benwebber/ansible-minecraft/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/benwebber/ansible-minecraft/downloads","issues_url":"https://api.github.com/repos/benwebber/ansible-minecraft/issues{/number}","pulls_url":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls{/number}","milestones_url":"https://api.github.com/repos/benwebber/ansible-minecraft/milestones{/number}","notifications_url":"https://api.github.com/repos/benwebber/ansible-minecraft/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/benwebber/ansible-minecraft/labels{/name}","releases_url":"https://api.github.com/repos/benwebber/ansible-minecraft/releases{/id}","deployments_url":"https://api.github.com/repos/benwebber/ansible-minecraft/deployments","created_at":"2014-11-09T20:05:21Z","updated_at":"2016-06-04T10:36:51Z","pushed_at":"2016-07-04T05:00:12Z","git_url":"git://github.com/benwebber/ansible-minecraft.git","ssh_url":"git@github.com:benwebber/ansible-minecraft.git","clone_url":"https://github.com/benwebber/ansible-minecraft.git","svn_url":"https://github.com/benwebber/ansible-minecraft","homepage":null,"size":55,"stargazers_count":10,"watchers_count":10,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":6,"mirror_url":null,"open_issues_count":1,"forks":6,"open_issues":1,"watchers":10,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/5"},"html":{"href":"https://github.com/benwebber/ansible-minecraft/pull/5"},"issue":{"href":"https://api.github.com/repos/benwebber/ansible-minecraft/issues/5"},"comments":{"href":"https://api.github.com/repos/benwebber/ansible-minecraft/issues/5/comments"},"review_comments":{"href":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/5/comments"},"review_comment":{"href":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/benwebber/ansible-minecraft/pulls/5/commits"},"statuses":{"href":"https://api.github.com/repos/benwebber/ansible-minecraft/statuses/65bf0fa0e37908d38feb8f7dc3290730c69fd96b"}}}}
{"actor":{"display_login":"benwebber"}}