{"action":"created","comment":{"url":"https://api.github.com/repos/saltstack/salt/pulls/comments/236360411","pull_request_review_id":178419550,"id":236360411,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzNjM2MDQxMQ==","diff_hunk":"@@ -4570,6 +4570,372 @@ def replace(name,\n     return ret\n \n \n+def keyvalue_list(","path":"salt/states/file.py","position":4,"original_position":4,"commit_id":"b3c331a35f218295658dbe1f7dbe30148c2765e2","original_commit_id":"b3c331a35f218295658dbe1f7dbe30148c2765e2","user":{"login":"teizz","id":1012641,"node_id":"MDQ6VXNlcjEwMTI2NDE=","avatar_url":"https://avatars1.githubusercontent.com/u/1012641?v=4","gravatar_id":"","url":"https://api.github.com/users/teizz","html_url":"https://github.com/teizz","followers_url":"https://api.github.com/users/teizz/followers","following_url":"https://api.github.com/users/teizz/following{/other_user}","gists_url":"https://api.github.com/users/teizz/gists{/gist_id}","starred_url":"https://api.github.com/users/teizz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/teizz/subscriptions","organizations_url":"https://api.github.com/users/teizz/orgs","repos_url":"https://api.github.com/users/teizz/repos","events_url":"https://api.github.com/users/teizz/events{/privacy}","received_events_url":"https://api.github.com/users/teizz/received_events","type":"User","site_admin":false},"body":"It is, and that's exactly what I'm doing in the other function below called exactly that. (https://github.com/saltstack/salt/pull/50627/files#diff-3b447dc835943beb1326f98f684fe783R4905-R4936)\r\n\r\n I'd written the first later, then figured it'd be nice to give a list. Since I didn't want to call the original function over and over I'd figured the shorthand version could instead call the list version.\r\n\r\nI'd be happy calling it ``file.keyvalues``, and do away with the shorthand version though.","created_at":"2018-11-26T17:51:46Z","updated_at":"2018-11-26T17:51:47Z","html_url":"https://github.com/saltstack/salt/pull/50627#discussion_r236360411","pull_request_url":"https://api.github.com/repos/saltstack/salt/pulls/50627","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/saltstack/salt/pulls/comments/236360411"},"html":{"href":"https://github.com/saltstack/salt/pull/50627#discussion_r236360411"},"pull_request":{"href":"https://api.github.com/repos/saltstack/salt/pulls/50627"}},"in_reply_to_id":236339886},"pull_request":{"url":"https://api.github.com/repos/saltstack/salt/pulls/50627","id":233337784,"node_id":"MDExOlB1bGxSZXF1ZXN0MjMzMzM3Nzg0","html_url":"https://github.com/saltstack/salt/pull/50627","diff_url":"https://github.com/saltstack/salt/pull/50627.diff","patch_url":"https://github.com/saltstack/salt/pull/50627.patch","issue_url":"https://api.github.com/repos/saltstack/salt/issues/50627","number":50627,"state":"open","locked":false,"title":"Adding a keyvalue function to the file state module","user":{"login":"teizz","id":1012641,"node_id":"MDQ6VXNlcjEwMTI2NDE=","avatar_url":"https://avatars1.githubusercontent.com/u/1012641?v=4","gravatar_id":"","url":"https://api.github.com/users/teizz","html_url":"https://github.com/teizz","followers_url":"https://api.github.com/users/teizz/followers","following_url":"https://api.github.com/users/teizz/following{/other_user}","gists_url":"https://api.github.com/users/teizz/gists{/gist_id}","starred_url":"https://api.github.com/users/teizz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/teizz/subscriptions","organizations_url":"https://api.github.com/users/teizz/orgs","repos_url":"https://api.github.com/users/teizz/repos","events_url":"https://api.github.com/users/teizz/events{/privacy}","received_events_url":"https://api.github.com/users/teizz/received_events","type":"User","site_admin":false},"body":"### What does this PR do?\r\n\r\nThis PR adds a new function to the file state-module, including documentation of all its parameters. It also includes the addition of an integration test to test the new behaviour added. \r\n\r\n### What issues does this PR fix or reference?\r\n\r\nNo open issues specifically. \r\n\r\n### New Behavior\r\n\r\nUsing the new keyvalue_list function (or its keyvalue shorthand version), it's possible to make and maintain changes to files. Some of the features of this function are:\r\n\r\n- uncommenting default lines, so all settings don't show up at the top/bottom of a file.\r\n- removing superfluous lines which might have otherwise overridden a setting.\r\n- matching either (or both) the key or value part of a setting case-insensitively as to not make unnecessary changes.\r\n- append or prepend a key/value setting if it wasn't found in the file.\r\n- fully supports a test=True run for detecting potential changes before actually committing them.\r\n\r\n### Tests written?\r\n\r\nYes. There is an additional integration test which checks if a commented out line is commented in and has its value updated to reflect the requested setting.\r\n\r\n### Commits signed with GPG?\r\n\r\nNo\r\n\r\n### Rationale\r\n\r\nAlthough it is already possible to (un)comment lines, append text, and replace lines through other functions in the file state-module, in some cases it would result in an sls file that's hard to read or a not-quite satisfactory result.\r\n\r\nAn example. Lots of products come with default configuration files which have the defaults commented out. Not all defaults might be in there though.\r\n\r\n- `file.replace` used to be my go-to function, it is capable appending or prepending if the line is not found, can do regex, accepts regex-flags to do case-insensitive matching etc. However, unless one want to go for negated forward lookups, a line would always match, always get replaced and thus always trigger a change.\r\n- `file.append`, and `file.blockreplace` can't really insert at an arbitrary location.\r\n- `file.line` which got added in Beryllium won't replace a line which matches but already has the replacement content, and handles regex. But it can't easily match case-insensitively, and needs a location in order to 'ensure' a line exists.\r\n\r\nI've been leaving a trail behind at different companies implementing the functionality in this PR in custom '_states' directories. All in different states of bug-fixing by now I'm sure.\r\nDuring Saltconf18 talk of this came up and it was suggested I'd try to submit it 'upstream', since it would also be helpful in some of the Salt for Secops check/remediation states.","created_at":"2018-11-24T20:10:14Z","updated_at":"2018-11-26T17:51:47Z","closed_at":null,"merged_at":null,"merge_commit_sha":"60bff7d5398c86709a0fab330b7a4a5498105ae9","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/saltstack/salt/pulls/50627/commits","review_comments_url":"https://api.github.com/repos/saltstack/salt/pulls/50627/comments","review_comment_url":"https://api.github.com/repos/saltstack/salt/pulls/comments{/number}","comments_url":"https://api.github.com/repos/saltstack/salt/issues/50627/comments","statuses_url":"https://api.github.com/repos/saltstack/salt/statuses/b3c331a35f218295658dbe1f7dbe30148c2765e2","head":{"label":"teizz:develop","ref":"develop","sha":"b3c331a35f218295658dbe1f7dbe30148c2765e2","user":{"login":"teizz","id":1012641,"node_id":"MDQ6VXNlcjEwMTI2NDE=","avatar_url":"https://avatars1.githubusercontent.com/u/1012641?v=4","gravatar_id":"","url":"https://api.github.com/users/teizz","html_url":"https://github.com/teizz","followers_url":"https://api.github.com/users/teizz/followers","following_url":"https://api.github.com/users/teizz/following{/other_user}","gists_url":"https://api.github.com/users/teizz/gists{/gist_id}","starred_url":"https://api.github.com/users/teizz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/teizz/subscriptions","organizations_url":"https://api.github.com/users/teizz/orgs","repos_url":"https://api.github.com/users/teizz/repos","events_url":"https://api.github.com/users/teizz/events{/privacy}","received_events_url":"https://api.github.com/users/teizz/received_events","type":"User","site_admin":false},"repo":{"id":158205003,"node_id":"MDEwOlJlcG9zaXRvcnkxNTgyMDUwMDM=","name":"salt","full_name":"teizz/salt","private":false,"owner":{"login":"teizz","id":1012641,"node_id":"MDQ6VXNlcjEwMTI2NDE=","avatar_url":"https://avatars1.githubusercontent.com/u/1012641?v=4","gravatar_id":"","url":"https://api.github.com/users/teizz","html_url":"https://github.com/teizz","followers_url":"https://api.github.com/users/teizz/followers","following_url":"https://api.github.com/users/teizz/following{/other_user}","gists_url":"https://api.github.com/users/teizz/gists{/gist_id}","starred_url":"https://api.github.com/users/teizz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/teizz/subscriptions","organizations_url":"https://api.github.com/users/teizz/orgs","repos_url":"https://api.github.com/users/teizz/repos","events_url":"https://api.github.com/users/teizz/events{/privacy}","received_events_url":"https://api.github.com/users/teizz/received_events","type":"User","site_admin":false},"html_url":"https://github.com/teizz/salt","description":"Software to automate the management and configuration of any infrastructure or application at scale. Get access to the Salt software package repository here: ","fork":true,"url":"https://api.github.com/repos/teizz/salt","forks_url":"https://api.github.com/repos/teizz/salt/forks","keys_url":"https://api.github.com/repos/teizz/salt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/teizz/salt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/teizz/salt/teams","hooks_url":"https://api.github.com/repos/teizz/salt/hooks","issue_events_url":"https://api.github.com/repos/teizz/salt/issues/events{/number}","events_url":"https://api.github.com/repos/teizz/salt/events","assignees_url":"https://api.github.com/repos/teizz/salt/assignees{/user}","branches_url":"https://api.github.com/repos/teizz/salt/branches{/branch}","tags_url":"https://api.github.com/repos/teizz/salt/tags","blobs_url":"https://api.github.com/repos/teizz/salt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/teizz/salt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/teizz/salt/git/refs{/sha}","trees_url":"https://api.github.com/repos/teizz/salt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/teizz/salt/statuses/{sha}","languages_url":"https://api.github.com/repos/teizz/salt/languages","stargazers_url":"https://api.github.com/repos/teizz/salt/stargazers","contributors_url":"https://api.github.com/repos/teizz/salt/contributors","subscribers_url":"https://api.github.com/repos/teizz/salt/subscribers","subscription_url":"https://api.github.com/repos/teizz/salt/subscription","commits_url":"https://api.github.com/repos/teizz/salt/commits{/sha}","git_commits_url":"https://api.github.com/repos/teizz/salt/git/commits{/sha}","comments_url":"https://api.github.com/repos/teizz/salt/comments{/number}","issue_comment_url":"https://api.github.com/repos/teizz/salt/issues/comments{/number}","contents_url":"https://api.github.com/repos/teizz/salt/contents/{+path}","compare_url":"https://api.github.com/repos/teizz/salt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/teizz/salt/merges","archive_url":"https://api.github.com/repos/teizz/salt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/teizz/salt/downloads","issues_url":"https://api.github.com/repos/teizz/salt/issues{/number}","pulls_url":"https://api.github.com/repos/teizz/salt/pulls{/number}","milestones_url":"https://api.github.com/repos/teizz/salt/milestones{/number}","notifications_url":"https://api.github.com/repos/teizz/salt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/teizz/salt/labels{/name}","releases_url":"https://api.github.com/repos/teizz/salt/releases{/id}","deployments_url":"https://api.github.com/repos/teizz/salt/deployments","created_at":"2018-11-19T10:45:43Z","updated_at":"2018-11-25T10:50:26Z","pushed_at":"2018-11-25T10:50:16Z","git_url":"git://github.com/teizz/salt.git","ssh_url":"git@github.com:teizz/salt.git","clone_url":"https://github.com/teizz/salt.git","svn_url":"https://github.com/teizz/salt","homepage":"https://repo.saltstack.com/","size":421338,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"saltstack:develop","ref":"develop","sha":"fe2b052fcbb36f482b21b2696724c2e0608c1087","user":{"login":"saltstack","id":1147473,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExNDc0NzM=","avatar_url":"https://avatars3.githubusercontent.com/u/1147473?v=4","gravatar_id":"","url":"https://api.github.com/users/saltstack","html_url":"https://github.com/saltstack","followers_url":"https://api.github.com/users/saltstack/followers","following_url":"https://api.github.com/users/saltstack/following{/other_user}","gists_url":"https://api.github.com/users/saltstack/gists{/gist_id}","starred_url":"https://api.github.com/users/saltstack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saltstack/subscriptions","organizations_url":"https://api.github.com/users/saltstack/orgs","repos_url":"https://api.github.com/users/saltstack/repos","events_url":"https://api.github.com/users/saltstack/events{/privacy}","received_events_url":"https://api.github.com/users/saltstack/received_events","type":"Organization","site_admin":false},"repo":{"id":1390248,"node_id":"MDEwOlJlcG9zaXRvcnkxMzkwMjQ4","name":"salt","full_name":"saltstack/salt","private":false,"owner":{"login":"saltstack","id":1147473,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExNDc0NzM=","avatar_url":"https://avatars3.githubusercontent.com/u/1147473?v=4","gravatar_id":"","url":"https://api.github.com/users/saltstack","html_url":"https://github.com/saltstack","followers_url":"https://api.github.com/users/saltstack/followers","following_url":"https://api.github.com/users/saltstack/following{/other_user}","gists_url":"https://api.github.com/users/saltstack/gists{/gist_id}","starred_url":"https://api.github.com/users/saltstack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saltstack/subscriptions","organizations_url":"https://api.github.com/users/saltstack/orgs","repos_url":"https://api.github.com/users/saltstack/repos","events_url":"https://api.github.com/users/saltstack/events{/privacy}","received_events_url":"https://api.github.com/users/saltstack/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/saltstack/salt","description":"Software to automate the management and configuration of any infrastructure or application at scale. Get access to the Salt software package repository here: ","fork":false,"url":"https://api.github.com/repos/saltstack/salt","forks_url":"https://api.github.com/repos/saltstack/salt/forks","keys_url":"https://api.github.com/repos/saltstack/salt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/saltstack/salt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/saltstack/salt/teams","hooks_url":"https://api.github.com/repos/saltstack/salt/hooks","issue_events_url":"https://api.github.com/repos/saltstack/salt/issues/events{/number}","events_url":"https://api.github.com/repos/saltstack/salt/events","assignees_url":"https://api.github.com/repos/saltstack/salt/assignees{/user}","branches_url":"https://api.github.com/repos/saltstack/salt/branches{/branch}","tags_url":"https://api.github.com/repos/saltstack/salt/tags","blobs_url":"https://api.github.com/repos/saltstack/salt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/saltstack/salt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/saltstack/salt/git/refs{/sha}","trees_url":"https://api.github.com/repos/saltstack/salt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/saltstack/salt/statuses/{sha}","languages_url":"https://api.github.com/repos/saltstack/salt/languages","stargazers_url":"https://api.github.com/repos/saltstack/salt/stargazers","contributors_url":"https://api.github.com/repos/saltstack/salt/contributors","subscribers_url":"https://api.github.com/repos/saltstack/salt/subscribers","subscription_url":"https://api.github.com/repos/saltstack/salt/subscription","commits_url":"https://api.github.com/repos/saltstack/salt/commits{/sha}","git_commits_url":"https://api.github.com/repos/saltstack/salt/git/commits{/sha}","comments_url":"https://api.github.com/repos/saltstack/salt/comments{/number}","issue_comment_url":"https://api.github.com/repos/saltstack/salt/issues/comments{/number}","contents_url":"https://api.github.com/repos/saltstack/salt/contents/{+path}","compare_url":"https://api.github.com/repos/saltstack/salt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/saltstack/salt/merges","archive_url":"https://api.github.com/repos/saltstack/salt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/saltstack/salt/downloads","issues_url":"https://api.github.com/repos/saltstack/salt/issues{/number}","pulls_url":"https://api.github.com/repos/saltstack/salt/pulls{/number}","milestones_url":"https://api.github.com/repos/saltstack/salt/milestones{/number}","notifications_url":"https://api.github.com/repos/saltstack/salt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/saltstack/salt/labels{/name}","releases_url":"https://api.github.com/repos/saltstack/salt/releases{/id}","deployments_url":"https://api.github.com/repos/saltstack/salt/deployments","created_at":"2011-02-20T20:16:56Z","updated_at":"2018-11-26T17:41:58Z","pushed_at":"2018-11-26T17:45:10Z","git_url":"git://github.com/saltstack/salt.git","ssh_url":"git@github.com:saltstack/salt.git","clone_url":"https://github.com/saltstack/salt.git","svn_url":"https://github.com/saltstack/salt","homepage":"https://repo.saltstack.com/","size":421590,"stargazers_count":9406,"watchers_count":9406,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4412,"mirror_url":null,"archived":false,"open_issues_count":2347,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":4412,"open_issues":2347,"watchers":9406,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/saltstack/salt/pulls/50627"},"html":{"href":"https://github.com/saltstack/salt/pull/50627"},"issue":{"href":"https://api.github.com/repos/saltstack/salt/issues/50627"},"comments":{"href":"https://api.github.com/repos/saltstack/salt/issues/50627/comments"},"review_comments":{"href":"https://api.github.com/repos/saltstack/salt/pulls/50627/comments"},"review_comment":{"href":"https://api.github.com/repos/saltstack/salt/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/saltstack/salt/pulls/50627/commits"},"statuses":{"href":"https://api.github.com/repos/saltstack/salt/statuses/b3c331a35f218295658dbe1f7dbe30148c2765e2"}},"author_association":"NONE"}}
{"actor":{"display_login":"teizz"}}