Roberto Polli, Solutions Architect 18.04.2015
Test Driven Deployment with
(i)python and nosetest
Agenda
• Who? What? Why?
• Who fears Continuous Delivery? • Test Driven Deployment
• Case Study: the LDAP infrastructure • Writing tests
• From test to setup
• Contributing to opensource
Who? What? Why?
• Roberto Polli - Solutions Architect @ par-tec.it. Loves writing in C, Java and Python. Red Hat Certified Engineer and Virtualization Administrator.
• Par-Tec – Proud sponsor of this talk ;) provides expertise in IT Infrastructure & Services and Business Intelligence solutions + Vertical Applications for the financial market. Contributes to various FLOSS.
• Implement a Test Driven Deployment strategy on big, legacy infrastructures: not an LDAP talk!
Who fears Continuous Delivery?
•
Legacy infrastructures:
–
Compliance to laws and regulations
–
"Old" software / libraries
–
resilient to new technologies ;)
•
Enterprise infrastructures
–
Slow release cycle
–
Eterogeneous environment
Who fears Continuous Delivery?
•
Legacy infrastructures:
–
Compliance to laws and regulations
–
"Old" software / libraries
–
resilient to new technologies ;)
•
Enterprise infrastructures
–
Slow release cycle
–
Eterogeneous environment
Who fears Continuous Delivery?
•
Why not add...
•
requiring...
•
and:
–
add external repositories →
Licenses, NOC
–
review upgrade policies SOC
→
–
modify network policies NOC
→
–
audit new services SOC
→
Who fears Continuous Delivery?
•
Switch to Continuous Delivery is hard:
–
customers are reluctant to distruptive changes;
–
operation teams must adapt to new technologies.
•
Use
Test Driven Deployment
and python:
–
testing is not disruptive;
–
ipython is easy to learn and use
–
shell compatibility
Test Driven Deployment
•
Write down infrastructural requirements
•
Create tests for each requirement
–
test configurations
–
test behavior
•
Setup/Update infrastructure
•
Run & Reuse tests for monitoring
Test Driven Deployment with python!
• Python is the way:
– already installed on all Linux servers
– PSL: batteries included (POP, SMTP, IMAP, HTTP, Telnet, ...)
– modules already in distribution repositories – further modules packaged as a zip file
– interactive
• TL;DR
– no external repositories – no policy changes
TDD Case Study: LDAP architecture
• Achievements
– eliminate maintenance issues
– simplify major upgrades
– identify issues and their effects on the platform – document with code
• Without reinventing the wheel!
– python-ldap in all linux distros
– reused bugfix script from 389 Team
• Small footprint:
– python already present on all nodes – further libs packed as one .zip file
TDD Case study: LDAP architecture
• How we applied TDD to this infrastructure:
– ~30 LDAP server
– replication and proxy topologies
TDD Strategy: Collect and Create
• Collect all LDAP-related platform requirements, eg:
– passwords expire periodically; – fine-grained user ACL;
– backend/frontend replication; – connectivity routes, ping, ...;
– …
• Create tests for every requirement
– failures explicits the symptoms on the platform eg. user can't change password
– human-readable configurations
Nose: Tests are easy
• Run every file and function starting with "test"
#nosetests v
• Filter test files and functions with -m :
#nosetests v test_ldap.py m "replication"
E
X
PL
IC
IT
N
A
M
ES
!
# test_ldap.py # def test_password_policy_enabled(): ... def test_password_policy_admin(): ... def test_password_policy_user():Writing tests: Document with code
• Describe the infrastructure with dicts, eg:
# # Specify slaves for each master # replication_t = { 'be5.foo.it': [ 'fe9.foo.it', 'fe10.foo.it'], 'be6.foo.it': [ 'fe9.foo.it', 'fe10.foo.it'], 'be7.foo.it': [ 'fe9.foo.it', 'fe10.foo.it'], ... }
Writing tests: for readability
• Keep test code simple!
• Parametrize your tests with Nose yield feature
def test_replica():
for master, slaves in replication_t.items(): for slave in slaves:
# generate a single test case
# for each masterslave agreement
yield has_replica, master, slave # nosetests v m replica
test_replica('be1.foo.it', 'fe9.foo.it') ... ok
def has_replica(master, slave):
Writing tests: explicit is better than implicit
• Be verbose with hostnames and features
• Goals
– clarity versus operation team – avoid this:
'be{hostid}.foo.it'.format(hostid=hostid)
– can use gethostname and getfqdn directly
backends_config = { 'be1.foo.it': [ {'name': 'userDb', 'cacheSize' : 1*GB, ..}, {'name': 'adminDb', 'cacheSize' : 1*MB, ..}, ], ..} backends_config = { '1': [ # name , cacheSize ['user', (1<<30)], ['admin', (1<<20)] ], .. }
Writing tests: for usability
• Developed the first tests, iteratively improving the ldap library.
• The code evolved:
1- infrastructure tests code moved to the library => remove duplicate code, improved UX
Writing test: for usability
• Operation team learnt python via auto-completion and test code.
• Code was focused for interactive use (eg. mnemonical names,
docstrings, ..) >>> from dsadmin import DSAdmin # verbose output via logs >>> logging.basicConfig(level=DEBUG) # connect to server using uris >>> cli = DSAdmin("ldaps://be1.foo.it") # get all databases on a server # easier than bash # ldapsearch … '(&(objectclass=nsBackendInstance)(..))' >>> cli.backend.list()
From test to setup
● Write the setup code: next deployments will go unattended!
• Use logging to produce an installation log.
• Reuse infrastructure tests as functional tests for the setup code. • Use the library with Ansible in new projects :D
def create_proxy_user(): ... def create_backends(): my_backends = backends[getfqdn()] for backend in my_backends: ...create_backend with the given attributes...
Contribute to open source
• The open source model gave us the opportunity to contribute, widespread python...
Contribute to open source
• Give new life to a great part of your code, moving part of the tests to unit/functional tests of the library
Wrap up
• Continuous Delivery is not welcome everywhere
• Test Driven Deployment in Python is a viable solution • Use iPython and Nose to introduce python to novices
• Write tests starting from requirements • Then Update/Deploy servers