mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2017.7' into winuser
This commit is contained in:
commit
0ca5224cbf
18 changed files with 2113 additions and 72 deletions
6
Gemfile
6
Gemfile
|
@ -2,8 +2,8 @@
|
|||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'test-kitchen', '>=1.21.0'
|
||||
gem 'kitchen-salt', :git => 'https://github.com/saltstack/kitchen-salt.git'
|
||||
gem 'test-kitchen', '~>1.21'
|
||||
gem 'kitchen-salt', '~>0.2'
|
||||
gem 'kitchen-sync'
|
||||
gem 'git'
|
||||
|
||||
|
@ -20,7 +20,7 @@ group :windows do
|
|||
gem 'vagrant-wrapper'
|
||||
gem 'kitchen-vagrant'
|
||||
gem 'winrm', '~>2.0'
|
||||
gem 'winrm-fs', :git => 'https://github.com/gtmanfred/winrm-fs.git'
|
||||
gem 'winrm-fs', :git => 'https://github.com/WinRb/winrm-fs.git'
|
||||
end
|
||||
|
||||
group :ec2 do
|
||||
|
|
|
@ -44,7 +44,7 @@ at ``/etc/salt/cloud.profiles`` or in the ``/etc/salt/cloud.profiles.d/`` direct
|
|||
|
||||
linode_1024:
|
||||
provider: my-linode-config
|
||||
size: Linode 2048
|
||||
size: Linode 2GB
|
||||
image: CentOS 7
|
||||
location: London, England, UK
|
||||
|
||||
|
@ -77,12 +77,14 @@ command:
|
|||
----------
|
||||
linode:
|
||||
----------
|
||||
Linode 1024:
|
||||
Linode 2GB:
|
||||
----------
|
||||
AVAIL:
|
||||
----------
|
||||
10:
|
||||
500
|
||||
11:
|
||||
500
|
||||
2:
|
||||
500
|
||||
3:
|
||||
|
@ -100,11 +102,19 @@ command:
|
|||
CORES:
|
||||
1
|
||||
DISK:
|
||||
24
|
||||
50
|
||||
HOURLY:
|
||||
0.015
|
||||
LABEL:
|
||||
Linode 1024
|
||||
Linode 2GB
|
||||
PLANID:
|
||||
2
|
||||
PRICE:
|
||||
10.0
|
||||
RAM:
|
||||
2048
|
||||
XFER:
|
||||
2000
|
||||
...SNIP...
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,6 +5,6 @@ msgpack>=0.5,!=0.5.5
|
|||
PyYAML
|
||||
MarkupSafe
|
||||
requests>=1.0.0
|
||||
tornado>=4.2.1,<5.0
|
||||
tornado>=4.2.1,<6.0
|
||||
# Required by Tornado to handle threads stuff.
|
||||
futures>=2.0; python_version < '3.0'
|
||||
|
|
|
@ -255,7 +255,7 @@ def file_hash(load, fnd):
|
|||
except OSError:
|
||||
pass
|
||||
return file_hash(load, fnd)
|
||||
if os.path.getmtime(path) == mtime:
|
||||
if str(os.path.getmtime(path)) == mtime:
|
||||
# check if mtime changed
|
||||
ret['hsum'] = hsum
|
||||
return ret
|
||||
|
|
|
@ -640,6 +640,10 @@ def disassociate_vpc_from_hosted_zone(HostedZoneId=None, Name=None, VPCId=None,
|
|||
r = conn.disassociate_vpc_from_hosted_zone(**args)
|
||||
return _wait_for_sync(r['ChangeInfo']['Id'], conn)
|
||||
except ClientError as e:
|
||||
if e.response.get('Error', {}).get('Code') == 'VPCAssociationNotFound':
|
||||
log.debug('No VPC Association exists.')
|
||||
# return True since the current state is the desired one
|
||||
return True
|
||||
if tries and e.response.get('Error', {}).get('Code') == 'Throttling':
|
||||
log.debug('Throttled by AWS API.')
|
||||
time.sleep(3)
|
||||
|
|
|
@ -476,6 +476,9 @@ def authorize(name=None, source_group_name=None,
|
|||
log.error(msg)
|
||||
return False
|
||||
except boto.exception.EC2ResponseError as e:
|
||||
# if we are trying to add the same rule then we are already in the desired state, return true
|
||||
if e.error_code == 'InvalidPermission.Duplicate':
|
||||
return True
|
||||
msg = ('Failed to add rule to security group {0} with id {1}.'
|
||||
.format(group.name, group.id))
|
||||
log.error(msg)
|
||||
|
|
|
@ -85,6 +85,7 @@ def _strip_headers(output, *args):
|
|||
if not args:
|
||||
args_lc = ('installed packages',
|
||||
'available packages',
|
||||
'available upgrades',
|
||||
'updated packages',
|
||||
'upgraded packages')
|
||||
else:
|
||||
|
|
|
@ -829,7 +829,7 @@ def _get_configured_repos():
|
|||
'''
|
||||
|
||||
repos_cfg = configparser.ConfigParser()
|
||||
repos_cfg.read([REPOS + '/' + fname for fname in os.listdir(REPOS)])
|
||||
repos_cfg.read([REPOS + '/' + fname for fname in os.listdir(REPOS) if fname.endswith(".repo")])
|
||||
|
||||
return repos_cfg
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ for managing outputters.
|
|||
# Import python libs
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import
|
||||
|
||||
import io
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
@ -169,7 +171,7 @@ def get_printout(out, opts=None, **kwargs):
|
|||
'''
|
||||
try:
|
||||
fileno = sys.stdout.fileno()
|
||||
except AttributeError:
|
||||
except (AttributeError, io.UnsupportedOperation):
|
||||
fileno = -1 # sys.stdout is StringIO or fake
|
||||
return not os.isatty(fileno)
|
||||
|
||||
|
|
|
@ -2449,13 +2449,21 @@ def latest(
|
|||
'result': None,
|
||||
'comment': '\n'.join(comments)}
|
||||
|
||||
# Build updated list of pkgs to exclude non-targeted ones
|
||||
targeted_pkgs = list(targets.keys()) if pkgs else None
|
||||
if salt.utils.is_windows():
|
||||
# pkg.install execution module on windows ensures the software
|
||||
# package is installed when no version is specified, it does not
|
||||
# upgrade the software to the latest. This is per the design.
|
||||
# Build updated list of pkgs *with verion number*, exclude
|
||||
# non-targeted ones
|
||||
targeted_pkgs = [{x: targets[x]} for x in targets]
|
||||
else:
|
||||
# Build updated list of pkgs to exclude non-targeted ones
|
||||
targeted_pkgs = list(targets)
|
||||
|
||||
# No need to refresh, if a refresh was necessary it would have been
|
||||
# performed above when pkg.latest_version was run.
|
||||
try:
|
||||
# No need to refresh, if a refresh was necessary it would have been
|
||||
# performed above when pkg.latest_version was run.
|
||||
changes = __salt__['pkg.install'](name,
|
||||
changes = __salt__['pkg.install'](name=None,
|
||||
refresh=False,
|
||||
fromrepo=fromrepo,
|
||||
skip_verify=skip_verify,
|
||||
|
|
|
@ -593,23 +593,22 @@ class TCPReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.tra
|
|||
self.payload_handler = payload_handler
|
||||
self.io_loop = io_loop
|
||||
self.serial = salt.payload.Serial(self.opts)
|
||||
if USE_LOAD_BALANCER:
|
||||
self.req_server = LoadBalancerWorker(self.socket_queue,
|
||||
self.handle_message,
|
||||
io_loop=self.io_loop,
|
||||
ssl_options=self.opts.get('ssl'))
|
||||
else:
|
||||
if salt.utils.is_windows():
|
||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
_set_tcp_keepalive(self._socket, self.opts)
|
||||
self._socket.setblocking(0)
|
||||
self._socket.bind((self.opts['interface'], int(self.opts['ret_port'])))
|
||||
self.req_server = SaltMessageServer(self.handle_message,
|
||||
io_loop=self.io_loop,
|
||||
ssl_options=self.opts.get('ssl'))
|
||||
self.req_server.add_socket(self._socket)
|
||||
self._socket.listen(self.backlog)
|
||||
with salt.utils.async.current_ioloop(self.io_loop):
|
||||
if USE_LOAD_BALANCER:
|
||||
self.req_server = LoadBalancerWorker(self.socket_queue,
|
||||
self.handle_message,
|
||||
ssl_options=self.opts.get('ssl'))
|
||||
else:
|
||||
if salt.utils.is_windows():
|
||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
_set_tcp_keepalive(self._socket, self.opts)
|
||||
self._socket.setblocking(0)
|
||||
self._socket.bind((self.opts['interface'], int(self.opts['ret_port'])))
|
||||
self.req_server = SaltMessageServer(self.handle_message,
|
||||
ssl_options=self.opts.get('ssl'))
|
||||
self.req_server.add_socket(self._socket)
|
||||
self._socket.listen(self.backlog)
|
||||
salt.transport.mixins.auth.AESReqServerMixin.post_fork(self, payload_handler, io_loop)
|
||||
|
||||
@tornado.gen.coroutine
|
||||
|
@ -694,6 +693,7 @@ class SaltMessageServer(tornado.tcpserver.TCPServer, object):
|
|||
'''
|
||||
def __init__(self, message_handler, *args, **kwargs):
|
||||
super(SaltMessageServer, self).__init__(*args, **kwargs)
|
||||
self.io_loop = tornado.ioloop.IOLoop.current()
|
||||
|
||||
self.clients = []
|
||||
self.message_handler = message_handler
|
||||
|
@ -797,7 +797,9 @@ class TCPClientKeepAlive(tornado.tcpclient.TCPClient):
|
|||
stream = tornado.iostream.IOStream(
|
||||
sock,
|
||||
max_buffer_size=max_buffer_size)
|
||||
return stream.connect(addr)
|
||||
if tornado.version_info < (5,):
|
||||
return stream.connect(addr)
|
||||
return stream, stream.connect(addr)
|
||||
|
||||
|
||||
class SaltMessageClientPool(salt.transport.MessageClientPool):
|
||||
|
@ -878,33 +880,33 @@ class SaltMessageClient(object):
|
|||
return
|
||||
self._closing = True
|
||||
if hasattr(self, '_stream') and not self._stream.closed():
|
||||
self._stream.close()
|
||||
if self._read_until_future is not None:
|
||||
# This will prevent this message from showing up:
|
||||
# '[ERROR ] Future exception was never retrieved:
|
||||
# StreamClosedError'
|
||||
# This happens because the logic is always waiting to read
|
||||
# the next message and the associated read future is marked
|
||||
# 'StreamClosedError' when the stream is closed.
|
||||
self._read_until_future.exception()
|
||||
if (not self._stream_return_future.done() and
|
||||
self.io_loop != tornado.ioloop.IOLoop.current(
|
||||
instance=False)):
|
||||
# If _stream_return() hasn't completed, it means the IO
|
||||
# Loop is stopped (such as when using
|
||||
# 'salt.utils.async.SyncWrapper'). Ensure that
|
||||
# _stream_return() completes by restarting the IO Loop.
|
||||
# This will prevent potential errors on shutdown.
|
||||
orig_loop = tornado.ioloop.IOLoop.current()
|
||||
self.io_loop.make_current()
|
||||
try:
|
||||
# If _stream_return() hasn't completed, it means the IO
|
||||
# Loop is stopped (such as when using
|
||||
# 'salt.utils.async.SyncWrapper'). Ensure that
|
||||
# _stream_return() completes by restarting the IO Loop.
|
||||
# This will prevent potential errors on shutdown.
|
||||
try:
|
||||
orig_loop = tornado.ioloop.IOLoop.current()
|
||||
self.io_loop.make_current()
|
||||
self._stream.close()
|
||||
if self._read_until_future is not None:
|
||||
# This will prevent this message from showing up:
|
||||
# '[ERROR ] Future exception was never retrieved:
|
||||
# StreamClosedError'
|
||||
# This happens because the logic is always waiting to read
|
||||
# the next message and the associated read future is marked
|
||||
# 'StreamClosedError' when the stream is closed.
|
||||
self._read_until_future.exception()
|
||||
if (not self._stream_return_future.done() and
|
||||
self.io_loop != tornado.ioloop.IOLoop.current(
|
||||
instance=False)):
|
||||
self.io_loop.add_future(
|
||||
self._stream_return_future,
|
||||
lambda future: self.io_loop.stop()
|
||||
)
|
||||
self.io_loop.start()
|
||||
finally:
|
||||
orig_loop.make_current()
|
||||
finally:
|
||||
orig_loop.make_current()
|
||||
self._tcp_client.close()
|
||||
# Clear callback references to allow the object that they belong to
|
||||
# to be deleted.
|
||||
|
|
|
@ -39,13 +39,13 @@ def __random_name(size=6):
|
|||
INSTANCE_NAME = __random_name()
|
||||
PROVIDER_NAME = 'ec2'
|
||||
HAS_WINRM = salt.utils.cloud.HAS_WINRM and salt.utils.cloud.HAS_SMB
|
||||
TIMEOUT = 1200
|
||||
|
||||
|
||||
class EC2Test(ShellCase):
|
||||
'''
|
||||
Integration tests for the EC2 cloud provider in Salt-Cloud
|
||||
'''
|
||||
TIMEOUT = 500
|
||||
|
||||
def _installer_name(self):
|
||||
'''
|
||||
|
@ -187,17 +187,17 @@ class EC2Test(ShellCase):
|
|||
'''
|
||||
# create the instance
|
||||
rename = INSTANCE_NAME + '-rename'
|
||||
instance = self.run_cloud('-p ec2-test {0} --no-deploy'.format(INSTANCE_NAME), timeout=500)
|
||||
instance = self.run_cloud('-p ec2-test {0} --no-deploy'.format(INSTANCE_NAME), timeout=TIMEOUT)
|
||||
ret_str = '{0}:'.format(INSTANCE_NAME)
|
||||
|
||||
# check if instance returned
|
||||
try:
|
||||
self.assertIn(ret_str, instance)
|
||||
except AssertionError:
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=TIMEOUT)
|
||||
raise
|
||||
|
||||
change_name = self.run_cloud('-a rename {0} newname={1} --assume-yes'.format(INSTANCE_NAME, rename), timeout=500)
|
||||
change_name = self.run_cloud('-a rename {0} newname={1} --assume-yes'.format(INSTANCE_NAME, rename), timeout=TIMEOUT)
|
||||
|
||||
check_rename = self.run_cloud('-a show_instance {0} --assume-yes'.format(rename), [rename])
|
||||
exp_results = [' {0}:'.format(rename), ' size:',
|
||||
|
@ -206,11 +206,11 @@ class EC2Test(ShellCase):
|
|||
for result in exp_results:
|
||||
self.assertIn(result, check_rename[0])
|
||||
except AssertionError:
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=TIMEOUT)
|
||||
raise
|
||||
|
||||
# delete the instance
|
||||
delete = self.run_cloud('-d {0} --assume-yes'.format(rename), timeout=500)
|
||||
delete = self.run_cloud('-d {0} --assume-yes'.format(rename), timeout=TIMEOUT)
|
||||
ret_str = ' shutting-down'
|
||||
|
||||
# check if deletion was performed appropriately
|
||||
|
@ -238,7 +238,7 @@ class EC2Test(ShellCase):
|
|||
'win_installer': self.copy_file(self.INSTALLER),
|
||||
},
|
||||
)
|
||||
self._test_instance('ec2-win2012r2-test', debug=True, timeout=500)
|
||||
self._test_instance('ec2-win2012r2-test', debug=True, timeout=TIMEOUT)
|
||||
|
||||
@skipIf(not HAS_WINRM, 'Skip when winrm dependencies are missing')
|
||||
def test_win2012r2_winrm(self):
|
||||
|
@ -252,10 +252,11 @@ class EC2Test(ShellCase):
|
|||
'userdata_file': self.copy_file('windows-firewall.ps1'),
|
||||
'win_installer': self.copy_file(self.INSTALLER),
|
||||
'winrm_ssl_verify': False,
|
||||
'use_winrm': True,
|
||||
}
|
||||
|
||||
)
|
||||
self._test_instance('ec2-win2012r2-test', debug=True, timeout=800)
|
||||
self._test_instance('ec2-win2012r2-test', debug=True, timeout=TIMEOUT)
|
||||
|
||||
@expectedFailure
|
||||
def test_win2016_winexe(self):
|
||||
|
@ -273,7 +274,7 @@ class EC2Test(ShellCase):
|
|||
'win_installer': self.copy_file(self.INSTALLER),
|
||||
},
|
||||
)
|
||||
self._test_instance('ec2-win2016-test', debug=True, timeout=500)
|
||||
self._test_instance('ec2-win2016-test', debug=True, timeout=TIMEOUT)
|
||||
|
||||
@skipIf(not HAS_WINRM, 'Skip when winrm dependencies are missing')
|
||||
def test_win2016_winrm(self):
|
||||
|
@ -287,10 +288,11 @@ class EC2Test(ShellCase):
|
|||
'userdata_file': self.copy_file('windows-firewall.ps1'),
|
||||
'win_installer': self.copy_file(self.INSTALLER),
|
||||
'winrm_ssl_verify': False,
|
||||
'use_winrm': True,
|
||||
}
|
||||
|
||||
)
|
||||
self._test_instance('ec2-win2016-test', debug=True, timeout=800)
|
||||
self._test_instance('ec2-win2016-test', debug=True, timeout=TIMEOUT)
|
||||
|
||||
def tearDown(self):
|
||||
'''
|
||||
|
@ -301,4 +303,4 @@ class EC2Test(ShellCase):
|
|||
|
||||
# if test instance is still present, delete it
|
||||
if ret_str in query:
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=self.TIMEOUT)
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=TIMEOUT)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
ec2-test:
|
||||
provider: ec2-config
|
||||
image: ami-98aa1cf0
|
||||
size: t1.micro
|
||||
size: m1.large
|
||||
sh_username: ec2-user
|
||||
script_args: '-P -Z'
|
||||
ec2-win2012r2-test:
|
||||
provider: ec2-config
|
||||
size: m1.small
|
||||
size: m1.large
|
||||
image: ami-eb1ecd96
|
||||
smb_port: 445
|
||||
win_installer: ''
|
||||
|
@ -19,7 +19,7 @@ ec2-win2012r2-test:
|
|||
deploy: True
|
||||
ec2-win2016-test:
|
||||
provider: ec2-config
|
||||
size: m1.small
|
||||
size: m1.large
|
||||
image: ami-ed14c790
|
||||
smb_port: 445
|
||||
win_installer: ''
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
linode-test:
|
||||
provider: linode-config
|
||||
size: Linode 2048
|
||||
size: Linode 2GB
|
||||
image: Ubuntu 14.04 LTS
|
||||
script_args: '-P -Z'
|
||||
|
|
|
@ -118,7 +118,7 @@ class ServiceModuleTest(ModuleCase):
|
|||
systemd = salt.utils.systemd.booted()
|
||||
|
||||
# check service was not enabled
|
||||
if systemd:
|
||||
if systemd or salt.utils.is_windows():
|
||||
self.assertIn('ERROR', enable)
|
||||
else:
|
||||
self.assertFalse(enable)
|
||||
|
|
26
tests/integration/modules/test_win_servermanager.py
Normal file
26
tests/integration/modules/test_win_servermanager.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.case import ModuleCase
|
||||
from tests.support.unit import skipIf
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
|
||||
|
||||
@skipIf(not salt.utils.is_windows(), 'windows test only')
|
||||
class WinServermanagerTest(ModuleCase):
|
||||
'''
|
||||
Test for salt.modules.win_servermanager
|
||||
'''
|
||||
def test_list_available(self):
|
||||
'''
|
||||
Test list available features to install
|
||||
'''
|
||||
cmd = self.run_function('win_servermanager.list_available')
|
||||
self.assertIn('DNS', cmd)
|
||||
self.assertIn('NetworkController', cmd)
|
||||
self.assertIn('RemoteAccess', cmd)
|
|
@ -220,6 +220,7 @@ class CMDMODTestCase(TestCase, LoaderModuleMockMixin):
|
|||
self.assertRaises(CommandExecutionError, cmdmod._run, 'foo')
|
||||
|
||||
@skipIf(salt.utils.is_windows(), 'Do not run on Windows')
|
||||
@skipIf(True, 'Test breaks unittests runs')
|
||||
def test_run(self):
|
||||
'''
|
||||
Tests end result when a command is not found
|
||||
|
|
Loading…
Add table
Reference in a new issue