chef 0.7.10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of chef might be problematic. Click here for more details.

Files changed (120) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +135 -0
  3. data/bin/chef-client +26 -0
  4. data/bin/chef-solo +26 -0
  5. data/lib/chef.rb +49 -0
  6. data/lib/chef/application.rb +98 -0
  7. data/lib/chef/application/agent.rb +18 -0
  8. data/lib/chef/application/client.rb +209 -0
  9. data/lib/chef/application/indexer.rb +141 -0
  10. data/lib/chef/application/server.rb +18 -0
  11. data/lib/chef/application/solo.rb +214 -0
  12. data/lib/chef/client.rb +396 -0
  13. data/lib/chef/compile.rb +138 -0
  14. data/lib/chef/config.rb +141 -0
  15. data/lib/chef/cookbook.rb +144 -0
  16. data/lib/chef/cookbook/metadata.rb +407 -0
  17. data/lib/chef/cookbook/metadata/version.rb +87 -0
  18. data/lib/chef/cookbook_loader.rb +168 -0
  19. data/lib/chef/couchdb.rb +172 -0
  20. data/lib/chef/daemon.rb +170 -0
  21. data/lib/chef/exceptions.rb +36 -0
  22. data/lib/chef/file_cache.rb +205 -0
  23. data/lib/chef/log.rb +39 -0
  24. data/lib/chef/mixin/check_helper.rb +31 -0
  25. data/lib/chef/mixin/checksum.rb +37 -0
  26. data/lib/chef/mixin/command.rb +351 -0
  27. data/lib/chef/mixin/create_path.rb +56 -0
  28. data/lib/chef/mixin/deep_merge.rb +36 -0
  29. data/lib/chef/mixin/find_preferred_file.rb +99 -0
  30. data/lib/chef/mixin/from_file.rb +36 -0
  31. data/lib/chef/mixin/generate_url.rb +48 -0
  32. data/lib/chef/mixin/language.rb +79 -0
  33. data/lib/chef/mixin/params_validate.rb +197 -0
  34. data/lib/chef/mixin/template.rb +84 -0
  35. data/lib/chef/node.rb +406 -0
  36. data/lib/chef/node/attribute.rb +412 -0
  37. data/lib/chef/openid_registration.rb +181 -0
  38. data/lib/chef/platform.rb +253 -0
  39. data/lib/chef/provider.rb +40 -0
  40. data/lib/chef/provider/cron.rb +137 -0
  41. data/lib/chef/provider/directory.rb +72 -0
  42. data/lib/chef/provider/execute.rb +58 -0
  43. data/lib/chef/provider/file.rb +191 -0
  44. data/lib/chef/provider/group.rb +120 -0
  45. data/lib/chef/provider/group/groupadd.rb +92 -0
  46. data/lib/chef/provider/group/pw.rb +88 -0
  47. data/lib/chef/provider/http_request.rb +102 -0
  48. data/lib/chef/provider/ifconfig.rb +131 -0
  49. data/lib/chef/provider/link.rb +157 -0
  50. data/lib/chef/provider/mount.rb +121 -0
  51. data/lib/chef/provider/mount/mount.rb +208 -0
  52. data/lib/chef/provider/package.rb +160 -0
  53. data/lib/chef/provider/package/apt.rb +110 -0
  54. data/lib/chef/provider/package/dpkg.rb +113 -0
  55. data/lib/chef/provider/package/freebsd.rb +153 -0
  56. data/lib/chef/provider/package/macports.rb +105 -0
  57. data/lib/chef/provider/package/portage.rb +124 -0
  58. data/lib/chef/provider/package/rpm.rb +99 -0
  59. data/lib/chef/provider/package/rubygems.rb +130 -0
  60. data/lib/chef/provider/package/yum-dump.py +104 -0
  61. data/lib/chef/provider/package/yum.rb +175 -0
  62. data/lib/chef/provider/remote_directory.rb +126 -0
  63. data/lib/chef/provider/remote_file.rb +134 -0
  64. data/lib/chef/provider/route.rb +118 -0
  65. data/lib/chef/provider/ruby_block.rb +15 -0
  66. data/lib/chef/provider/script.rb +42 -0
  67. data/lib/chef/provider/service.rb +129 -0
  68. data/lib/chef/provider/service/debian.rb +64 -0
  69. data/lib/chef/provider/service/freebsd.rb +157 -0
  70. data/lib/chef/provider/service/gentoo.rb +54 -0
  71. data/lib/chef/provider/service/init.rb +126 -0
  72. data/lib/chef/provider/service/redhat.rb +62 -0
  73. data/lib/chef/provider/template.rb +141 -0
  74. data/lib/chef/provider/user.rb +170 -0
  75. data/lib/chef/provider/user/pw.rb +113 -0
  76. data/lib/chef/provider/user/useradd.rb +107 -0
  77. data/lib/chef/queue.rb +145 -0
  78. data/lib/chef/recipe.rb +210 -0
  79. data/lib/chef/resource.rb +256 -0
  80. data/lib/chef/resource/apt_package.rb +34 -0
  81. data/lib/chef/resource/bash.rb +33 -0
  82. data/lib/chef/resource/cron.rb +143 -0
  83. data/lib/chef/resource/csh.rb +33 -0
  84. data/lib/chef/resource/directory.rb +76 -0
  85. data/lib/chef/resource/dpkg_package.rb +34 -0
  86. data/lib/chef/resource/execute.rb +127 -0
  87. data/lib/chef/resource/file.rb +84 -0
  88. data/lib/chef/resource/gem_package.rb +41 -0
  89. data/lib/chef/resource/group.rb +68 -0
  90. data/lib/chef/resource/http_request.rb +52 -0
  91. data/lib/chef/resource/ifconfig.rb +134 -0
  92. data/lib/chef/resource/link.rb +78 -0
  93. data/lib/chef/resource/macports_package.rb +29 -0
  94. data/lib/chef/resource/mount.rb +135 -0
  95. data/lib/chef/resource/package.rb +80 -0
  96. data/lib/chef/resource/perl.rb +33 -0
  97. data/lib/chef/resource/portage_package.rb +33 -0
  98. data/lib/chef/resource/python.rb +33 -0
  99. data/lib/chef/resource/remote_directory.rb +91 -0
  100. data/lib/chef/resource/remote_file.rb +60 -0
  101. data/lib/chef/resource/route.rb +135 -0
  102. data/lib/chef/resource/ruby.rb +33 -0
  103. data/lib/chef/resource/ruby_block.rb +20 -0
  104. data/lib/chef/resource/script.rb +51 -0
  105. data/lib/chef/resource/service.rb +134 -0
  106. data/lib/chef/resource/template.rb +60 -0
  107. data/lib/chef/resource/user.rb +98 -0
  108. data/lib/chef/resource_collection.rb +176 -0
  109. data/lib/chef/resource_definition.rb +67 -0
  110. data/lib/chef/rest.rb +238 -0
  111. data/lib/chef/role.rb +231 -0
  112. data/lib/chef/run_list.rb +156 -0
  113. data/lib/chef/runner.rb +123 -0
  114. data/lib/chef/search.rb +88 -0
  115. data/lib/chef/search/result.rb +64 -0
  116. data/lib/chef/search_index.rb +77 -0
  117. data/lib/chef/tasks/chef_repo.rake +345 -0
  118. data/lib/chef/util/file_edit.rb +125 -0
  119. data/lib/chef/util/fileedit.rb +121 -0
  120. metadata +262 -0
@@ -0,0 +1,156 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'chef/mixin/deep_merge'
19
+
20
+ class Chef
21
+ class RunList
22
+ include Enumerable
23
+
24
+ attr_reader :recipes, :roles, :run_list
25
+
26
+ def initialize
27
+ @run_list = Array.new
28
+ @recipes = Array.new
29
+ @roles = Array.new
30
+ end
31
+
32
+ def <<(item)
33
+ type, entry, fentry = parse_entry(item)
34
+ case type
35
+ when 'recipe'
36
+ @recipes << entry unless @recipes.include?(entry)
37
+ when 'role'
38
+ @roles << entry unless @roles.include?(entry)
39
+ end
40
+ @run_list << fentry unless @run_list.include?(fentry)
41
+ self
42
+ end
43
+
44
+ def ==(*isequal)
45
+ check_array = nil
46
+ if isequal[0].kind_of?(Chef::RunList)
47
+ check_array = isequal[0].run_list
48
+ else
49
+ check_array = isequal.flatten
50
+ end
51
+
52
+ return false if check_array.length != @run_list.length
53
+
54
+ check_array.each_index do |i|
55
+ to_check = check_array[i]
56
+ type, name, fname = parse_entry(to_check)
57
+ return false if @run_list[i] != fname
58
+ end
59
+
60
+ true
61
+ end
62
+
63
+ def empty?
64
+ @run_list.length == 0 ? true : false
65
+ end
66
+
67
+ def [](pos)
68
+ @run_list[pos]
69
+ end
70
+
71
+ def []=(pos, item)
72
+ type, entry, fentry = parse_entry(item)
73
+ @run_list[pos] = fentry
74
+ end
75
+
76
+ def each(&block)
77
+ @run_list.each { |i| block.call(i) }
78
+ end
79
+
80
+ def each_index(&block)
81
+ @run_list.each_index { |i| block.call(i) }
82
+ end
83
+
84
+ def include?(item)
85
+ type, entry, fentry = parse_entry(item)
86
+ @run_list.include?(fentry)
87
+ end
88
+
89
+ def reset(*args)
90
+ @run_list = Array.new
91
+ @recipes = Array.new
92
+ @roles = Array.new
93
+ args.flatten.each do |item|
94
+ if item.kind_of?(Chef::RunList)
95
+ item.each { |r| self << r }
96
+ else
97
+ self << item
98
+ end
99
+ end
100
+ self
101
+ end
102
+
103
+ def remove(item)
104
+ type, entry, fentry = parse_entry(item)
105
+ @run_list.delete_if { |i| i == fentry }
106
+ if type == "recipe"
107
+ @recipes.delete_if { |i| i == entry }
108
+ elsif type == "role"
109
+ @roles.delete_if { |i| i == entry }
110
+ end
111
+ self
112
+ end
113
+
114
+ def expand(from='server')
115
+ recipes = Array.new
116
+ default_attrs = Mash.new
117
+ override_attrs = Mash.new
118
+
119
+ @run_list.each do |entry|
120
+ type, name, fname = parse_entry(entry)
121
+ case type
122
+ when 'recipe'
123
+ recipes << name unless recipes.include?(name)
124
+ when 'role'
125
+ role = nil
126
+ if from == 'disk' || Chef::Config[:solo]
127
+ # Load the role from disk
128
+ role = Chef::Role.from_disk("#{name}")
129
+ elsif from == 'server'
130
+ # Load the role from the server
131
+ r = Chef::REST.new(Chef::Config[:role_url])
132
+ role = r.get_rest("roles/#{name}")
133
+ elsif from == 'couchdb'
134
+ # Load the role from couchdb
135
+ role = Chef::Role.load(name)
136
+ end
137
+ role.recipes.each { |r| recipes << r unless recipes.include?(r) }
138
+ default_attrs = Chef::Mixin::DeepMerge.merge(default_attrs, role.default_attributes)
139
+ override_attrs = Chef::Mixin::DeepMerge.merge(override_attrs, role.override_attributes)
140
+ end
141
+ end
142
+ return recipes, default_attrs, override_attrs
143
+ end
144
+
145
+ def parse_entry(entry)
146
+ case entry
147
+ when /^(.+)\[(.+)\]$/
148
+ [ $1, $2, entry ]
149
+ else
150
+ [ 'recipe', entry, "recipe[#{entry}]" ]
151
+ end
152
+ end
153
+
154
+ end
155
+ end
156
+
@@ -0,0 +1,123 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/mixin/params_validate'
20
+ require 'chef/node'
21
+ require 'chef/resource_collection'
22
+ require 'chef/platform'
23
+
24
+ class Chef
25
+ class Runner
26
+
27
+ include Chef::Mixin::ParamsValidate
28
+
29
+ def initialize(node, collection)
30
+ validate(
31
+ {
32
+ :node => node,
33
+ :collection => collection,
34
+ },
35
+ {
36
+ :node => {
37
+ :kind_of => Chef::Node,
38
+ },
39
+ :collection => {
40
+ :kind_of => Chef::ResourceCollection,
41
+ },
42
+ }
43
+ )
44
+ @node = node
45
+ @collection = collection
46
+ end
47
+
48
+ def build_provider(resource)
49
+ provider_klass = resource.provider
50
+ provider_klass ||= Chef::Platform.find_provider_for_node(@node, resource)
51
+ Chef::Log.debug("#{resource} using #{provider_klass.to_s}")
52
+ provider = provider_klass.new(@node, resource)
53
+ provider.load_current_resource
54
+ provider
55
+ end
56
+
57
+ def converge
58
+
59
+ delayed_actions = Hash.new
60
+
61
+ @collection.each do |resource|
62
+ begin
63
+ Chef::Log.debug("Processing #{resource}")
64
+
65
+ # Check if this resource has an only_if block - if it does, skip it.
66
+ if resource.only_if
67
+ unless Chef::Mixin::Command.only_if(resource.only_if)
68
+ Chef::Log.debug("Skipping #{resource} due to only_if")
69
+ next
70
+ end
71
+ end
72
+
73
+ # Check if this resource has a not_if block - if it does, skip it.
74
+ if resource.not_if
75
+ unless Chef::Mixin::Command.not_if(resource.not_if)
76
+ Chef::Log.debug("Skipping #{resource} due to not_if")
77
+ next
78
+ end
79
+ end
80
+
81
+ # Walk the actions for this resource, building the provider and running each.
82
+ action_list = resource.action.kind_of?(Array) ? resource.action : [ resource.action ]
83
+ action_list.each do |ra|
84
+ provider = build_provider(resource)
85
+ provider.send("action_#{ra}")
86
+ if resource.updated
87
+ resource.actions.each_key do |action|
88
+ if resource.actions[action].has_key?(:immediate)
89
+ resource.actions[action][:immediate].each do |r|
90
+ Chef::Log.info("#{resource} sending #{action} action to #{r} (immediate)")
91
+ build_provider(r).send("action_#{action}")
92
+ end
93
+ end
94
+ if resource.actions[action].has_key?(:delayed)
95
+ resource.actions[action][:delayed].each do |r|
96
+ delayed_actions[r] = Hash.new unless delayed_actions.has_key?(r)
97
+ delayed_actions[r][action] = Array.new unless delayed_actions[r].has_key?(action)
98
+ delayed_actions[r][action] << lambda {
99
+ Chef::Log.info("#{resource} sending #{action} action to #{r} (delayed)")
100
+ }
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ rescue => e
107
+ Chef::Log.error("#{resource} (#{resource.source_line}) had an error:\n#{e}\n#{e.backtrace}")
108
+ raise e unless resource.ignore_failure
109
+ end
110
+ end
111
+
112
+ # Run all our :delayed actions
113
+ delayed_actions.each do |resource, action_hash|
114
+ action_hash.each do |action, log_array|
115
+ log_array.each { |l| l.call } # Call each log message
116
+ build_provider(resource).send("action_#{action}")
117
+ end
118
+ end
119
+
120
+ true
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,88 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/search/result'
20
+ require 'ferret'
21
+
22
+ class Chef
23
+ class Search
24
+
25
+ attr_reader :index
26
+
27
+ def initialize
28
+ @index = Ferret::Index::Index.new(:path => Chef::Config[:search_index_path])
29
+ end
30
+
31
+ def search(type, query="*", attributes=[], &block)
32
+ search_query = build_search_query(type, query)
33
+ start_time = Time.now
34
+ results = []
35
+ block ||= lambda { |b| b }
36
+
37
+ @index.search_each(search_query, :limit => :all) do |id, score|
38
+ results << block.call(build_hash(@index.doc(id)))
39
+ end
40
+
41
+ Chef::Log.debug("Search #{search_query} complete in #{Time.now - start_time} seconds")
42
+
43
+ attributes.empty? ? results : filter_by_attributes(results,attributes)
44
+ end
45
+
46
+ def filter_by_attributes(results, attributes)
47
+ results.collect do |r|
48
+ nr = Hash.new
49
+ nr[:index_name] = r[:index_name]
50
+ nr[:id] = r[:id]
51
+ attributes.each do |attrib|
52
+ if r.has_key?(attrib)
53
+ nr[attrib] = r[attrib]
54
+ end
55
+ end
56
+ nr
57
+ end
58
+ end
59
+
60
+ private :filter_by_attributes
61
+
62
+ def list_indexes
63
+ indexes = Hash.new
64
+ @index.search_each("index_name:*", :limit => :all) do |id, score|
65
+ indexes[@index.doc(id)["index_name"]] = true
66
+ end
67
+ indexes.keys
68
+ end
69
+
70
+ def has_index?(index)
71
+ list_indexes.detect { |i| i == index }
72
+ end
73
+
74
+ private
75
+ def build_search_query(type, query)
76
+ query = "id:*" if query == '*'
77
+ "index_name:#{type} AND (#{query})"
78
+ end
79
+
80
+ def build_hash(doc)
81
+ result = Chef::Search::Result.new
82
+ doc.fields.each do |f|
83
+ result[f] = doc[f]
84
+ end
85
+ result
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,64 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'extlib'
20
+
21
+ class Chef
22
+ class Search
23
+ class Result
24
+
25
+ def initialize
26
+ proc = lambda do |h,k|
27
+ newhash = Mash.new(&proc)
28
+ h.each do |pk, pv|
29
+ rx = /^#{k.to_s}_/
30
+ if pk =~ rx
31
+ newhash[ pk.gsub(rx,'') ] = pv
32
+ end
33
+ end
34
+ newhash
35
+ end
36
+ @internal = Mash.new(&proc)
37
+ end
38
+
39
+ def method_missing(symbol, *args, &block)
40
+ @internal.send(symbol, *args, &block)
41
+ end
42
+
43
+ # Serialize this object as a hash
44
+ def to_json(*a)
45
+ result = {
46
+ 'json_class' => self.class.name,
47
+ 'results' => @internal
48
+ }
49
+ result.to_json(*a)
50
+ end
51
+
52
+ # Create a Chef::Search::Result from JSON
53
+ def self.json_create(o)
54
+ result = self.new
55
+ o['results'].each do |k,v|
56
+ result[k] = v
57
+ end
58
+ result
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+
@@ -0,0 +1,77 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'ferret'
20
+
21
+ class Chef
22
+ class SearchIndex
23
+
24
+ attr_reader :index
25
+
26
+ def initialize
27
+ @index = Ferret::Index::Index.new(
28
+ :path => Chef::Config[:search_index_path],
29
+ :key => [ :id ]
30
+ )
31
+ end
32
+
33
+ def add(new_object)
34
+ index_hash = create_index_object(new_object)
35
+ Chef::Log.debug("Indexing #{index_hash[:index_name]} with #{index_hash.inspect}")
36
+ @index.add_document(index_hash)
37
+ end
38
+
39
+ def create_index_object(new_object)
40
+ index_hash = nil
41
+
42
+ if new_object.respond_to?(:to_index)
43
+ index_hash = new_object.to_index
44
+ elsif new_object.kind_of?(Hash)
45
+ index_hash = new_object
46
+ else
47
+ raise Chef::Exceptions::SearchIndex, "Cannot transform argument to a Hash!"
48
+ end
49
+
50
+ unless index_hash.has_key?(:index_name) || index_hash.has_key?("index_name")
51
+ raise Chef::Exceptions::SearchIndex, "Cannot index without an index_name key: #{index_hash.inspect}"
52
+ end
53
+
54
+ unless index_hash.has_key?(:id) || index_hash.has_key?("id")
55
+ raise Chef::Exceptions::SearchIndex, "Cannot index without an id key: #{index_hash.inspect}"
56
+ end
57
+
58
+ sanitized_hash = Hash.new
59
+ index_hash.each do |k,v|
60
+ sanitized_hash[k.to_sym] = v
61
+ end
62
+
63
+ sanitized_hash
64
+ end
65
+
66
+ def delete(index_obj)
67
+ to_delete = create_index_object(index_obj)
68
+ Chef::Log.debug("Removing #{to_delete.inspect} from the #{to_delete[:index_name]} index")
69
+ @index.delete(to_delete[:id])
70
+ end
71
+
72
+ def commit
73
+ @index.commit
74
+ end
75
+
76
+ end
77
+ end