提交 38834844 编写于 作者: D David Krieger 提交者: David Krieger

gpconfig: expand Behave tests

Co-authored-by: NJamie McAtamney <jmcatamney@pivotal.io>
Co-authored-by: NMark Sliva <msliva@pivotal.io>
Co-authored-by: NKalen Krempely <kkrempely@pivotal.io>
上级 1cf60c46
......@@ -7,6 +7,7 @@ from test.behave_utils.utils import drop_database_if_exists, start_database_if_n
create_database, \
run_command, check_user_permissions, run_gpcommand
from steps.mirrors_mgmt_utils import MirrorMgmtContext
from steps.gpconfig_mgmt_utils import GpConfigContext
from gppylib.db import dbconn
def before_all(context):
......@@ -15,7 +16,7 @@ def before_all(context):
def before_feature(context, feature):
# we should be able to run gpexpand without having a cluster initialized
tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpstate', 'gpmovemirrors']
tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpstate', 'gpmovemirrors', 'gpconfig']
if set(context.feature.tags).intersection(tags_to_skip):
return
......@@ -65,7 +66,11 @@ def after_feature(context, feature):
context.conn.close()
if 'minirepro' in feature.tags:
context.conn.close()
if 'gpconfig' in feature.tags:
context.execute_steps(u'''
Then the user runs "gpstop -ar"
And gpstop should return a return code of 0
''')
def before_scenario(context, scenario):
if "skip" in scenario.effective_tags:
......@@ -75,7 +80,10 @@ def before_scenario(context, scenario):
if 'gpmovemirrors' in context.feature.tags:
context.mirror_context = MirrorMgmtContext()
tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpstate', 'gpmovemirrors']
if 'gpconfig' in context.feature.tags:
context.gpconfig_context = GpConfigContext()
tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpstate', 'gpmovemirrors', 'gpconfig']
if set(context.feature.tags).intersection(tags_to_skip):
return
......@@ -89,7 +97,8 @@ def after_scenario(context, scenario):
for tablespace in context.tablespaces.values():
tablespace.cleanup()
tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpstate', 'gpinitstandby']
# NOTE: gpconfig after_scenario cleanup is in the step `the gpconfig context is setup`
tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpstate', 'gpinitstandby', 'gpconfig']
if set(context.feature.tags).intersection(tags_to_skip):
return
......
@gpconfig
Feature: gpconfig integration tests
Scenario: gpconfig -s option with --file option with difference between db and conf file
# it requires 2 change (-c) operations to prove that we have changed a file,
# because any existing postgresql.conf file could already have the first value in it a priori
Given the user runs "gpconfig -c statement_mem -v 130MB"
Then gpconfig should return a return code of 0
Given the user runs "gpconfig -c statement_mem -v 129MB"
Then gpconfig should return a return code of 0
Then verify that the last line of the file "postgresql.conf" in the master data directory contains the string "129MB"
Then verify that the last line of the file "postgresql.conf" in each segment data directory contains the string "129MB"
Given the user runs "gpconfig -s statement_mem --file"
Then gpconfig should return a return code of 0
Then gpconfig should print "Master[\s]*value: 129MB" to stdout
Then gpconfig should print "Segment[\s]*value: 129MB" to stdout
Scenario: gpconfig with an empty string passed as a value
# As above, two calls to gpconfig -c are needed to guarantee a change.
Given the user runs "gpconfig -c unix_socket_directories -v 'foo'"
Then gpconfig should return a return code of 0
Given the user runs "gpconfig -c unix_socket_directories -v ''"
Then gpconfig should return a return code of 0
Then verify that the last line of the file "postgresql.conf" in the master data directory contains the string "unix_socket_directories=''"
Then verify that the last line of the file "postgresql.conf" in each segment data directory contains the string "unix_socket_directories=''"
Given the user runs "gpconfig -s unix_socket_directories --file"
Then gpconfig should return a return code of 0
Then gpconfig should print "Master[\s]*value: ''" to stdout
Then gpconfig should print "Segment[\s]*value: ''" to stdout
# Below, we use 2 change (-c) operations to prove that we have changed a file,
# because any existing postgresql.conf file could already have the first value in it a priori
# NOTE: since we are restarting the database with the given paramaters, do not change parameters
# that will cause the database to not restart given your machine setup.
@concourse_cluster
@demo_cluster
Scenario Outline: run each gpconfig command for guc type: <type>
Given the user runs "gpstop -ar"
And gpstop should return a return code of 0
And the gpconfig context is setup
And the user runs "gpconfig -c <guc> -v <seed_value>"
And gpconfig should return a return code of 0
# ensure <guc> is set to <seed_value> for the tests below
And the user runs "gpstop -ar"
And gpstop should return a return code of 0
# set same value on master and segments
When the user runs "gpconfig -c <guc> -v <value>"
Then gpconfig should return a return code of 0
And verify that the last line of the file "postgresql.conf" in the master data directory contains the string "<guc>=<file_value>"
And verify that the last line of the file "postgresql.conf" in each segment data directory contains the string "<guc>=<file_value>"
# set value on master only, leaving segments the same
When the user runs "gpconfig -c <guc> -v <value_master_only> --masteronly "
Then gpconfig should return a return code of 0
And verify that the last line of the file "postgresql.conf" in the master data directory contains the string "<guc>=<file_value_master_only>"
And verify that the last line of the file "postgresql.conf" in each segment data directory contains the string "<guc>=<file_value>"
# set value on master with a different value from the segments
When the user runs "gpconfig -c <guc> -v <value> -m <value_master>"
Then gpconfig should return a return code of 0
And verify that the last line of the file "postgresql.conf" in the master data directory contains the string "<guc>=<file_value_master>"
And verify that the last line of the file "postgresql.conf" in each segment data directory contains the string "<guc>=<file_value>"
# now make sure the last changes took full effect as seen by gpconfig
When the user runs "gpconfig -s <guc> --file"
Then gpconfig should return a return code of 0
And gpconfig should print "Master[\s]*value: <file_value_master>" to stdout
And gpconfig should print "Segment[\s]*value: <file_value>" to stdout
When the user runs "gpconfig -s <guc> --file-compare"
Then gpconfig should return a return code of 0
And gpconfig should print "GUCS ARE OUT OF SYNC" to stdout
And gpconfig should print "value: <seed_value> \| file: <file_value_master>" to stdout
And gpconfig should print "value: <seed_value> \| file: <file_value>" to stdout
When the user runs "gpstop -ar"
Then gpstop should return a return code of 0
# FIXME: a file string value of 'my_value' (quotes in file) has a live value of my_value (no quotes) and
# --file-compare is currently broken on that case.
# When the user runs "gpconfig -s <guc> --file-compare"
# Then gpconfig should return a return code of 0
# And gpconfig should print "Master[\s]*value: <live_value_master> \| file: <file_value_master>" to stdout
# And gpconfig should print "Segment[\s]*value: <live_value> \| file: <file_value>" to stdout
When the user runs "gpconfig -s <guc>"
Then gpconfig should return a return code of 0
And gpconfig should print "Master[\s]*value: <live_value_master>" to stdout
And gpconfig should print "Segment[\s]*value: <live_value>" to stdout
# test for each type documented for gpconfig
Examples:
| guc | type | seed_value | value | file_value | live_value | value_master_only | file_value_master_only | value_master | file_value_master | live_value_master |
| log_connections | bool | off | on | on | on | off | off | off | off | off |
| gp_resgroup_memory_policy | enum | eager_free | auto | auto | auto | eager_free | eager_free | eager_free | eager_free | eager_free |
| vacuum_cost_limit | integer | 300 | 400 | 400 | 400 | 555 | 555 | 500 | 500 | 500 |
| gp_resource_group_cpu_limit | real | 0.4 | 0.5 | 0.5 | 0.5 | 0.33 | 0.33 | 0.7 | 0.7 | 0.7 |
| application_name | string | xxxxxx | bodhi | 'bodhi' | bodhi | lucy | 'lucy' | bengie | 'bengie' | bengie |
| application_name | string | yyyyyy | 'bod hi' | 'bod hi' | bod hi | 'lu cy' | 'lu cy' | 'ben gie' | 'ben gie' | ben gie |
| application_name | string | zzzzzz | '' | '' | | '' | '' | '' | '' | |
@concourse_cluster
@demo_cluster
Scenario Outline: gpconfig edge cases for type: <type>
Given the user runs "gpstop -ar"
And gpstop should return a return code of 0
And the gpconfig context is setup
And the user runs "gpconfig -c <guc> -v <seed_value>"
And gpconfig should return a return code of 0
# set same value on master and segments
When the user runs "gpconfig -c <guc> -v <value>"
Then gpconfig should return a return code of 0
And verify that the last line of the file "postgresql.conf" in the master data directory contains the string "<guc>=<file_value>" escaped
And verify that the last line of the file "postgresql.conf" in each segment data directory contains the string "<guc>=<file_value>"
# now make sure the last changes took full effect as seen by gpconfig
When the user runs "gpconfig -s <guc> --file"
Then gpconfig should return a return code of 0
And gpconfig should print "Master value: <file_value>" escaped to stdout
And gpconfig should print "Segment value: <file_value>" escaped to stdout
When the user runs "gpstop -ar"
And gpstop should return a return code of 0
When the user runs "gpconfig -s <guc>"
Then gpconfig should return a return code of 0
And gpconfig should print "Master value: <live_value>" escaped to stdout
And gpconfig should print "Segment value: <live_value>" escaped to stdout
# NOTE: <value> is a command-line value
Examples:
| guc | type | seed_value | value | file_value | live_value |
| application_name | string | boo | "'\''" | '\\''' | \' |
#| application_name | string | boo | 'C:\\home\\fun' | 'C:\\home\\fun' | 'C:\\home\\fun' |
@concourse_cluster
@demo_cluster
Scenario Outline: write directly to postgresql.conf file: <type>
Given the user runs "gpstop -ar"
And gpstop should return a return code of 0
And the gpconfig context is setup
# we do this to make sure all segment files contain this <guc>, both in file and live
And the user runs "gpconfig -c <guc> -v <seed_value>"
And gpconfig should return a return code of 0
And the user runs "gpstop -ar"
And gpstop should return a return code of 0
When the user writes "<guc>" as "<value>" to the master config file
Then verify that the last line of the file "postgresql.conf" in the master data directory contains the string "<guc>=<value>" escaped
# now make sure the last changes took full effect as seen by gpconfig
When the user runs "gpconfig -s <guc> --file"
Then gpconfig should return a return code of 0
And gpconfig should print "Master value: <file_value>" escaped to stdout
When the user runs "gpstop -ar"
Then gpstop should return a return code of 0
When the user runs "gpconfig -s <guc>"
Then gpconfig should return a return code of 0
And gpconfig should print "Master value: <live_value>" escaped to stdout
# NOTE: <value> is directly entered into postgresql.conf
Examples:
| guc | type | seed_value | value | file_value | live_value |
| log_connections | bool | off | on | on | on |
| gp_resgroup_memory_policy | enum | eager_free | auto | auto | auto |
| vacuum_cost_limit | integer | 300 | 400 | 400 | 400 |
| gp_resource_group_cpu_limit | real | 0.4 | 0.5 | 0.5 | 0.5 |
| application_name | string | xxxxxx | bodhi | bodhi | bodhi |
| application_name | string | xxxxxx | 'bod hi' | 'bod hi' | bod hi |
| application_name | string | xxxxxx | '' | '' | |
@concourse_cluster
@demo_cluster
Scenario Outline: gpconfig basic removal for type: <type>
Given the user runs "gpstop -ar"
And gpstop should return a return code of 0
And the gpconfig context is setup
And the user runs "gpconfig -c <guc> -v <value>"
And gpconfig should return a return code of 0
And verify that the file "postgresql.conf" in the master data directory has "some" line starting with "<guc>="
And verify that the file "postgresql.conf" in each segment data directory has "some" line starting with "<guc>="
When the user runs "gpconfig -r <guc>"
And gpconfig should return a return code of 0
Then verify that the file "postgresql.conf" in the master data directory has "no" line starting with "<guc>="
And verify that the file "postgresql.conf" in the master data directory has "some" line starting with "#<guc>="
And verify that the file "postgresql.conf" in each segment data directory has "no" line starting with "<guc>="
And verify that the file "postgresql.conf" in each segment data directory has "some" line starting with "#<guc>="
Examples:
| guc | type | value |
| log_connections | bool | off |
| gp_resgroup_memory_policy | enum | eager_free |
| vacuum_cost_limit | integer | 300 |
| gp_resource_group_cpu_limit | real | 0.4 |
| application_name | string | bengie |
| application_name | string | 'ben gie' |
| application_name | string | '' |
from os import path
import subprocess
from gppylib.db import dbconn
from gppylib.gparray import GpArray
from behave import given, when, then
from test.behave_utils.utils import *
from mgmt_utils import *
# This class is intended to store per-Scenario state that is built up over
# a series of steps.
class GpConfigContext:
def __init__(self):
self.master_postgres_file = ''
self.standby_postgres_file = ''
@given('the gpconfig context is setup')
def impl(context):
make_temp_dir(context, path.join('/tmp', 'gpconfig'))
temp_base_dir = context.temp_base_dir
context.gpconfig_context.working_directory = temp_base_dir
gparray = GpArray.initFromCatalog(dbconn.DbURL())
segments = gparray.getDbList()
restore_commands = []
for segment in segments:
segment_tmp_directory = path.join(temp_base_dir, str(segment.dbid))
os.mkdir(segment_tmp_directory)
backup_path = path.join(segment_tmp_directory, 'postgresql.conf')
original_path = path.join(segment.datadir, 'postgresql.conf')
copy_command = ('scp %s:%s %s' % (segment.hostname, original_path, backup_path)).split(' ')
restore_command = ('scp %s %s:%s' % (backup_path, segment.hostname, original_path)).split(' ')
restore_commands.append(restore_command)
subprocess.check_call(copy_command)
if segment.content == -1:
if segment.role == 'p':
context.gpconfig_context.master_postgres_file = original_path
else:
context.gpconfig_context.standby_postgres_file = original_path
def delete_temp_directory():
if 'temp_base_dir' in context:
shutil.rmtree(context.temp_base_dir)
def restore_conf_files():
for cmd in restore_commands:
subprocess.check_call(cmd)
context.add_cleanup(delete_temp_directory)
context.add_cleanup(restore_conf_files)
@given('the user runs gpconfig sets guc "{guc}" with "{value}"')
def impl(context, guc, value):
cmd = 'gpconfig -c %s -v %s' % (guc, value)
context.execute_steps(u'''
Given the user runs "%s"
Then gpconfig should return a return code of 0
''' % cmd)
# FIXME: this assumes the standby host is the same as the master host
# This is currently true for our demo_cluster and concourse_cluster
@when('the user writes "{guc}" as "{value}" to the master config file')
def impl(context, guc, value):
if context.gpconfig_context.master_postgres_file:
with open(context.gpconfig_context.master_postgres_file, 'a') as fd:
fd.write("%s=%s\n" % (guc, value))
fd.flush()
if context.gpconfig_context.standby_postgres_file:
with open(context.gpconfig_context.standby_postgres_file, 'a') as fd:
fd.write("%s=%s\n" % (guc, value))
fd.flush()
......@@ -424,6 +424,11 @@ def impl(context, HOST, port, dir, ctxt):
def impl(context, command, err_msg):
check_err_msg(context, err_msg)
@when('{command} should print "{out_msg}" escaped to stdout')
@then('{command} should print "{out_msg}" escaped to stdout')
@then('{command} should print a "{out_msg}" escaped warning')
def impl(context, command, out_msg):
check_stdout_msg(context, out_msg, True)
@when('{command} should print "{out_msg}" to stdout')
@then('{command} should print "{out_msg}" to stdout')
......@@ -1302,19 +1307,96 @@ def impl(context, filename, output):
print contents
check_stdout_msg(context, output)
@then('verify that the last line of the file "{filename}" in the master data directory contains the string "{output}" escaped')
def impl(context, filename, output):
find_string_in_master_data_directory(context, filename, output, True)
@then('verify that the last line of the file "{filename}" in the master data directory contains the string "{output}"')
def impl(context, filename, output):
find_string_in_master_data_directory(context, filename, output)
def find_string_in_master_data_directory(context, filename, output, escapeStr=False):
contents = ''
file_path = os.path.join(master_data_dir, filename)
with open(file_path) as fr:
for line in fr:
contents = line.strip()
if escapeStr:
output = re.escape(output)
pat = re.compile(output)
if not pat.search(contents):
err_str = "Expected stdout string '%s' and found: '%s'" % (output, contents)
raise Exception(err_str)
@given('verify that the file "{filename}" in the master data directory has "{some}" line starting with "{output}"')
@then('verify that the file "{filename}" in the master data directory has "{some}" line starting with "{output}"')
def impl(context, filename, some, output):
if (some == 'some'):
valuesShouldExist = True
elif (some == 'no'):
valuesShouldExist = False
else:
raise Exception("only 'some' and 'no' are valid inputs")
regexStr = "%s%s" % ("^[\s]*", output)
pat = re.compile(regexStr)
file_path = os.path.join(master_data_dir, filename)
with open(file_path) as fr:
for line in fr:
contents = line.strip()
match = pat.search(contents)
if not valuesShouldExist:
if match:
err_str = "Expected no stdout string '%s' and found: '%s'" % (regexStr, contents)
raise Exception(err_str)
else:
if match:
return
if valuesShouldExist:
err_str = "xx Expected stdout string '%s' and found: '%s'" % (regexStr, contents)
raise Exception(err_str)
@given('verify that the file "{filename}" in each segment data directory has "{some}" line starting with "{output}"')
@then('verify that the file "{filename}" in each segment data directory has "{some}" line starting with "{output}"')
def impl(context, filename, some, output):
try:
with dbconn.connect(dbconn.DbURL(dbname='template1')) as conn:
curs = dbconn.execSQL(conn, "SELECT hostname, datadir FROM gp_segment_configuration WHERE role='p' AND content > -1;")
result = curs.fetchall()
segment_info = [(result[s][0], result[s][1]) for s in range(len(result))]
except Exception as e:
raise Exception("Could not retrieve segment information: %s" % e.message)
if (some == 'some'):
valuesShouldExist = True
elif (some == 'no'):
valuesShouldExist = False
else:
raise Exception("only 'some' and 'no' are valid inputs")
for info in segment_info:
host, datadir = info
filepath = os.path.join(datadir, filename)
regex = "%s%s" % ("^[%s]*", output)
cmd_str = 'ssh %s "grep -c %s %s"' % (host, regex, filepath)
cmd = Command(name='Running remote command: %s' % cmd_str, cmdStr=cmd_str)
cmd.run(validateAfter=False)
try:
val = int(cmd.get_stdout().strip())
if not valuesShouldExist:
if val:
raise Exception('File %s on host %s does start with "%s"(val error: %s)' % (filepath, host, output, val))
else:
if not val:
raise Exception('File %s on host %s does start not with "%s"(val error: %s)' % (filepath, host, output, val))
except:
raise Exception('File %s on host %s does start with "%s"(parse error)' % (filepath, host, output))
@then('verify that the last line of the file "{filename}" in each segment data directory contains the string "{output}"')
def impl(context, filename, output):
segment_info = []
......
......@@ -125,8 +125,9 @@ def run_gpcommand_async(context, command):
cmd = Command(name='run %s' % command, cmdStr='$GPHOME/bin/%s' % (command))
context.asyncproc = cmd.runNoWait()
def check_stdout_msg(context, msg):
def check_stdout_msg(context, msg, escapeStr = False):
if escapeStr:
msg = re.escape(msg)
pat = re.compile(msg)
if not pat.search(context.stdout_message):
err_str = "Expected stdout string '%s' and found: '%s'" % (msg, context.stdout_message)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册