00:00:11.840
Thank you.
00:00:21.439
I'm still here to talk about configuration management software.
00:00:25.199
Hello, my name is Richard Crowley. I work for a company called Dev Structure.
00:00:27.660
We focus on configuration management software, primarily with a package called Puppet.
00:00:29.279
We built a tool called Blueprint that figures out what you did to your server so that we can generate Puppet code for you.
00:00:33.899
I'm here to conduct a survey over many sysadmin tools and try to understand why they are written in Ruby and what patterns we can extract from that.
00:00:46.980
We will start with Puppet, the subject I know most about, and tell a little story about Luke.
00:00:51.660
Luke was a sysadmin who was very dissatisfied with writing CFEngine installations all day every day. So, he set out to write a new configuration management system that was data-driven, relying less on diffing of files and applying patches, and more on managing whole resources.
00:01:03.480
Ruby ended up being chosen as the implementation language because Perl was already considered outdated, and Luke struggled to achieve the auto-loading behavior he wanted with Python.
00:01:24.420
The auto-loading behavior in Puppet is pervasive and works as follows: when you request a type or something that isn’t loaded, it gathers source files and figures out their location.
00:01:35.460
Each of those source files, as they are executed, uses an internal DSL to register code blocks, classes, or other constructs it creates.
00:01:58.320
So, this is how the package type is introduced to Puppet: without stating the obvious, you call a new type and provide it with a block that is evaluated in class scope, allowing it to define methods needed to perform its work.
00:02:08.160
The internal DSL exists because using constant missing wasn’t a viable option back in Ruby 1.8, especially for ensuring that if a type hadn’t been seen yet, it would find it. However, by using this DSL, we miss the context about what scope we're executing in.
00:02:31.020
It isn't very clear that a block with nothing else in it is where you can define methods, which is one of the drawbacks. It makes dealing with Puppet's internals difficult since one shouldn't need to consult documentation to see if you can define a method.
00:02:50.460
Also, because blocks are often defined at the top level, you can't always return from them, which can be frustrating for a programmer who enjoys short-circuiting out of methods.
00:03:03.900
Here are a couple of Puppet resources in Puppet syntax. Puppet is not just a Ruby-implemented DSL; it’s an actual language with grammar and a parser. For example, here are two resources: one for ensuring Ruby is installed and another for setting up 'etc/ctl.com'. These are orthogonal resources, meaning they have no relationship to each other.
00:03:40.440
When Puppet executes them, it autoloads the appropriate types and providers, determining the current state of the system versus the desired state. This best-effort nature means Puppet can manage the failures effectively when dealing with unrelated resources.
00:04:05.640
Puppet uses an extensive logging system accessed via various command-line options. If you encounter issues, verbose mode can provide stack traces in line with output without interrupting execution.
00:05:05.840
The logging system employs syslog terminology, allowing you to filter output from info-only warnings to debug messages or reduce it to just errors. This level of detail makes it relatively easy to identify and debug problems.
00:05:30.720
Now, I’d like to switch topics and discuss Chef, which many of you might be more familiar with. Chef is a system very similar to Puppet, and it has its own loyal followers; however, I'm not here to ignite that debate.
00:06:10.920
I am here to make a few comparisons: both of these packages focus on system administration tasks and express these tasks in code.
00:06:38.760
Chef, in contrast to Puppet, uses an external Ruby DSL. The Puppet resources I previously showed are implemented within the structure of Puppet instead of an external DSL.
00:06:56.520
Thus, when writing resources in Chef, you deal with actual Ruby code using real classes and methods, allowing clear definition of methods, constants, and namespaces.
00:07:11.100
This distinction has pros and cons; while the external DSL offers clarity in coding, it misses out on the auto-loading behavior seen in Puppet, leaving users to deal with potential issues when managing dependencies.
00:07:50.640
That said, there is merit in considering what is best for your project, whether you choose an external DSL or pure Ruby implementation. The internal DSL can often create complications that require one to understand someone else's design, making it less appealing.
00:08:30.240
Despite my preference for Puppet, I recognize that both tools have unique advantages and disadvantages. Despite the differences, both Chef and Puppet's goal is to codify systems administration, which is a task long tackled with Perl and shell scripts.
00:09:12.420
Everyone has heard that Ruby is a better Perl. Ruby's first-class regular expressions, backtick operators, and flexible syntax make for a language capable of almost any solution.
00:10:00.360
With Ruby gems acting much like CPAN, we also face challenges with release management, making it crucial for maintainers to be diligent about regression testing and clear versioning practices.
00:10:24.480
Slow and steady release cycles are beneficial, as hasty releases can lead to critical errors after deployment. It's essential that maintainers reflect on their testing processes and adjust their expectations for reliability.
00:10:58.500
Rake plays a crucial role in building Ruby gems, coupling an external DSL with an internal implementation, much like Chef. It exemplifies dependency management, working on Convention, ensuring that all tasks execute in proper order.
00:12:02.340
This programming model helps maintain the integrity of complex systems by defining goals and addressing failures effectively.
00:12:50.760
Capistrano adopts a similar dependency model but extends it to multiple hosts simultaneously while managing more intricate failure cases. In parallel execution, it provides clarity when diagnosing anomalous host behavior.
00:13:48.960
This functionality eases troubleshooting, especially in interactive systems administration tasks.
00:14:29.460
The MCollective package represents a significant evolution in systems administration tooling by enabling communication and job assignments across several servers, enhancing the scalability and maintainability of administrative tasks.
00:15:02.760
The framework is not a monolithic tool; rather, it consists of numerous smaller utilities like MC Ping and MC Find that handle specific tasks effectively.
00:15:53.280
The design encourages collaboration across different systems and allows the easy composition of tools to create more complex workflows that rely on previously established smaller, functional components.
00:16:32.880
Transitioning from simple scripts to scalable programs requires careful consideration of concerns like idempotence, ensuring commands can be reapplied without adverse effects.
00:17:16.080
This principle reflects good API design by favoring operations that can be safely repeated, which in terms of web application development, corresponds to using PUT methods instead of POST, focusing on a file's entire management.
00:18:04.800
Proper namespacing should also be a priority, as evident from MCollective using concise prefixed naming conventions to enhance clarity.
00:18:54.960
Moreover, Ruby offers ample facilities for managing file input/output, user permissions, and process management, enhancing capabilities for creating robust Unix applications.
00:19:38.640
The dynamic nature of Ruby allows constructing both data structures and system manipulation applications optimizing its effectiveness across diverse program scopes.
00:20:30.720
In working with Ruby, I advocate for declaring all file dependencies to facilitate ease of pull requests and maintenance, ensuring that components function independently without relying on complex imports.
00:21:21.600
This dedication to explicit dependency declaration makes code more maintainable and accessible, likening it to Python’s philosophy of clear naming and modular organization.
00:22:02.640
I encourage everyone to assess their gems and ensure that they are designed for easy usability, even if they are nested within a larger framework.
00:22:54.840
In conclusion, operable, maintainable code should remain accessible, clean, and straightforward, enabling developers to understand its workings.
00:23:37.680
It’s acceptable for code to look like code, demanding clear dependency declarations to minimize hidden complexities that may lead to issues of maintainability.
00:24:31.080
The prevalent patterns across the frameworks discussed emphasize the importance of external DSLs for clarity, while minimizing the downside of internal DSLs for maintainability.
00:25:33.300
Dependency programming encourages error handling with clear patterns that facilitate easy return to previous states in case of failures through effective logging and elevated item potency.
00:26:10.800
This focus on maintainability accounts for generic approaches that contribute stability, promoting tools that avoid special cases to ensure longevity and usability.
00:27:00.000
I'm here to listen to your questions.
00:27:27.720
I appreciate you sharing your thoughts regarding reusable libraries, noting the importance of modularizing large codebases into manageable, independent components.
00:28:28.560
If you have a large gem reliant on a massive loader, it’s prudent to separate unrelated functionalities into their own gems, fostering better management as these pieces evolve.
00:28:55.440
Thank you for your input.
00:29:00.840
Thank you!