Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Dispatch LimitCpuRequest

Configure cgroup cpu.shares to set cpu limit for a container.

[#62173454]

Signed-off-by: Eric Malm <emalm@pivotallabs.com>
  • Loading branch information...
commit fe9ec4865821d1574905dbdcb4848a27fcd963f6 1 parent 406af46
@mariash mariash authored ematpl committed
View
2  warden/Gemfile.lock
@@ -22,7 +22,7 @@ GIT
GIT
remote: https://github.com/cloudfoundry/warden.git
- revision: 8d3ff4621ae6a9824e06d4af2bd7cd93998144dc
+ revision: 406af4631854f9b149f81eb5ed9cd47757707867
specs:
warden-client (0.1.0)
warden-protocol (~> 0.1.0)
View
15 warden/lib/warden/container/base.rb
@@ -696,6 +696,21 @@ def do_limit_bandwidth(request, response)
raise WardenError.new("not implemented")
end
+ def around_limit_cpu
+ check_state_in(State::Active, State::Stopped)
+
+ begin
+ delete_snapshot
+ yield
+ ensure
+ write_snapshot
+ end
+ end
+
+ def do_limit_cpu(request, response)
+ raise WardenError.new("not implemented")
+ end
+
def before_info
check_state_in(State::Active, State::Stopped)
end
View
33 warden/lib/warden/container/features/cgroup.rb
@@ -14,6 +14,14 @@ def cgroup_path(subsystem)
File.join("/tmp/warden/cgroup", subsystem.to_s, "instance-#{self.container_id}")
end
+ def restore
+ super
+
+ if @resources.has_key?("limit_cpu")
+ limit_cpu(@resources["limit_cpu"])
+ end
+ end
+
def do_info(request, response)
super(request, response)
@@ -34,6 +42,31 @@ def do_info(request, response)
nil
end
+ def limit_cpu(limit_in_shares)
+ File.open(File.join(cgroup_path(:cpu), "cpu.shares"), 'w') do |f|
+ f.write(limit_in_shares.to_s)
+ end
+ end
+
+ private :limit_cpu
+
+ def do_limit_cpu(request, response)
+ if request.limit_in_shares
+ begin
+ limit_cpu(request.limit_in_shares)
+ rescue => e
+ raise WardenError.new("Failed setting cpu shares: #{e}")
+ else
+ @resources["limit_cpu"] = request.limit_in_shares
+ end
+ end
+
+ limit_in_shares = File.read(File.join(cgroup_path(:cpu), "cpu.shares"))
+ response.limit_in_shares = limit_in_shares.to_i
+
+ nil
+ end
+
def read_memory_stats
lines = File.read(File.join(cgroup_path(:memory), "memory.stat")).split(/\r?\n/)
View
1  warden/lib/warden/repl/commands_manager.rb
@@ -121,6 +121,7 @@ def command_descriptions
"destroy" => "Shutdown a container.",
"echo" => "Echo a message.",
"info" => "Show metadata for a container.",
+ "limit_cpu" => "Set or get the CPU limit in shares for the container.",
"limit_disk" => "set or get the disk limit for the container.",
"limit_memory" => "Set or get the memory limit for the container.",
"link" => "Do blocking read on results from a job.",
View
10 warden/spec/container/base_spec.rb
@@ -630,5 +630,15 @@ def initialize_container
container.dispatch(Warden::Protocol::LimitBandwidthRequest.new)
}
end
+
+ describe "limit_cpu" do
+ before(:each) do
+ @container.stub(:do_limit_cpu)
+ end
+
+ include_examples "succeeds when active or stopped", Proc.new {
+ container.dispatch(Warden::Protocol::LimitCpuRequest.new)
+ }
+ end
end
end
View
67 warden/spec/container/linux_spec.rb
@@ -10,6 +10,8 @@
require "warden/container/linux"
describe "linux", :platform => "linux", :needs_root => true do
+ include Helpers::Drain
+
let(:work_path) { File.join(Dir.tmpdir, "warden", "spec") }
let(:unix_domain_path) { File.join(work_path, "warden.sock") }
let(:container_klass) { "Warden::Container::Linux" }
@@ -398,6 +400,57 @@ def limit_bandwidth(options = {})
end
end
+ describe "limit_cpu" do
+ attr_reader :handle
+
+ def integer_from_cgroup_cpu_shares
+ File.read(File.join("/tmp/warden/cgroup/cpu", "instance-#{@handle}", "cpu.shares")).to_i
+ end
+
+ def limit_cpu(options = {})
+ response = client.limit_cpu(options.merge(:handle => handle))
+ response.should be_ok
+ response
+ end
+
+ before do
+ @handle = client.create.handle
+ end
+
+ it "should return the current shares if no share value specified" do
+ current_cpu_shares = integer_from_cgroup_cpu_shares
+ response = limit_cpu
+ expect(response.limit_in_shares).to be current_cpu_shares
+ end
+
+ it "should set the cpu shares" do
+ response = limit_cpu(:limit_in_shares => 100)
+ expect(response.limit_in_shares).to be 100
+
+ expect(integer_from_cgroup_cpu_shares).to be 100
+ end
+
+ it "should update the cpu shares" do
+ response = limit_cpu(:limit_in_shares => 100)
+ expect(response.limit_in_shares).to be 100
+
+ expect(integer_from_cgroup_cpu_shares).to be 100
+
+ response = limit_cpu(:limit_in_shares => 200)
+ expect(response.limit_in_shares).to be 200
+
+ expect(integer_from_cgroup_cpu_shares).to be 200
+ end
+
+ it "should not set the cpu shares below 2" do
+ response = limit_cpu(:limit_in_shares => 1)
+ expect(response.limit_in_shares).to be 2
+
+ expect(integer_from_cgroup_cpu_shares).to be 2
+ end
+ end
+
+
describe "net_out" do
def net_out(options = {})
response = client.net_out(options)
@@ -868,4 +921,18 @@ def create
Process.kill("KILL", File.read(wshd_pid_path).to_i)
end
end
+
+ describe "restoring from snapshot" do
+ it "should reset cpu shares for restored containers" do
+ handle = client.create.handle
+ client.limit_cpu(:handle => handle, :limit_in_shares => 100)
+
+ drain_and_restart
+
+ new_client = create_client
+
+ response = new_client.limit_cpu(:handle => handle)
+ expect(response.limit_in_shares).to be 100
+ end
+ end
end
View
51 warden/spec/support/examples/drain.rb
@@ -1,6 +1,8 @@
require "thread"
shared_examples "drain" do
+ include Helpers::Drain
+
def warden_running?
# After hook reaps exit status
File.read("/proc/#{@pid}/status") =~ /zombie/
@@ -306,53 +308,4 @@ def received_messages(num)
link_response.stderr.should == ""
link_response.exit_status.should == 2
end
-
- def drain
- Process.kill("USR2", @pid)
- Process.waitpid(@pid)
- end
-
- def drain_and_restart
- drain
- start_warden
- end
-
- def read_streams(cli, handle, job_id)
- streams = Hash.new { |k, v| "" }
-
- cli.write(Warden::Protocol::StreamRequest.new(:handle => handle,
- :job_id => job_id))
-
- loop do
- resp = cli.read
- break if resp.name.nil?
-
- streams[resp.name] += resp.data
- end
-
- streams
- end
-
- def get_uid(client, handle)
- run_resp = client.run(:handle => handle, :script => "id -u")
- run_resp.exit_status.should == 0
- Integer(run_resp.stdout.chomp)
- end
-
- def check_request_broken(&blk)
- handle = client.create.handle
-
- t = Thread.new do
- expect do
- blk.call
- end.to raise_error
- end
-
- # Force the request before the drain
- t.run if t.alive?
-
- drain
-
- t.join
- end
end
View
52 warden/spec/support/helpers/drain.rb
@@ -0,0 +1,52 @@
+module Helpers
+ module Drain
+ def drain
+ Process.kill("USR2", @pid)
+ Process.waitpid(@pid)
+ end
+
+ def drain_and_restart
+ drain
+ start_warden
+ end
+
+ def read_streams(cli, handle, job_id)
+ streams = Hash.new { |k, v| "" }
+
+ cli.write(Warden::Protocol::StreamRequest.new(:handle => handle,
+ :job_id => job_id))
+
+ loop do
+ resp = cli.read
+ break if resp.name.nil?
+
+ streams[resp.name] += resp.data
+ end
+
+ streams
+ end
+
+ def get_uid(client, handle)
+ run_resp = client.run(:handle => handle, :script => "id -u")
+ run_resp.exit_status.should == 0
+ Integer(run_resp.stdout.chomp)
+ end
+
+ def check_request_broken(&blk)
+ handle = client.create.handle
+
+ t = Thread.new do
+ expect do
+ blk.call
+ end.to raise_error
+ end
+
+ # Force the request before the drain
+ t.run if t.alive?
+
+ drain
+
+ t.join
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.