[00:00.000 --> 00:11.640] Okay. Take a picture. So, good morning. My name is Daniel Mejado. I'm here today with [00:11.640 --> 00:15.760] my colleague Miguel. Here we are both principals of our engineers at Weihardt. I'm currently [00:15.760 --> 00:22.880] working in the Epsilon DTT team, basically monitoring so, and he's on the OpenShift virtualization [00:22.880 --> 00:28.680] team. I guess we just wanted to speak today a little bit about CNI, which stands for Container [00:28.680 --> 00:32.800] Network Interface, which this is basically all the networking in Kubernetes, but not [00:32.800 --> 00:37.760] only limited to that. Because I think one of the things that this project lacks the most [00:37.760 --> 00:42.000] is documentation and how does this work, how you create a plugin, what are the primitives. [00:42.000 --> 00:46.680] I think that's something that is super simple, but there's little to no documentation besides [00:46.680 --> 00:53.640] the spec. So, let's go watch through that. Yeah, this is a quick intro. So, you may have [00:53.640 --> 00:57.600] noticed and that we are going to be speaking about CNI plugins changed specifically. That [00:57.600 --> 01:02.640] means that we are going to be basically putting a couple of plugins in CNI mode. But first [01:02.640 --> 01:07.240] of all, CNI may stand for three different things and I want to be a little bit clear [01:07.240 --> 01:12.440] here. Like you're going to have the CNI specification itself, which is a document. It's fine. You [01:12.440 --> 01:17.360] can read it. Then you've got the plugins. So, if you go to GitHub, you can see the CNI [01:17.360 --> 01:22.880] plugins. This is a set of plugins basically maintained by the community. Bridge, Mac [01:22.880 --> 01:27.760] BLM, you name it. There's a couple of those. And the third thing, it may be just a couple [01:27.760 --> 01:32.880] of plugins. So, those are basically the three things that you may be interested into. We [01:32.880 --> 01:37.040] are going to be always speaking about the plugins here in this session, but we'll hopefully [01:37.040 --> 01:44.280] discuss anything after that. So, just in case, I mean, here we are going to be speaking about [01:44.280 --> 01:49.920] Kubernetes, but let me take a note that the CNI specification on the plugins, they do [01:49.920 --> 01:55.360] work totally without Kubernetes. So, with any runtime engine such as Rocket, and in fact, [01:55.360 --> 01:59.040] these are started out with Rocket. Well, I guess in case you started that out, you can [01:59.040 --> 02:04.160] be running this out with cryo, Rocket, whatever you want to play with this. So, what is the [02:04.160 --> 02:10.440] CNI plugin specifically? CNI plugins are binaries, which are basically copied over any of the [02:10.440 --> 02:17.560] hosts that are in your Kubernetes clusters. So, if you want to install this out, you probably [02:17.560 --> 02:25.200] need a demo set to deploy it on or do that manually. So, here, who runs this CNI plugins? [02:25.200 --> 02:30.240] You could either run that yourself, but usually, you would need to have the kubelet run those [02:30.240 --> 02:36.080] for you. Is there anybody here who doesn't know what a kubelet is? Okay. The kubelet, [02:36.080 --> 02:43.800] oh, you fuck, anyway. They were trying me out. So, anyways, I'll go ahead. So, basically, [02:43.800 --> 02:47.920] when you go and have the kubelet, which is a mystery thing that you don't know what it's [02:47.920 --> 02:53.400] about, runs the binary here in the host, it basically creates a network name and space, [02:53.400 --> 02:57.920] which is tied to a BF interface. As this guy doesn't know what a BF interface is, I'm [02:57.920 --> 03:04.680] going to be basically speaking out for him. So, it's a B12 point-to-point tunnel, so you [03:04.680 --> 03:09.520] can basically hook this up into whatever you want to. Like, usually, these are connected [03:09.520 --> 03:17.880] to OVN, OpenB streets, or whatever, B12 streets, or it depends on the CNI plugin implementation. [03:17.880 --> 03:23.200] How do I configure the thing? These are used, like, configured by a configuration file. [03:23.200 --> 03:28.440] We're going to be speaking later on a little bit about how this changes. But, yes, FYI, [03:28.440 --> 03:35.040] and Miguel will be speaking later on about this more. Prior to the CNI specification 0.3.0, [03:35.040 --> 03:40.840] you weren't able to, you know, put your plugins in CL mode. It would just break. So, you would [03:40.840 --> 03:46.680] need to know, basically, there's a couple of CNI components that you need to put. Basically, [03:46.680 --> 03:51.160] the name of your CNI plugin, the type, and the type should match the name of your binary [03:51.160 --> 03:57.360] or it would probably break afterwards. Then, later on, you need also some mbars, which [03:57.360 --> 04:02.720] basically are specifically about telling you, like, okay, it can switch both the way around [04:02.720 --> 04:08.800] this, you see, because it may be a part of whatever. If you put all this out, and, again, [04:08.800 --> 04:14.600] recall, a CNI plugin, it's a binary, so you just run it as any binary you would run, goes [04:14.600 --> 04:20.800] to your CNI plugin, and then you can exit code. If you did your things right, like that, [04:20.800 --> 04:25.680] this should be getting you an exit code of zero, not error, and then you would be getting [04:25.680 --> 04:31.000] your standard out here, which is just JSON. Why JSON? And why not using the RPC and the [04:31.000 --> 04:35.360] daemon, like everything in Kubernetes, you may ask? Because, again, this wasn't meant [04:35.360 --> 04:42.000] to be used on a lot of Kubernetes, and if you get to stay at the more networking session, [04:42.000 --> 04:46.720] they'll probably be speaking about some point using YAML for that, too. But here, for the [04:46.720 --> 04:53.200] sake of this session, what you would need to remember here is this prep results, basically, [04:53.200 --> 04:58.320] and advancing some stuff later from my colleague. This is the previous results as per JSON [04:58.320 --> 05:04.600] of the plugin, and if any plugin gets a previous result here, it should output the next one, [05:04.600 --> 05:12.160] just in a serial mode way. I was speaking about the specification. What the hell is [05:12.160 --> 05:20.240] that? So, yes, if you're implementing your plugin, you need to make sure that it observes [05:20.240 --> 05:26.680] several primitives, which is add, del, check, and version. So, for instance, you may want [05:26.680 --> 05:33.360] to add, okay, I want to add a pod. I want to delete a pod network interface. A second [05:33.360 --> 05:39.080] version, to be honest, and even if the specification really requests you to, you know, honor this [05:39.080 --> 05:44.600] and observe this, they aren't really used by most of the commercial appliances. So, [05:44.600 --> 05:50.520] for instance, Celium, I guess they don't do any kind of check, maybe version, but those [05:50.520 --> 05:56.840] aren't really needed, and we'll be discussing that later on. So, this is what a coffee file [05:56.840 --> 06:04.040] usually looks like. So, this is nothing like a big deal. Super simple. CNA version, again, [06:04.040 --> 06:08.760] for example, I'm getting you backwards, like with the things that we were speaking before. [06:08.760 --> 06:14.720] This one would work with CNA plugin change, because it's 0.3.1. If you put 0.2.0, it would [06:14.720 --> 06:19.280] basically fail miserably telling you that he hates you, and it would crash. Then you [06:19.280 --> 06:24.600] got here the name, I don't care, but this type should match the name of your binary [06:24.600 --> 06:30.400] there later on. Then you can put out some plugin-specific things that are, for instance, [06:30.400 --> 06:35.800] this basically comes from the Brits plugin, which Miguel will be showcasing later on. [06:35.800 --> 06:39.840] So you can put here, okay, this, what do I need for a Brits? I need the name. You see [06:39.840 --> 06:46.320] the default gateway, I'm going to be forcing address later on. This is a little bit special, [06:46.320 --> 06:53.200] but I won't be discussing that because you'll be seeing that later on the conflict. Again, [06:53.200 --> 07:00.920] like, okay, so you told me about CNA plugins or binaries, and also I got Jameson's configurations. [07:00.920 --> 07:07.240] So where do I store these things out? So basically, when you define the cubular configuration, [07:07.240 --> 07:13.320] you tell the CNA binary path, and by CNA binary path, it's just a directory where I store [07:13.320 --> 07:18.480] all the plugins. Sadly, there's little to no login in most of the plugins, so if something [07:18.480 --> 07:24.360] breaks, you would have to go and see what's going on here. So you would just go, one of [07:24.360 --> 07:27.960] the things that you'd like to fail the most is just like you don't have the binary here [07:27.960 --> 07:31.840] installed, and it would fail and tell you that it hits again. And the conflict directory [07:31.840 --> 07:37.360] is here, and here's just a couple of Jameson who maps to the name of the binaries usually, [07:37.360 --> 07:44.440] and where do you put this specific plugin configs? Plug-in chains. Let's go for that. [07:44.440 --> 07:53.440] So that's the big deal. Again, available since CNA or CO2-0, those are required since the [07:53.440 --> 07:58.760] version 1, because now everything is the chain, even if it's just a single plugin, and everything [07:58.760 --> 08:04.080] is the conflict, again, even if it's just a single plugin. So if you just want to put [08:04.080 --> 08:10.920] the things in the old way, it would just break. I was also pointing out that now, let's assume [08:10.920 --> 08:14.920] that you've got a couple of plugins here, like, you've got the Bricks plugin, then you've [08:14.920 --> 08:22.560] got a Firewall plugin, which is one of the most used cases for this, and then, let's [08:22.560 --> 08:28.480] say, put a couple of plugins bound with Firewall because I like to, and I'm going to be putting [08:28.480 --> 08:37.240] another Macbillan interface here. So all these plugins are expected to honor, like, that [08:37.240 --> 08:41.320] they're going to be passing the prep results to each other. So even if they don't know [08:41.320 --> 08:45.000] anything about what comes before, because the Bricks plugin will know how to make a [08:45.000 --> 08:50.360] Bricks, but the Macbillan plugin won't. So everything comes through the plugins, all [08:50.360 --> 08:53.400] the info goes through using prep results, and we'll be showing you later on as in with [08:53.400 --> 09:01.680] the demo, if it doesn't fail. This is the config, this is not a config, this is not a [09:01.680 --> 09:06.480] config for CNI plugins, this is a config list. So if you see, this pretty much resembles [09:06.480 --> 09:12.520] what you had in the config file for a plugin before. So now we got CNI version, okay, I [09:12.520 --> 09:16.760] know I'm just saying this a lot, but it should be greater than 3.0, otherwise this would [09:16.760 --> 09:22.440] just break. This is your config list, and then you get, basically, a list of plugins. [09:22.440 --> 09:29.040] At one, we got a few of those, and those would be just working together. So in this case, [09:29.040 --> 09:33.800] we got a name, because all the plugins would need the name, it would need the type, which [09:33.800 --> 09:39.120] would match the binary, and then we got the specific configurations. So in the demo, we'll [09:39.120 --> 09:44.960] be showing you how these plugins get to link with the prep results and so forth. But if [09:44.960 --> 09:51.360] you see, it's just a list of plugins with name, type, and then specific configurations, [09:51.360 --> 09:57.480] but if you just want to do something, okay, I'm buying this, I want to create my own plugin, [09:57.480 --> 10:04.600] you really need a name and type, even if it doesn't do anything. Okay, go ahead, Miguel. [10:04.600 --> 10:09.040] Now, use cases, and then, I guess I spoke maybe a little bit too much, and even if we [10:09.040 --> 10:13.080] are late because we got a rush to the airport and that, but I'm handing over to Miko Miguel, [10:13.080 --> 10:17.920] who's going to be showing you this a little bit around, and then if it works again, demo. [10:17.920 --> 10:29.600] Go ahead, Miguel. Hello? Well, I'll use this. So this is like a list of the plugins that [10:29.600 --> 10:34.560] are provided by the CNI maintainers and are available on the container networking slash [10:34.560 --> 10:41.800] plugins repository. So the first is Tuning CNI. It allows you to configure like a list [10:41.800 --> 10:46.440] of syscuddles. So if you need some syscuddles to happen inside of your pod, you use this [10:46.440 --> 10:52.200] Tuning CNI. The bandwidth CNI, as the name implies, like quite evident, what it does [10:52.200 --> 10:57.800] is to throttle either the ingress or egress traffic to your pod, if you want to do those [10:57.800 --> 11:04.800] kind of things. The firewall plugin, what it does is only allow access to and from the [11:04.800 --> 11:10.400] IP addresses that are referenced in the results that the plugin got, for instance. And the [11:10.400 --> 11:17.200] port mapping, as the name kind of implies, it does port forwarding, configures port forwarding [11:17.200 --> 11:26.640] from the host into the container for the set of ports that you specify in the configuration. [11:26.640 --> 11:32.440] Now, having said this, let's go to the demo, and I'm now wondering how can I do this while [11:32.440 --> 11:52.080] holding the microphone. This doesn't work, I think. Easy. Okay, so the first thing that [11:52.080 --> 11:59.760] I should mention, like Daniel referenced Kubernetes a lot, but please remember that CNI is more [11:59.760 --> 12:08.760] like, like Kubernetes is a user of CNI. Like, it's not that you can use CNI by itself, and [12:08.760 --> 12:15.360] as such, we're going to be using something called CNI tool. It's just a binary that you [12:15.360 --> 12:23.480] point at your, at a certain CNI binary and give it like a set of environment variables, [12:23.480 --> 12:28.520] the plugins configuration via standard then, as Daniel said before, like, you basically [12:28.520 --> 12:33.560] pass the configuration of the plugins, you give it the input parameters, which are the [12:33.560 --> 12:39.680] environment variables, and you execute the binary. And this is how we're going to be [12:39.680 --> 12:47.920] seeing, well, the demo. You can follow it on this, on this link. But yeah, the first [12:47.920 --> 12:52.600] thing I think I should explain is like the scenario we're trying to achieve. And this [12:52.600 --> 12:59.040] can be seen here in the bottom right corner. So very simple thing. We just want to have [12:59.040 --> 13:05.840] like a bridge, two namespaces interconnected via this bridge, and we're going to be configuring [13:05.840 --> 13:11.960] a static IPM on static IP addresses on each of the namespaces. And then we'll run an [13:11.960 --> 13:20.200] IPer from the client to the server, and we're going to see how it fares. So first, I also [13:20.200 --> 13:26.560] need to show you the configuration that I'm using for this. Okay, it's this one. And this [13:26.560 --> 13:30.120] is the configuration that we're going to be using to achieve this scenario. Like, we're [13:30.120 --> 13:34.120] going to be using the plugin of type bridge. This is the name of the binary that will be [13:34.120 --> 13:41.160] invoked on the host to create the bridge. We get the name, we enable the IPs capability [13:41.160 --> 13:46.920] so you can put a static IP in it and you tell it that you want static IPM. And that's pretty [13:46.920 --> 13:55.600] much the configuration that you need to give it. So let's just run the example. Okay, like [13:55.600 --> 14:00.080] this parameter here that you see, can you see it? It's like the font big enough. It's [14:00.080 --> 14:05.160] good. Okay, thanks. Like, you have to give this like the name of the configuration if [14:05.160 --> 14:11.000] you see like unlimited bandwidth, it's the attribute here on the upper left corner that [14:11.000 --> 14:17.280] you see under name. It's the same thing. It must match. And now this configured the scenario [14:17.280 --> 14:22.320] and it's now running the IPer session between the both the client and the server. And as [14:22.320 --> 14:28.280] you see, we're getting like a very big bit rate of around 60 gigabits per second. So [14:28.280 --> 14:35.320] this is with a straight configuration. Now let's use a different configuration where [14:35.320 --> 14:42.640] we put we reuse the first configuration, but we use it in a chain. And afterwards, we put [14:42.640 --> 14:49.200] like the bandwidth plugin. Again, what this does, it will throttle the input traffic into [14:49.200 --> 14:57.400] the network interface of both namespaces. So we're going to be doing the exact same thing. [14:57.400 --> 15:06.200] But with this added with this different configuration and bandwidth limiter. And as we see, like [15:06.200 --> 15:11.920] the bit rate that we're getting is literally a lot less. And it should map somehow to the [15:11.920 --> 15:17.760] values that we've configured here. So what this shows is that you can use like this band [15:17.760 --> 15:22.520] with plug in a chain in order to achieve a different use case than you had before, like [15:22.520 --> 15:29.120] you want to throttle traffic to this, you use this type of plug in. Yeah, I think that's [15:29.120 --> 15:33.960] let me just check the time. Yeah, we're good. We still have 10 minutes. So let's run the [15:33.960 --> 15:44.680] second. The second demo we had, we have actually sorry, okay, the scenario is a lot simpler [15:44.680 --> 15:49.040] this time, like we just have the same bridge as before, but we just have like a network [15:49.040 --> 15:56.560] namespace connected to it. And what we're going to be doing is showing you how the chain [15:56.560 --> 16:04.320] actually works, focusing on the on what Daniel said before, like you need to handle always [16:04.320 --> 16:08.200] the previous result, and you need to account for it. And you need to pass it along the [16:08.200 --> 16:18.360] chain continuously. And okay, let's just show the configuration of this plugin. So this [16:18.360 --> 16:23.160] chain might look a lot more complex than the one before because it has more things in it, [16:23.160 --> 16:27.080] but it's very, very, very simple. So this thing first will in it will call the bridge [16:27.080 --> 16:33.520] plugin, it will create the bridge. Then we'll invoke the debug CNI, like this CNI plugin [16:33.520 --> 16:39.000] is very, very simple. The only thing it does is print the result it got from the previous [16:39.000 --> 16:43.040] plugin. So what we're going to be seeing is here is the result of the first plugin in [16:43.040 --> 16:49.600] the chain. Afterwards, we run the tuning CNI plugin. And what we're going to do is to change [16:49.600 --> 16:55.120] the MAC address that we got on the interface of this dummy namespace that we see here. [16:55.120 --> 17:00.360] So the idea is we first run the bridge plugin. This thing will assign a random MAC address [17:00.360 --> 17:07.440] to the interface that is on this namespace. We'll print that will run tuning to change [17:07.440 --> 17:11.800] that MAC address. And we're going to print that again to see like the result of the previous [17:11.800 --> 17:22.040] plugin. And that's pretty much it. Let's just run the example and show you the, what I actually [17:22.040 --> 17:34.760] mean. So here we see like, and so this log here is the result of the, of the first call [17:34.760 --> 17:43.000] of the debug CNI. And as we see in the, in the pods, in what would be the pod interface [17:43.000 --> 17:47.800] which is identified by this attribute sandbox that points to the name of the namespace or [17:47.800 --> 17:52.480] actually to the path of the namespace. We see that in its result, we have the interface [17:52.480 --> 17:58.200] name that was created on that network namespace. And we see that it was assigned like a random [17:58.200 --> 18:04.360] MAC address that is identified here. We then run the tuning plugin that changed this MAC [18:04.360 --> 18:10.480] address and we finally printed the previous result again. And we see that this changed [18:10.480 --> 18:18.400] to the MAC address that we specifically specified statically in the, in the plugin configuration, [18:18.400 --> 18:24.560] which is exactly what we wanted to show. Like that's like very simple demo, but I think [18:24.560 --> 18:31.520] it kind of illustrates in a very neat way how chaining actually works in a step-by-step [18:31.520 --> 18:43.720] manner. And with this, we arrived at the conclusions. And I'd like to basically tell you again [18:43.720 --> 18:49.120] what we just told you, like focusing on the most important things that we think are about [18:49.120 --> 18:55.640] this slide. First thing is like these plugins, remember what they do. They add more stuff [18:55.640 --> 19:02.080] to the pod. So they enable different use cases. Like they can, you can prevent IP spoofing. [19:02.080 --> 19:06.200] You can throttle bandwidth as we've seen. You can configure port forwarding from the [19:06.200 --> 19:12.760] host to different containers. You can configure different syscuttles. And actually you can [19:12.760 --> 19:22.080] also create an allow list of the syscuttles that you can use inside of the pod. And finally [19:22.080 --> 19:30.680] I think like if you have to, to keep one thing from this presentation is that a meta-plugin [19:30.680 --> 19:37.360] must always handle the result of the previous plugin. Like you need to account for it. First [19:37.360 --> 19:41.600] of all, because you don't know if, if you're a plugin, you don't know if anything will [19:41.600 --> 19:45.280] run after you in a chain. Like the user will configure it. So you don't know what's going [19:45.280 --> 19:49.280] to happen afterwards. So you need to send a result. And if you're somewhere in the middle [19:49.280 --> 19:54.800] of the chain, least you can do, or least you must do is grab the result you got from the [19:54.800 --> 20:05.240] previous one and echo it into the next one. Now finally, remember that two things, plugin [20:05.240 --> 20:13.560] chains are only allowed starting from CNI 0.3. And they're the only configuration type [20:13.560 --> 20:20.240] allowed starting from CNI version 1.0. Like if you use the first example, configuration [20:20.240 --> 20:26.840] example we shown on CNI 1.0, it wouldn't work. It will explode, it will fail, make you miserable. [20:26.840 --> 20:33.120] And like the idea here is know your previous result always because that's probably the [20:33.120 --> 20:39.560] most information you'll get from anything that ran before you. Like as Daniel said, [20:39.560 --> 20:44.960] everything is clearly not the thing that's most prevalent on, at least on the CNI, on [20:44.960 --> 20:52.800] the plugins that are maintained by the CNI maintainers. And yeah, this concludes our, [20:52.800 --> 21:08.000] our talk. And so thanks a lot guys. Questions? Questions? Thank you. Thank you. Can you tell [21:08.000 --> 21:12.200] a little more about use cases of CNI without Kubernetes? [21:12.200 --> 21:17.520] Okay, that's a really good question. And it's going to probably eat all the time that we [21:17.520 --> 21:24.520] have. Like I can, like I work for Qvert, like, and one thing we do in it is, so there's a [21:24.520 --> 21:31.360] pod. And inside the pod, you run a virtual machine in it. Now, CNI, what it does is configure [21:31.360 --> 21:36.280] the pod interface. But what you want to have is like networking inside of VM, like you [21:36.280 --> 21:41.640] need some way to get like the extended connectivity from the pod, from the pod interface and into [21:41.640 --> 21:46.880] the VM, you need to have something there. And we have code in our code base in the Qvert [21:46.880 --> 21:52.720] code base to achieve this, like one of the thing we could do using CNI chains to offload [21:52.720 --> 21:57.960] that entire code to CNI plugins that would create, for instance, the bridge that you [21:57.960 --> 22:03.240] have inside of the pod to extend that connectivity that would create the tap device from which [22:03.240 --> 22:09.480] the VM would create the emulated network device from. So I really think this could be [22:09.480 --> 22:17.840] moduled using CNI. That's something we still need to see, like it's a very rough idea yet, [22:17.840 --> 22:19.920] but it's an example, I think. [22:19.920 --> 22:23.500] And so besides that, I guess the quickest one is, yes, you know, he was mentioning CNI [22:23.500 --> 22:28.160] tool, CNI tool is just used to develop CNI plugins. It doesn't have Kubernetes at all. [22:28.160 --> 22:33.560] And you can see that the plugins have just run there. Even that, Rocket, so Rocket, it [22:33.560 --> 22:37.920] was on its own. He didn't have to use Kubernetes at all. And it's where most of the CNI plugins [22:37.920 --> 22:41.920] were originally developed. You just put any kind of, you know, runtime engine, and it [22:41.920 --> 22:49.080] would work. No Kubernetes needed. [22:49.080 --> 22:54.080] So if I have a CNI plugin that sets up some external state, for example, a firewall that [22:54.080 --> 22:58.720] might even be a separate device, and something goes wrong, and I lose the delete, how do [22:58.720 --> 23:00.400] I recover from that? [23:00.400 --> 23:07.320] So that's an amazing question. Like you need all, like you must design everything assuming [23:07.320 --> 23:13.080] that CNI deletes will be missed. Like it might happen like all the time. So you need [23:13.080 --> 23:20.240] to have a reconciled loops of sorts that knows about your state, relevant state, and reconciles [23:20.240 --> 23:21.240] it somehow. [23:21.240 --> 23:27.120] I didn't see a way to even check if the stage should still be active. How does my CNI plugin [23:27.120 --> 23:29.320] know that something is still there? [23:29.320 --> 23:40.920] It's clueless. Like you need to monitor this little kid and assume that he will not fall [23:40.920 --> 23:48.080] and hit the head in the corner of the table. You need to do that out of bed. It's not designed [23:48.080 --> 23:51.600] to allow for that. I'm sorry. [23:51.600 --> 23:59.160] Hi. During the presentation you mentioned that some CNI plugin like Cilium, but I guess [23:59.160 --> 24:03.560] that you were also mentioning other plugins, they're not doing the logins and they're not [24:03.560 --> 24:05.760] using the entire APIs. How come? [24:05.760 --> 24:13.640] I guess you hear it. It's okay? So it kind of depends on your CNI plugin presentation. [24:13.640 --> 24:19.160] So for instance, some CNI do implement logging, but it's not something that is within implementation [24:19.160 --> 24:23.440] itself at all. So you may be totally fine in doing no logging at all, but then go back [24:23.440 --> 24:27.440] to debugging it so you can have to go to the queue with loops, you can have to go to cry [24:27.440 --> 24:34.240] over loops, and then go back to debugging all those. So it depends on your implementation. [24:34.240 --> 24:39.800] For instance, that, which is here, he implemented some of his logins for enamel, but that's [24:39.800 --> 24:44.600] not something that is on every plugin. For service, not in any of the community maintained [24:44.600 --> 24:45.600] ones. [24:45.600 --> 24:46.600] Okay, thanks. [24:46.600 --> 24:47.600] Any more questions? [24:47.600 --> 25:10.600] Well, thanks a lot for your time and for listening to us and bye-bye. Enjoy your time.