summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Crosta <dcrosta@10gen.com>2012-05-11 09:16:07 -0400
committerBryan McLellan <btm@opscode.com>2012-06-21 12:21:40 -0700
commit3a80eeba592325338d808a69064f39858e808d03 (patch)
tree7a82e33cad82eac9559e7960eaaea2c5fa0164d1
parente173977a0599db22a2f266eb9dbe54bdc745c147 (diff)
downloadchef-3a80eeba592325338d808a69064f39858e808d03.tar.gz
Implement a group provider using group & user commands for NetBSD
The only questionable thing here is the manage_group method, which has to rename the old group, create a new group, set members on the new group, then delete the old group. To my mind this is preferable to editing /etc/group directly, though that is the "preferred" way (for humans, at least). I believe that doing this (rather than deleteing/readding) will avoid issues with file permissions during the time that the provider is executing; however, commands that manipulate permissions symbolically may fail during this time (as a group with the correct name may not exist, and users may not be present in that group). The amount of time during which these problems exist is negligible.
-rw-r--r--chef/lib/chef/platform.rb2
-rw-r--r--chef/lib/chef/provider/group/groupmod.rb123
-rw-r--r--chef/lib/chef/providers.rb1
3 files changed, 125 insertions, 1 deletions
diff --git a/chef/lib/chef/platform.rb b/chef/lib/chef/platform.rb
index e49c530cae..8aa3605f6b 100644
--- a/chef/lib/chef/platform.rb
+++ b/chef/lib/chef/platform.rb
@@ -251,7 +251,7 @@ class Chef
:netbsd => {
:default => {
:service => Chef::Provider::Service::Freebsd,
- :group => Chef::Provider::Group::Usermod
+ :group => Chef::Provider::Group::Groupmod
}
},
:openbsd => {
diff --git a/chef/lib/chef/provider/group/groupmod.rb b/chef/lib/chef/provider/group/groupmod.rb
new file mode 100644
index 0000000000..e5ba94965d
--- /dev/null
+++ b/chef/lib/chef/provider/group/groupmod.rb
@@ -0,0 +1,123 @@
+#
+# Author:: Dan Crosta (<dcrosta@late.am>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Provider
+ class Group
+ class Groupmod < Chef::Provider::Group
+
+ def load_current_resource
+ super
+ [ "group", "user" ].each do |binary|
+ raise Chef::Exceptions::Group, "Could not find binary /usr/sbin/#{binary} for #{@new_resource}" unless ::File.exists?("/usr/sbin/#{binary}")
+ end
+ end
+
+ # Create the group
+ def create_group
+ command = "group add"
+ command << set_options
+ run_command(:command => command)
+
+ add_group_members(@new_resource.members)
+ end
+
+ # Manage the group when it already exists
+ def manage_group
+ current_members = get_group_members
+
+ # Create an array of current members who
+ # should be removed from the group, that
+ # is, if they are not in the resource's
+ # members array
+ to_delete = Array.new(current_members)
+ to_delete.reject! { |user| @new_resource.members.include?(user) }
+
+ # Now create an array of members specified
+ # in the resource but not yet present in
+ # the current_members.
+ to_add = Array.new(@new_resource.members)
+ to_add.reject! { |user| current_members.include?(user) }
+
+ if !to_delete.empty?
+ # This is tricky, but works: rename the existing
+ # group to "<name>_bak", create a new group with
+ # the same GID and "<name>", then set correct
+ # members on that group
+ rename = "group mod -n #{@new_resource.group_name}_bak #{@new_resource.group_name}"
+ run_command(:command => rename)
+
+ create = "group add"
+ create << set_options(:overwrite_gid => true)
+ run_command(:command => create)
+
+ add_group_members(@new_resource.members)
+
+ remove = "group del #{@new_resource.group_name}_bak"
+ run_command(:command => remove)
+ else
+ # If we are only adding new members to this group,
+ # then call add_group_members with only those users
+ add_group_members(to_add)
+ end
+ end
+
+ # Remove the group
+ def remove_group
+ run_command(:command => "group del #{@new_resource.group_name}")
+ end
+
+ # Adds a list of usernames to the group using `user mod`
+ def add_group_members(members)
+ members.each do |user|
+ command = "user mod -G #{@new_resource.group_name} #{user}"
+ run_command(:command => command)
+ end
+ end
+
+ # Little bit of magic as per Adam's useradd provider to pull and assign the command line flags
+ #
+ # ==== Returns
+ # <string>:: A string containing the option and then the quoted value
+ def set_options(overwrite_gid=false)
+ opts = ""
+ if overwrite_gid || @new_resource.gid && (@current_resource.gid != @new_resource.gid)
+ Chef::Log.debug("#{@new_resource}: current gid (#{@current_resource.gid}) doesnt match target gid (#{@new_resource.gid}), changing it")
+ opts << " -g '#{@new_resource.gid}'"
+ end
+ if overwrite_gid
+ opts << " -o"
+ end
+ opts << " #{@new_resource.group_name}"
+ opts
+ end
+
+ # Parse the output of "group info <groupanme>" to determine the members
+ def get_group_members
+ command = "group info #{@new_resource.group_name}"
+ status, stdout, stderr = output_of_command(command, {})
+
+ raise Chef::Exceptions::Group, "#{command} returned status #{status}, expected 0" if status != 0
+
+ members = /members\s+([\w, ]+)$/m.match(stdout)[1]
+ members.split(/, +/)
+ end
+ end
+ end
+ end
+end
diff --git a/chef/lib/chef/providers.rb b/chef/lib/chef/providers.rb
index 1e395fa0b7..35c9964789 100644
--- a/chef/lib/chef/providers.rb
+++ b/chef/lib/chef/providers.rb
@@ -86,6 +86,7 @@ require 'chef/provider/group/aix'
require 'chef/provider/group/dscl'
require 'chef/provider/group/gpasswd'
require 'chef/provider/group/groupadd'
+require 'chef/provider/group/groupmod'
require 'chef/provider/group/pw'
require 'chef/provider/group/suse'
require 'chef/provider/group/usermod'