Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove yaml (psych) requirement in StateFile #2784

Merged
merged 2 commits into from Jan 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/puma/cli.rb
Expand Up @@ -21,6 +21,7 @@ class << self
# Handles invoke a Puma::Server in a command line style.
#
class CLI
# @deprecated 6.0.0
KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE

# Create a new CLI object using +argv+ as the command line
Expand Down
1 change: 1 addition & 0 deletions lib/puma/launcher.rb
Expand Up @@ -15,6 +15,7 @@ module Puma
# It is responsible for either launching a cluster of Puma workers or a single
# puma server.
class Launcher
# @deprecated 6.0.0
KEYS_NOT_TO_PERSIST_IN_STATE = [
:logger, :lowlevel_error_handler,
:before_worker_shutdown, :before_worker_boot, :before_worker_fork,
Expand Down
48 changes: 41 additions & 7 deletions lib/puma/state_file.rb
@@ -1,15 +1,40 @@
# frozen_string_literal: true

require 'yaml'

module Puma

# Puma::Launcher uses StateFile to write a yaml file for use with Puma::ControlCLI.
#
# In previous versions of Puma, YAML was used to read/write the state file.
MSP-Greg marked this conversation as resolved.
Show resolved Hide resolved
# Since Puma is similar to Bundler/RubyGems in that it may load before one's app
# does, minimizing the dependencies that may be shared with the app is desired.
#
# At present, it only works with numeric and string values. It is still a valid
# yaml file, and the CI tests parse it with Psych.
#
class StateFile

ALLOWED_FIELDS = %w!control_url control_auth_token pid running_from!

# @deprecated 6.0.0
FIELDS = ALLOWED_FIELDS

def initialize
@options = {}
end

def save(path, permission = nil)
contents =YAML.dump @options
contents = "---\n".dup
@options.each do |k,v|
next unless ALLOWED_FIELDS.include? k
case v
when Numeric
contents << "#{k}: #{v}\n"
when String
next if v.strip.empty?
contents << (k == 'running_from' || v.to_s.include?(' ') ?
"#{k}: \"#{v}\"\n" : "#{k}: #{v}\n")
end
end
if permission
File.write path, contents, mode: 'wb:UTF-8'
else
Expand All @@ -18,12 +43,21 @@ def save(path, permission = nil)
end

def load(path)
@options = YAML.load File.read(path)
File.read(path).lines.each do |line|
next if line.start_with? '#'
k,v = line.split ':', 2
next unless v && ALLOWED_FIELDS.include?(k)
v = v.strip
@options[k] =
case v
when /\A\d+\z/ then v.to_i
when /\A\d+\.\d+\z/ then v.to_f
else v.gsub(/\A"|"\z/, '')
end
end
end

FIELDS = %w!control_url control_auth_token pid running_from!

FIELDS.each do |f|
ALLOWED_FIELDS.each do |f|
define_method f do
@options[f]
end
Expand Down
24 changes: 18 additions & 6 deletions test/test_cli.rb
Expand Up @@ -4,6 +4,7 @@

require "puma/cli"
require "json"
require "psych"

class TestCLI < Minitest::Test
include SSLHelper if ::Puma::HAS_SSL
Expand Down Expand Up @@ -347,9 +348,21 @@ def test_tmp_control
cli = Puma::CLI.new ["--state", @tmp_path, "--control-url", "auto"]
cli.launcher.write_state

data = YAML.load File.read(@tmp_path)
opts = cli.launcher.instance_variable_get(:@options)

assert_equal Process.pid, data["pid"]
data = Psych.load_file @tmp_path

Puma::StateFile::ALLOWED_FIELDS.each do |key|
val =
case key
when 'pid' then Process.pid
when 'running_from' then File.expand_path('.') # same as Launcher
else opts[key.to_sym]
end
assert_equal val, data[key]
end

assert_equal (Puma::StateFile::ALLOWED_FIELDS & data.keys).sort, data.keys.sort

url = data["control_url"]

Expand All @@ -364,10 +377,9 @@ def test_state_file_callback_filtering
"--state", @tmp_path ]
cli.launcher.write_state

data = YAML.load_file(@tmp_path)
data = Psych.load_file @tmp_path

keys_not_stripped = data.keys & Puma::CLI::KEYS_NOT_TO_PERSIST_IN_STATE
assert_empty keys_not_stripped
assert_equal (Puma::StateFile::ALLOWED_FIELDS & data.keys).sort, data.keys.sort
end

def test_log_formatter_default_single
Expand Down Expand Up @@ -401,7 +413,7 @@ def test_state
cli = Puma::CLI.new ["--state", @tmp_path, "--control-url", url]
cli.launcher.write_state

data = YAML.load File.read(@tmp_path)
data = Psych.load_file @tmp_path

assert_equal Process.pid, data["pid"]
assert_equal url, data["control_url"]
Expand Down