 Good morning, you all. My name is Moises. I'm a software developer from Brazil. Last year, I relocated to Czech Republic to work at Red Hat. And I've been toying with configuration files through the last year, basically. So I took what I have learned last year and what I've been toying with to show it to you. So configuration, it's kind of what we need to do to our software so it can behave differently. We don't need always a hello world that will execute it and it will always print hello world. In different situations, we might need our software or service to behave in a different way. So that's why we need to provide configuration to our software. In the beginning, we're used to read values and output values when we start learning how to program. So basically, we toy with the standard input and standard output. So we can, through the standard input, provide some information and control the behavior of the software. But as a service, we might need it to get that configuration before. We also have the standard error, like a different way out to print stuff, to return to the console. And then the first thing we have to toy with configuration in our applications is the command line arguments. So in Python, we have arg parts, which is included in the standard library. And you can do arg parts. You basically have to define your parser. So I can define my program here to process some integers. I'm adding one argument called integers, which I can call it n later. And the type is int, and I can have more than one number provided. A helper string like integer for the accumulator and another argument, minus, minus sum, which is the destination is this accumulate. And the action, I will store this function sum. But its default value is max. So I have a program that process some integers here. And it should sum these integers if I provide minus, minus sum. Otherwise, it just find the maximum number. So I can parse my args. This will process all the args I've provided to the command line. And I can just print here args.accumulate, which is with my HP, the max function or the sum function. And I will accumulate the integers past here. So I will get the list of numbers as I have a plus arg here. So I'm in my console. I can type minus h in my program. And I will get usage of my program. I have a minus h for this helper. I have a minus, minus sum, which is optional. And I have n or more for my numbers. It processes some integers. n is a positional argument, which means it has a specific place to be in the command line to be processed as expected. And the optional arguments, I have the helper. And also the minus, minus sum, which is to change the behavior of our software. So it normally finds me the biggest number. But if I want to change its behavior to sum all the numbers, then I provide a minus, minus sum. And it will behave differently for me. So if I call my program with 1, 2, 3, 4, it will find me the biggest one, the 4. But if I provide a minus, minus sum, then I get a 10, which is the sum from 1, 2, 3, and 4. And if I try different values, which are not numbers, it crashes and says, yeah, the usage of the program. You're not using it right. The argument n is a type of int, so a is not an int. Then you get type checks on your arguments. So we've seen how to use arguments in the command line to change the behavior of a software. And ArcPars, it provides us with the command line arguments and very nice flexibility on those, and also type checking so far. Another way to change the behavior of software is through environment variables. And in Python, we can use OSEnvironment. So basically what I have to do is import OS and call OS.Environment. Let's say home, and it will print my home folder. Or I can try to print my homebrew GitHub API token. And it doesn't show up because the Unicode I put there didn't get to the PDF, sorry. It was a winky, not my token, actually. So it's nice to grab information out of the environment. But sometimes we might be exposing some information that is kind of sensitive. And we have to be careful about that. And what is in our environment can also be dependent on how we put that value in environment variables. It might be accessed by other applications. The third type is through configuration files. It's nice to have the flexibility to use the command line so you can very fast change how you're using some software. And then sometimes when you're in the development process, you're not yet have your configuration fixed. So you kind of toying, and then you're always changing, need a specific behavior. So you're not using it all the same. But sometimes configuration files, they can have all that specific configuration that we know that we need for a specific case. And it's not going to change. Sometimes in the command line, we do some typos and then have to call the command again. And with config files, you have all your configuration set, and you know that it works, and it will not break when you try to run it. So Python gives us config parser. And this is an example configuration file. I have it is in the INI format. It's composed by sessions. So I have a session called default, and I have another session called bit bucket and top secret server. These values in the default session, they are shared with the other sessions. So if I try to access bitbuck.org compression, if it doesn't exist here, it will fall back to the default group. So to use config parser, I have to load the file. So I create my config parser, and then I feed it with my INI file with the configuration. So now I can access, I can see the sections. So I had two sections other than the default one. And if I try to see bit bucket user, it will get me the user. And config default compression, what is in the default. But I can also try to see what is in config bit bucket, like everything. So if I do this, I will see that it merges what is stored in bit bucket, but also what was stored in the default group. And then if I try, then I can also get things from the top secret server, which he had this option to know. But this one comes from the default. It could get a no from the default, yes, from the default if it wasn't all there. But then as it subscribes the value, then we can have it different here. Well, in the arg pars, I had my parser. And I could just dot in the name of the argument I was expecting. And with config pars, I had kind of like a dict. So I had all to be like brackets, and quotes, and everything. And so basically what I brought today to show it is a mix of arguments, environment, and config. But I still have some other stuff to mention about you can always have inputs as signals to your software. This is not covered by the Oslo config, which I'm using. But this is another way how you can interact with your software or application. And always you can also, as an output, you can have an exit code. So you can pipe. So you can create scripts with your software and see what it is like if it runs OK or not. So basically, this is all the input you can get. And this is basically the output you can use. Of course, that might be like you can interact with other ways like networking. But this is kind of the basics. If I had this in my first semester or first year, I would be very glad with my professor. So in the last year at OpenStack, I've been working in a project called Oslo config, which handles configuration files. And the Oslo is a project inside OpenStack, which means OpenStack common libraries. So you don't actually need to be running OpenStack to use these guys. They're kind of like built with just over-pytoned itself. And then it doesn't have dependencies to other OpenStack projects. So Oslo config, at the beginning, it works with configuration files. But also, you can have arguments, which will have priority over the configuration files. So you can have your config in the configuration files. And if you need any change, then you can just overload them with the arguments. And also, in the last semester of last year, it was merged a driver. So now we can also get values from the environment. So we have the configuration files. And they can be overloaded by environment variables. And then you can provide arguments on the common line. And much more, we have these new drivers in Oslo config. So you can create your own driver to how you can fetch configuration data. Lately, I've been working on two drivers, one driver to fetch configuration data through the network. So you can have in your configuration file keys to a server. So you can have an HTTPS connection to the server. So you can fetch the configuration and then use that configuration. The second driver I was working in, it's a Castelon driver for this Castelon library in also in OpenStack Oslo. And using it, you can store configuration data in, let's say, hashCoreVault or Barbican, which is a secret manager for OpenStack. In Oslo config, we have the basic types, strings, Booleans, integers, flows, lists, and dicts. So we can do the same type checks that ArcPars does. So then we have this that is now extensible as well to the configuration files. As config parser, it only gives me a dictionary. It doesn't do any validation on the types. Also, very common in OpenStack are the networking types. So we have also validations to this kind of data. And now I will show an example. So from Oslo config, I'm importing my config module. And I have some common options and the greeting options. So in my example, I will build a hello world, but it will be configurable in different ways. So I have the name, which is the name to greet. The default is world. So it will print hello world for me at first. And the greetings, the default one is hello. It's the greeting to be used. I can just say, I can change the greeting to high and say high world. Or I can change to another language. And also, I add another option here, like times. So how many times I will repeat the greeting? So if I say times equal five, it will say hello world, hello world, hello world, hello world, hello world. In my main function, I create my config object. And then here I can also have some kind of control. I'm registering as a CLI option my common ops, so which is the name to be greeted. Here I'm registering the greetings options, which was the hello, the greeting, and then the times here. I'm registering them differently. But I could just pass the whole list here with register ops with an S in the end, like here. The difference is this one, I'm exposing to the common line. And this one, it can only be fed by either environment variables and the configuration files. So this one will not be exposed as a common line option. Then here I have for a in range times just to say hello world many times. So I'm getting the times config. And then I'll print, formatting, the greeting, and the name, capitalize, and just my main boilerplate here. So here I have the helper. So Oslo config also provides me with the helper. So I get the usage of my program. I have all the positional arguments, optional arguments, and the greeting options, which was in a section. So it will expose me greeting times. But I didn't expose the greeting itself to be used. You can provide here a configuration file, a path to a configuration file to run your software, your program, or else a folder. And then it will read all the configuration files inside that folder. It also has default paths where it will look for the configuration files if you don't provide them through the common line. So here I have my hello world, basically. And it will print me, yeah, hello world. It's like his basic behavior. But I can change it through the common line, passing the name to be greet. And then I have hello Brussels. So I was able to change the behavior of the program using the common line. I can use the minus three argument, and it will print hello Brussels three times. And then I have this configuration file, which in the default section, I have the name Brussels, still. And the greeting now I'm changing to hello three times. So now if I call my program passing dash dash config file, my configuration file, it will now behave as what I told it to behave in the configuration file. So now I don't have to. If I put this file in, let's say, slash etc slash hello, it would read it automatically. But also I can have the configuration file and still change it using the common line. So the common line will take precedence. And I will have both the configuration coming from the configuration file and the behavior coming from the common line, both the times, how many times to greet, and the name to be greeted. So this way you can fine tune your configuration. Let's say you want to build a container with your application, but you don't want to put your configurations file inside a container. So you could just either run the container and mount the configuration file. You could run the container and load the configuration using environment variables. You could use a driver to fetch a configuration file from a remote server. You could use a driver to load sensitive configuration files, like database credentials, API tokens, to fetch them from services like from HashiCore Vault or other secret managers. And then you can also write your own driver to fetch the configuration the way you want. And that was it. I'm open for questions now. You can find the slides here. Thank you all. Questions? OK. I can't hear you. I still can't hear you. Oh, you're asking if I can use YAML or JSON. Well, not yet. I heard some rumors on that on the IRC channel. We are on free node. You can go there, hang out with us. But it's doable. Right now, we load the file and then feed it to any parser to mount some dictionary structure. So if you can replace that class and then provide a JSON reader or a YAML reader, and then you can do that. You can read the configuration out of any other format. Next question? OK. OK. The question is, if I can specify in my configuration file if some specific option cannot be overloaded. Is that right? OK. So far, what I've seen is that when you're doing the coding, you specify if you're exposing it to the command lines or not. So I don't know if you can then go to the configuration and shut that down. Yeah, basically, when you're registering your option, you either register it just to the configuration file or to the configuration file and also the command line, depending on which the way you register the option. Next question? Oh, OK. The environment variables, it works like magic. No, I'm kidding. It will fetch the, it will try to fetch the value from the environment after going to the configuration file. So first, it checks the command line if it's registered to the command line. And then we have a specific naming. So it's quite new, the driver, to fetch variables for the environment. But right now, we have some prefix to the environment variable that you can fetch it from there. If you go into the documentation, you will find that information. Or you can just email me and I'll point you to it. I haven't toyed with the environment variables that much, so I wasn't able to put it on the slides yet. Thanks. Next one? OK. Oh, OK. I was indexing them. Yeah. You can provide the whole list if the method, let's see. OK. The question is, if I can here, because I saw Greetings, Option 0, and then Greetings, Option 1, I can provide a whole list. Here, I'm providing a list. And then it's opts in the plural. But I can also register just a single option. I could just provide a list. The thing is, this one I'm registering just to the configuration file. But this one, I'm exposing it to the CLI option. It was just another way I was trying to show how you can use it. This is kind of the section it is in. So if I go back to the configuration file, oh no, it's here. This is what I'm trying to access. This is the 0. This is the configuration 0. And this is the configuration 1. This is the game accessing right here. So basically, this configuration file will be like this in the greeting in the session. OK, time's up. You can either talk to me outside if you have more questions, or contact me with my email or anything else. OK, thank you. Thank you all.